Theme_Upgrader

Core class used for upgrading/installing themes.

Defined (1)

The class is defined in the following location(s).

/wp-admin/includes/class-theme-upgrader.php  
  1. class Theme_Upgrader extends WP_Upgrader { 
  2.  
  3. /** 
  4. * Result of the theme upgrade offer. 
  5. * @since 2.8.0 
  6. * @access public 
  7. * @var array|WP_Error $result 
  8. * @see WP_Upgrader::$result 
  9. */ 
  10. public $result; 
  11.  
  12. /** 
  13. * Whether multiple themes are being upgraded/installed in bulk. 
  14. * @since 2.9.0 
  15. * @access public 
  16. * @var bool $bulk 
  17. */ 
  18. public $bulk = false; 
  19.  
  20. /** 
  21. * Initialize the upgrade strings. 
  22. * @since 2.8.0 
  23. * @access public 
  24. */ 
  25. public function upgrade_strings() { 
  26. $this->strings['up_to_date'] = __('The theme is at the latest version.'); 
  27. $this->strings['no_package'] = __('Update package not available.'); 
  28. $this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>…'); 
  29. $this->strings['unpack_package'] = __('Unpacking the update…'); 
  30. $this->strings['remove_old'] = __('Removing the old version of the theme…'); 
  31. $this->strings['remove_old_failed'] = __('Could not remove the old theme.'); 
  32. $this->strings['process_failed'] = __('Theme update failed.'); 
  33. $this->strings['process_success'] = __('Theme updated successfully.'); 
  34.  
  35. /** 
  36. * Initialize the install strings. 
  37. * @since 2.8.0 
  38. * @access public 
  39. */ 
  40. public function install_strings() { 
  41. $this->strings['no_package'] = __('Install package not available.'); 
  42. $this->strings['downloading_package'] = __('Downloading install package from <span class="code">%s</span>…'); 
  43. $this->strings['unpack_package'] = __('Unpacking the package…'); 
  44. $this->strings['installing_package'] = __('Installing the theme…'); 
  45. $this->strings['no_files'] = __('The theme contains no files.'); 
  46. $this->strings['process_failed'] = __('Theme install failed.'); 
  47. $this->strings['process_success'] = __('Theme installed successfully.'); 
  48. /** translators: 1: theme name, 2: version */ 
  49. $this->strings['process_success_specific'] = __('Successfully installed the theme <strong>%1$s %2$s</strong>.'); 
  50. $this->strings['parent_theme_search'] = __('This theme requires a parent theme. Checking if it is installed…'); 
  51. /** translators: 1: theme name, 2: version */ 
  52. $this->strings['parent_theme_prepare_install'] = __('Preparing to install <strong>%1$s %2$s</strong>…'); 
  53. /** translators: 1: theme name, 2: version */ 
  54. $this->strings['parent_theme_currently_installed'] = __('The parent theme, <strong>%1$s %2$s</strong>, is currently installed.'); 
  55. /** translators: 1: theme name, 2: version */ 
  56. $this->strings['parent_theme_install_success'] = __('Successfully installed the parent theme, <strong>%1$s %2$s</strong>.'); 
  57. $this->strings['parent_theme_not_found'] = __('<strong>The parent theme could not be found.</strong> You will need to install the parent theme, <strong>%s</strong>, before you can use this child theme.'); 
  58.  
  59. /** 
  60. * Check if a child theme is being installed and we need to install its parent. 
  61. * Hooked to the {@see 'upgrader_post_install'} filter by Theme_Upgrader::install(). 
  62. * @since 3.4.0 
  63. * @access public 
  64. * @param bool $install_result 
  65. * @param array $hook_extra 
  66. * @param array $child_result 
  67. * @return type 
  68. */ 
  69. public function check_parent_theme_filter( $install_result, $hook_extra, $child_result ) { 
  70. // Check to see if we need to install a parent theme 
  71. $theme_info = $this->theme_info(); 
  72.  
  73. if ( ! $theme_info->parent() ) 
  74. return $install_result; 
  75.  
  76. $this->skin->feedback( 'parent_theme_search' ); 
  77.  
  78. if ( ! $theme_info->parent()->errors() ) { 
  79. $this->skin->feedback( 'parent_theme_currently_installed', $theme_info->parent()->display('Name'), $theme_info->parent()->display('Version') ); 
  80. // We already have the theme, fall through. 
  81. return $install_result; 
  82.  
  83. // We don't have the parent theme, let's install it. 
  84. $api = themes_api('theme_information', array('slug' => $theme_info->get('Template'), 'fields' => array('sections' => false, 'tags' => false) ) ); //Save on a bit of bandwidth. 
  85.  
  86. if ( ! $api || is_wp_error($api) ) { 
  87. $this->skin->feedback( 'parent_theme_not_found', $theme_info->get('Template') ); 
  88. // Don't show activate or preview actions after install 
  89. add_filter('install_theme_complete_actions', array($this, 'hide_activate_preview_actions') ); 
  90. return $install_result; 
  91.  
  92. // Backup required data we're going to override: 
  93. $child_api = $this->skin->api; 
  94. $child_success_message = $this->strings['process_success']; 
  95.  
  96. // Override them 
  97. $this->skin->api = $api; 
  98. $this->strings['process_success_specific'] = $this->strings['parent_theme_install_success'];//, $api->name, $api->version); 
  99.  
  100. $this->skin->feedback('parent_theme_prepare_install', $api->name, $api->version); 
  101.  
  102. add_filter('install_theme_complete_actions', '__return_false', 999); // Don't show any actions after installing the theme. 
  103.  
  104. // Install the parent theme 
  105. $parent_result = $this->run( array( 
  106. 'package' => $api->download_link,  
  107. 'destination' => get_theme_root(),  
  108. 'clear_destination' => false, //Do not overwrite files. 
  109. 'clear_working' => true 
  110. ) ); 
  111.  
  112. if ( is_wp_error($parent_result) ) 
  113. add_filter('install_theme_complete_actions', array($this, 'hide_activate_preview_actions') ); 
  114.  
  115. // Start cleaning up after the parents installation 
  116. remove_filter('install_theme_complete_actions', '__return_false', 999); 
  117.  
  118. // Reset child's result and data 
  119. $this->result = $child_result; 
  120. $this->skin->api = $child_api; 
  121. $this->strings['process_success'] = $child_success_message; 
  122.  
  123. return $install_result; 
  124.  
  125. /** 
  126. * Don't display the activate and preview actions to the user. 
  127. * Hooked to the {@see 'install_theme_complete_actions'} filter by 
  128. * Theme_Upgrader::check_parent_theme_filter() when installing 
  129. * a child theme and installing the parent theme fails. 
  130. * @since 3.4.0 
  131. * @access public 
  132. * @param array $actions Preview actions. 
  133. * @return array 
  134. */ 
  135. public function hide_activate_preview_actions( $actions ) { 
  136. unset($actions['activate'], $actions['preview']); 
  137. return $actions; 
  138.  
  139. /** 
  140. * Install a theme package. 
  141. * @since 2.8.0 
  142. * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional. 
  143. * @access public 
  144. * @param string $package The full local path or URI of the package. 
  145. * @param array $args { 
  146. * Optional. Other arguments for installing a theme package. Default empty array. 
  147. * @type bool $clear_update_cache Whether to clear the updates cache if successful. 
  148. * Default true. 
  149. * } 
  150. * @return bool|WP_Error True if the install was successful, false or a WP_Error object otherwise. 
  151. */ 
  152. public function install( $package, $args = array() ) { 
  153.  
  154. $defaults = array( 
  155. 'clear_update_cache' => true,  
  156. ); 
  157. $parsed_args = wp_parse_args( $args, $defaults ); 
  158.  
  159. $this->init(); 
  160. $this->install_strings(); 
  161.  
  162. add_filter('upgrader_source_selection', array($this, 'check_package') ); 
  163. add_filter('upgrader_post_install', array($this, 'check_parent_theme_filter'), 10, 3); 
  164. // Clear cache so wp_update_themes() knows about the new theme. 
  165. add_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9, 0 ); 
  166.  
  167. $this->run( array( 
  168. 'package' => $package,  
  169. 'destination' => get_theme_root(),  
  170. 'clear_destination' => false, //Do not overwrite files. 
  171. 'clear_working' => true,  
  172. 'hook_extra' => array( 
  173. 'type' => 'theme',  
  174. 'action' => 'install',  
  175. ),  
  176. ) ); 
  177.  
  178. remove_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9 ); 
  179. remove_filter('upgrader_source_selection', array($this, 'check_package') ); 
  180. remove_filter('upgrader_post_install', array($this, 'check_parent_theme_filter')); 
  181.  
  182. if ( ! $this->result || is_wp_error($this->result) ) 
  183. return $this->result; 
  184.  
  185. // Refresh the Theme Update information 
  186. wp_clean_themes_cache( $parsed_args['clear_update_cache'] ); 
  187.  
  188. return true; 
  189.  
  190. /** 
  191. * Upgrade a theme. 
  192. * @since 2.8.0 
  193. * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional. 
  194. * @access public 
  195. * @param string $theme The theme slug. 
  196. * @param array $args { 
  197. * Optional. Other arguments for upgrading a theme. Default empty array. 
  198. * @type bool $clear_update_cache Whether to clear the update cache if successful. 
  199. * Default true. 
  200. * } 
  201. * @return bool|WP_Error True if the upgrade was successful, false or a WP_Error object otherwise. 
  202. */ 
  203. public function upgrade( $theme, $args = array() ) { 
  204.  
  205. $defaults = array( 
  206. 'clear_update_cache' => true,  
  207. ); 
  208. $parsed_args = wp_parse_args( $args, $defaults ); 
  209.  
  210. $this->init(); 
  211. $this->upgrade_strings(); 
  212.  
  213. // Is an update available? 
  214. $current = get_site_transient( 'update_themes' ); 
  215. if ( !isset( $current->response[ $theme ] ) ) { 
  216. $this->skin->before(); 
  217. $this->skin->set_result(false); 
  218. $this->skin->error( 'up_to_date' ); 
  219. $this->skin->after(); 
  220. return false; 
  221.  
  222. $r = $current->response[ $theme ]; 
  223.  
  224. add_filter('upgrader_pre_install', array($this, 'current_before'), 10, 2); 
  225. add_filter('upgrader_post_install', array($this, 'current_after'), 10, 2); 
  226. add_filter('upgrader_clear_destination', array($this, 'delete_old_theme'), 10, 4); 
  227. // Clear cache so wp_update_themes() knows about the new theme. 
  228. add_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9, 0 ); 
  229.  
  230. $this->run( array( 
  231. 'package' => $r['package'],  
  232. 'destination' => get_theme_root( $theme ),  
  233. 'clear_destination' => true,  
  234. 'clear_working' => true,  
  235. 'hook_extra' => array( 
  236. 'theme' => $theme,  
  237. 'type' => 'theme',  
  238. 'action' => 'update',  
  239. ),  
  240. ) ); 
  241.  
  242. remove_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9 ); 
  243. remove_filter('upgrader_pre_install', array($this, 'current_before')); 
  244. remove_filter('upgrader_post_install', array($this, 'current_after')); 
  245. remove_filter('upgrader_clear_destination', array($this, 'delete_old_theme')); 
  246.  
  247. if ( ! $this->result || is_wp_error($this->result) ) 
  248. return $this->result; 
  249.  
  250. wp_clean_themes_cache( $parsed_args['clear_update_cache'] ); 
  251.  
  252. return true; 
  253.  
  254. /** 
  255. * Upgrade several themes at once. 
  256. * @since 3.0.0 
  257. * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional. 
  258. * @access public 
  259. * @param array $themes The theme slugs. 
  260. * @param array $args { 
  261. * Optional. Other arguments for upgrading several themes at once. Default empty array. 
  262. * @type bool $clear_update_cache Whether to clear the update cache if successful. 
  263. * Default true. 
  264. * } 
  265. * @return array[]|false An array of results, or false if unable to connect to the filesystem. 
  266. */ 
  267. public function bulk_upgrade( $themes, $args = array() ) { 
  268.  
  269. $defaults = array( 
  270. 'clear_update_cache' => true,  
  271. ); 
  272. $parsed_args = wp_parse_args( $args, $defaults ); 
  273.  
  274. $this->init(); 
  275. $this->bulk = true; 
  276. $this->upgrade_strings(); 
  277.  
  278. $current = get_site_transient( 'update_themes' ); 
  279.  
  280. add_filter('upgrader_pre_install', array($this, 'current_before'), 10, 2); 
  281. add_filter('upgrader_post_install', array($this, 'current_after'), 10, 2); 
  282. add_filter('upgrader_clear_destination', array($this, 'delete_old_theme'), 10, 4); 
  283. // Clear cache so wp_update_themes() knows about the new theme. 
  284. add_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9, 0 ); 
  285.  
  286. $this->skin->header(); 
  287.  
  288. // Connect to the Filesystem first. 
  289. $res = $this->fs_connect( array(WP_CONTENT_DIR) ); 
  290. if ( ! $res ) { 
  291. $this->skin->footer(); 
  292. return false; 
  293.  
  294. $this->skin->bulk_header(); 
  295.  
  296. // Only start maintenance mode if: 
  297. // - running Multisite and there are one or more themes specified, OR 
  298. // - a theme with an update available is currently in use. 
  299. // @TODO: For multisite, maintenance mode should only kick in for individual sites if at all possible. 
  300. $maintenance = ( is_multisite() && ! empty( $themes ) ); 
  301. foreach ( $themes as $theme ) 
  302. $maintenance = $maintenance || $theme == get_stylesheet() || $theme == get_template(); 
  303. if ( $maintenance ) 
  304. $this->maintenance_mode(true); 
  305.  
  306. $results = array(); 
  307.  
  308. $this->update_count = count($themes); 
  309. $this->update_current = 0; 
  310. foreach ( $themes as $theme ) { 
  311. $this->update_current++; 
  312.  
  313. $this->skin->theme_info = $this->theme_info($theme); 
  314.  
  315. if ( !isset( $current->response[ $theme ] ) ) { 
  316. $this->skin->set_result(true); 
  317. $this->skin->before(); 
  318. $this->skin->feedback( 'up_to_date' ); 
  319. $this->skin->after(); 
  320. $results[$theme] = true; 
  321. continue; 
  322.  
  323. // Get the URL to the zip file 
  324. $r = $current->response[ $theme ]; 
  325.  
  326. $result = $this->run( array( 
  327. 'package' => $r['package'],  
  328. 'destination' => get_theme_root( $theme ),  
  329. 'clear_destination' => true,  
  330. 'clear_working' => true,  
  331. 'is_multi' => true,  
  332. 'hook_extra' => array( 
  333. 'theme' => $theme 
  334. ),  
  335. ) ); 
  336.  
  337. $results[$theme] = $this->result; 
  338.  
  339. // Prevent credentials auth screen from displaying multiple times 
  340. if ( false === $result ) 
  341. break; 
  342. } //end foreach $plugins 
  343.  
  344. $this->maintenance_mode(false); 
  345.  
  346. /** This action is documented in wp-admin/includes/class-wp-upgrader.php */ 
  347. do_action( 'upgrader_process_complete', $this, array( 
  348. 'action' => 'update',  
  349. 'type' => 'theme',  
  350. 'bulk' => true,  
  351. 'themes' => $themes,  
  352. ) ); 
  353.  
  354. $this->skin->bulk_footer(); 
  355.  
  356. $this->skin->footer(); 
  357.  
  358. // Cleanup our hooks, in case something else does a upgrade on this connection. 
  359. remove_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9 ); 
  360. remove_filter('upgrader_pre_install', array($this, 'current_before')); 
  361. remove_filter('upgrader_post_install', array($this, 'current_after')); 
  362. remove_filter('upgrader_clear_destination', array($this, 'delete_old_theme')); 
  363.  
  364. // Refresh the Theme Update information 
  365. wp_clean_themes_cache( $parsed_args['clear_update_cache'] ); 
  366.  
  367. return $results; 
  368.  
  369. /** 
  370. * Check that the package source contains a valid theme. 
  371. * Hooked to the {@see 'upgrader_source_selection'} filter by Theme_Upgrader::install(). 
  372. * It will return an error if the theme doesn't have style.css or index.php 
  373. * files. 
  374. * @since 3.3.0 
  375. * @access public 
  376. * @global WP_Filesystem_Base $wp_filesystem Subclass 
  377. * @param string $source The full path to the package source. 
  378. * @return string|WP_Error The source or a WP_Error. 
  379. */ 
  380. public function check_package( $source ) { 
  381. global $wp_filesystem; 
  382.  
  383. if ( is_wp_error($source) ) 
  384. return $source; 
  385.  
  386. // Check the folder contains a valid theme 
  387. $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit(WP_CONTENT_DIR), $source); 
  388. if ( ! is_dir($working_directory) ) // Sanity check, if the above fails, let's not prevent installation. 
  389. return $source; 
  390.  
  391. // A proper archive should have a style.css file in the single subdirectory 
  392. if ( ! file_exists( $working_directory . 'style.css' ) ) { 
  393. return new WP_Error( 'incompatible_archive_theme_no_style', $this->strings['incompatible_archive'],  
  394. /** translators: %s: style.css */ 
  395. sprintf( __( 'The theme is missing the %s stylesheet.' ),  
  396. '<code>style.css</code>' 
  397. ); 
  398.  
  399. $info = get_file_data( $working_directory . 'style.css', array( 'Name' => 'Theme Name', 'Template' => 'Template' ) ); 
  400.  
  401. if ( empty( $info['Name'] ) ) { 
  402. return new WP_Error( 'incompatible_archive_theme_no_name', $this->strings['incompatible_archive'],  
  403. /** translators: %s: style.css */ 
  404. sprintf( __( 'The %s stylesheet doesn’t contain a valid theme header.' ),  
  405. '<code>style.css</code>' 
  406. ); 
  407.  
  408. // If it's not a child theme, it must have at least an index.php to be legit. 
  409. if ( empty( $info['Template'] ) && ! file_exists( $working_directory . 'index.php' ) ) { 
  410. return new WP_Error( 'incompatible_archive_theme_no_index', $this->strings['incompatible_archive'],  
  411. /** translators: %s: index.php */ 
  412. sprintf( __( 'The theme is missing the %s file.' ),  
  413. '<code>index.php</code>' 
  414. ); 
  415.  
  416. return $source; 
  417.  
  418. /** 
  419. * Turn on maintenance mode before attempting to upgrade the current theme. 
  420. * Hooked to the {@see 'upgrader_pre_install'} filter by Theme_Upgrader::upgrade() and 
  421. * Theme_Upgrader::bulk_upgrade(). 
  422. * @since 2.8.0 
  423. * @access public 
  424. * @param bool|WP_Error $return 
  425. * @param array $theme 
  426. * @return bool|WP_Error 
  427. */ 
  428. public function current_before($return, $theme) { 
  429. if ( is_wp_error($return) ) 
  430. return $return; 
  431.  
  432. $theme = isset($theme['theme']) ? $theme['theme'] : ''; 
  433.  
  434. if ( $theme != get_stylesheet() ) //If not current 
  435. return $return; 
  436. //Change to maintenance mode now. 
  437. if ( ! $this->bulk ) 
  438. $this->maintenance_mode(true); 
  439.  
  440. return $return; 
  441.  
  442. /** 
  443. * Turn off maintenance mode after upgrading the current theme. 
  444. * Hooked to the {@see 'upgrader_post_install'} filter by Theme_Upgrader::upgrade() 
  445. * and Theme_Upgrader::bulk_upgrade(). 
  446. * @since 2.8.0 
  447. * @access public 
  448. * @param bool|WP_Error $return 
  449. * @param array $theme 
  450. * @return bool|WP_Error 
  451. */ 
  452. public function current_after($return, $theme) { 
  453. if ( is_wp_error($return) ) 
  454. return $return; 
  455.  
  456. $theme = isset($theme['theme']) ? $theme['theme'] : ''; 
  457.  
  458. if ( $theme != get_stylesheet() ) // If not current 
  459. return $return; 
  460.  
  461. // Ensure stylesheet name hasn't changed after the upgrade: 
  462. if ( $theme == get_stylesheet() && $theme != $this->result['destination_name'] ) { 
  463. wp_clean_themes_cache(); 
  464. $stylesheet = $this->result['destination_name']; 
  465. switch_theme( $stylesheet ); 
  466.  
  467. //Time to remove maintenance mode 
  468. if ( ! $this->bulk ) 
  469. $this->maintenance_mode(false); 
  470. return $return; 
  471.  
  472. /** 
  473. * Delete the old theme during an upgrade. 
  474. * Hooked to the {@see 'upgrader_clear_destination'} filter by Theme_Upgrader::upgrade() 
  475. * and Theme_Upgrader::bulk_upgrade(). 
  476. * @since 2.8.0 
  477. * @access public 
  478. * @global WP_Filesystem_Base $wp_filesystem Subclass 
  479. * @param bool $removed 
  480. * @param string $local_destination 
  481. * @param string $remote_destination 
  482. * @param array $theme 
  483. * @return bool 
  484. */ 
  485. public function delete_old_theme( $removed, $local_destination, $remote_destination, $theme ) { 
  486. global $wp_filesystem; 
  487.  
  488. if ( is_wp_error( $removed ) ) 
  489. return $removed; // Pass errors through. 
  490.  
  491. if ( ! isset( $theme['theme'] ) ) 
  492. return $removed; 
  493.  
  494. $theme = $theme['theme']; 
  495. $themes_dir = trailingslashit( $wp_filesystem->wp_themes_dir( $theme ) ); 
  496. if ( $wp_filesystem->exists( $themes_dir . $theme ) ) { 
  497. if ( ! $wp_filesystem->delete( $themes_dir . $theme, true ) ) 
  498. return false; 
  499.  
  500. return true; 
  501.  
  502. /** 
  503. * Get the WP_Theme object for a theme. 
  504. * @since 2.8.0 
  505. * @since 3.0.0 The `$theme` argument was added. 
  506. * @access public 
  507. * @param string $theme The directory name of the theme. This is optional, and if not supplied,  
  508. * the directory name from the last result will be used. 
  509. * @return WP_Theme|false The theme's info object, or false `$theme` is not supplied 
  510. * and the last result isn't set. 
  511. */ 
  512. public function theme_info($theme = null) { 
  513.  
  514. if ( empty($theme) ) { 
  515. if ( !empty($this->result['destination_name']) ) 
  516. $theme = $this->result['destination_name']; 
  517. else 
  518. return false; 
  519. return wp_get_theme( $theme ); 
  520.