Changeset 25227 for trunk/src/wp-admin/includes/class-wp-upgrader-skins.php
- Timestamp:
- 09/04/2013 06:34:38 AM (12 years ago)
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/includes/class-wp-upgrader-skins.php
r25226 r25227 1 1 <?php 2 2 /** 3 * A File upgrader class for WordPress. 4 * 5 * This set of classes are designed to be used to upgrade/install a local set of files on the filesystem via the Filesystem Abstraction classes. 6 * 7 * @link http://trac.wordpress.org/ticket/7875 consolidate plugin/theme/core upgrade/install functions 3 * The User Interface "Skins" for the WordPress File Upgrader 8 4 * 9 5 * @package WordPress … … 13 9 14 10 /** 15 * WordPress Upgrader class for Upgrading/Installing a local set of files via the Filesystem Abstraction classes from a Zip file.16 *17 * @TODO More Detailed docs, for methods as well.18 *19 * @package WordPress20 * @subpackage Upgrader21 * @since 2.8.022 */23 class WP_Upgrader {24 var $strings = array();25 var $skin = null;26 var $result = array();27 28 function __construct($skin = null) {29 if ( null == $skin )30 $this->skin = new WP_Upgrader_Skin();31 else32 $this->skin = $skin;33 }34 35 function init() {36 $this->skin->set_upgrader($this);37 $this->generic_strings();38 }39 40 function generic_strings() {41 $this->strings['bad_request'] = __('Invalid Data provided.');42 $this->strings['fs_unavailable'] = __('Could not access filesystem.');43 $this->strings['fs_error'] = __('Filesystem error.');44 $this->strings['fs_no_root_dir'] = __('Unable to locate WordPress Root directory.');45 $this->strings['fs_no_content_dir'] = __('Unable to locate WordPress Content directory (wp-content).');46 $this->strings['fs_no_plugins_dir'] = __('Unable to locate WordPress Plugin directory.');47 $this->strings['fs_no_themes_dir'] = __('Unable to locate WordPress Theme directory.');48 /* translators: %s: directory name */49 $this->strings['fs_no_folder'] = __('Unable to locate needed folder (%s).');50 51 $this->strings['download_failed'] = __('Download failed.');52 $this->strings['installing_package'] = __('Installing the latest version…');53 $this->strings['no_files'] = __('The package contains no files.');54 $this->strings['folder_exists'] = __('Destination folder already exists.');55 $this->strings['mkdir_failed'] = __('Could not create directory.');56 $this->strings['incompatible_archive'] = __('The package could not be installed.');57 58 $this->strings['maintenance_start'] = __('Enabling Maintenance mode…');59 $this->strings['maintenance_end'] = __('Disabling Maintenance mode…');60 }61 62 function fs_connect( $directories = array() ) {63 global $wp_filesystem;64 65 if ( false === ($credentials = $this->skin->request_filesystem_credentials()) )66 return false;67 68 if ( ! WP_Filesystem($credentials) ) {69 $error = true;70 if ( is_object($wp_filesystem) && $wp_filesystem->errors->get_error_code() )71 $error = $wp_filesystem->errors;72 $this->skin->request_filesystem_credentials($error); //Failed to connect, Error and request again73 return false;74 }75 76 if ( ! is_object($wp_filesystem) )77 return new WP_Error('fs_unavailable', $this->strings['fs_unavailable'] );78 79 if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() )80 return new WP_Error('fs_error', $this->strings['fs_error'], $wp_filesystem->errors);81 82 foreach ( (array)$directories as $dir ) {83 switch ( $dir ) {84 case ABSPATH:85 if ( ! $wp_filesystem->abspath() )86 return new WP_Error('fs_no_root_dir', $this->strings['fs_no_root_dir']);87 break;88 case WP_CONTENT_DIR:89 if ( ! $wp_filesystem->wp_content_dir() )90 return new WP_Error('fs_no_content_dir', $this->strings['fs_no_content_dir']);91 break;92 case WP_PLUGIN_DIR:93 if ( ! $wp_filesystem->wp_plugins_dir() )94 return new WP_Error('fs_no_plugins_dir', $this->strings['fs_no_plugins_dir']);95 break;96 case get_theme_root():97 if ( ! $wp_filesystem->wp_themes_dir() )98 return new WP_Error('fs_no_themes_dir', $this->strings['fs_no_themes_dir']);99 break;100 default:101 if ( ! $wp_filesystem->find_folder($dir) )102 return new WP_Error( 'fs_no_folder', sprintf( $this->strings['fs_no_folder'], esc_html( basename( $dir ) ) ) );103 break;104 }105 }106 return true;107 } //end fs_connect();108 109 function download_package($package) {110 111 if ( ! preg_match('!^(http|https|ftp)://!i', $package) && file_exists($package) ) //Local file or remote?112 return $package; //must be a local file..113 114 if ( empty($package) )115 return new WP_Error('no_package', $this->strings['no_package']);116 117 $this->skin->feedback('downloading_package', $package);118 119 $download_file = download_url($package);120 121 if ( is_wp_error($download_file) )122 return new WP_Error('download_failed', $this->strings['download_failed'], $download_file->get_error_message());123 124 return $download_file;125 }126 127 function unpack_package($package, $delete_package = true) {128 global $wp_filesystem;129 130 $this->skin->feedback('unpack_package');131 132 $upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/';133 134 //Clean up contents of upgrade directory beforehand.135 $upgrade_files = $wp_filesystem->dirlist($upgrade_folder);136 if ( !empty($upgrade_files) ) {137 foreach ( $upgrade_files as $file )138 $wp_filesystem->delete($upgrade_folder . $file['name'], true);139 }140 141 //We need a working directory142 $working_dir = $upgrade_folder . basename($package, '.zip');143 144 // Clean up working directory145 if ( $wp_filesystem->is_dir($working_dir) )146 $wp_filesystem->delete($working_dir, true);147 148 // Unzip package to working directory149 $result = unzip_file($package, $working_dir); //TODO optimizations, Copy when Move/Rename would suffice?150 151 // Once extracted, delete the package if required.152 if ( $delete_package )153 unlink($package);154 155 if ( is_wp_error($result) ) {156 $wp_filesystem->delete($working_dir, true);157 if ( 'incompatible_archive' == $result->get_error_code() ) {158 return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], $result->get_error_data() );159 }160 return $result;161 }162 163 return $working_dir;164 }165 166 function install_package($args = array()) {167 global $wp_filesystem, $wp_theme_directories;168 169 $defaults = array( 'source' => '', 'destination' => '', //Please always pass these170 'clear_destination' => false, 'clear_working' => false,171 'abort_if_destination_exists' => true,172 'hook_extra' => array());173 174 $args = wp_parse_args($args, $defaults);175 extract($args);176 177 @set_time_limit( 300 );178 179 if ( empty($source) || empty($destination) )180 return new WP_Error('bad_request', $this->strings['bad_request']);181 182 $this->skin->feedback('installing_package');183 184 $res = apply_filters('upgrader_pre_install', true, $hook_extra);185 if ( is_wp_error($res) )186 return $res;187 188 //Retain the Original source and destinations189 $remote_source = $source;190 $local_destination = $destination;191 192 $source_files = array_keys( $wp_filesystem->dirlist($remote_source) );193 $remote_destination = $wp_filesystem->find_folder($local_destination);194 195 //Locate which directory to copy to the new folder, This is based on the actual folder holding the files.196 if ( 1 == count($source_files) && $wp_filesystem->is_dir( trailingslashit($source) . $source_files[0] . '/') ) //Only one folder? Then we want its contents.197 $source = trailingslashit($source) . trailingslashit($source_files[0]);198 elseif ( count($source_files) == 0 )199 return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], $this->strings['no_files'] ); //There are no files?200 else //It's only a single file, the upgrader will use the foldername of this file as the destination folder. foldername is based on zip filename.201 $source = trailingslashit($source);202 203 //Hook ability to change the source file location..204 $source = apply_filters('upgrader_source_selection', $source, $remote_source, $this);205 if ( is_wp_error($source) )206 return $source;207 208 //Has the source location changed? If so, we need a new source_files list.209 if ( $source !== $remote_source )210 $source_files = array_keys( $wp_filesystem->dirlist($source) );211 212 // Protection against deleting files in any important base directories.213 // Theme_Upgrader & Plugin_Upgrader also trigger this, as they pass the destination directory (WP_PLUGIN_DIR / wp-content/themes)214 // intending to copy the directory into the directory, whilst they pass the source as the actual files to copy.215 $protected_directories = array( ABSPATH, WP_CONTENT_DIR, WP_PLUGIN_DIR, WP_CONTENT_DIR . '/themes' );216 if ( is_array( $wp_theme_directories ) )217 $protected_directories = array_merge( $protected_directories, $wp_theme_directories );218 if ( in_array( $destination, $protected_directories ) ) {219 $remote_destination = trailingslashit($remote_destination) . trailingslashit(basename($source));220 $destination = trailingslashit($destination) . trailingslashit(basename($source));221 }222 223 if ( $clear_destination ) {224 //We're going to clear the destination if there's something there225 $this->skin->feedback('remove_old');226 $removed = true;227 if ( $wp_filesystem->exists($remote_destination) )228 $removed = $wp_filesystem->delete($remote_destination, true);229 $removed = apply_filters('upgrader_clear_destination', $removed, $local_destination, $remote_destination, $hook_extra);230 231 if ( is_wp_error($removed) )232 return $removed;233 else if ( ! $removed )234 return new WP_Error('remove_old_failed', $this->strings['remove_old_failed']);235 } elseif ( $abort_if_destination_exists && $wp_filesystem->exists($remote_destination) ) {236 //If we're not clearing the destination folder and something exists there already, Bail.237 //But first check to see if there are actually any files in the folder.238 $_files = $wp_filesystem->dirlist($remote_destination);239 if ( ! empty($_files) ) {240 $wp_filesystem->delete($remote_source, true); //Clear out the source files.241 return new WP_Error('folder_exists', $this->strings['folder_exists'], $remote_destination );242 }243 }244 245 //Create destination if needed246 if ( !$wp_filesystem->exists($remote_destination) )247 if ( !$wp_filesystem->mkdir($remote_destination, FS_CHMOD_DIR) )248 return new WP_Error('mkdir_failed', $this->strings['mkdir_failed'], $remote_destination);249 250 // Copy new version of item into place.251 $result = copy_dir($source, $remote_destination);252 if ( is_wp_error($result) ) {253 if ( $clear_working )254 $wp_filesystem->delete($remote_source, true);255 return $result;256 }257 258 //Clear the Working folder?259 if ( $clear_working )260 $wp_filesystem->delete($remote_source, true);261 262 $destination_name = basename( str_replace($local_destination, '', $destination) );263 if ( '.' == $destination_name )264 $destination_name = '';265 266 $this->result = compact('local_source', 'source', 'source_name', 'source_files', 'destination', 'destination_name', 'local_destination', 'remote_destination', 'clear_destination', 'delete_source_dir');267 268 $res = apply_filters('upgrader_post_install', true, $hook_extra, $this->result);269 if ( is_wp_error($res) ) {270 $this->result = $res;271 return $res;272 }273 274 //Bombard the calling function will all the info which we've just used.275 return $this->result;276 }277 278 function run($options) {279 280 $defaults = array( 'package' => '', //Please always pass this.281 'destination' => '', //And this282 'clear_destination' => false,283 'abort_if_destination_exists' => true, // Abort if the Destination directory exists, Pass clear_destination as false please284 'clear_working' => true,285 'is_multi' => false,286 'hook_extra' => array() //Pass any extra $hook_extra args here, this will be passed to any hooked filters.287 );288 289 $options = wp_parse_args($options, $defaults);290 extract($options);291 292 //Connect to the Filesystem first.293 $res = $this->fs_connect( array(WP_CONTENT_DIR, $destination) );294 if ( ! $res ) //Mainly for non-connected filesystem.295 return false;296 297 if ( is_wp_error($res) ) {298 $this->skin->error($res);299 return $res;300 }301 302 if ( !$is_multi ) // call $this->header separately if running multiple times303 $this->skin->header();304 305 $this->skin->before();306 307 //Download the package (Note, This just returns the filename of the file if the package is a local file)308 $download = $this->download_package( $package );309 if ( is_wp_error($download) ) {310 $this->skin->error($download);311 $this->skin->after();312 return $download;313 }314 315 $delete_package = ($download != $package); // Do not delete a "local" file316 317 //Unzips the file into a temporary directory318 $working_dir = $this->unpack_package( $download, $delete_package );319 if ( is_wp_error($working_dir) ) {320 $this->skin->error($working_dir);321 $this->skin->after();322 return $working_dir;323 }324 325 //With the given options, this installs it to the destination directory.326 $result = $this->install_package( array(327 'source' => $working_dir,328 'destination' => $destination,329 'clear_destination' => $clear_destination,330 'abort_if_destination_exists' => $abort_if_destination_exists,331 'clear_working' => $clear_working,332 'hook_extra' => $hook_extra333 ) );334 $this->skin->set_result($result);335 if ( is_wp_error($result) ) {336 $this->skin->error($result);337 $this->skin->feedback('process_failed');338 } else {339 //Install Succeeded340 $this->skin->feedback('process_success');341 }342 $this->skin->after();343 344 if ( !$is_multi )345 $this->skin->footer();346 347 return $result;348 }349 350 function maintenance_mode($enable = false) {351 global $wp_filesystem;352 $file = $wp_filesystem->abspath() . '.maintenance';353 if ( $enable ) {354 $this->skin->feedback('maintenance_start');355 // Create maintenance file to signal that we are upgrading356 $maintenance_string = '<?php $upgrading = ' . time() . '; ?>';357 $wp_filesystem->delete($file);358 $wp_filesystem->put_contents($file, $maintenance_string, FS_CHMOD_FILE);359 } else if ( !$enable && $wp_filesystem->exists($file) ) {360 $this->skin->feedback('maintenance_end');361 $wp_filesystem->delete($file);362 }363 }364 365 }366 367 /**368 * Plugin Upgrader class for WordPress Plugins, It is designed to upgrade/install plugins from a local zip, remote zip URL, or uploaded zip file.369 *370 * @TODO More Detailed docs, for methods as well.371 *372 * @package WordPress373 * @subpackage Upgrader374 * @since 2.8.0375 */376 class Plugin_Upgrader extends WP_Upgrader {377 378 var $result;379 var $bulk = false;380 var $show_before = '';381 382 function upgrade_strings() {383 $this->strings['up_to_date'] = __('The plugin is at the latest version.');384 $this->strings['no_package'] = __('Update package not available.');385 $this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>…');386 $this->strings['unpack_package'] = __('Unpacking the update…');387 $this->strings['remove_old'] = __('Removing the old version of the plugin…');388 $this->strings['remove_old_failed'] = __('Could not remove the old plugin.');389 $this->strings['process_failed'] = __('Plugin update failed.');390 $this->strings['process_success'] = __('Plugin updated successfully.');391 }392 393 function install_strings() {394 $this->strings['no_package'] = __('Install package not available.');395 $this->strings['downloading_package'] = __('Downloading install package from <span class="code">%s</span>…');396 $this->strings['unpack_package'] = __('Unpacking the package…');397 $this->strings['installing_package'] = __('Installing the plugin…');398 $this->strings['no_files'] = __('The plugin contains no files.');399 $this->strings['process_failed'] = __('Plugin install failed.');400 $this->strings['process_success'] = __('Plugin installed successfully.');401 }402 403 function install($package) {404 405 $this->init();406 $this->install_strings();407 408 add_filter('upgrader_source_selection', array(&$this, 'check_package') );409 410 $this->run(array(411 'package' => $package,412 'destination' => WP_PLUGIN_DIR,413 'clear_destination' => false, //Do not overwrite files.414 'clear_working' => true,415 'hook_extra' => array()416 ));417 418 remove_filter('upgrader_source_selection', array(&$this, 'check_package') );419 420 if ( ! $this->result || is_wp_error($this->result) )421 return $this->result;422 423 // Force refresh of plugin update information424 delete_site_transient('update_plugins');425 wp_cache_delete( 'plugins', 'plugins' );426 do_action( 'upgrader_process_complete', $this, array( 'action' => 'install', 'type' => 'plugin' ), $package );427 428 return true;429 }430 431 function upgrade($plugin) {432 433 $this->init();434 $this->upgrade_strings();435 436 $current = get_site_transient( 'update_plugins' );437 if ( !isset( $current->response[ $plugin ] ) ) {438 $this->skin->before();439 $this->skin->set_result(false);440 $this->skin->error('up_to_date');441 $this->skin->after();442 return false;443 }444 445 // Get the URL to the zip file446 $r = $current->response[ $plugin ];447 448 add_filter('upgrader_pre_install', array(&$this, 'deactivate_plugin_before_upgrade'), 10, 2);449 add_filter('upgrader_clear_destination', array(&$this, 'delete_old_plugin'), 10, 4);450 //'source_selection' => array(&$this, 'source_selection'), //there's a trac ticket to move up the directory for zip's which are made a bit differently, useful for non-.org plugins.451 452 $this->run(array(453 'package' => $r->package,454 'destination' => WP_PLUGIN_DIR,455 'clear_destination' => true,456 'clear_working' => true,457 'hook_extra' => array(458 'plugin' => $plugin459 )460 ));461 462 // Cleanup our hooks, in case something else does a upgrade on this connection.463 remove_filter('upgrader_pre_install', array(&$this, 'deactivate_plugin_before_upgrade'));464 remove_filter('upgrader_clear_destination', array(&$this, 'delete_old_plugin'));465 466 if ( ! $this->result || is_wp_error($this->result) )467 return $this->result;468 469 // Force refresh of plugin update information470 delete_site_transient('update_plugins');471 wp_cache_delete( 'plugins', 'plugins' );472 do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'plugin' ), $plugin );473 474 return true;475 }476 477 function bulk_upgrade($plugins) {478 479 $this->init();480 $this->bulk = true;481 $this->upgrade_strings();482 483 $current = get_site_transient( 'update_plugins' );484 485 add_filter('upgrader_clear_destination', array(&$this, 'delete_old_plugin'), 10, 4);486 487 $this->skin->header();488 489 // Connect to the Filesystem first.490 $res = $this->fs_connect( array(WP_CONTENT_DIR, WP_PLUGIN_DIR) );491 if ( ! $res ) {492 $this->skin->footer();493 return false;494 }495 496 $this->skin->bulk_header();497 498 // Only start maintenance mode if:499 // - running Multisite and there are one or more plugins specified, OR500 // - a plugin with an update available is currently active.501 // @TODO: For multisite, maintenance mode should only kick in for individual sites if at all possible.502 $maintenance = ( is_multisite() && ! empty( $plugins ) );503 foreach ( $plugins as $plugin )504 $maintenance = $maintenance || ( is_plugin_active( $plugin ) && isset( $current->response[ $plugin] ) );505 if ( $maintenance )506 $this->maintenance_mode(true);507 508 $results = array();509 510 $this->update_count = count($plugins);511 $this->update_current = 0;512 foreach ( $plugins as $plugin ) {513 $this->update_current++;514 $this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true);515 516 if ( !isset( $current->response[ $plugin ] ) ) {517 $this->skin->set_result(true);518 $this->skin->before();519 $this->skin->feedback('up_to_date');520 $this->skin->after();521 $results[$plugin] = true;522 continue;523 }524 525 // Get the URL to the zip file526 $r = $current->response[ $plugin ];527 528 $this->skin->plugin_active = is_plugin_active($plugin);529 530 $result = $this->run(array(531 'package' => $r->package,532 'destination' => WP_PLUGIN_DIR,533 'clear_destination' => true,534 'clear_working' => true,535 'is_multi' => true,536 'hook_extra' => array(537 'plugin' => $plugin538 )539 ));540 541 $results[$plugin] = $this->result;542 543 // Prevent credentials auth screen from displaying multiple times544 if ( false === $result )545 break;546 } //end foreach $plugins547 548 $this->maintenance_mode(false);549 550 $this->skin->bulk_footer();551 552 $this->skin->footer();553 554 // Cleanup our hooks, in case something else does a upgrade on this connection.555 remove_filter('upgrader_clear_destination', array(&$this, 'delete_old_plugin'));556 557 // Force refresh of plugin update information558 delete_site_transient('update_plugins');559 wp_cache_delete( 'plugins', 'plugins' );560 do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'plugin', 'bulk' => true ), $plugins );561 562 return $results;563 }564 565 function check_package($source) {566 global $wp_filesystem;567 568 if ( is_wp_error($source) )569 return $source;570 571 $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit(WP_CONTENT_DIR), $source);572 if ( ! is_dir($working_directory) ) // Sanity check, if the above fails, lets not prevent installation.573 return $source;574 575 // Check the folder contains at least 1 valid plugin.576 $plugins_found = false;577 foreach ( glob( $working_directory . '*.php' ) as $file ) {578 $info = get_plugin_data($file, false, false);579 if ( !empty( $info['Name'] ) ) {580 $plugins_found = true;581 break;582 }583 }584 585 if ( ! $plugins_found )586 return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], __('No valid plugins were found.') );587 588 return $source;589 }590 591 //return plugin info.592 function plugin_info() {593 if ( ! is_array($this->result) )594 return false;595 if ( empty($this->result['destination_name']) )596 return false;597 598 $plugin = get_plugins('/' . $this->result['destination_name']); //Ensure to pass with leading slash599 if ( empty($plugin) )600 return false;601 602 $pluginfiles = array_keys($plugin); //Assume the requested plugin is the first in the list603 604 return $this->result['destination_name'] . '/' . $pluginfiles[0];605 }606 607 //Hooked to pre_install608 function deactivate_plugin_before_upgrade($return, $plugin) {609 610 if ( is_wp_error($return) ) //Bypass.611 return $return;612 613 $plugin = isset($plugin['plugin']) ? $plugin['plugin'] : '';614 if ( empty($plugin) )615 return new WP_Error('bad_request', $this->strings['bad_request']);616 617 if ( is_plugin_active($plugin) ) {618 //Deactivate the plugin silently, Prevent deactivation hooks from running.619 deactivate_plugins($plugin, true);620 }621 }622 623 //Hooked to upgrade_clear_destination624 function delete_old_plugin($removed, $local_destination, $remote_destination, $plugin) {625 global $wp_filesystem;626 627 if ( is_wp_error($removed) )628 return $removed; //Pass errors through.629 630 $plugin = isset($plugin['plugin']) ? $plugin['plugin'] : '';631 if ( empty($plugin) )632 return new WP_Error('bad_request', $this->strings['bad_request']);633 634 $plugins_dir = $wp_filesystem->wp_plugins_dir();635 $this_plugin_dir = trailingslashit( dirname($plugins_dir . $plugin) );636 637 if ( ! $wp_filesystem->exists($this_plugin_dir) ) //If it's already vanished.638 return $removed;639 640 // If plugin is in its own directory, recursively delete the directory.641 if ( strpos($plugin, '/') && $this_plugin_dir != $plugins_dir ) //base check on if plugin includes directory separator AND that it's not the root plugin folder642 $deleted = $wp_filesystem->delete($this_plugin_dir, true);643 else644 $deleted = $wp_filesystem->delete($plugins_dir . $plugin);645 646 if ( ! $deleted )647 return new WP_Error('remove_old_failed', $this->strings['remove_old_failed']);648 649 return true;650 }651 }652 653 /**654 * Theme Upgrader class for WordPress Themes, It is designed to upgrade/install themes from a local zip, remote zip URL, or uploaded zip file.655 *656 * @TODO More Detailed docs, for methods as well.657 *658 * @package WordPress659 * @subpackage Upgrader660 * @since 2.8.0661 */662 class Theme_Upgrader extends WP_Upgrader {663 664 var $result;665 var $bulk = false;666 667 function upgrade_strings() {668 $this->strings['up_to_date'] = __('The theme is at the latest version.');669 $this->strings['no_package'] = __('Update package not available.');670 $this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>…');671 $this->strings['unpack_package'] = __('Unpacking the update…');672 $this->strings['remove_old'] = __('Removing the old version of the theme…');673 $this->strings['remove_old_failed'] = __('Could not remove the old theme.');674 $this->strings['process_failed'] = __('Theme update failed.');675 $this->strings['process_success'] = __('Theme updated successfully.');676 }677 678 function install_strings() {679 $this->strings['no_package'] = __('Install package not available.');680 $this->strings['downloading_package'] = __('Downloading install package from <span class="code">%s</span>…');681 $this->strings['unpack_package'] = __('Unpacking the package…');682 $this->strings['installing_package'] = __('Installing the theme…');683 $this->strings['no_files'] = __('The theme contains no files.');684 $this->strings['process_failed'] = __('Theme install failed.');685 $this->strings['process_success'] = __('Theme installed successfully.');686 /* translators: 1: theme name, 2: version */687 $this->strings['process_success_specific'] = __('Successfully installed the theme <strong>%1$s %2$s</strong>.');688 $this->strings['parent_theme_search'] = __('This theme requires a parent theme. Checking if it is installed…');689 /* translators: 1: theme name, 2: version */690 $this->strings['parent_theme_prepare_install'] = __('Preparing to install <strong>%1$s %2$s</strong>…');691 /* translators: 1: theme name, 2: version */692 $this->strings['parent_theme_currently_installed'] = __('The parent theme, <strong>%1$s %2$s</strong>, is currently installed.');693 /* translators: 1: theme name, 2: version */694 $this->strings['parent_theme_install_success'] = __('Successfully installed the parent theme, <strong>%1$s %2$s</strong>.');695 $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.');696 }697 698 function check_parent_theme_filter($install_result, $hook_extra, $child_result) {699 // Check to see if we need to install a parent theme700 $theme_info = $this->theme_info();701 702 if ( ! $theme_info->parent() )703 return $install_result;704 705 $this->skin->feedback( 'parent_theme_search' );706 707 if ( ! $theme_info->parent()->errors() ) {708 $this->skin->feedback( 'parent_theme_currently_installed', $theme_info->parent()->display('Name'), $theme_info->parent()->display('Version') );709 // We already have the theme, fall through.710 return $install_result;711 }712 713 // We don't have the parent theme, lets install it714 $api = themes_api('theme_information', array('slug' => $theme_info->get('Template'), 'fields' => array('sections' => false, 'tags' => false) ) ); //Save on a bit of bandwidth.715 716 if ( ! $api || is_wp_error($api) ) {717 $this->skin->feedback( 'parent_theme_not_found', $theme_info->get('Template') );718 // Don't show activate or preview actions after install719 add_filter('install_theme_complete_actions', array(&$this, 'hide_activate_preview_actions') );720 return $install_result;721 }722 723 // Backup required data we're going to override:724 $child_api = $this->skin->api;725 $child_success_message = $this->strings['process_success'];726 727 // Override them728 $this->skin->api = $api;729 $this->strings['process_success_specific'] = $this->strings['parent_theme_install_success'];//, $api->name, $api->version);730 731 $this->skin->feedback('parent_theme_prepare_install', $api->name, $api->version);732 733 add_filter('install_theme_complete_actions', '__return_false', 999); // Don't show any actions after installing the theme.734 735 // Install the parent theme736 $parent_result = $this->run( array(737 'package' => $api->download_link,738 'destination' => get_theme_root(),739 'clear_destination' => false, //Do not overwrite files.740 'clear_working' => true741 ) );742 743 if ( is_wp_error($parent_result) )744 add_filter('install_theme_complete_actions', array(&$this, 'hide_activate_preview_actions') );745 746 // Start cleaning up after the parents installation747 remove_filter('install_theme_complete_actions', '__return_false', 999);748 749 // Reset child's result and data750 $this->result = $child_result;751 $this->skin->api = $child_api;752 $this->strings['process_success'] = $child_success_message;753 754 return $install_result;755 }756 757 function hide_activate_preview_actions($actions) {758 unset($actions['activate'], $actions['preview']);759 return $actions;760 }761 762 function install($package) {763 764 $this->init();765 $this->install_strings();766 767 add_filter('upgrader_source_selection', array(&$this, 'check_package') );768 add_filter('upgrader_post_install', array(&$this, 'check_parent_theme_filter'), 10, 3);769 770 $options = array(771 'package' => $package,772 'destination' => get_theme_root(),773 'clear_destination' => false, //Do not overwrite files.774 'clear_working' => true775 );776 777 $this->run($options);778 779 remove_filter('upgrader_source_selection', array(&$this, 'check_package') );780 remove_filter('upgrader_post_install', array(&$this, 'check_parent_theme_filter'));781 782 if ( ! $this->result || is_wp_error($this->result) )783 return $this->result;784 785 // Force refresh of theme update information786 wp_clean_themes_cache();787 do_action( 'upgrader_process_complete', $this, array( 'action' => 'install', 'type' => 'theme' ), $package );788 789 return true;790 }791 792 function upgrade($theme) {793 794 $this->init();795 $this->upgrade_strings();796 797 // Is an update available?798 $current = get_site_transient( 'update_themes' );799 if ( !isset( $current->response[ $theme ] ) ) {800 $this->skin->before();801 $this->skin->set_result(false);802 $this->skin->error('up_to_date');803 $this->skin->after();804 return false;805 }806 807 $r = $current->response[ $theme ];808 809 add_filter('upgrader_pre_install', array(&$this, 'current_before'), 10, 2);810 add_filter('upgrader_post_install', array(&$this, 'current_after'), 10, 2);811 add_filter('upgrader_clear_destination', array(&$this, 'delete_old_theme'), 10, 4);812 813 $options = array(814 'package' => $r['package'],815 'destination' => get_theme_root( $theme ),816 'clear_destination' => true,817 'clear_working' => true,818 'hook_extra' => array(819 'theme' => $theme820 ),821 );822 823 $this->run($options);824 825 remove_filter('upgrader_pre_install', array(&$this, 'current_before'));826 remove_filter('upgrader_post_install', array(&$this, 'current_after'));827 remove_filter('upgrader_clear_destination', array(&$this, 'delete_old_theme'));828 829 if ( ! $this->result || is_wp_error($this->result) )830 return $this->result;831 832 // Force refresh of theme update information833 wp_clean_themes_cache();834 do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'theme' ), $theme );835 836 return true;837 }838 839 function bulk_upgrade($themes) {840 841 $this->init();842 $this->bulk = true;843 $this->upgrade_strings();844 845 $current = get_site_transient( 'update_themes' );846 847 add_filter('upgrader_pre_install', array(&$this, 'current_before'), 10, 2);848 add_filter('upgrader_post_install', array(&$this, 'current_after'), 10, 2);849 add_filter('upgrader_clear_destination', array(&$this, 'delete_old_theme'), 10, 4);850 851 $this->skin->header();852 853 // Connect to the Filesystem first.854 $res = $this->fs_connect( array(WP_CONTENT_DIR) );855 if ( ! $res ) {856 $this->skin->footer();857 return false;858 }859 860 $this->skin->bulk_header();861 862 // Only start maintenance mode if:863 // - running Multisite and there are one or more themes specified, OR864 // - a theme with an update available is currently in use.865 // @TODO: For multisite, maintenance mode should only kick in for individual sites if at all possible.866 $maintenance = ( is_multisite() && ! empty( $themes ) );867 foreach ( $themes as $theme )868 $maintenance = $maintenance || $theme == get_stylesheet() || $theme == get_template();869 if ( $maintenance )870 $this->maintenance_mode(true);871 872 $results = array();873 874 $this->update_count = count($themes);875 $this->update_current = 0;876 foreach ( $themes as $theme ) {877 $this->update_current++;878 879 $this->skin->theme_info = $this->theme_info($theme);880 881 if ( !isset( $current->response[ $theme ] ) ) {882 $this->skin->set_result(true);883 $this->skin->before();884 $this->skin->feedback('up_to_date');885 $this->skin->after();886 $results[$theme] = true;887 continue;888 }889 890 // Get the URL to the zip file891 $r = $current->response[ $theme ];892 893 $options = array(894 'package' => $r['package'],895 'destination' => get_theme_root( $theme ),896 'clear_destination' => true,897 'clear_working' => true,898 'hook_extra' => array(899 'theme' => $theme900 ),901 );902 903 $result = $this->run($options);904 905 $results[$theme] = $this->result;906 907 // Prevent credentials auth screen from displaying multiple times908 if ( false === $result )909 break;910 } //end foreach $plugins911 912 $this->maintenance_mode(false);913 914 $this->skin->bulk_footer();915 916 $this->skin->footer();917 918 // Cleanup our hooks, in case something else does a upgrade on this connection.919 remove_filter('upgrader_pre_install', array(&$this, 'current_before'));920 remove_filter('upgrader_post_install', array(&$this, 'current_after'));921 remove_filter('upgrader_clear_destination', array(&$this, 'delete_old_theme'));922 923 // Force refresh of theme update information924 wp_clean_themes_cache();925 do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'theme', 'bulk' => true ), $themes );926 927 return $results;928 }929 930 function check_package($source) {931 global $wp_filesystem;932 933 if ( is_wp_error($source) )934 return $source;935 936 // Check the folder contains a valid theme937 $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit(WP_CONTENT_DIR), $source);938 if ( ! is_dir($working_directory) ) // Sanity check, if the above fails, lets not prevent installation.939 return $source;940 941 // A proper archive should have a style.css file in the single subdirectory942 if ( ! file_exists( $working_directory . 'style.css' ) )943 return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], __('The theme is missing the <code>style.css</code> stylesheet.') );944 945 $info = get_file_data( $working_directory . 'style.css', array( 'Name' => 'Theme Name', 'Template' => 'Template' ) );946 947 if ( empty( $info['Name'] ) )948 return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], __("The <code>style.css</code> stylesheet doesn't contain a valid theme header.") );949 950 // If it's not a child theme, it must have at least an index.php to be legit.951 if ( empty( $info['Template'] ) && ! file_exists( $working_directory . 'index.php' ) )952 return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], __('The theme is missing the <code>index.php</code> file.') );953 954 return $source;955 }956 957 function current_before($return, $theme) {958 959 if ( is_wp_error($return) )960 return $return;961 962 $theme = isset($theme['theme']) ? $theme['theme'] : '';963 964 if ( $theme != get_stylesheet() ) //If not current965 return $return;966 //Change to maintenance mode now.967 if ( ! $this->bulk )968 $this->maintenance_mode(true);969 970 return $return;971 }972 973 function current_after($return, $theme) {974 if ( is_wp_error($return) )975 return $return;976 977 $theme = isset($theme['theme']) ? $theme['theme'] : '';978 979 if ( $theme != get_stylesheet() ) // If not current980 return $return;981 982 // Ensure stylesheet name hasn't changed after the upgrade:983 if ( $theme == get_stylesheet() && $theme != $this->result['destination_name'] ) {984 wp_clean_themes_cache();985 $stylesheet = $this->result['destination_name'];986 switch_theme( $stylesheet );987 }988 989 //Time to remove maintenance mode990 if ( ! $this->bulk )991 $this->maintenance_mode(false);992 return $return;993 }994 995 function delete_old_theme( $removed, $local_destination, $remote_destination, $theme ) {996 global $wp_filesystem;997 998 if ( is_wp_error( $removed ) )999 return $removed; // Pass errors through.1000 1001 if ( ! isset( $theme['theme'] ) )1002 return $removed;1003 1004 $theme = $theme['theme'];1005 $themes_dir = trailingslashit( $wp_filesystem->wp_themes_dir( $theme ) );1006 if ( $wp_filesystem->exists( $themes_dir . $theme ) ) {1007 if ( ! $wp_filesystem->delete( $themes_dir . $theme, true ) )1008 return false;1009 }1010 1011 return true;1012 }1013 1014 function theme_info($theme = null) {1015 1016 if ( empty($theme) ) {1017 if ( !empty($this->result['destination_name']) )1018 $theme = $this->result['destination_name'];1019 else1020 return false;1021 }1022 return wp_get_theme( $theme );1023 }1024 1025 }1026 1027 /**1028 * Core Upgrader class for WordPress. It allows for WordPress to upgrade itself in combination with the wp-admin/includes/update-core.php file1029 *1030 * @TODO More Detailed docs, for methods as well.1031 *1032 * @package WordPress1033 * @subpackage Upgrader1034 * @since 2.8.01035 */1036 class Core_Upgrader extends WP_Upgrader {1037 1038 function upgrade_strings() {1039 $this->strings['up_to_date'] = __('WordPress is at the latest version.');1040 $this->strings['no_package'] = __('Update package not available.');1041 $this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>…');1042 $this->strings['unpack_package'] = __('Unpacking the update…');1043 $this->strings['copy_failed'] = __('Could not copy files.');1044 $this->strings['copy_failed_space'] = __('Could not copy files. You may have run out of disk space.' );1045 }1046 1047 function upgrade($current) {1048 global $wp_filesystem, $wp_version;1049 1050 $this->init();1051 $this->upgrade_strings();1052 1053 // Is an update available?1054 if ( !isset( $current->response ) || $current->response == 'latest' )1055 return new WP_Error('up_to_date', $this->strings['up_to_date']);1056 1057 $res = $this->fs_connect( array(ABSPATH, WP_CONTENT_DIR) );1058 if ( is_wp_error($res) )1059 return $res;1060 1061 $wp_dir = trailingslashit($wp_filesystem->abspath());1062 1063 // If partial update is returned from the API, use that, unless we're doing a reinstall.1064 // If we cross the new_bundled version number, then use the new_bundled zip.1065 // Don't though if the constant is set to skip bundled items.1066 // If the API returns a no_content zip, go with it. Finally, default to the full zip.1067 if ( $current->packages->partial && 'reinstall' != $current->response && $wp_version == $current->partial_version )1068 $to_download = 'partial';1069 elseif ( $current->packages->new_bundled && version_compare( $wp_version, $current->new_bundled, '<' )1070 && ( ! defined( 'CORE_UPGRADE_SKIP_NEW_BUNDLED' ) || ! CORE_UPGRADE_SKIP_NEW_BUNDLED ) )1071 $to_download = 'new_bundled';1072 elseif ( $current->packages->no_content )1073 $to_download = 'no_content';1074 else1075 $to_download = 'full';1076 1077 $download = $this->download_package( $current->packages->$to_download );1078 if ( is_wp_error($download) )1079 return $download;1080 1081 $working_dir = $this->unpack_package( $download );1082 if ( is_wp_error($working_dir) )1083 return $working_dir;1084 1085 // Copy update-core.php from the new version into place.1086 if ( !$wp_filesystem->copy($working_dir . '/wordpress/wp-admin/includes/update-core.php', $wp_dir . 'wp-admin/includes/update-core.php', true) ) {1087 $wp_filesystem->delete($working_dir, true);1088 return new WP_Error('copy_failed', $this->strings['copy_failed']);1089 }1090 $wp_filesystem->chmod($wp_dir . 'wp-admin/includes/update-core.php', FS_CHMOD_FILE);1091 1092 require(ABSPATH . 'wp-admin/includes/update-core.php');1093 1094 if ( ! function_exists( 'update_core' ) )1095 return new WP_Error( 'copy_failed_space', $this->strings['copy_failed_space'] );1096 1097 $result = update_core( $working_dir, $wp_dir );1098 do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'core' ), $result );1099 return $result;1100 }1101 1102 }1103 1104 /**1105 11 * Generic Skin for the WordPress Upgrader classes. This skin is designed to be extended for specific purposes. 1106 *1107 * @TODO More Detailed docs, for methods as well.1108 12 * 1109 13 * @package WordPress … … 1195 99 * Plugin Upgrader Skin for WordPress Plugin Upgrades. 1196 100 * 1197 * @TODO More Detailed docs, for methods as well.1198 *1199 101 * @package WordPress 1200 102 * @subpackage Upgrader … … 1433 335 * Plugin Installer Skin for WordPress Plugin Installer. 1434 336 * 1435 * @TODO More Detailed docs, for methods as well.1436 *1437 337 * @package WordPress 1438 338 * @subpackage Upgrader … … 1498 398 * Theme Installer Skin for the WordPress Theme Installer. 1499 399 * 1500 * @TODO More Detailed docs, for methods as well.1501 *1502 400 * @package WordPress 1503 401 * @subpackage Upgrader … … 1572 470 /** 1573 471 * Theme Upgrader Skin for WordPress Theme Upgrades. 1574 *1575 * @TODO More Detailed docs, for methods as well.1576 472 * 1577 473 * @package WordPress … … 1632 528 } 1633 529 } 1634 1635 /**1636 * Upgrade Skin helper for File uploads. This class handles the upload process and passes it as if it's a local file to the Upgrade/Installer functions.1637 *1638 * @TODO More Detailed docs, for methods as well.1639 *1640 * @package WordPress1641 * @subpackage Upgrader1642 * @since 2.8.01643 */1644 class File_Upload_Upgrader {1645 var $package;1646 var $filename;1647 var $id = 0;1648 1649 function __construct($form, $urlholder) {1650 1651 if ( empty($_FILES[$form]['name']) && empty($_GET[$urlholder]) )1652 wp_die(__('Please select a file'));1653 1654 //Handle a newly uploaded file, Else assume it's already been uploaded1655 if ( ! empty($_FILES) ) {1656 $overrides = array( 'test_form' => false, 'test_type' => false );1657 $file = wp_handle_upload( $_FILES[$form], $overrides );1658 1659 if ( isset( $file['error'] ) )1660 wp_die( $file['error'] );1661 1662 $this->filename = $_FILES[$form]['name'];1663 $this->package = $file['file'];1664 1665 // Construct the object array1666 $object = array(1667 'post_title' => $this->filename,1668 'post_content' => $file['url'],1669 'post_mime_type' => $file['type'],1670 'guid' => $file['url'],1671 'context' => 'upgrader',1672 'post_status' => 'private'1673 );1674 1675 // Save the data1676 $this->id = wp_insert_attachment( $object, $file['file'] );1677 1678 // schedule a cleanup for 2 hours from now in case of failed install1679 wp_schedule_single_event( time() + 7200, 'upgrader_scheduled_cleanup', array( $this->id ) );1680 1681 } elseif ( is_numeric( $_GET[$urlholder] ) ) {1682 // Numeric Package = previously uploaded file, see above.1683 $this->id = (int) $_GET[$urlholder];1684 $attachment = get_post( $this->id );1685 if ( empty($attachment) )1686 wp_die(__('Please select a file'));1687 1688 $this->filename = $attachment->post_title;1689 $this->package = get_attached_file( $attachment->ID );1690 } else {1691 // Else, It's set to something, Back compat for plugins using the old (pre-3.3) File_Uploader handler.1692 if ( ! ( ( $uploads = wp_upload_dir() ) && false === $uploads['error'] ) )1693 wp_die( $uploads['error'] );1694 1695 $this->filename = $_GET[$urlholder];1696 $this->package = $uploads['basedir'] . '/' . $this->filename;1697 }1698 }1699 1700 function cleanup() {1701 if ( $this->id )1702 wp_delete_attachment( $this->id );1703 1704 elseif ( file_exists( $this->package ) )1705 return @unlink( $this->package );1706 1707 return true;1708 }1709 }
Note: See TracChangeset
for help on using the changeset viewer.