[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

/wp-admin/includes/ -> class-language-pack-upgrader.php (source)

   1  <?php
   2  /**
   3   * Upgrade API: Language_Pack_Upgrader class
   4   *
   5   * @package WordPress
   6   * @subpackage Upgrader
   7   * @since 4.6.0
   8   */
   9  
  10  /**
  11   * Core class used for updating/installing language packs (translations)
  12   * for plugins, themes, and core.
  13   *
  14   * @since 3.7.0
  15   * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader.php.
  16   *
  17   * @see WP_Upgrader
  18   */
  19  class Language_Pack_Upgrader extends WP_Upgrader {
  20  
  21      /**
  22       * Result of the language pack upgrade.
  23       *
  24       * @since 3.7.0
  25       * @var array|WP_Error $result
  26       * @see WP_Upgrader::$result
  27       */
  28      public $result;
  29  
  30      /**
  31       * Whether a bulk upgrade/installation is being performed.
  32       *
  33       * @since 3.7.0
  34       * @var bool $bulk
  35       */
  36      public $bulk = true;
  37  
  38      /**
  39       * Asynchronously upgrades language packs after other upgrades have been made.
  40       *
  41       * Hooked to the {@see 'upgrader_process_complete'} action by default.
  42       *
  43       * @since 3.7.0
  44       *
  45       * @param false|WP_Upgrader $upgrader Optional. WP_Upgrader instance or false. If `$upgrader` is
  46       *                                    a Language_Pack_Upgrader instance, the method will bail to
  47       *                                    avoid recursion. Otherwise unused. Default false.
  48       */
  49  	public static function async_upgrade( $upgrader = false ) {
  50          // Avoid recursion.
  51          if ( $upgrader && $upgrader instanceof Language_Pack_Upgrader ) {
  52              return;
  53          }
  54  
  55          // Nothing to do?
  56          $language_updates = wp_get_translation_updates();
  57          if ( ! $language_updates ) {
  58              return;
  59          }
  60  
  61          /*
  62           * Avoid messing with VCS installations, at least for now.
  63           * Noted: this is not the ideal way to accomplish this.
  64           */
  65          $check_vcs = new WP_Automatic_Updater;
  66          if ( $check_vcs->is_vcs_checkout( WP_CONTENT_DIR ) ) {
  67              return;
  68          }
  69  
  70          foreach ( $language_updates as $key => $language_update ) {
  71              $update = ! empty( $language_update->autoupdate );
  72  
  73              /**
  74               * Filters whether to asynchronously update translation for core, a plugin, or a theme.
  75               *
  76               * @since 4.0.0
  77               *
  78               * @param bool   $update          Whether to update.
  79               * @param object $language_update The update offer.
  80               */
  81              $update = apply_filters( 'async_update_translation', $update, $language_update );
  82  
  83              if ( ! $update ) {
  84                  unset( $language_updates[ $key ] );
  85              }
  86          }
  87  
  88          if ( empty( $language_updates ) ) {
  89              return;
  90          }
  91  
  92          // Re-use the automatic upgrader skin if the parent upgrader is using it.
  93          if ( $upgrader && $upgrader->skin instanceof Automatic_Upgrader_Skin ) {
  94              $skin = $upgrader->skin;
  95          } else {
  96              $skin = new Language_Pack_Upgrader_Skin(
  97                  array(
  98                      'skip_header_footer' => true,
  99                  )
 100              );
 101          }
 102  
 103          $lp_upgrader = new Language_Pack_Upgrader( $skin );
 104          $lp_upgrader->bulk_upgrade( $language_updates );
 105      }
 106  
 107      /**
 108       * Initialize the upgrade strings.
 109       *
 110       * @since 3.7.0
 111       */
 112  	public function upgrade_strings() {
 113          $this->strings['starting_upgrade'] = __( 'Some of your translations need updating. Sit tight for a few more seconds while we update them as well.' );
 114          $this->strings['up_to_date']       = __( 'Your translations are all up to date.' );
 115          $this->strings['no_package']       = __( 'Update package not available.' );
 116          /* translators: %s: Package URL. */
 117          $this->strings['downloading_package'] = sprintf( __( 'Downloading translation from %s&#8230;' ), '<span class="code">%s</span>' );
 118          $this->strings['unpack_package']      = __( 'Unpacking the update&#8230;' );
 119          $this->strings['process_failed']      = __( 'Translation update failed.' );
 120          $this->strings['process_success']     = __( 'Translation updated successfully.' );
 121          $this->strings['remove_old']          = __( 'Removing the old version of the translation&#8230;' );
 122          $this->strings['remove_old_failed']   = __( 'Could not remove the old translation.' );
 123      }
 124  
 125      /**
 126       * Upgrade a language pack.
 127       *
 128       * @since 3.7.0
 129       *
 130       * @param string|false $update Optional. Whether an update offer is available. Default false.
 131       * @param array        $args   Optional. Other optional arguments, see
 132       *                             Language_Pack_Upgrader::bulk_upgrade(). Default empty array.
 133       * @return array|bool|WP_Error The result of the upgrade, or a WP_Error object instead.
 134       */
 135  	public function upgrade( $update = false, $args = array() ) {
 136          if ( $update ) {
 137              $update = array( $update );
 138          }
 139  
 140          $results = $this->bulk_upgrade( $update, $args );
 141  
 142          if ( ! is_array( $results ) ) {
 143              return $results;
 144          }
 145  
 146          return $results[0];
 147      }
 148  
 149      /**
 150       * Bulk upgrade language packs.
 151       *
 152       * @since 3.7.0
 153       *
 154       * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
 155       *
 156       * @param object[] $language_updates Optional. Array of language packs to update. @see wp_get_translation_updates().
 157       *                                   Default empty array.
 158       * @param array    $args {
 159       *     Other arguments for upgrading multiple language packs. Default empty array.
 160       *
 161       *     @type bool $clear_update_cache Whether to clear the update cache when done.
 162       *                                    Default true.
 163       * }
 164       * @return array|bool|WP_Error Will return an array of results, or true if there are no updates,
 165       *                             false or WP_Error for initial errors.
 166       */
 167  	public function bulk_upgrade( $language_updates = array(), $args = array() ) {
 168          global $wp_filesystem;
 169  
 170          $defaults    = array(
 171              'clear_update_cache' => true,
 172          );
 173          $parsed_args = wp_parse_args( $args, $defaults );
 174  
 175          $this->init();
 176          $this->upgrade_strings();
 177  
 178          if ( ! $language_updates ) {
 179              $language_updates = wp_get_translation_updates();
 180          }
 181  
 182          if ( empty( $language_updates ) ) {
 183              $this->skin->header();
 184              $this->skin->set_result( true );
 185              $this->skin->feedback( 'up_to_date' );
 186              $this->skin->bulk_footer();
 187              $this->skin->footer();
 188              return true;
 189          }
 190  
 191          if ( 'upgrader_process_complete' == current_filter() ) {
 192              $this->skin->feedback( 'starting_upgrade' );
 193          }
 194  
 195          // Remove any existing upgrade filters from the plugin/theme upgraders #WP29425 & #WP29230
 196          remove_all_filters( 'upgrader_pre_install' );
 197          remove_all_filters( 'upgrader_clear_destination' );
 198          remove_all_filters( 'upgrader_post_install' );
 199          remove_all_filters( 'upgrader_source_selection' );
 200  
 201          add_filter( 'upgrader_source_selection', array( $this, 'check_package' ), 10, 2 );
 202  
 203          $this->skin->header();
 204  
 205          // Connect to the Filesystem first.
 206          $res = $this->fs_connect( array( WP_CONTENT_DIR, WP_LANG_DIR ) );
 207          if ( ! $res ) {
 208              $this->skin->footer();
 209              return false;
 210          }
 211  
 212          $results = array();
 213  
 214          $this->update_count   = count( $language_updates );
 215          $this->update_current = 0;
 216  
 217          /*
 218           * The filesystem's mkdir() is not recursive. Make sure WP_LANG_DIR exists,
 219           * as we then may need to create a /plugins or /themes directory inside of it.
 220           */
 221          $remote_destination = $wp_filesystem->find_folder( WP_LANG_DIR );
 222          if ( ! $wp_filesystem->exists( $remote_destination ) ) {
 223              if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) {
 224                  return new WP_Error( 'mkdir_failed_lang_dir', $this->strings['mkdir_failed'], $remote_destination );
 225              }
 226          }
 227  
 228          $language_updates_results = array();
 229  
 230          foreach ( $language_updates as $language_update ) {
 231  
 232              $this->skin->language_update = $language_update;
 233  
 234              $destination = WP_LANG_DIR;
 235              if ( 'plugin' == $language_update->type ) {
 236                  $destination .= '/plugins';
 237              } elseif ( 'theme' == $language_update->type ) {
 238                  $destination .= '/themes';
 239              }
 240  
 241              $this->update_current++;
 242  
 243              $options = array(
 244                  'package'                     => $language_update->package,
 245                  'destination'                 => $destination,
 246                  'clear_destination'           => true,
 247                  'abort_if_destination_exists' => false, // We expect the destination to exist.
 248                  'clear_working'               => true,
 249                  'is_multi'                    => true,
 250                  'hook_extra'                  => array(
 251                      'language_update_type' => $language_update->type,
 252                      'language_update'      => $language_update,
 253                  ),
 254              );
 255  
 256              $result = $this->run( $options );
 257  
 258              $results[] = $this->result;
 259  
 260              // Prevent credentials auth screen from displaying multiple times.
 261              if ( false === $result ) {
 262                  break;
 263              }
 264  
 265              $language_updates_results[] = array(
 266                  'language' => $language_update->language,
 267                  'type'     => $language_update->type,
 268                  'slug'     => isset( $language_update->slug ) ? $language_update->slug : 'default',
 269                  'version'  => $language_update->version,
 270              );
 271          }
 272  
 273          // Remove upgrade hooks which are not required for translation updates.
 274          remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
 275          remove_action( 'upgrader_process_complete', 'wp_version_check' );
 276          remove_action( 'upgrader_process_complete', 'wp_update_plugins' );
 277          remove_action( 'upgrader_process_complete', 'wp_update_themes' );
 278  
 279          /** This action is documented in wp-admin/includes/class-wp-upgrader.php */
 280          do_action(
 281              'upgrader_process_complete',
 282              $this,
 283              array(
 284                  'action'       => 'update',
 285                  'type'         => 'translation',
 286                  'bulk'         => true,
 287                  'translations' => $language_updates_results,
 288              )
 289          );
 290  
 291          // Re-add upgrade hooks.
 292          add_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
 293          add_action( 'upgrader_process_complete', 'wp_version_check', 10, 0 );
 294          add_action( 'upgrader_process_complete', 'wp_update_plugins', 10, 0 );
 295          add_action( 'upgrader_process_complete', 'wp_update_themes', 10, 0 );
 296  
 297          $this->skin->bulk_footer();
 298  
 299          $this->skin->footer();
 300  
 301          // Clean up our hooks, in case something else does an upgrade on this connection.
 302          remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
 303  
 304          if ( $parsed_args['clear_update_cache'] ) {
 305              wp_clean_update_cache();
 306          }
 307  
 308          return $results;
 309      }
 310  
 311      /**
 312       * Check the package source to make sure there are .mo and .po files.
 313       *
 314       * Hooked to the {@see 'upgrader_source_selection'} filter by
 315       * Language_Pack_Upgrader::bulk_upgrade().
 316       *
 317       * @since 3.7.0
 318       *
 319       * @global WP_Filesystem_Base $wp_filesystem Subclass
 320       *
 321       * @param string|WP_Error $source
 322       * @param string          $remote_source
 323       */
 324  	public function check_package( $source, $remote_source ) {
 325          global $wp_filesystem;
 326  
 327          if ( is_wp_error( $source ) ) {
 328              return $source;
 329          }
 330  
 331          // Check that the folder contains a valid language.
 332          $files = $wp_filesystem->dirlist( $remote_source );
 333  
 334          // Check to see if a .po and .mo exist in the folder.
 335          $po = false;
 336          $mo = false;
 337          foreach ( (array) $files as $file => $filedata ) {
 338              if ( '.po' == substr( $file, -3 ) ) {
 339                  $po = true;
 340              } elseif ( '.mo' == substr( $file, -3 ) ) {
 341                  $mo = true;
 342              }
 343          }
 344  
 345          if ( ! $mo || ! $po ) {
 346              return new WP_Error(
 347                  'incompatible_archive_pomo',
 348                  $this->strings['incompatible_archive'],
 349                  sprintf(
 350                      /* translators: 1: .po, 2: .mo */
 351                      __( 'The language pack is missing either the %1$s or %2$s files.' ),
 352                      '<code>.po</code>',
 353                      '<code>.mo</code>'
 354                  )
 355              );
 356          }
 357  
 358          return $source;
 359      }
 360  
 361      /**
 362       * Get the name of an item being updated.
 363       *
 364       * @since 3.7.0
 365       *
 366       * @param object $update The data for an update.
 367       * @return string The name of the item being updated.
 368       */
 369  	public function get_name_for_update( $update ) {
 370          switch ( $update->type ) {
 371              case 'core':
 372                  return 'WordPress'; // Not translated
 373  
 374              case 'theme':
 375                  $theme = wp_get_theme( $update->slug );
 376                  if ( $theme->exists() ) {
 377                      return $theme->Get( 'Name' );
 378                  }
 379                  break;
 380              case 'plugin':
 381                  $plugin_data = get_plugins( '/' . $update->slug );
 382                  $plugin_data = reset( $plugin_data );
 383                  if ( $plugin_data ) {
 384                      return $plugin_data['Name'];
 385                  }
 386                  break;
 387          }
 388          return '';
 389      }
 390  
 391      /**
 392       * Clears existing translations where this item is going to be installed into.
 393       *
 394       * @since 5.1.0
 395       *
 396       * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
 397       *
 398       * @param string $remote_destination The location on the remote filesystem to be cleared.
 399       * @return bool|WP_Error True upon success, WP_Error on failure.
 400       */
 401  	public function clear_destination( $remote_destination ) {
 402          global $wp_filesystem;
 403  
 404          $language_update    = $this->skin->language_update;
 405          $language_directory = WP_LANG_DIR . '/'; // Local path for use with glob().
 406  
 407          if ( 'core' === $language_update->type ) {
 408              $files = array(
 409                  $remote_destination . $language_update->language . '.po',
 410                  $remote_destination . $language_update->language . '.mo',
 411                  $remote_destination . 'admin-' . $language_update->language . '.po',
 412                  $remote_destination . 'admin-' . $language_update->language . '.mo',
 413                  $remote_destination . 'admin-network-' . $language_update->language . '.po',
 414                  $remote_destination . 'admin-network-' . $language_update->language . '.mo',
 415                  $remote_destination . 'continents-cities-' . $language_update->language . '.po',
 416                  $remote_destination . 'continents-cities-' . $language_update->language . '.mo',
 417              );
 418  
 419              $json_translation_files = glob( $language_directory . $language_update->language . '-*.json' );
 420              if ( $json_translation_files ) {
 421                  foreach ( $json_translation_files as $json_translation_file ) {
 422                      $files[] = str_replace( $language_directory, $remote_destination, $json_translation_file );
 423                  }
 424              }
 425          } else {
 426              $files = array(
 427                  $remote_destination . $language_update->slug . '-' . $language_update->language . '.po',
 428                  $remote_destination . $language_update->slug . '-' . $language_update->language . '.mo',
 429              );
 430  
 431              $language_directory     = $language_directory . $language_update->type . 's/';
 432              $json_translation_files = glob( $language_directory . $language_update->slug . '-' . $language_update->language . '-*.json' );
 433              if ( $json_translation_files ) {
 434                  foreach ( $json_translation_files as $json_translation_file ) {
 435                      $files[] = str_replace( $language_directory, $remote_destination, $json_translation_file );
 436                  }
 437              }
 438          }
 439  
 440          $files = array_filter( $files, array( $wp_filesystem, 'exists' ) );
 441  
 442          // No files to delete.
 443          if ( ! $files ) {
 444              return true;
 445          }
 446  
 447          // Check all files are writable before attempting to clear the destination.
 448          $unwritable_files = array();
 449  
 450          // Check writability.
 451          foreach ( $files as $file ) {
 452              if ( ! $wp_filesystem->is_writable( $file ) ) {
 453                  // Attempt to alter permissions to allow writes and try again.
 454                  $wp_filesystem->chmod( $file, FS_CHMOD_FILE );
 455                  if ( ! $wp_filesystem->is_writable( $file ) ) {
 456                      $unwritable_files[] = $file;
 457                  }
 458              }
 459          }
 460  
 461          if ( ! empty( $unwritable_files ) ) {
 462              return new WP_Error( 'files_not_writable', $this->strings['files_not_writable'], implode( ', ', $unwritable_files ) );
 463          }
 464  
 465          foreach ( $files as $file ) {
 466              if ( ! $wp_filesystem->delete( $file ) ) {
 467                  return new WP_Error( 'remove_old_failed', $this->strings['remove_old_failed'] );
 468              }
 469          }
 470  
 471          return true;
 472      }
 473  }


Generated: Tue Oct 22 08:20:01 2019 Cross-referenced by PHPXref 0.7