wp/wp-admin/includes/class-language-pack-upgrader.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
    39 	 * Asynchronously upgrades language packs after other upgrades have been made.
    39 	 * Asynchronously upgrades language packs after other upgrades have been made.
    40 	 *
    40 	 *
    41 	 * Hooked to the {@see 'upgrader_process_complete'} action by default.
    41 	 * Hooked to the {@see 'upgrader_process_complete'} action by default.
    42 	 *
    42 	 *
    43 	 * @since 3.7.0
    43 	 * @since 3.7.0
    44 	 * @static
       
    45 	 *
    44 	 *
    46 	 * @param false|WP_Upgrader $upgrader Optional. WP_Upgrader instance or false. If `$upgrader` is
    45 	 * @param false|WP_Upgrader $upgrader Optional. WP_Upgrader instance or false. If `$upgrader` is
    47 	 *                                    a Language_Pack_Upgrader instance, the method will bail to
    46 	 *                                    a Language_Pack_Upgrader instance, the method will bail to
    48 	 *                                    avoid recursion. Otherwise unused. Default false.
    47 	 *                                    avoid recursion. Otherwise unused. Default false.
    49 	 */
    48 	 */
    92 
    91 
    93 		// Re-use the automatic upgrader skin if the parent upgrader is using it.
    92 		// Re-use the automatic upgrader skin if the parent upgrader is using it.
    94 		if ( $upgrader && $upgrader->skin instanceof Automatic_Upgrader_Skin ) {
    93 		if ( $upgrader && $upgrader->skin instanceof Automatic_Upgrader_Skin ) {
    95 			$skin = $upgrader->skin;
    94 			$skin = $upgrader->skin;
    96 		} else {
    95 		} else {
    97 			$skin = new Language_Pack_Upgrader_Skin( array(
    96 			$skin = new Language_Pack_Upgrader_Skin(
    98 				'skip_header_footer' => true,
    97 				array(
    99 			) );
    98 					'skip_header_footer' => true,
       
    99 				)
       
   100 			);
   100 		}
   101 		}
   101 
   102 
   102 		$lp_upgrader = new Language_Pack_Upgrader( $skin );
   103 		$lp_upgrader = new Language_Pack_Upgrader( $skin );
   103 		$lp_upgrader->bulk_upgrade( $language_updates );
   104 		$lp_upgrader->bulk_upgrade( $language_updates );
   104 	}
   105 	}
   108 	 *
   109 	 *
   109 	 * @since 3.7.0
   110 	 * @since 3.7.0
   110 	 */
   111 	 */
   111 	public function upgrade_strings() {
   112 	public function upgrade_strings() {
   112 		$this->strings['starting_upgrade'] = __( 'Some of your translations need updating. Sit tight for a few more seconds while we update them as well.' );
   113 		$this->strings['starting_upgrade'] = __( 'Some of your translations need updating. Sit tight for a few more seconds while we update them as well.' );
   113 		$this->strings['up_to_date'] = __( 'The translations are up to date.' );
   114 		$this->strings['up_to_date']       = __( 'The translations are up to date.' );
   114 		$this->strings['no_package'] = __( 'Update package not available.' );
   115 		$this->strings['no_package']       = __( 'Update package not available.' );
   115 		/* translators: %s: package URL */
   116 		/* translators: %s: package URL */
   116 		$this->strings['downloading_package'] = sprintf( __( 'Downloading translation from %s&#8230;' ), '<span class="code">%s</span>' );
   117 		$this->strings['downloading_package'] = sprintf( __( 'Downloading translation from %s&#8230;' ), '<span class="code">%s</span>' );
   117 		$this->strings['unpack_package'] = __( 'Unpacking the update&#8230;' );
   118 		$this->strings['unpack_package']      = __( 'Unpacking the update&#8230;' );
   118 		$this->strings['process_failed'] = __( 'Translation update failed.' );
   119 		$this->strings['process_failed']      = __( 'Translation update failed.' );
   119 		$this->strings['process_success'] = __( 'Translation updated successfully.' );
   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.' );
   120 	}
   123 	}
   121 
   124 
   122 	/**
   125 	/**
   123 	 * Upgrade a language pack.
   126 	 * Upgrade a language pack.
   124 	 *
   127 	 *
   146 	/**
   149 	/**
   147 	 * Bulk upgrade language packs.
   150 	 * Bulk upgrade language packs.
   148 	 *
   151 	 *
   149 	 * @since 3.7.0
   152 	 * @since 3.7.0
   150 	 *
   153 	 *
   151 	 * @global WP_Filesystem_Base $wp_filesystem Subclass
   154 	 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
   152 	 *
   155 	 *
   153 	 * @param array $language_updates Optional. Language pack updates. Default empty array.
   156 	 * @param object[] $language_updates Optional. Array of language packs to update. @see wp_get_translation_updates().
   154 	 * @param array $args {
   157 	 *                                   Default empty array.
   155 	 *     Optional. Other arguments for upgrading multiple language packs. Default empty array
   158 	 * @param array    $args {
       
   159 	 *     Other arguments for upgrading multiple language packs. Default empty array.
   156 	 *
   160 	 *
   157 	 *     @type bool $clear_update_cache Whether to clear the update cache when done.
   161 	 *     @type bool $clear_update_cache Whether to clear the update cache when done.
   158 	 *                                    Default true.
   162 	 *                                    Default true.
   159 	 * }
   163 	 * }
   160 	 * @return array|bool|WP_Error Will return an array of results, or true if there are no updates,
   164 	 * @return array|bool|WP_Error Will return an array of results, or true if there are no updates,
   161 	 *                                   false or WP_Error for initial errors.
   165 	 *                             false or WP_Error for initial errors.
   162 	 */
   166 	 */
   163 	public function bulk_upgrade( $language_updates = array(), $args = array() ) {
   167 	public function bulk_upgrade( $language_updates = array(), $args = array() ) {
   164 		global $wp_filesystem;
   168 		global $wp_filesystem;
   165 
   169 
   166 		$defaults = array(
   170 		$defaults    = array(
   167 			'clear_update_cache' => true,
   171 			'clear_update_cache' => true,
   168 		);
   172 		);
   169 		$parsed_args = wp_parse_args( $args, $defaults );
   173 		$parsed_args = wp_parse_args( $args, $defaults );
   170 
   174 
   171 		$this->init();
   175 		$this->init();
   172 		$this->upgrade_strings();
   176 		$this->upgrade_strings();
   173 
   177 
   174 		if ( ! $language_updates )
   178 		if ( ! $language_updates ) {
   175 			$language_updates = wp_get_translation_updates();
   179 			$language_updates = wp_get_translation_updates();
       
   180 		}
   176 
   181 
   177 		if ( empty( $language_updates ) ) {
   182 		if ( empty( $language_updates ) ) {
   178 			$this->skin->header();
   183 			$this->skin->header();
   179 			$this->skin->set_result( true );
   184 			$this->skin->set_result( true );
   180 			$this->skin->feedback( 'up_to_date' );
   185 			$this->skin->feedback( 'up_to_date' );
   181 			$this->skin->bulk_footer();
   186 			$this->skin->bulk_footer();
   182 			$this->skin->footer();
   187 			$this->skin->footer();
   183 			return true;
   188 			return true;
   184 		}
   189 		}
   185 
   190 
   186 		if ( 'upgrader_process_complete' == current_filter() )
   191 		if ( 'upgrader_process_complete' == current_filter() ) {
   187 			$this->skin->feedback( 'starting_upgrade' );
   192 			$this->skin->feedback( 'starting_upgrade' );
       
   193 		}
   188 
   194 
   189 		// Remove any existing upgrade filters from the plugin/theme upgraders #WP29425 & #WP29230
   195 		// Remove any existing upgrade filters from the plugin/theme upgraders #WP29425 & #WP29230
   190 		remove_all_filters( 'upgrader_pre_install' );
   196 		remove_all_filters( 'upgrader_pre_install' );
   191 		remove_all_filters( 'upgrader_clear_destination' );
   197 		remove_all_filters( 'upgrader_clear_destination' );
   192 		remove_all_filters( 'upgrader_post_install' );
   198 		remove_all_filters( 'upgrader_post_install' );
   203 			return false;
   209 			return false;
   204 		}
   210 		}
   205 
   211 
   206 		$results = array();
   212 		$results = array();
   207 
   213 
   208 		$this->update_count = count( $language_updates );
   214 		$this->update_count   = count( $language_updates );
   209 		$this->update_current = 0;
   215 		$this->update_current = 0;
   210 
   216 
   211 		/*
   217 		/*
   212 		 * The filesystem's mkdir() is not recursive. Make sure WP_LANG_DIR exists,
   218 		 * The filesystem's mkdir() is not recursive. Make sure WP_LANG_DIR exists,
   213 		 * as we then may need to create a /plugins or /themes directory inside of it.
   219 		 * as we then may need to create a /plugins or /themes directory inside of it.
   214 		 */
   220 		 */
   215 		$remote_destination = $wp_filesystem->find_folder( WP_LANG_DIR );
   221 		$remote_destination = $wp_filesystem->find_folder( WP_LANG_DIR );
   216 		if ( ! $wp_filesystem->exists( $remote_destination ) )
   222 		if ( ! $wp_filesystem->exists( $remote_destination ) ) {
   217 			if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) )
   223 			if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) {
   218 				return new WP_Error( 'mkdir_failed_lang_dir', $this->strings['mkdir_failed'], $remote_destination );
   224 				return new WP_Error( 'mkdir_failed_lang_dir', $this->strings['mkdir_failed'], $remote_destination );
       
   225 			}
       
   226 		}
   219 
   227 
   220 		$language_updates_results = array();
   228 		$language_updates_results = array();
   221 
   229 
   222 		foreach ( $language_updates as $language_update ) {
   230 		foreach ( $language_updates as $language_update ) {
   223 
   231 
   224 			$this->skin->language_update = $language_update;
   232 			$this->skin->language_update = $language_update;
   225 
   233 
   226 			$destination = WP_LANG_DIR;
   234 			$destination = WP_LANG_DIR;
   227 			if ( 'plugin' == $language_update->type )
   235 			if ( 'plugin' == $language_update->type ) {
   228 				$destination .= '/plugins';
   236 				$destination .= '/plugins';
   229 			elseif ( 'theme' == $language_update->type )
   237 			} elseif ( 'theme' == $language_update->type ) {
   230 				$destination .= '/themes';
   238 				$destination .= '/themes';
       
   239 			}
   231 
   240 
   232 			$this->update_current++;
   241 			$this->update_current++;
   233 
   242 
   234 			$options = array(
   243 			$options = array(
   235 				'package' => $language_update->package,
   244 				'package'                     => $language_update->package,
   236 				'destination' => $destination,
   245 				'destination'                 => $destination,
   237 				'clear_destination' => false,
   246 				'clear_destination'           => true,
   238 				'abort_if_destination_exists' => false, // We expect the destination to exist.
   247 				'abort_if_destination_exists' => false, // We expect the destination to exist.
   239 				'clear_working' => true,
   248 				'clear_working'               => true,
   240 				'is_multi' => true,
   249 				'is_multi'                    => true,
   241 				'hook_extra' => array(
   250 				'hook_extra'                  => array(
   242 					'language_update_type' => $language_update->type,
   251 					'language_update_type' => $language_update->type,
   243 					'language_update' => $language_update,
   252 					'language_update'      => $language_update,
   244 				)
   253 				),
   245 			);
   254 			);
   246 
   255 
   247 			$result = $this->run( $options );
   256 			$result = $this->run( $options );
   248 
   257 
   249 			$results[] = $this->result;
   258 			$results[] = $this->result;
   266 		remove_action( 'upgrader_process_complete', 'wp_version_check' );
   275 		remove_action( 'upgrader_process_complete', 'wp_version_check' );
   267 		remove_action( 'upgrader_process_complete', 'wp_update_plugins' );
   276 		remove_action( 'upgrader_process_complete', 'wp_update_plugins' );
   268 		remove_action( 'upgrader_process_complete', 'wp_update_themes' );
   277 		remove_action( 'upgrader_process_complete', 'wp_update_themes' );
   269 
   278 
   270 		/** This action is documented in wp-admin/includes/class-wp-upgrader.php */
   279 		/** This action is documented in wp-admin/includes/class-wp-upgrader.php */
   271 		do_action( 'upgrader_process_complete', $this, array(
   280 		do_action(
   272 			'action'       => 'update',
   281 			'upgrader_process_complete',
   273 			'type'         => 'translation',
   282 			$this,
   274 			'bulk'         => true,
   283 			array(
   275 			'translations' => $language_updates_results
   284 				'action'       => 'update',
   276 		) );
   285 				'type'         => 'translation',
       
   286 				'bulk'         => true,
       
   287 				'translations' => $language_updates_results,
       
   288 			)
       
   289 		);
   277 
   290 
   278 		// Re-add upgrade hooks.
   291 		// Re-add upgrade hooks.
   279 		add_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
   292 		add_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
   280 		add_action( 'upgrader_process_complete', 'wp_version_check', 10, 0 );
   293 		add_action( 'upgrader_process_complete', 'wp_version_check', 10, 0 );
   281 		add_action( 'upgrader_process_complete', 'wp_update_plugins', 10, 0 );
   294 		add_action( 'upgrader_process_complete', 'wp_update_plugins', 10, 0 );
   309 	 * @param string          $remote_source
   322 	 * @param string          $remote_source
   310 	 */
   323 	 */
   311 	public function check_package( $source, $remote_source ) {
   324 	public function check_package( $source, $remote_source ) {
   312 		global $wp_filesystem;
   325 		global $wp_filesystem;
   313 
   326 
   314 		if ( is_wp_error( $source ) )
   327 		if ( is_wp_error( $source ) ) {
   315 			return $source;
   328 			return $source;
       
   329 		}
   316 
   330 
   317 		// Check that the folder contains a valid language.
   331 		// Check that the folder contains a valid language.
   318 		$files = $wp_filesystem->dirlist( $remote_source );
   332 		$files = $wp_filesystem->dirlist( $remote_source );
   319 
   333 
   320 		// Check to see if a .po and .mo exist in the folder.
   334 		// Check to see if a .po and .mo exist in the folder.
   321 		$po = $mo = false;
   335 		$po = $mo = false;
   322 		foreach ( (array) $files as $file => $filedata ) {
   336 		foreach ( (array) $files as $file => $filedata ) {
   323 			if ( '.po' == substr( $file, -3 ) )
   337 			if ( '.po' == substr( $file, -3 ) ) {
   324 				$po = true;
   338 				$po = true;
   325 			elseif ( '.mo' == substr( $file, -3 ) )
   339 			} elseif ( '.mo' == substr( $file, -3 ) ) {
   326 				$mo = true;
   340 				$mo = true;
       
   341 			}
   327 		}
   342 		}
   328 
   343 
   329 		if ( ! $mo || ! $po ) {
   344 		if ( ! $mo || ! $po ) {
   330 			return new WP_Error( 'incompatible_archive_pomo', $this->strings['incompatible_archive'],
   345 			return new WP_Error(
   331 				/* translators: 1: .po 2: .mo */
   346 				'incompatible_archive_pomo',
   332 				sprintf( __( 'The language pack is missing either the %1$s or %2$s files.' ),
   347 				$this->strings['incompatible_archive'],
       
   348 				sprintf(
       
   349 					/* translators: 1: .po, 2: .mo */
       
   350 					__( 'The language pack is missing either the %1$s or %2$s files.' ),
   333 					'<code>.po</code>',
   351 					'<code>.po</code>',
   334 					'<code>.mo</code>'
   352 					'<code>.mo</code>'
   335 				)
   353 				)
   336 			);
   354 			);
   337 		}
   355 		}
   352 			case 'core':
   370 			case 'core':
   353 				return 'WordPress'; // Not translated
   371 				return 'WordPress'; // Not translated
   354 
   372 
   355 			case 'theme':
   373 			case 'theme':
   356 				$theme = wp_get_theme( $update->slug );
   374 				$theme = wp_get_theme( $update->slug );
   357 				if ( $theme->exists() )
   375 				if ( $theme->exists() ) {
   358 					return $theme->Get( 'Name' );
   376 					return $theme->Get( 'Name' );
       
   377 				}
   359 				break;
   378 				break;
   360 			case 'plugin':
   379 			case 'plugin':
   361 				$plugin_data = get_plugins( '/' . $update->slug );
   380 				$plugin_data = get_plugins( '/' . $update->slug );
   362 				$plugin_data = reset( $plugin_data );
   381 				$plugin_data = reset( $plugin_data );
   363 				if ( $plugin_data )
   382 				if ( $plugin_data ) {
   364 					return $plugin_data['Name'];
   383 					return $plugin_data['Name'];
       
   384 				}
   365 				break;
   385 				break;
   366 		}
   386 		}
   367 		return '';
   387 		return '';
   368 	}
   388 	}
   369 
   389 
       
   390 	/**
       
   391 	 * Clears existing translations where this item is going to be installed into.
       
   392 	 *
       
   393 	 * @since 5.1.0
       
   394 	 *
       
   395 	 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
       
   396 	 *
       
   397 	 * @param string $remote_destination The location on the remote filesystem to be cleared.
       
   398 	 * @return bool|WP_Error True upon success, WP_Error on failure.
       
   399 	 */
       
   400 	public function clear_destination( $remote_destination ) {
       
   401 		global $wp_filesystem;
       
   402 
       
   403 		$language_update    = $this->skin->language_update;
       
   404 		$language_directory = WP_LANG_DIR . '/'; // Local path for use with glob().
       
   405 
       
   406 		if ( 'core' === $language_update->type ) {
       
   407 			$files = array(
       
   408 				$remote_destination . $language_update->language . '.po',
       
   409 				$remote_destination . $language_update->language . '.mo',
       
   410 				$remote_destination . 'admin-' . $language_update->language . '.po',
       
   411 				$remote_destination . 'admin-' . $language_update->language . '.mo',
       
   412 				$remote_destination . 'admin-network-' . $language_update->language . '.po',
       
   413 				$remote_destination . 'admin-network-' . $language_update->language . '.mo',
       
   414 				$remote_destination . 'continents-cities-' . $language_update->language . '.po',
       
   415 				$remote_destination . 'continents-cities-' . $language_update->language . '.mo',
       
   416 			);
       
   417 
       
   418 			$json_translation_files = glob( $language_directory . $language_update->language . '-*.json' );
       
   419 			if ( $json_translation_files ) {
       
   420 				foreach ( $json_translation_files as $json_translation_file ) {
       
   421 					$files[] = str_replace( $language_directory, $remote_destination, $json_translation_file );
       
   422 				}
       
   423 			}
       
   424 		} else {
       
   425 			$files = array(
       
   426 				$remote_destination . $language_update->slug . '-' . $language_update->language . '.po',
       
   427 				$remote_destination . $language_update->slug . '-' . $language_update->language . '.mo',
       
   428 			);
       
   429 
       
   430 			$language_directory     = $language_directory . $language_update->type . 's/';
       
   431 			$json_translation_files = glob( $language_directory . $language_update->slug . '-' . $language_update->language . '-*.json' );
       
   432 			if ( $json_translation_files ) {
       
   433 				foreach ( $json_translation_files as $json_translation_file ) {
       
   434 					$files[] = str_replace( $language_directory, $remote_destination, $json_translation_file );
       
   435 				}
       
   436 			}
       
   437 		}
       
   438 
       
   439 		$files = array_filter( $files, array( $wp_filesystem, 'exists' ) );
       
   440 
       
   441 		// No files to delete.
       
   442 		if ( ! $files ) {
       
   443 			return true;
       
   444 		}
       
   445 
       
   446 		// Check all files are writable before attempting to clear the destination.
       
   447 		$unwritable_files = array();
       
   448 
       
   449 		// Check writability.
       
   450 		foreach ( $files as $file ) {
       
   451 			if ( ! $wp_filesystem->is_writable( $file ) ) {
       
   452 				// Attempt to alter permissions to allow writes and try again.
       
   453 				$wp_filesystem->chmod( $file, FS_CHMOD_FILE );
       
   454 				if ( ! $wp_filesystem->is_writable( $file ) ) {
       
   455 					$unwritable_files[] = $file;
       
   456 				}
       
   457 			}
       
   458 		}
       
   459 
       
   460 		if ( ! empty( $unwritable_files ) ) {
       
   461 			return new WP_Error( 'files_not_writable', $this->strings['files_not_writable'], implode( ', ', $unwritable_files ) );
       
   462 		}
       
   463 
       
   464 		foreach ( $files as $file ) {
       
   465 			if ( ! $wp_filesystem->delete( $file ) ) {
       
   466 				return new WP_Error( 'remove_old_failed', $this->strings['remove_old_failed'] );
       
   467 			}
       
   468 		}
       
   469 
       
   470 		return true;
       
   471 	}
   370 }
   472 }