updater.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. <?php
  2. /*
  3. Plugin Name: WordPress.com Theme Updates
  4. Description: Update themes downloaded from WordPress.com seamlessly with the rest of your WordPress updates.
  5. Author: Automattic
  6. Version: 1.0
  7. Author URI: http://automattic.com/
  8. License: GPL v2 or later
  9. Network: true
  10. This program is free software; you can redistribute it and/or modify
  11. it under the terms of the GNU General Public License as published by
  12. the Free Software Foundation; either version 2 of the License, or
  13. (at your option) any later version.
  14. This program is distributed in the hope that it will be useful,
  15. but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. GNU General Public License for more details.
  18. You should have received a copy of the GNU General Public License
  19. along with this program; if not, write to the Free Software
  20. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  21. */
  22. /**
  23. * Hooks to update themes from WordPress.com using the native WordPress upgrader
  24. * @package WordPress.com Updater
  25. * @version 1.0
  26. */
  27. if ( ! class_exists( 'WPCom_Theme_Updater') ) :
  28. class WPCom_Theme_Updater {
  29. const VERSION = '1.0';
  30. const PLUGIN_SLUG = 'wordpresscom-theme-updates';
  31. /**
  32. * Initialize the plugin by attaching the needed actions & filters.
  33. * @return void
  34. */
  35. public static function init() {
  36. add_filter( 'pre_set_site_transient_update_themes', array( __CLASS__, 'check_for_updates' ) );
  37. // only for MS users not running this as a plugin
  38. if ( is_multisite() && ! ( 0 === strpos( __FILE__, WP_PLUGIN_DIR ) || 0 === strpos( __FILE__, WPMU_PLUGIN_DIR ) ) ) {
  39. add_action( 'admin_notices', array( __CLASS__, 'maybe_nag_for_plugin' ) );
  40. add_action( 'wp_ajax_wpcom-theme-updates-hide-nag' , array( __CLASS__, 'hide_plugin_nag' ) );
  41. }
  42. }
  43. /**
  44. * Fires after WordPress core checks for updates. Go through the list, find any
  45. * WordPress.com themes, and check for updates on them as well.
  46. * @param object $update_data stdClass object containing theme update data
  47. * @return object A possibly modified object including relevant update data
  48. */
  49. public static function check_for_updates( $update_data ) {
  50. if ( ! is_object( $update_data ) || ! property_exists( $update_data, 'checked' ) || ! is_array( $update_data->checked ) )
  51. return $update_data;
  52. $wpcom_themes_to_check = array_filter( array_keys( $update_data->checked ), array( __CLASS__, 'is_wpcom_slug' ) );
  53. if ( empty( $wpcom_themes_to_check ) )
  54. return $update_data;
  55. $request_data = array();
  56. foreach ( $wpcom_themes_to_check as $theme_to_check ) {
  57. $request_data[] = array(
  58. 'theme' => $theme_to_check,
  59. 'version' => $update_data->checked[ $theme_to_check ]
  60. );
  61. }
  62. $user_agent = is_multisite() ? 'WordPress MS' : 'WordPress';
  63. $user_agent .= '/' . $GLOBALS['wp_version'] . '; ' . home_url();
  64. $options = array(
  65. 'timeout' => ( ( defined('DOING_CRON') && DOING_CRON ) ? 30 : 3 ),
  66. 'body' => json_encode( $request_data ),
  67. 'user-agent' => $user_agent,
  68. 'headers' => array( 'Content-Type' => 'application/json' )
  69. );
  70. $raw_response = wp_remote_post( 'http://public-api.wordpress.com/rest/v1/themes/update-check', $options );
  71. // something went wrong. leave it alone.
  72. if ( 200 !== wp_remote_retrieve_response_code( $raw_response ) )
  73. return $update_data;
  74. $response_data = json_decode( wp_remote_retrieve_body( $raw_response ), true );
  75. if ( ! $response_data || ! is_array( $response_data ) || ! isset( $response_data['updates'] ) )
  76. return $update_data;
  77. $update_data->response = array_merge( $update_data->response, $response_data['updates'] );
  78. return $update_data;
  79. }
  80. /**
  81. * Fire on admin_notices on a multisite network to try to get the updater plugin installed.
  82. * Will show admin notices and email the site admin.
  83. * Won't fire if this file is in a plugin directory.
  84. * @return void
  85. */
  86. public function maybe_nag_for_plugin() {
  87. // Show to super admins, email site admin when a non-super-admin views an admin page.
  88. if ( is_super_admin() )
  89. self::output_notice();
  90. else
  91. self::maybe_email_site_admin();
  92. }
  93. /**
  94. * Admin-ajax callback for hiding the admin nag shown to super admins
  95. * @return void
  96. */
  97. public function hide_plugin_nag() {
  98. if ( isset( $_REQUEST['nonce'] ) && wp_verify_nonce( $_REQUEST['nonce'], 'wpcom-theme-updates-hide-nag' ) ) {
  99. self::set_option( 'hide_admin_nag', true );
  100. }
  101. }
  102. /**
  103. * Emails the network site admin about installing the WP.com Theme Updater plugin. Will only email once.
  104. * @return void
  105. */
  106. public function maybe_email_site_admin() {
  107. if ( self::get_option( 'sent_email_nag' ) )
  108. return;
  109. $theme = wp_get_theme();
  110. $message = "The theme \"$theme->Name\" has recently been installed and activated on your Network.\n";
  111. $message .= 'To automatically receive updates for this WordPress.com theme, please install the WordPress.com Theme Updates plugin:' . "\n\n";
  112. $message .= 'http://wordpress.org/plugins/' . self::PLUGIN_SLUG . "\n\n";
  113. $message .= 'You will only receive this reminder once.';
  114. $subject = sprintf( '[%s] Theme Updates', get_site_option( 'site_name' ) );
  115. wp_mail( get_site_option( 'admin_email' ), $subject, $message );
  116. self::set_option( 'sent_email_nag', true );
  117. }
  118. /**
  119. * Prints the admin notice for installing the WP.com Theme Updater plugin.
  120. * @return void
  121. */
  122. protected function output_notice() {
  123. if ( self::get_option( 'hide_admin_nag' ) )
  124. return;
  125. $theme = wp_get_theme();
  126. $install_url = wp_nonce_url( network_admin_url( 'update.php?action=install-plugin&plugin=' . self::PLUGIN_SLUG ), 'install-plugin_' . self::PLUGIN_SLUG );
  127. $message = sprintf(
  128. __( 'Thanks for installing %1$s from WordPress.com!<br/>To keep this theme up-to-date on your Multisite network, please <a href="%2$s">install the WordPress.com Theme Updates plugin</a> and Network Activate it.', 'wordpresscom-theme-updates' ),
  129. $theme->Name,
  130. esc_url( $install_url )
  131. );
  132. $hide = __( 'Don&lsquo;t show me this.', 'wordpresscom-theme-updates' );
  133. $message .= " <a href='#' id='wpcom-hide-nag' class='hide-if-no-js'>{$hide}</a>";
  134. ?>
  135. <div class="updated" id="wpcom-nag"><p>
  136. <?php echo $message; ?>
  137. </p></div>
  138. <style>#wpcom-nag{overflow:hidden;}#wpcom-hide-nag{float:right;margin-left:1em;}</style>
  139. <script>
  140. jQuery && jQuery('#wpcom-hide-nag').on('click', function(e){
  141. e.preventDefault();
  142. jQuery.ajax( '<?php echo admin_url( 'admin-ajax.php' ) ?>', {
  143. data: {
  144. action: 'wpcom-theme-updates-hide-nag',
  145. nonce: '<?php echo wp_create_nonce( 'wpcom-theme-updates-hide-nag' ) ?>'
  146. }
  147. } );
  148. jQuery( '#wpcom-nag' ).slideUp();
  149. });
  150. </script>
  151. <?php
  152. }
  153. /**
  154. * Gets an option that is a member of our single site option.
  155. * @param string $key A member of our site option
  156. * @return mixed The key's value, false if not found.
  157. */
  158. protected function get_option( $key ) {
  159. $opt = get_site_option( self::PLUGIN_SLUG, array(
  160. 'hide_admin_nag' => false,
  161. 'sent_email_nag' => false
  162. ) );
  163. if ( isset( $opt[ $key ] ) )
  164. return $opt[ $key ];
  165. return false;
  166. }
  167. /**
  168. * Set/update an option as a member of our single site option.
  169. * @param string $key The key to set on our site option
  170. * @param string $value The value to set in $key
  171. */
  172. protected function set_option( $key, $value ) {
  173. $opt = get_site_option( self::PLUGIN_SLUG, array(
  174. 'hide_admin_nag' => false,
  175. 'sent_email_nag' => false
  176. ) );
  177. $opt[ $key ] = $value;
  178. update_site_option( self::PLUGIN_SLUG, $opt );
  179. }
  180. /**
  181. * Checks if a theme slug is a WordPress.com slug. Looks for `-wpcom` suffix.
  182. * @param string $slug WordPress theme id/slug
  183. * @return boolean True if it is a WordPress.com theme slug
  184. */
  185. public static function is_wpcom_slug( $slug ) {
  186. return preg_match( '/-wpcom$/', $slug );
  187. }
  188. }
  189. // Hook it up.
  190. add_action( 'after_setup_theme', array( 'WPCom_Theme_Updater', 'init' ) );
  191. endif; // if class_exists()