class.jetpack-display-posts-widget.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. <?php
  2. /*
  3. * Display a list of recent posts from a WordPress.com or Jetpack-enabled blog.
  4. */
  5. class Jetpack_Display_Posts_Widget extends Jetpack_Display_Posts_Widget__Base {
  6. /**
  7. * @var string Widget options key prefix.
  8. */
  9. public $widget_options_key_prefix = 'display_posts_site_data_';
  10. /**
  11. * @var string The name of the cron that will update widget data.
  12. */
  13. public static $cron_name = 'jetpack_display_posts_widget_cron_update';
  14. // DATA STORE
  15. /**
  16. * Gets blog data from the cache.
  17. *
  18. * @param string $site
  19. *
  20. * @return array|WP_Error
  21. */
  22. public function get_blog_data( $site ) {
  23. // load from cache, if nothing return an error
  24. $site_hash = $this->get_site_hash( $site );
  25. $cached_data = $this->wp_get_option( $this->widget_options_key_prefix . $site_hash );
  26. /**
  27. * If the cache is empty, return an empty_cache error.
  28. */
  29. if ( false === $cached_data ) {
  30. return new WP_Error(
  31. 'empty_cache',
  32. __( 'Information about this blog is currently being retrieved.', 'jetpack' )
  33. );
  34. }
  35. return $cached_data;
  36. }
  37. /**
  38. * Update a widget instance.
  39. *
  40. * @param string $site The site to fetch the latest data for.
  41. *
  42. * @return array - the new data
  43. */
  44. public function update_instance( $site ) {
  45. /**
  46. * Fetch current information for a site.
  47. */
  48. $site_hash = $this->get_site_hash( $site );
  49. $option_key = $this->widget_options_key_prefix . $site_hash;
  50. $instance_data = $this->wp_get_option( $option_key );
  51. /**
  52. * Fetch blog data and save it in $instance_data.
  53. */
  54. $new_data = $this->fetch_blog_data( $site, $instance_data );
  55. /**
  56. * If the option doesn't exist yet - create a new option
  57. */
  58. if ( false === $instance_data ) {
  59. $this->wp_add_option( $option_key, $new_data );
  60. }
  61. else {
  62. $this->wp_update_option( $option_key, $new_data );
  63. }
  64. return $new_data;
  65. }
  66. // WIDGET API
  67. public function update( $new_instance, $old_instance ) {
  68. $instance = parent::update( $new_instance, $old_instance );
  69. /**
  70. * Forcefully activate the update cron when saving widget instance.
  71. *
  72. * So we can be sure that it will be running later.
  73. */
  74. $this->activate_cron();
  75. return $instance;
  76. }
  77. // CRON
  78. /**
  79. * Activates widget update cron task.
  80. */
  81. public static function activate_cron() {
  82. if ( ! wp_next_scheduled( self::$cron_name ) ) {
  83. wp_schedule_event( time(), 'minutes_10', self::$cron_name );
  84. }
  85. }
  86. /**
  87. * Deactivates widget update cron task.
  88. *
  89. * This is a wrapper over the static method as it provides some syntactic sugar.
  90. */
  91. public function deactivate_cron() {
  92. self::deactivate_cron_static();
  93. }
  94. /**
  95. * Deactivates widget update cron task.
  96. */
  97. public static function deactivate_cron_static() {
  98. $next_scheduled_time = wp_next_scheduled( self::$cron_name );
  99. wp_unschedule_event( $next_scheduled_time, self::$cron_name );
  100. }
  101. /**
  102. * Checks if the update cron should be running and returns appropriate result.
  103. *
  104. * @return bool If the cron should be running or not.
  105. */
  106. public function should_cron_be_running() {
  107. /**
  108. * The cron doesn't need to run empty loops.
  109. */
  110. $widget_instances = $this->get_instances_sites();
  111. if ( empty( $widget_instances ) || ! is_array( $widget_instances ) ) {
  112. return false;
  113. }
  114. if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) {
  115. /**
  116. * If Jetpack is not active or in development mode, we don't want to update widget data.
  117. */
  118. if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
  119. return false;
  120. }
  121. /**
  122. * If Extra Sidebar Widgets module is not active, we don't need to update widget data.
  123. */
  124. if ( ! Jetpack::is_module_active( 'widgets' ) ) {
  125. return false;
  126. }
  127. }
  128. /**
  129. * If none of the above checks failed, then we definitely want to update widget data.
  130. */
  131. return true;
  132. }
  133. /**
  134. * Main cron code. Updates all instances of the widget.
  135. *
  136. * @return bool
  137. */
  138. public function cron_task() {
  139. /**
  140. * If the cron should not be running, disable it.
  141. */
  142. if ( false === $this->should_cron_be_running() ) {
  143. return true;
  144. }
  145. $instances_to_update = $this->get_instances_sites();
  146. /**
  147. * If no instances are found to be updated - stop.
  148. */
  149. if ( empty( $instances_to_update ) || ! is_array( $instances_to_update ) ) {
  150. return true;
  151. }
  152. foreach ( $instances_to_update as $site_url ) {
  153. $this->update_instance( $site_url );
  154. }
  155. return true;
  156. }
  157. /**
  158. * Get a list of unique sites from all instances of the widget.
  159. *
  160. * @return array|bool
  161. */
  162. public function get_instances_sites() {
  163. $widget_settings = $this->wp_get_option( 'widget_jetpack_display_posts_widget' );
  164. /**
  165. * If the widget still hasn't been added anywhere, the config will not be present.
  166. *
  167. * In such case we don't want to continue execution.
  168. */
  169. if ( false === $widget_settings || ! is_array( $widget_settings ) ) {
  170. return false;
  171. }
  172. $urls = array();
  173. foreach ( $widget_settings as $widget_instance_data ) {
  174. if ( isset( $widget_instance_data['url'] ) && ! empty( $widget_instance_data['url'] ) ) {
  175. $urls[] = $widget_instance_data['url'];
  176. }
  177. }
  178. /**
  179. * Make sure only unique URLs are returned.
  180. */
  181. $urls = array_unique( $urls );
  182. return $urls;
  183. }
  184. // MOCKABLES
  185. /**
  186. * This is just to make method mocks in the unit tests easier.
  187. *
  188. * @param string $param Option key to get
  189. *
  190. * @return mixed
  191. *
  192. * @codeCoverageIgnore
  193. */
  194. public function wp_get_option( $param ) {
  195. return get_option( $param );
  196. }
  197. /**
  198. * This is just to make method mocks in the unit tests easier.
  199. *
  200. * @param string $option_name Option name to be added
  201. * @param mixed $option_value Option value
  202. *
  203. * @return mixed
  204. *
  205. * @codeCoverageIgnore
  206. */
  207. public function wp_add_option( $option_name, $option_value ) {
  208. return add_option( $option_name, $option_value );
  209. }
  210. /**
  211. * This is just to make method mocks in the unit tests easier.
  212. *
  213. * @param string $option_name Option name to be updated
  214. * @param mixed $option_value Option value
  215. *
  216. * @return mixed
  217. *
  218. * @codeCoverageIgnore
  219. */
  220. public function wp_update_option( $option_name, $option_value ) {
  221. return update_option( $option_name, $option_value );
  222. }
  223. }