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. if ( $parsed_args['clear_update_cache'] ) { 
  165. // Clear cache so wp_update_themes() knows about the new theme. 
  166. add_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9, 0 ); 
  167.  
  168. $this->run( array( 
  169. 'package' => $package,  
  170. 'destination' => get_theme_root(),  
  171. 'clear_destination' => false, //Do not overwrite files. 
  172. 'clear_working' => true,  
  173. 'hook_extra' => array( 
  174. 'type' => 'theme',  
  175. 'action' => 'install',  
  176. ),  
  177. ) ); 
  178.  
  179. remove_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9 ); 
  180. remove_filter('upgrader_source_selection', array($this, 'check_package') ); 
  181. remove_filter('upgrader_post_install', array($this, 'check_parent_theme_filter')); 
  182.  
  183. if ( ! $this->result || is_wp_error($this->result) ) 
  184. return $this->result; 
  185.  
  186. // Refresh the Theme Update information 
  187. wp_clean_themes_cache( $parsed_args['clear_update_cache'] ); 
  188.  
  189. return true; 
  190.  
  191. /** 
  192. * Upgrade a theme. 
  193. * @since 2.8.0 
  194. * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional. 
  195. * @access public 
  196. * @param string $theme The theme slug. 
  197. * @param array $args { 
  198. * Optional. Other arguments for upgrading a theme. Default empty array. 
  199. * @type bool $clear_update_cache Whether to clear the update cache if successful. 
  200. * Default true. 
  201. * } 
  202. * @return bool|WP_Error True if the upgrade was successful, false or a WP_Error object otherwise. 
  203. */ 
  204. public function upgrade( $theme, $args = array() ) { 
  205.  
  206. $defaults = array( 
  207. 'clear_update_cache' => true,  
  208. ); 
  209. $parsed_args = wp_parse_args( $args, $defaults ); 
  210.  
  211. $this->init(); 
  212. $this->upgrade_strings(); 
  213.  
  214. // Is an update available? 
  215. $current = get_site_transient( 'update_themes' ); 
  216. if ( !isset( $current->response[ $theme ] ) ) { 
  217. $this->skin->before(); 
  218. $this->skin->set_result(false); 
  219. $this->skin->error( 'up_to_date' ); 
  220. $this->skin->after(); 
  221. return false; 
  222.  
  223. $r = $current->response[ $theme ]; 
  224.  
  225. add_filter('upgrader_pre_install', array($this, 'current_before'), 10, 2); 
  226. add_filter('upgrader_post_install', array($this, 'current_after'), 10, 2); 
  227. add_filter('upgrader_clear_destination', array($this, 'delete_old_theme'), 10, 4); 
  228. if ( $parsed_args['clear_update_cache'] ) { 
  229. // Clear cache so wp_update_themes() knows about the new theme. 
  230. add_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9, 0 ); 
  231.  
  232. $this->run( array( 
  233. 'package' => $r['package'],  
  234. 'destination' => get_theme_root( $theme ),  
  235. 'clear_destination' => true,  
  236. 'clear_working' => true,  
  237. 'hook_extra' => array( 
  238. 'theme' => $theme,  
  239. 'type' => 'theme',  
  240. 'action' => 'update',  
  241. ),  
  242. ) ); 
  243.  
  244. remove_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9 ); 
  245. remove_filter('upgrader_pre_install', array($this, 'current_before')); 
  246. remove_filter('upgrader_post_install', array($this, 'current_after')); 
  247. remove_filter('upgrader_clear_destination', array($this, 'delete_old_theme')); 
  248.  
  249. if ( ! $this->result || is_wp_error($this->result) ) 
  250. return $this->result; 
  251.  
  252. wp_clean_themes_cache( $parsed_args['clear_update_cache'] ); 
  253.  
  254. return true; 
  255.  
  256. /** 
  257. * Upgrade several themes at once. 
  258. * @since 3.0.0 
  259. * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional. 
  260. * @access public 
  261. * @param array $themes The theme slugs. 
  262. * @param array $args { 
  263. * Optional. Other arguments for upgrading several themes at once. Default empty array. 
  264. * @type bool $clear_update_cache Whether to clear the update cache if successful. 
  265. * Default true. 
  266. * } 
  267. * @return array[]|false An array of results, or false if unable to connect to the filesystem. 
  268. */ 
  269. public function bulk_upgrade( $themes, $args = array() ) { 
  270.  
  271. $defaults = array( 
  272. 'clear_update_cache' => true,  
  273. ); 
  274. $parsed_args = wp_parse_args( $args, $defaults ); 
  275.  
  276. $this->init(); 
  277. $this->bulk = true; 
  278. $this->upgrade_strings(); 
  279.  
  280. $current = get_site_transient( 'update_themes' ); 
  281.  
  282. add_filter('upgrader_pre_install', array($this, 'current_before'), 10, 2); 
  283. add_filter('upgrader_post_install', array($this, 'current_after'), 10, 2); 
  284. add_filter('upgrader_clear_destination', array($this, 'delete_old_theme'), 10, 4); 
  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. // Refresh the Theme Update information 
  347. wp_clean_themes_cache( $parsed_args['clear_update_cache'] ); 
  348.  
  349. /** This action is documented in wp-admin/includes/class-wp-upgrader.php */ 
  350. do_action( 'upgrader_process_complete', $this, array( 
  351. 'action' => 'update',  
  352. 'type' => 'theme',  
  353. 'bulk' => true,  
  354. 'themes' => $themes,  
  355. ) ); 
  356.  
  357. $this->skin->bulk_footer(); 
  358.  
  359. $this->skin->footer(); 
  360.  
  361. // Cleanup our hooks, in case something else does a upgrade on this connection. 
  362. remove_filter('upgrader_pre_install', array($this, 'current_before')); 
  363. remove_filter('upgrader_post_install', array($this, 'current_after')); 
  364. remove_filter('upgrader_clear_destination', array($this, 'delete_old_theme')); 
  365.  
  366. return $results; 
  367.  
  368. /** 
  369. * Check that the package source contains a valid theme. 
  370. * Hooked to the {@see 'upgrader_source_selection'} filter by Theme_Upgrader::install(). 
  371. * It will return an error if the theme doesn't have style.css or index.php 
  372. * files. 
  373. * @since 3.3.0 
  374. * @access public 
  375. * @global WP_Filesystem_Base $wp_filesystem Subclass 
  376. * @param string $source The full path to the package source. 
  377. * @return string|WP_Error The source or a WP_Error. 
  378. */ 
  379. public function check_package( $source ) { 
  380. global $wp_filesystem; 
  381.  
  382. if ( is_wp_error($source) ) 
  383. return $source; 
  384.  
  385. // Check the folder contains a valid theme 
  386. $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit(WP_CONTENT_DIR), $source); 
  387. if ( ! is_dir($working_directory) ) // Sanity check, if the above fails, let's not prevent installation. 
  388. return $source; 
  389.  
  390. // A proper archive should have a style.css file in the single subdirectory 
  391. if ( ! file_exists( $working_directory . 'style.css' ) ) { 
  392. return new WP_Error( 'incompatible_archive_theme_no_style', $this->strings['incompatible_archive'],  
  393. /** translators: %s: style.css */ 
  394. sprintf( __( 'The theme is missing the %s stylesheet.' ),  
  395. '<code>style.css</code>' 
  396. ); 
  397.  
  398. $info = get_file_data( $working_directory . 'style.css', array( 'Name' => 'Theme Name', 'Template' => 'Template' ) ); 
  399.  
  400. if ( empty( $info['Name'] ) ) { 
  401. return new WP_Error( 'incompatible_archive_theme_no_name', $this->strings['incompatible_archive'],  
  402. /** translators: %s: style.css */ 
  403. sprintf( __( 'The %s stylesheet doesn’t contain a valid theme header.' ),  
  404. '<code>style.css</code>' 
  405. ); 
  406.  
  407. // If it's not a child theme, it must have at least an index.php to be legit. 
  408. if ( empty( $info['Template'] ) && ! file_exists( $working_directory . 'index.php' ) ) { 
  409. return new WP_Error( 'incompatible_archive_theme_no_index', $this->strings['incompatible_archive'],  
  410. /** translators: %s: index.php */ 
  411. sprintf( __( 'The theme is missing the %s file.' ),  
  412. '<code>index.php</code>' 
  413. ); 
  414.  
  415. return $source; 
  416.  
  417. /** 
  418. * Turn on maintenance mode before attempting to upgrade the current theme. 
  419. * Hooked to the {@see 'upgrader_pre_install'} filter by Theme_Upgrader::upgrade() and 
  420. * Theme_Upgrader::bulk_upgrade(). 
  421. * @since 2.8.0 
  422. * @access public 
  423. * @param bool|WP_Error $return 
  424. * @param array $theme 
  425. * @return bool|WP_Error 
  426. */ 
  427. public function current_before($return, $theme) { 
  428. if ( is_wp_error($return) ) 
  429. return $return; 
  430.  
  431. $theme = isset($theme['theme']) ? $theme['theme'] : ''; 
  432.  
  433. if ( $theme != get_stylesheet() ) //If not current 
  434. return $return; 
  435. //Change to maintenance mode now. 
  436. if ( ! $this->bulk ) 
  437. $this->maintenance_mode(true); 
  438.  
  439. return $return; 
  440.  
  441. /** 
  442. * Turn off maintenance mode after upgrading the current theme. 
  443. * Hooked to the {@see 'upgrader_post_install'} filter by Theme_Upgrader::upgrade() 
  444. * and Theme_Upgrader::bulk_upgrade(). 
  445. * @since 2.8.0 
  446. * @access public 
  447. * @param bool|WP_Error $return 
  448. * @param array $theme 
  449. * @return bool|WP_Error 
  450. */ 
  451. public function current_after($return, $theme) { 
  452. if ( is_wp_error($return) ) 
  453. return $return; 
  454.  
  455. $theme = isset($theme['theme']) ? $theme['theme'] : ''; 
  456.  
  457. if ( $theme != get_stylesheet() ) // If not current 
  458. return $return; 
  459.  
  460. // Ensure stylesheet name hasn't changed after the upgrade: 
  461. if ( $theme == get_stylesheet() && $theme != $this->result['destination_name'] ) { 
  462. wp_clean_themes_cache(); 
  463. $stylesheet = $this->result['destination_name']; 
  464. switch_theme( $stylesheet ); 
  465.  
  466. //Time to remove maintenance mode 
  467. if ( ! $this->bulk ) 
  468. $this->maintenance_mode(false); 
  469. return $return; 
  470.  
  471. /** 
  472. * Delete the old theme during an upgrade. 
  473. * Hooked to the {@see 'upgrader_clear_destination'} filter by Theme_Upgrader::upgrade() 
  474. * and Theme_Upgrader::bulk_upgrade(). 
  475. * @since 2.8.0 
  476. * @access public 
  477. * @global WP_Filesystem_Base $wp_filesystem Subclass 
  478. * @param bool $removed 
  479. * @param string $local_destination 
  480. * @param string $remote_destination 
  481. * @param array $theme 
  482. * @return bool 
  483. */ 
  484. public function delete_old_theme( $removed, $local_destination, $remote_destination, $theme ) { 
  485. global $wp_filesystem; 
  486.  
  487. if ( is_wp_error( $removed ) ) 
  488. return $removed; // Pass errors through. 
  489.  
  490. if ( ! isset( $theme['theme'] ) ) 
  491. return $removed; 
  492.  
  493. $theme = $theme['theme']; 
  494. $themes_dir = trailingslashit( $wp_filesystem->wp_themes_dir( $theme ) ); 
  495. if ( $wp_filesystem->exists( $themes_dir . $theme ) ) { 
  496. if ( ! $wp_filesystem->delete( $themes_dir . $theme, true ) ) 
  497. return false; 
  498.  
  499. return true; 
  500.  
  501. /** 
  502. * Get the WP_Theme object for a theme. 
  503. * @since 2.8.0 
  504. * @since 3.0.0 The `$theme` argument was added. 
  505. * @access public 
  506. * @param string $theme The directory name of the theme. This is optional, and if not supplied,  
  507. * the directory name from the last result will be used. 
  508. * @return WP_Theme|false The theme's info object, or false `$theme` is not supplied 
  509. * and the last result isn't set. 
  510. */ 
  511. public function theme_info($theme = null) { 
  512.  
  513. if ( empty($theme) ) { 
  514. if ( !empty($this->result['destination_name']) ) 
  515. $theme = $this->result['destination_name']; 
  516. else 
  517. return false; 
  518. return wp_get_theme( $theme ); 
  519.