class-update-manager.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. <?php
  2. if ( ! class_exists( "Yoast_Update_Manager", false ) ) {
  3. class Yoast_Update_Manager {
  4. /**
  5. * @var Yoast_Product
  6. */
  7. protected $product;
  8. /**
  9. * @var Yoast_License_Manager
  10. */
  11. protected $license_manager;
  12. /**
  13. * @var string
  14. */
  15. protected $error_message = '';
  16. /**
  17. * @var object
  18. */
  19. protected $update_response = null;
  20. /**
  21. * @var string The transient name storing the API response
  22. */
  23. private $response_transient_key = '';
  24. /**
  25. * @var string The transient name that stores failed request tries
  26. */
  27. private $request_failed_transient_key = '';
  28. /**
  29. * Constructor
  30. *
  31. * @param Yoast_Product $product The product.
  32. * @param Yoast_License_Manager $license_manager The License Manager.
  33. */
  34. public function __construct( Yoast_Product $product, $license_manager ) {
  35. $this->product = $product;
  36. $this->license_manager = $license_manager;
  37. // generate transient names
  38. $this->response_transient_key = $this->product->get_transient_prefix() . '-update-response';
  39. $this->request_failed_transient_key = $this->product->get_transient_prefix() . '-update-request-failed';
  40. // maybe delete transient
  41. $this->maybe_delete_transients();
  42. }
  43. /**
  44. * Deletes the various transients
  45. * If we're on the update-core.php?force-check=1 page
  46. */
  47. private function maybe_delete_transients() {
  48. global $pagenow;
  49. if ( $pagenow === 'update-core.php' && isset( $_GET['force-check'] ) ) {
  50. delete_transient( $this->response_transient_key );
  51. delete_transient( $this->request_failed_transient_key );
  52. }
  53. }
  54. /**
  55. * If the update check returned a WP_Error, show it to the user
  56. */
  57. public function show_update_error() {
  58. if ( $this->error_message === '' ) {
  59. return;
  60. }
  61. ?>
  62. <div class="notice notice-error yoast-notice-error">
  63. <p><?php printf( __( '%s failed to check for updates because of the following error: <em>%s</em>', $this->product->get_text_domain() ), $this->product->get_item_name(), $this->error_message ); ?></p>
  64. </div>
  65. <?php
  66. }
  67. /**
  68. * Calls the API and, if successfull, returns the object delivered by the API.
  69. *
  70. * @uses get_bloginfo()
  71. * @uses wp_remote_post()
  72. * @uses is_wp_error()
  73. *
  74. * @return false||object
  75. */
  76. private function call_remote_api() {
  77. // only check if the failed transient is not set (or if it's expired)
  78. if ( get_transient( $this->request_failed_transient_key ) !== false ) {
  79. return false;
  80. }
  81. // start request process
  82. global $wp_version;
  83. // set a transient to prevent failed update checks on every page load
  84. // this transient will be removed if a request succeeds
  85. set_transient( $this->request_failed_transient_key, 'failed', 10800 );
  86. // setup api parameters
  87. $api_params = array(
  88. 'edd_action' => 'get_version',
  89. 'license' => $this->license_manager->get_license_key(),
  90. 'item_name' => $this->product->get_item_name(),
  91. 'wp_version' => $wp_version,
  92. 'item_version' => $this->product->get_version(),
  93. 'url' => $this->license_manager->get_url(),
  94. 'slug' => $this->product->get_slug(),
  95. );
  96. // Add product ID from product if it is implemented.
  97. if ( method_exists( $this->product, 'get_product_id' ) ) {
  98. $product_id = $this->product->get_product_id();
  99. if ( $product_id > 0 ) {
  100. $api_params['product_id'] = $this->product->get_product_id();
  101. }
  102. }
  103. // setup request parameters
  104. $request_params = array(
  105. 'method' => 'POST',
  106. 'body' => $api_params
  107. );
  108. require_once dirname( __FILE__ ) . '/class-api-request.php';
  109. $request = new Yoast_API_Request( $this->product->get_api_url(), $request_params );
  110. if ( $request->is_valid() !== true ) {
  111. // show error message
  112. $this->error_message = $request->get_error_message();
  113. add_action( 'admin_notices', array( $this, 'show_update_error' ) );
  114. return false;
  115. }
  116. // request succeeded, delete transient indicating a request failed
  117. delete_transient( $this->request_failed_transient_key );
  118. // decode response
  119. $response = $request->get_response();
  120. // check if response returned that a given site was inactive
  121. if ( isset( $response->license_check ) && ! empty( $response->license_check ) && $response->license_check != 'valid' ) {
  122. // deactivate local license
  123. $this->license_manager->set_license_status( 'invalid' );
  124. // show notice to let the user know we deactivated his/her license
  125. $this->error_message = __( "This site has not been activated properly on yoast.com and thus cannot check for future updates. Please activate your site with a valid license key.", $this->product->get_text_domain() );
  126. /**
  127. * Filter: 'yoast-show-license-notice' - Show the license notice.
  128. *
  129. * @api bool $show True if notices should be shown.
  130. */
  131. if ( apply_filters( 'yoast-show-license-notice', true ) ) {
  132. add_action( 'admin_notices', array( $this, 'show_update_error' ) );
  133. }
  134. }
  135. $response->sections = maybe_unserialize( $response->sections );
  136. // store response
  137. set_transient( $this->response_transient_key, $response, 10800 );
  138. return $response;
  139. }
  140. /**
  141. * Gets the remote product data (from the EDD API)
  142. *
  143. * - If it was previously fetched in the current requests, this gets it from the instance property
  144. * - Next, it tries the 3-hour transient
  145. * - Next, it calls the remote API and stores the result
  146. *
  147. * @return object
  148. */
  149. protected function get_remote_data() {
  150. // always use property if it's set
  151. if ( null !== $this->update_response ) {
  152. return $this->update_response;
  153. }
  154. // get cached remote data
  155. $data = $this->get_cached_remote_data();
  156. // if cache is empty or expired, call remote api
  157. if ( $data === false ) {
  158. $data = $this->call_remote_api();
  159. }
  160. $this->update_response = $data;
  161. return $data;
  162. }
  163. /**
  164. * Gets the remote product data from a 3-hour transient
  165. *
  166. * @return bool|mixed
  167. */
  168. private function get_cached_remote_data() {
  169. $data = get_transient( $this->response_transient_key );
  170. if ( $data ) {
  171. return $data;
  172. }
  173. return false;
  174. }
  175. }
  176. }