[ Index ]

PHP Cross Reference of WordPress

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 they are updated 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       * Checks that the package source contains .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 WordPress filesystem subclass.
 320       *
 321       * @param string|WP_Error $source        The path to the downloaded package source.
 322       * @param string          $remote_source Remote file source location.
 323       * @return string|WP_Error The source as passed, or a WP_Error object on failure.
 324       */
 325  	public function check_package( $source, $remote_source ) {
 326          global $wp_filesystem;
 327  
 328          if ( is_wp_error( $source ) ) {
 329              return $source;
 330          }
 331  
 332          // Check that the folder contains a valid language.
 333          $files = $wp_filesystem->dirlist( $remote_source );
 334  
 335          // Check to see if a .po and .mo exist in the folder.
 336          $po = false;
 337          $mo = false;
 338          foreach ( (array) $files as $file => $filedata ) {
 339              if ( '.po' === substr( $file, -3 ) ) {
 340                  $po = true;
 341              } elseif ( '.mo' === substr( $file, -3 ) ) {
 342                  $mo = true;
 343              }
 344          }
 345  
 346          if ( ! $mo || ! $po ) {
 347              return new WP_Error(
 348                  'incompatible_archive_pomo',
 349                  $this->strings['incompatible_archive'],
 350                  sprintf(
 351                      /* translators: 1: .po, 2: .mo */
 352                      __( 'The language pack is missing either the %1$s or %2$s files.' ),
 353                      '<code>.po</code>',
 354                      '<code>.mo</code>'
 355                  )
 356              );
 357          }
 358  
 359          return $source;
 360      }
 361  
 362      /**
 363       * Get the name of an item being updated.
 364       *
 365       * @since 3.7.0
 366       *
 367       * @param object $update The data for an update.
 368       * @return string The name of the item being updated.
 369       */
 370  	public function get_name_for_update( $update ) {
 371          switch ( $update->type ) {
 372              case 'core':
 373                  return 'WordPress'; // Not translated.
 374  
 375              case 'theme':
 376                  $theme = wp_get_theme( $update->slug );
 377                  if ( $theme->exists() ) {
 378                      return $theme->Get( 'Name' );
 379                  }
 380                  break;
 381              case 'plugin':
 382                  $plugin_data = get_plugins( '/' . $update->slug );
 383                  $plugin_data = reset( $plugin_data );
 384                  if ( $plugin_data ) {
 385                      return $plugin_data['Name'];
 386                  }
 387                  break;
 388          }
 389          return '';
 390      }
 391  
 392      /**
 393       * Clears existing translations where this item is going to be installed into.
 394       *
 395       * @since 5.1.0
 396       *
 397       * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
 398       *
 399       * @param string $remote_destination The location on the remote filesystem to be cleared.
 400       * @return bool|WP_Error True upon success, WP_Error on failure.
 401       */
 402  	public function clear_destination( $remote_destination ) {
 403          global $wp_filesystem;
 404  
 405          $language_update    = $this->skin->language_update;
 406          $language_directory = WP_LANG_DIR . '/'; // Local path for use with glob().
 407  
 408          if ( 'core' === $language_update->type ) {
 409              $files = array(
 410                  $remote_destination . $language_update->language . '.po',
 411                  $remote_destination . $language_update->language . '.mo',
 412                  $remote_destination . 'admin-' . $language_update->language . '.po',
 413                  $remote_destination . 'admin-' . $language_update->language . '.mo',
 414                  $remote_destination . 'admin-network-' . $language_update->language . '.po',
 415                  $remote_destination . 'admin-network-' . $language_update->language . '.mo',
 416                  $remote_destination . 'continents-cities-' . $language_update->language . '.po',
 417                  $remote_destination . 'continents-cities-' . $language_update->language . '.mo',
 418              );
 419  
 420              $json_translation_files = glob( $language_directory . $language_update->language . '-*.json' );
 421              if ( $json_translation_files ) {
 422                  foreach ( $json_translation_files as $json_translation_file ) {
 423                      $files[] = str_replace( $language_directory, $remote_destination, $json_translation_file );
 424                  }
 425              }
 426          } else {
 427              $files = array(
 428                  $remote_destination . $language_update->slug . '-' . $language_update->language . '.po',
 429                  $remote_destination . $language_update->slug . '-' . $language_update->language . '.mo',
 430              );
 431  
 432              $language_directory     = $language_directory . $language_update->type . 's/';
 433              $json_translation_files = glob( $language_directory . $language_update->slug . '-' . $language_update->language . '-*.json' );
 434              if ( $json_translation_files ) {
 435                  foreach ( $json_translation_files as $json_translation_file ) {
 436                      $files[] = str_replace( $language_directory, $remote_destination, $json_translation_file );
 437                  }
 438              }
 439          }
 440  
 441          $files = array_filter( $files, array( $wp_filesystem, 'exists' ) );
 442  
 443          // No files to delete.
 444          if ( ! $files ) {
 445              return true;
 446          }
 447  
 448          // Check all files are writable before attempting to clear the destination.
 449          $unwritable_files = array();
 450  
 451          // Check writability.
 452          foreach ( $files as $file ) {
 453              if ( ! $wp_filesystem->is_writable( $file ) ) {
 454                  // Attempt to alter permissions to allow writes and try again.
 455                  $wp_filesystem->chmod( $file, FS_CHMOD_FILE );
 456                  if ( ! $wp_filesystem->is_writable( $file ) ) {
 457                      $unwritable_files[] = $file;
 458                  }
 459              }
 460          }
 461  
 462          if ( ! empty( $unwritable_files ) ) {
 463              return new WP_Error( 'files_not_writable', $this->strings['files_not_writable'], implode( ', ', $unwritable_files ) );
 464          }
 465  
 466          foreach ( $files as $file ) {
 467              if ( ! $wp_filesystem->delete( $file ) ) {
 468                  return new WP_Error( 'remove_old_failed', $this->strings['remove_old_failed'] );
 469              }
 470          }
 471  
 472          return true;
 473      }
 474  }


Generated: Tue Mar 19 01:00:02 2024 Cross-referenced by PHPXref 0.7.1