run:R W Run
7.85 KB
2026-03-11 16:18:51
R W Run
3.54 KB
2026-03-11 16:18:51
R W Run
148.33 KB
2026-03-11 16:18:51
R W Run
11.45 KB
2026-03-11 16:18:51
R W Run
3.58 KB
2026-03-11 16:18:51
R W Run
2.53 KB
2026-03-11 16:18:51
R W Run
2.6 KB
2026-03-11 16:18:51
R W Run
6.59 KB
2026-03-11 16:18:51
R W Run
14.83 KB
2026-03-11 16:18:51
R W Run
21.18 KB
2026-03-11 16:18:51
R W Run
48.13 KB
2026-03-11 16:18:51
R W Run
4.07 KB
2026-03-11 16:18:51
R W Run
5.3 KB
2026-03-11 16:18:51
R W Run
8.28 KB
2026-03-11 16:18:51
R W Run
26.73 KB
2026-03-11 16:18:51
R W Run
2.8 KB
2026-03-11 16:18:51
R W Run
15.2 KB
2026-03-11 16:18:51
R W Run
192.08 KB
2026-03-11 16:18:51
R W Run
11.77 KB
2026-03-11 16:18:51
R W Run
3.2 KB
2026-03-11 16:18:51
R W Run
22.89 KB
2026-03-11 16:18:51
R W Run
12.77 KB
2026-03-11 16:18:51
R W Run
4.08 KB
2026-03-11 16:18:51
R W Run
26.27 KB
2026-03-11 16:18:51
R W Run
4.97 KB
2026-03-11 16:18:51
R W Run
5.57 KB
2026-03-11 16:18:51
R W Run
13.93 KB
2026-03-11 16:18:51
R W Run
4.09 KB
2026-03-11 16:18:51
R W Run
6.79 KB
2026-03-11 16:18:51
R W Run
60.45 KB
2026-03-11 16:18:51
R W Run
32.4 KB
2026-03-11 16:18:51
R W Run
18.24 KB
2026-03-11 16:18:51
R W Run
66.01 KB
2026-03-11 16:18:51
R W Run
23.84 KB
2026-03-11 16:18:51
R W Run
17.72 KB
2026-03-11 16:18:51
R W Run
22.71 KB
2026-03-11 16:18:51
R W Run
18.05 KB
2026-03-11 16:18:51
R W Run
22.76 KB
2026-03-11 16:18:51
R W Run
7.34 KB
2026-03-11 16:18:51
R W Run
4.51 KB
2026-03-11 16:18:51
R W Run
9.02 KB
2026-03-11 16:18:51
R W Run
1.46 KB
2026-03-11 16:18:51
R W Run
51.76 KB
2026-03-11 16:18:51
R W Run
25.29 KB
2026-03-11 16:18:51
R W Run
21.61 KB
2026-03-11 16:18:51
R W Run
27.77 KB
2026-03-11 16:18:51
R W Run
15.35 KB
2026-03-11 16:18:51
R W Run
24.54 KB
2026-03-11 16:18:51
R W Run
56.44 KB
2026-03-11 16:18:51
R W Run
1.42 KB
2026-03-11 16:18:51
R W Run
63.66 KB
2026-03-11 16:18:51
R W Run
31.9 KB
2026-03-11 16:18:51
R W Run
14.44 KB
2026-03-11 16:18:51
R W Run
36.47 KB
2026-03-11 16:18:51
R W Run
14 KB
2026-03-11 16:18:51
R W Run
121.89 KB
2026-03-11 16:18:51
R W Run
6.26 KB
2026-03-11 16:18:51
R W Run
20.73 KB
2026-03-11 16:18:51
R W Run
15.23 KB
2026-03-11 16:18:51
R W Run
10.14 KB
2026-03-11 16:18:51
R W Run
6.94 KB
2026-03-11 16:18:51
R W Run
1.44 KB
2026-03-11 16:18:51
R W Run
46.85 KB
2026-03-11 16:18:51
R W Run
18.61 KB
2026-03-11 16:18:51
R W Run
6.08 KB
2026-03-11 16:18:51
R W Run
20.06 KB
2026-03-11 16:18:51
R W Run
5.73 KB
2026-03-11 16:18:51
R W Run
68.18 KB
2026-03-11 16:18:51
R W Run
40.8 KB
2026-03-11 16:18:51
R W Run
1.44 KB
2026-03-11 16:18:51
R W Run
25.26 KB
2026-03-11 16:18:51
R W Run
95.94 KB
2026-03-11 16:18:51
R W Run
43.12 KB
2026-03-11 16:18:51
R W Run
41.73 KB
2026-03-11 16:18:51
R W Run
6.46 KB
2026-03-11 16:18:51
R W Run
3.71 KB
2026-03-11 16:18:51
R W Run
116.31 KB
2026-03-11 16:18:51
R W Run
9.39 KB
2026-03-11 16:18:51
R W Run
64.34 KB
2026-03-11 16:18:51
R W Run
44.73 KB
2026-03-11 16:18:51
R W Run
1.27 KB
2026-03-11 16:18:51
R W Run
3.68 KB
2026-03-11 16:18:51
R W Run
33.53 KB
2026-03-11 16:18:51
R W Run
48.84 KB
2026-03-11 16:18:51
R W Run
26.35 KB
2026-03-11 16:18:51
R W Run
1.12 KB
2026-03-11 16:18:51
R W Run
4.19 KB
2026-03-11 16:18:51
R W Run
38.19 KB
2026-03-11 16:18:51
R W Run
91.33 KB
2026-03-11 16:18:51
R W Run
80.39 KB
2026-03-11 16:18:51
R W Run
32.67 KB
2026-03-11 16:18:51
R W Run
16.18 KB
2026-03-11 16:18:51
R W Run
44.46 KB
2026-03-11 16:18:51
R W Run
6.23 KB
2026-03-11 16:18:51
R W Run
8.23 KB
2026-03-11 16:18:51
R W Run
96.96 KB
2026-03-11 16:18:51
R W Run
6.83 KB
2026-03-11 16:18:51
R W Run
46.62 KB
2026-03-11 16:18:51
R W Run
10.82 KB
2026-03-11 16:18:51
R W Run
68.86 KB
2026-03-11 16:18:51
R W Run
33.63 KB
2026-03-11 16:18:51
R W Run
113.3 KB
2026-03-11 16:18:51
R W Run
22.98 KB
2026-03-11 16:18:51
R W Run
10.66 KB
2026-03-11 16:18:51
R W Run
error_log
📄class-wp-upgrader.php
1<?php
2/**
3 * Upgrade API: WP_Upgrader class
4 *
5 * Requires skin classes and WP_Upgrader subclasses for backward compatibility.
6 *
7 * @package WordPress
8 * @subpackage Upgrader
9 * @since 2.8.0
10 */
11
12/** WP_Upgrader_Skin class */
13require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skin.php';
14
15/** Plugin_Upgrader_Skin class */
16require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader-skin.php';
17
18/** Theme_Upgrader_Skin class */
19require_once ABSPATH . 'wp-admin/includes/class-theme-upgrader-skin.php';
20
21/** Bulk_Upgrader_Skin class */
22require_once ABSPATH . 'wp-admin/includes/class-bulk-upgrader-skin.php';
23
24/** Bulk_Plugin_Upgrader_Skin class */
25require_once ABSPATH . 'wp-admin/includes/class-bulk-plugin-upgrader-skin.php';
26
27/** Bulk_Theme_Upgrader_Skin class */
28require_once ABSPATH . 'wp-admin/includes/class-bulk-theme-upgrader-skin.php';
29
30/** Plugin_Installer_Skin class */
31require_once ABSPATH . 'wp-admin/includes/class-plugin-installer-skin.php';
32
33/** Theme_Installer_Skin class */
34require_once ABSPATH . 'wp-admin/includes/class-theme-installer-skin.php';
35
36/** Language_Pack_Upgrader_Skin class */
37require_once ABSPATH . 'wp-admin/includes/class-language-pack-upgrader-skin.php';
38
39/** Automatic_Upgrader_Skin class */
40require_once ABSPATH . 'wp-admin/includes/class-automatic-upgrader-skin.php';
41
42/** WP_Ajax_Upgrader_Skin class */
43require_once ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php';
44
45/**
46 * Core class used for upgrading/installing a local set of files via
47 * the Filesystem Abstraction classes from a Zip file.
48 *
49 * @since 2.8.0
50 */
51#[AllowDynamicProperties]
52class WP_Upgrader {
53
54 /**
55 * The error/notification strings used to update the user on the progress.
56 *
57 * @since 2.8.0
58 * @var array $strings
59 */
60 public $strings = array();
61
62 /**
63 * The upgrader skin being used.
64 *
65 * @since 2.8.0
66 * @var Automatic_Upgrader_Skin|WP_Upgrader_Skin $skin
67 */
68 public $skin = null;
69
70 /**
71 * The result of the installation.
72 *
73 * This is set by WP_Upgrader::install_package(), only when the package is installed
74 * successfully. It will then be an array, unless a WP_Error is returned by the
75 * {@see 'upgrader_post_install'} filter. In that case, the WP_Error will be assigned to
76 * it.
77 *
78 * @since 2.8.0
79 *
80 * @var array|WP_Error $result {
81 * @type string $source The full path to the source the files were installed from.
82 * @type string $source_files List of all the files in the source directory.
83 * @type string $destination The full path to the installation destination folder.
84 * @type string $destination_name The name of the destination folder, or empty if `$destination`
85 * and `$local_destination` are the same.
86 * @type string $local_destination The full local path to the destination folder. This is usually
87 * the same as `$destination`.
88 * @type string $remote_destination The full remote path to the destination folder
89 * (i.e., from `$wp_filesystem`).
90 * @type bool $clear_destination Whether the destination folder was cleared.
91 * }
92 */
93 public $result = array();
94
95 /**
96 * The total number of updates being performed.
97 *
98 * Set by the bulk update methods.
99 *
100 * @since 3.0.0
101 * @var int $update_count
102 */
103 public $update_count = 0;
104
105 /**
106 * The current update if multiple updates are being performed.
107 *
108 * Used by the bulk update methods, and incremented for each update.
109 *
110 * @since 3.0.0
111 * @var int
112 */
113 public $update_current = 0;
114
115 /**
116 * Stores the list of plugins or themes added to temporary backup directory.
117 *
118 * Used by the rollback functions.
119 *
120 * @since 6.3.0
121 * @var array
122 */
123 private $temp_backups = array();
124
125 /**
126 * Stores the list of plugins or themes to be restored from temporary backup directory.
127 *
128 * Used by the rollback functions.
129 *
130 * @since 6.3.0
131 * @var array
132 */
133 private $temp_restores = array();
134
135 /**
136 * Construct the upgrader with a skin.
137 *
138 * @since 2.8.0
139 *
140 * @param WP_Upgrader_Skin $skin The upgrader skin to use. Default is a WP_Upgrader_Skin
141 * instance.
142 */
143 public function __construct( $skin = null ) {
144 if ( null === $skin ) {
145 $this->skin = new WP_Upgrader_Skin();
146 } else {
147 $this->skin = $skin;
148 }
149 }
150
151 /**
152 * Initializes the upgrader.
153 *
154 * This will set the relationship between the skin being used and this upgrader,
155 * and also add the generic strings to `WP_Upgrader::$strings`.
156 *
157 * Additionally, it will schedule a weekly task to clean up the temporary backup directory.
158 *
159 * @since 2.8.0
160 * @since 6.3.0 Added the `schedule_temp_backup_cleanup()` task.
161 */
162 public function init() {
163 $this->skin->set_upgrader( $this );
164 $this->generic_strings();
165
166 if ( ! wp_installing() ) {
167 $this->schedule_temp_backup_cleanup();
168 }
169 }
170
171 /**
172 * Schedules the cleanup of the temporary backup directory.
173 *
174 * @since 6.3.0
175 */
176 protected function schedule_temp_backup_cleanup() {
177 if ( false === wp_next_scheduled( 'wp_delete_temp_updater_backups' ) ) {
178 wp_schedule_event( time(), 'weekly', 'wp_delete_temp_updater_backups' );
179 }
180 }
181
182 /**
183 * Adds the generic strings to WP_Upgrader::$strings.
184 *
185 * @since 2.8.0
186 */
187 public function generic_strings() {
188 $this->strings['bad_request'] = __( 'Invalid data provided.' );
189 $this->strings['fs_unavailable'] = __( 'Could not access filesystem.' );
190 $this->strings['fs_error'] = __( 'Filesystem error.' );
191 $this->strings['fs_no_root_dir'] = __( 'Unable to locate WordPress root directory.' );
192 /* translators: %s: Directory name. */
193 $this->strings['fs_no_content_dir'] = sprintf( __( 'Unable to locate WordPress content directory (%s).' ), 'wp-content' );
194 $this->strings['fs_no_plugins_dir'] = __( 'Unable to locate WordPress plugin directory.' );
195 $this->strings['fs_no_themes_dir'] = __( 'Unable to locate WordPress theme directory.' );
196 /* translators: %s: Directory name. */
197 $this->strings['fs_no_folder'] = __( 'Unable to locate needed folder (%s).' );
198
199 $this->strings['no_package'] = __( 'Package not available.' );
200 $this->strings['download_failed'] = __( 'Download failed.' );
201 $this->strings['installing_package'] = __( 'Installing the latest version&#8230;' );
202 $this->strings['no_files'] = __( 'The package contains no files.' );
203 $this->strings['folder_exists'] = __( 'Destination folder already exists.' );
204 $this->strings['mkdir_failed'] = __( 'Could not create directory.' );
205 $this->strings['incompatible_archive'] = __( 'The package could not be installed.' );
206 $this->strings['files_not_writable'] = __( 'The update cannot be installed because some files could not be copied. This is usually due to inconsistent file permissions.' );
207 $this->strings['dir_not_readable'] = __( 'A directory could not be read.' );
208
209 $this->strings['maintenance_start'] = __( 'Enabling Maintenance mode&#8230;' );
210 $this->strings['maintenance_end'] = __( 'Disabling Maintenance mode&#8230;' );
211
212 /* translators: %s: upgrade-temp-backup */
213 $this->strings['temp_backup_mkdir_failed'] = sprintf( __( 'Could not create the %s directory.' ), 'upgrade-temp-backup' );
214 /* translators: %s: upgrade-temp-backup */
215 $this->strings['temp_backup_move_failed'] = sprintf( __( 'Could not move the old version to the %s directory.' ), 'upgrade-temp-backup' );
216 /* translators: %s: The plugin or theme slug. */
217 $this->strings['temp_backup_restore_failed'] = __( 'Could not restore the original version of %s.' );
218 /* translators: %s: The plugin or theme slug. */
219 $this->strings['temp_backup_delete_failed'] = __( 'Could not delete the temporary backup directory for %s.' );
220 }
221
222 /**
223 * Connects to the filesystem.
224 *
225 * @since 2.8.0
226 *
227 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
228 *
229 * @param string[] $directories Optional. Array of directories. If any of these do
230 * not exist, a WP_Error object will be returned.
231 * Default empty array.
232 * @param bool $allow_relaxed_file_ownership Whether to allow relaxed file ownership.
233 * Default false.
234 * @return bool|WP_Error True if able to connect, false or a WP_Error otherwise.
235 */
236 public function fs_connect( $directories = array(), $allow_relaxed_file_ownership = false ) {
237 global $wp_filesystem;
238
239 $credentials = $this->skin->request_filesystem_credentials( false, $directories[0], $allow_relaxed_file_ownership );
240 if ( false === $credentials ) {
241 return false;
242 }
243
244 if ( ! WP_Filesystem( $credentials, $directories[0], $allow_relaxed_file_ownership ) ) {
245 $error = true;
246 if ( is_object( $wp_filesystem ) && $wp_filesystem->errors->has_errors() ) {
247 $error = $wp_filesystem->errors;
248 }
249 // Failed to connect. Error and request again.
250 $this->skin->request_filesystem_credentials( $error, $directories[0], $allow_relaxed_file_ownership );
251 return false;
252 }
253
254 if ( ! is_object( $wp_filesystem ) ) {
255 return new WP_Error( 'fs_unavailable', $this->strings['fs_unavailable'] );
256 }
257
258 if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) {
259 return new WP_Error( 'fs_error', $this->strings['fs_error'], $wp_filesystem->errors );
260 }
261
262 foreach ( (array) $directories as $dir ) {
263 switch ( $dir ) {
264 case ABSPATH:
265 if ( ! $wp_filesystem->abspath() ) {
266 return new WP_Error( 'fs_no_root_dir', $this->strings['fs_no_root_dir'] );
267 }
268 break;
269 case WP_CONTENT_DIR:
270 if ( ! $wp_filesystem->wp_content_dir() ) {
271 return new WP_Error( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] );
272 }
273 break;
274 case WP_PLUGIN_DIR:
275 if ( ! $wp_filesystem->wp_plugins_dir() ) {
276 return new WP_Error( 'fs_no_plugins_dir', $this->strings['fs_no_plugins_dir'] );
277 }
278 break;
279 case get_theme_root():
280 if ( ! $wp_filesystem->wp_themes_dir() ) {
281 return new WP_Error( 'fs_no_themes_dir', $this->strings['fs_no_themes_dir'] );
282 }
283 break;
284 default:
285 if ( ! $wp_filesystem->find_folder( $dir ) ) {
286 return new WP_Error( 'fs_no_folder', sprintf( $this->strings['fs_no_folder'], esc_html( basename( $dir ) ) ) );
287 }
288 break;
289 }
290 }
291 return true;
292 }
293
294 /**
295 * Downloads a package.
296 *
297 * @since 2.8.0
298 * @since 5.2.0 Added the `$check_signatures` parameter.
299 * @since 5.5.0 Added the `$hook_extra` parameter.
300 *
301 * @param string $package The URI of the package. If this is the full path to an
302 * existing local file, it will be returned untouched.
303 * @param bool $check_signatures Whether to validate file signatures. Default false.
304 * @param array $hook_extra Extra arguments to pass to the filter hooks. Default empty array.
305 * @return string|WP_Error The full path to the downloaded package file, or a WP_Error object.
306 */
307 public function download_package( $package, $check_signatures = false, $hook_extra = array() ) {
308 /**
309 * Filters whether to return the package.
310 *
311 * @since 3.7.0
312 * @since 5.5.0 Added the `$hook_extra` parameter.
313 *
314 * @param bool $reply Whether to bail without returning the package.
315 * Default false.
316 * @param string $package The package file name.
317 * @param WP_Upgrader $upgrader The WP_Upgrader instance.
318 * @param array $hook_extra Extra arguments passed to hooked filters.
319 */
320 $reply = apply_filters( 'upgrader_pre_download', false, $package, $this, $hook_extra );
321 if ( false !== $reply ) {
322 return $reply;
323 }
324
325 if ( ! preg_match( '!^(http|https|ftp)://!i', $package ) && file_exists( $package ) ) { // Local file or remote?
326 return $package; // Must be a local file.
327 }
328
329 if ( empty( $package ) ) {
330 return new WP_Error( 'no_package', $this->strings['no_package'] );
331 }
332
333 $this->skin->feedback( 'downloading_package', $package );
334
335 $download_file = download_url( $package, 300, $check_signatures );
336
337 if ( is_wp_error( $download_file ) && ! $download_file->get_error_data( 'softfail-filename' ) ) {
338 return new WP_Error( 'download_failed', $this->strings['download_failed'], $download_file->get_error_message() );
339 }
340
341 return $download_file;
342 }
343
344 /**
345 * Unpacks a compressed package file.
346 *
347 * @since 2.8.0
348 *
349 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
350 *
351 * @param string $package Full path to the package file.
352 * @param bool $delete_package Optional. Whether to delete the package file after attempting
353 * to unpack it. Default true.
354 * @return string|WP_Error The path to the unpacked contents, or a WP_Error on failure.
355 */
356 public function unpack_package( $package, $delete_package = true ) {
357 global $wp_filesystem;
358
359 $this->skin->feedback( 'unpack_package' );
360
361 if ( ! $wp_filesystem->wp_content_dir() ) {
362 return new WP_Error( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] );
363 }
364
365 $upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/';
366
367 // Clean up contents of upgrade directory beforehand.
368 $upgrade_files = $wp_filesystem->dirlist( $upgrade_folder );
369 if ( ! empty( $upgrade_files ) ) {
370 foreach ( $upgrade_files as $file ) {
371 $wp_filesystem->delete( $upgrade_folder . $file['name'], true );
372 }
373 }
374
375 // We need a working directory - strip off any .tmp or .zip suffixes.
376 $working_dir = $upgrade_folder . basename( basename( $package, '.tmp' ), '.zip' );
377
378 // Clean up working directory.
379 if ( $wp_filesystem->is_dir( $working_dir ) ) {
380 $wp_filesystem->delete( $working_dir, true );
381 }
382
383 // Unzip package to working directory.
384 $result = unzip_file( $package, $working_dir );
385
386 // Once extracted, delete the package if required.
387 if ( $delete_package ) {
388 unlink( $package );
389 }
390
391 if ( is_wp_error( $result ) ) {
392 $wp_filesystem->delete( $working_dir, true );
393 if ( 'incompatible_archive' === $result->get_error_code() ) {
394 return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], $result->get_error_data() );
395 }
396 return $result;
397 }
398
399 return $working_dir;
400 }
401
402 /**
403 * Flattens the results of WP_Filesystem_Base::dirlist() for iterating over.
404 *
405 * @since 4.9.0
406 *
407 * @param array $nested_files Array of files as returned by WP_Filesystem_Base::dirlist().
408 * @param string $path Relative path to prepend to child nodes. Optional.
409 * @return array A flattened array of the $nested_files specified.
410 */
411 protected function flatten_dirlist( $nested_files, $path = '' ) {
412 $files = array();
413
414 foreach ( $nested_files as $name => $details ) {
415 $files[ $path . $name ] = $details;
416
417 // Append children recursively.
418 if ( ! empty( $details['files'] ) ) {
419 $children = $this->flatten_dirlist( $details['files'], $path . $name . '/' );
420
421 // Merge keeping possible numeric keys, which array_merge() will reindex from 0..n.
422 $files = $files + $children;
423 }
424 }
425
426 return $files;
427 }
428
429 /**
430 * Clears the directory where this item is going to be installed into.
431 *
432 * @since 4.3.0
433 *
434 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
435 *
436 * @param string $remote_destination The location on the remote filesystem to be cleared.
437 * @return true|WP_Error True upon success, WP_Error on failure.
438 */
439 public function clear_destination( $remote_destination ) {
440 global $wp_filesystem;
441
442 $files = $wp_filesystem->dirlist( $remote_destination, true, true );
443
444 // False indicates that the $remote_destination doesn't exist.
445 if ( false === $files ) {
446 return true;
447 }
448
449 // Flatten the file list to iterate over.
450 $files = $this->flatten_dirlist( $files );
451
452 // Check all files are writable before attempting to clear the destination.
453 $unwritable_files = array();
454
455 // Check writability.
456 foreach ( $files as $filename => $file_details ) {
457 if ( ! $wp_filesystem->is_writable( $remote_destination . $filename ) ) {
458 // Attempt to alter permissions to allow writes and try again.
459 $wp_filesystem->chmod( $remote_destination . $filename, ( 'd' === $file_details['type'] ? FS_CHMOD_DIR : FS_CHMOD_FILE ) );
460 if ( ! $wp_filesystem->is_writable( $remote_destination . $filename ) ) {
461 $unwritable_files[] = $filename;
462 }
463 }
464 }
465
466 if ( ! empty( $unwritable_files ) ) {
467 return new WP_Error( 'files_not_writable', $this->strings['files_not_writable'], implode( ', ', $unwritable_files ) );
468 }
469
470 if ( ! $wp_filesystem->delete( $remote_destination, true ) ) {
471 return new WP_Error( 'remove_old_failed', $this->strings['remove_old_failed'] );
472 }
473
474 return true;
475 }
476
477 /**
478 * Install a package.
479 *
480 * Copies the contents of a package from a source directory, and installs them in
481 * a destination directory. Optionally removes the source. It can also optionally
482 * clear out the destination folder if it already exists.
483 *
484 * @since 2.8.0
485 * @since 6.2.0 Use move_dir() instead of copy_dir() when possible.
486 *
487 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
488 * @global string[] $wp_theme_directories
489 *
490 * @param array|string $args {
491 * Optional. Array or string of arguments for installing a package. Default empty array.
492 *
493 * @type string $source Required path to the package source. Default empty.
494 * @type string $destination Required path to a folder to install the package in.
495 * Default empty.
496 * @type bool $clear_destination Whether to delete any files already in the destination
497 * folder. Default false.
498 * @type bool $clear_working Whether to delete the files from the working directory
499 * after copying them to the destination. Default false.
500 * @type bool $abort_if_destination_exists Whether to abort the installation if
501 * the destination folder already exists. Default true.
502 * @type array $hook_extra Extra arguments to pass to the filter hooks called by
503 * WP_Upgrader::install_package(). Default empty array.
504 * }
505 *
506 * @return array|WP_Error The result (also stored in `WP_Upgrader::$result`), or a WP_Error on failure.
507 */
508 public function install_package( $args = array() ) {
509 global $wp_filesystem, $wp_theme_directories;
510
511 $defaults = array(
512 'source' => '', // Please always pass this.
513 'destination' => '', // ...and this.
514 'clear_destination' => false,
515 'clear_working' => false,
516 'abort_if_destination_exists' => true,
517 'hook_extra' => array(),
518 );
519
520 $args = wp_parse_args( $args, $defaults );
521
522 // These were previously extract()'d.
523 $source = $args['source'];
524 $destination = $args['destination'];
525 $clear_destination = $args['clear_destination'];
526
527 /*
528 * Give the upgrade an additional 300 seconds (5 minutes) to ensure the install
529 * doesn't prematurely timeout having used up the maximum script execution time
530 * upacking and downloading in WP_Upgrader->run().
531 */
532 if ( function_exists( 'set_time_limit' ) ) {
533 set_time_limit( 300 );
534 }
535
536 if (
537 ( ! is_string( $source ) || '' === $source || trim( $source ) !== $source ) ||
538 ( ! is_string( $destination ) || '' === $destination || trim( $destination ) !== $destination )
539 ) {
540 return new WP_Error( 'bad_request', $this->strings['bad_request'] );
541 }
542 $this->skin->feedback( 'installing_package' );
543
544 /**
545 * Filters the installation response before the installation has started.
546 *
547 * Returning a value that could be evaluated as a `WP_Error` will effectively
548 * short-circuit the installation, returning that value instead.
549 *
550 * @since 2.8.0
551 *
552 * @param bool|WP_Error $response Installation response.
553 * @param array $hook_extra Extra arguments passed to hooked filters.
554 */
555 $res = apply_filters( 'upgrader_pre_install', true, $args['hook_extra'] );
556
557 if ( is_wp_error( $res ) ) {
558 return $res;
559 }
560
561 // Retain the original source and destinations.
562 $remote_source = $args['source'];
563 $local_destination = $destination;
564
565 $dirlist = $wp_filesystem->dirlist( $remote_source );
566
567 if ( false === $dirlist ) {
568 return new WP_Error( 'source_read_failed', $this->strings['fs_error'], $this->strings['dir_not_readable'] );
569 }
570
571 $source_files = array_keys( $dirlist );
572 $remote_destination = $wp_filesystem->find_folder( $local_destination );
573
574 // Locate which directory to copy to the new folder. This is based on the actual folder holding the files.
575 if ( 1 === count( $source_files ) && $wp_filesystem->is_dir( trailingslashit( $args['source'] ) . $source_files[0] . '/' ) ) {
576 // Only one folder? Then we want its contents.
577 $source = trailingslashit( $args['source'] ) . trailingslashit( $source_files[0] );
578 } elseif ( 0 === count( $source_files ) ) {
579 // There are no files?
580 return new WP_Error( 'incompatible_archive_empty', $this->strings['incompatible_archive'], $this->strings['no_files'] );
581 } else {
582 /*
583 * It's only a single file, the upgrader will use the folder name of this file as the destination folder.
584 * Folder name is based on zip filename.
585 */
586 $source = trailingslashit( $args['source'] );
587 }
588
589 /**
590 * Filters the source file location for the upgrade package.
591 *
592 * @since 2.8.0
593 * @since 4.4.0 The $hook_extra parameter became available.
594 *
595 * @param string $source File source location.
596 * @param string $remote_source Remote file source location.
597 * @param WP_Upgrader $upgrader WP_Upgrader instance.
598 * @param array $hook_extra Extra arguments passed to hooked filters.
599 */
600 $source = apply_filters( 'upgrader_source_selection', $source, $remote_source, $this, $args['hook_extra'] );
601
602 if ( is_wp_error( $source ) ) {
603 return $source;
604 }
605
606 if ( ! empty( $args['hook_extra']['temp_backup'] ) ) {
607 $temp_backup = $this->move_to_temp_backup_dir( $args['hook_extra']['temp_backup'] );
608
609 if ( is_wp_error( $temp_backup ) ) {
610 return $temp_backup;
611 }
612
613 $this->temp_backups[] = $args['hook_extra']['temp_backup'];
614 }
615
616 // Has the source location changed? If so, we need a new source_files list.
617 if ( $source !== $remote_source ) {
618 $dirlist = $wp_filesystem->dirlist( $source );
619
620 if ( false === $dirlist ) {
621 return new WP_Error( 'new_source_read_failed', $this->strings['fs_error'], $this->strings['dir_not_readable'] );
622 }
623
624 $source_files = array_keys( $dirlist );
625 }
626
627 /*
628 * Protection against deleting files in any important base directories.
629 * Theme_Upgrader & Plugin_Upgrader also trigger this, as they pass the
630 * destination directory (WP_PLUGIN_DIR / wp-content/themes) intending
631 * to copy the directory into the directory, whilst they pass the source
632 * as the actual files to copy.
633 */
634 $protected_directories = array( ABSPATH, WP_CONTENT_DIR, WP_PLUGIN_DIR, WP_CONTENT_DIR . '/themes' );
635
636 if ( is_array( $wp_theme_directories ) ) {
637 $protected_directories = array_merge( $protected_directories, $wp_theme_directories );
638 }
639
640 if ( in_array( $destination, $protected_directories, true ) ) {
641 $remote_destination = trailingslashit( $remote_destination ) . trailingslashit( basename( $source ) );
642 $destination = trailingslashit( $destination ) . trailingslashit( basename( $source ) );
643 }
644
645 if ( $clear_destination ) {
646 // We're going to clear the destination if there's something there.
647 $this->skin->feedback( 'remove_old' );
648
649 $removed = $this->clear_destination( $remote_destination );
650
651 /**
652 * Filters whether the upgrader cleared the destination.
653 *
654 * @since 2.8.0
655 *
656 * @param true|WP_Error $removed Whether the destination was cleared.
657 * True upon success, WP_Error on failure.
658 * @param string $local_destination The local package destination.
659 * @param string $remote_destination The remote package destination.
660 * @param array $hook_extra Extra arguments passed to hooked filters.
661 */
662 $removed = apply_filters( 'upgrader_clear_destination', $removed, $local_destination, $remote_destination, $args['hook_extra'] );
663
664 if ( is_wp_error( $removed ) ) {
665 return $removed;
666 }
667 } elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists( $remote_destination ) ) {
668 /*
669 * If we're not clearing the destination folder and something exists there already, bail.
670 * But first check to see if there are actually any files in the folder.
671 */
672 $_files = $wp_filesystem->dirlist( $remote_destination );
673 if ( ! empty( $_files ) ) {
674 $wp_filesystem->delete( $remote_source, true ); // Clear out the source files.
675 return new WP_Error( 'folder_exists', $this->strings['folder_exists'], $remote_destination );
676 }
677 }
678
679 /*
680 * If 'clear_working' is false, the source should not be removed, so use copy_dir() instead.
681 *
682 * Partial updates, like language packs, may want to retain the destination.
683 * If the destination exists or has contents, this may be a partial update,
684 * and the destination should not be removed, so use copy_dir() instead.
685 */
686 if ( $args['clear_working']
687 && (
688 // Destination does not exist or has no contents.
689 ! $wp_filesystem->exists( $remote_destination )
690 || empty( $wp_filesystem->dirlist( $remote_destination ) )
691 )
692 ) {
693 $result = move_dir( $source, $remote_destination, true );
694 } else {
695 // Create destination if needed.
696 if ( ! $wp_filesystem->exists( $remote_destination ) ) {
697 if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) {
698 return new WP_Error( 'mkdir_failed_destination', $this->strings['mkdir_failed'], $remote_destination );
699 }
700 }
701 $result = copy_dir( $source, $remote_destination );
702 }
703
704 // Clear the working directory?
705 if ( $args['clear_working'] ) {
706 $wp_filesystem->delete( $remote_source, true );
707 }
708
709 if ( is_wp_error( $result ) ) {
710 return $result;
711 }
712
713 $destination_name = basename( str_replace( $local_destination, '', $destination ) );
714 if ( '.' === $destination_name ) {
715 $destination_name = '';
716 }
717
718 $this->result = compact( 'source', 'source_files', 'destination', 'destination_name', 'local_destination', 'remote_destination', 'clear_destination' );
719
720 /**
721 * Filters the installation response after the installation has finished.
722 *
723 * @since 2.8.0
724 *
725 * @param bool $response Installation response.
726 * @param array $hook_extra Extra arguments passed to hooked filters.
727 * @param array $result Installation result data.
728 */
729 $res = apply_filters( 'upgrader_post_install', true, $args['hook_extra'], $this->result );
730
731 if ( is_wp_error( $res ) ) {
732 $this->result = $res;
733 return $res;
734 }
735
736 // Bombard the calling function will all the info which we've just used.
737 return $this->result;
738 }
739
740 /**
741 * Runs an upgrade/installation.
742 *
743 * Attempts to download the package (if it is not a local file), unpack it, and
744 * install it in the destination folder.
745 *
746 * @since 2.8.0
747 *
748 * @param array $options {
749 * Array or string of arguments for upgrading/installing a package.
750 *
751 * @type string $package The full path or URI of the package to install.
752 * Default empty.
753 * @type string $destination The full path to the destination folder.
754 * Default empty.
755 * @type bool $clear_destination Whether to delete any files already in the
756 * destination folder. Default false.
757 * @type bool $clear_working Whether to delete the files from the working
758 * directory after copying them to the destination.
759 * Default true.
760 * @type bool $abort_if_destination_exists Whether to abort the installation if the destination
761 * folder already exists. When true, `$clear_destination`
762 * should be false. Default true.
763 * @type bool $is_multi Whether this run is one of multiple upgrade/installation
764 * actions being performed in bulk. When true, the skin
765 * WP_Upgrader::header() and WP_Upgrader::footer()
766 * aren't called. Default false.
767 * @type array $hook_extra Extra arguments to pass to the filter hooks called by
768 * WP_Upgrader::run().
769 * }
770 * @return array|false|WP_Error The result from self::install_package() on success, otherwise a WP_Error,
771 * or false if unable to connect to the filesystem.
772 */
773 public function run( $options ) {
774
775 $defaults = array(
776 'package' => '', // Please always pass this.
777 'destination' => '', // ...and this.
778 'clear_destination' => false,
779 'clear_working' => true,
780 'abort_if_destination_exists' => true, // Abort if the destination directory exists. Pass clear_destination as false please.
781 'is_multi' => false,
782 'hook_extra' => array(), // Pass any extra $hook_extra args here, this will be passed to any hooked filters.
783 );
784
785 $options = wp_parse_args( $options, $defaults );
786
787 /**
788 * Filters the package options before running an update.
789 *
790 * See also {@see 'upgrader_process_complete'}.
791 *
792 * @since 4.3.0
793 *
794 * @param array $options {
795 * Options used by the upgrader.
796 *
797 * @type string $package Package for update.
798 * @type string $destination Update location.
799 * @type bool $clear_destination Clear the destination resource.
800 * @type bool $clear_working Clear the working resource.
801 * @type bool $abort_if_destination_exists Abort if the Destination directory exists.
802 * @type bool $is_multi Whether the upgrader is running multiple times.
803 * @type array $hook_extra {
804 * Extra hook arguments.
805 *
806 * @type string $action Type of action. Default 'update'.
807 * @type string $type Type of update process. Accepts 'plugin', 'theme', or 'core'.
808 * @type bool $bulk Whether the update process is a bulk update. Default true.
809 * @type string $plugin Path to the plugin file relative to the plugins directory.
810 * @type string $theme The stylesheet or template name of the theme.
811 * @type string $language_update_type The language pack update type. Accepts 'plugin', 'theme',
812 * or 'core'.
813 * @type object $language_update The language pack update offer.
814 * }
815 * }
816 */
817 $options = apply_filters( 'upgrader_package_options', $options );
818
819 if ( ! $options['is_multi'] ) { // Call $this->header separately if running multiple times.
820 $this->skin->header();
821 }
822
823 // Connect to the filesystem first.
824 $res = $this->fs_connect( array( WP_CONTENT_DIR, $options['destination'] ) );
825 // Mainly for non-connected filesystem.
826 if ( ! $res ) {
827 if ( ! $options['is_multi'] ) {
828 $this->skin->footer();
829 }
830 return false;
831 }
832
833 $this->skin->before();
834
835 if ( is_wp_error( $res ) ) {
836 $this->skin->error( $res );
837 $this->skin->after();
838 if ( ! $options['is_multi'] ) {
839 $this->skin->footer();
840 }
841 return $res;
842 }
843
844 /*
845 * Download the package. Note: If the package is the full path
846 * to an existing local file, it will be returned untouched.
847 */
848 $download = $this->download_package( $options['package'], false, $options['hook_extra'] );
849
850 /*
851 * Allow for signature soft-fail.
852 * WARNING: This may be removed in the future.
853 */
854 if ( is_wp_error( $download ) && $download->get_error_data( 'softfail-filename' ) ) {
855
856 // Don't output the 'no signature could be found' failure message for now.
857 if ( 'signature_verification_no_signature' !== $download->get_error_code() || WP_DEBUG ) {
858 // Output the failure error as a normal feedback, and not as an error.
859 $this->skin->feedback( $download->get_error_message() );
860
861 // Report this failure back to WordPress.org for debugging purposes.
862 wp_version_check(
863 array(
864 'signature_failure_code' => $download->get_error_code(),
865 'signature_failure_data' => $download->get_error_data(),
866 )
867 );
868 }
869
870 // Pretend this error didn't happen.
871 $download = $download->get_error_data( 'softfail-filename' );
872 }
873
874 if ( is_wp_error( $download ) ) {
875 $this->skin->error( $download );
876 $this->skin->after();
877 if ( ! $options['is_multi'] ) {
878 $this->skin->footer();
879 }
880 return $download;
881 }
882
883 $delete_package = ( $download !== $options['package'] ); // Do not delete a "local" file.
884
885 // Unzips the file into a temporary directory.
886 $working_dir = $this->unpack_package( $download, $delete_package );
887 if ( is_wp_error( $working_dir ) ) {
888 $this->skin->error( $working_dir );
889 $this->skin->after();
890 if ( ! $options['is_multi'] ) {
891 $this->skin->footer();
892 }
893 return $working_dir;
894 }
895
896 // With the given options, this installs it to the destination directory.
897 $result = $this->install_package(
898 array(
899 'source' => $working_dir,
900 'destination' => $options['destination'],
901 'clear_destination' => $options['clear_destination'],
902 'abort_if_destination_exists' => $options['abort_if_destination_exists'],
903 'clear_working' => $options['clear_working'],
904 'hook_extra' => $options['hook_extra'],
905 )
906 );
907
908 /**
909 * Filters the result of WP_Upgrader::install_package().
910 *
911 * @since 5.7.0
912 *
913 * @param array|WP_Error $result Result from WP_Upgrader::install_package().
914 * @param array $hook_extra Extra arguments passed to hooked filters.
915 */
916 $result = apply_filters( 'upgrader_install_package_result', $result, $options['hook_extra'] );
917
918 $this->skin->set_result( $result );
919
920 if ( is_wp_error( $result ) ) {
921 // An automatic plugin update will have already performed its rollback.
922 if ( ! empty( $options['hook_extra']['temp_backup'] ) ) {
923 $this->temp_restores[] = $options['hook_extra']['temp_backup'];
924
925 /*
926 * Restore the backup on shutdown.
927 * Actions running on `shutdown` are immune to PHP timeouts,
928 * so in case the failure was due to a PHP timeout,
929 * it will still be able to properly restore the previous version.
930 *
931 * Zero arguments are accepted as a string can sometimes be passed
932 * internally during actions, causing an error because
933 * `WP_Upgrader::restore_temp_backup()` expects an array.
934 */
935 add_action( 'shutdown', array( $this, 'restore_temp_backup' ), 10, 0 );
936 }
937 $this->skin->error( $result );
938
939 if ( ! method_exists( $this->skin, 'hide_process_failed' ) || ! $this->skin->hide_process_failed( $result ) ) {
940 $this->skin->feedback( 'process_failed' );
941 }
942 } else {
943 // Installation succeeded.
944 $this->skin->feedback( 'process_success' );
945 }
946
947 $this->skin->after();
948
949 // Clean up the backup kept in the temporary backup directory.
950 if ( ! empty( $options['hook_extra']['temp_backup'] ) ) {
951 // Delete the backup on `shutdown` to avoid a PHP timeout.
952 add_action( 'shutdown', array( $this, 'delete_temp_backup' ), 100, 0 );
953 }
954
955 if ( ! $options['is_multi'] ) {
956
957 /**
958 * Fires when the upgrader process is complete.
959 *
960 * See also {@see 'upgrader_package_options'}.
961 *
962 * @since 3.6.0
963 * @since 3.7.0 Added to WP_Upgrader::run().
964 * @since 4.6.0 `$translations` was added as a possible argument to `$hook_extra`.
965 *
966 * @param WP_Upgrader $upgrader WP_Upgrader instance. In other contexts this might be a
967 * Theme_Upgrader, Plugin_Upgrader, Core_Upgrade, or Language_Pack_Upgrader instance.
968 * @param array $hook_extra {
969 * Array of bulk item update data.
970 *
971 * @type string $action Type of action. Default 'update'.
972 * @type string $type Type of update process. Accepts 'plugin', 'theme', 'translation', or 'core'.
973 * @type bool $bulk Whether the update process is a bulk update. Default true.
974 * @type array $plugins Array of the basename paths of the plugins' main files.
975 * @type array $themes The theme slugs.
976 * @type array $translations {
977 * Array of translations update data.
978 *
979 * @type string $language The locale the translation is for.
980 * @type string $type Type of translation. Accepts 'plugin', 'theme', or 'core'.
981 * @type string $slug Text domain the translation is for. The slug of a theme/plugin or
982 * 'default' for core translations.
983 * @type string $version The version of a theme, plugin, or core.
984 * }
985 * }
986 */
987 do_action( 'upgrader_process_complete', $this, $options['hook_extra'] );
988
989 $this->skin->footer();
990 }
991
992 return $result;
993 }
994
995 /**
996 * Toggles maintenance mode for the site.
997 *
998 * Creates/deletes the maintenance file to enable/disable maintenance mode.
999 *
1000 * @since 2.8.0
1001 *
1002 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
1003 *
1004 * @param bool $enable True to enable maintenance mode, false to disable.
1005 */
1006 public function maintenance_mode( $enable = false ) {
1007 global $wp_filesystem;
1008
1009 if ( ! $wp_filesystem ) {
1010 if ( ! function_exists( 'WP_Filesystem' ) ) {
1011 require_once ABSPATH . 'wp-admin/includes/file.php';
1012 }
1013
1014 ob_start();
1015 $credentials = request_filesystem_credentials( '' );
1016 ob_end_clean();
1017
1018 if ( false === $credentials || ! WP_Filesystem( $credentials ) ) {
1019 wp_trigger_error( __FUNCTION__, __( 'Could not access filesystem.' ) );
1020 return;
1021 }
1022 }
1023
1024 $file = $wp_filesystem->abspath() . '.maintenance';
1025
1026 if ( $enable ) {
1027 if ( ! wp_doing_cron() ) {
1028 $this->skin->feedback( 'maintenance_start' );
1029 }
1030
1031 // Create maintenance file to signal that we are upgrading.
1032 $maintenance_string = '<?php $upgrading = ' . time() . '; ?>';
1033 $wp_filesystem->delete( $file );
1034 $wp_filesystem->put_contents( $file, $maintenance_string, FS_CHMOD_FILE );
1035 } elseif ( $wp_filesystem->exists( $file ) ) {
1036 if ( ! wp_doing_cron() ) {
1037 $this->skin->feedback( 'maintenance_end' );
1038 }
1039
1040 $wp_filesystem->delete( $file );
1041 }
1042 }
1043
1044 /**
1045 * Creates a lock using WordPress options.
1046 *
1047 * @since 4.5.0
1048 *
1049 * @global wpdb $wpdb The WordPress database abstraction object.
1050 *
1051 * @param string $lock_name The name of this unique lock.
1052 * @param int $release_timeout Optional. The duration in seconds to respect an existing lock.
1053 * Default: 1 hour.
1054 * @return bool False if a lock couldn't be created or if the lock is still valid. True otherwise.
1055 */
1056 public static function create_lock( $lock_name, $release_timeout = null ) {
1057 global $wpdb;
1058 if ( ! $release_timeout ) {
1059 $release_timeout = HOUR_IN_SECONDS;
1060 }
1061 $lock_option = $lock_name . '.lock';
1062
1063 // Try to lock.
1064 $lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'off') /* LOCK */", $lock_option, time() ) );
1065
1066 if ( ! $lock_result ) {
1067 $lock_result = get_option( $lock_option );
1068
1069 // If a lock couldn't be created, and there isn't a lock, bail.
1070 if ( ! $lock_result ) {
1071 return false;
1072 }
1073
1074 // Check to see if the lock is still valid. If it is, bail.
1075 if ( $lock_result > ( time() - $release_timeout ) ) {
1076 return false;
1077 }
1078
1079 // There must exist an expired lock, clear it and re-gain it.
1080 WP_Upgrader::release_lock( $lock_name );
1081
1082 return WP_Upgrader::create_lock( $lock_name, $release_timeout );
1083 }
1084
1085 // Update the lock, as by this point we've definitely got a lock, just need to fire the actions.
1086 update_option( $lock_option, time(), false );
1087
1088 return true;
1089 }
1090
1091 /**
1092 * Releases an upgrader lock.
1093 *
1094 * @since 4.5.0
1095 *
1096 * @see WP_Upgrader::create_lock()
1097 *
1098 * @param string $lock_name The name of this unique lock.
1099 * @return bool True if the lock was successfully released. False on failure.
1100 */
1101 public static function release_lock( $lock_name ) {
1102 return delete_option( $lock_name . '.lock' );
1103 }
1104
1105 /**
1106 * Moves the plugin or theme being updated into a temporary backup directory.
1107 *
1108 * @since 6.3.0
1109 *
1110 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
1111 *
1112 * @param string[] $args {
1113 * Array of data for the temporary backup.
1114 *
1115 * @type string $slug Plugin or theme slug.
1116 * @type string $src Path to the root directory for plugins or themes.
1117 * @type string $dir Destination subdirectory name. Accepts 'plugins' or 'themes'.
1118 * }
1119 *
1120 * @return bool|WP_Error True on success, false on early exit, otherwise WP_Error.
1121 */
1122 public function move_to_temp_backup_dir( $args ) {
1123 global $wp_filesystem;
1124
1125 if ( empty( $args['slug'] ) || empty( $args['src'] ) || empty( $args['dir'] ) ) {
1126 return false;
1127 }
1128
1129 /*
1130 * Skip any plugin that has "." as its slug.
1131 * A slug of "." will result in a `$src` value ending in a period.
1132 *
1133 * On Windows, this will cause the 'plugins' folder to be moved,
1134 * and will cause a failure when attempting to call `mkdir()`.
1135 */
1136 if ( '.' === $args['slug'] ) {
1137 return false;
1138 }
1139
1140 if ( ! $wp_filesystem->wp_content_dir() ) {
1141 return new WP_Error( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] );
1142 }
1143
1144 $dest_dir = $wp_filesystem->wp_content_dir() . 'upgrade-temp-backup/';
1145 $sub_dir = $dest_dir . $args['dir'] . '/';
1146
1147 // Create the temporary backup directory if it does not exist.
1148 if ( ! $wp_filesystem->is_dir( $sub_dir ) ) {
1149 if ( ! $wp_filesystem->is_dir( $dest_dir ) ) {
1150 $wp_filesystem->mkdir( $dest_dir, FS_CHMOD_DIR );
1151 }
1152
1153 if ( ! $wp_filesystem->mkdir( $sub_dir, FS_CHMOD_DIR ) ) {
1154 // Could not create the backup directory.
1155 return new WP_Error( 'fs_temp_backup_mkdir', $this->strings['temp_backup_mkdir_failed'] );
1156 }
1157 }
1158
1159 $src_dir = $wp_filesystem->find_folder( $args['src'] );
1160 $src = trailingslashit( $src_dir ) . $args['slug'];
1161 $dest = $dest_dir . trailingslashit( $args['dir'] ) . $args['slug'];
1162
1163 // Delete the temporary backup directory if it already exists.
1164 if ( $wp_filesystem->is_dir( $dest ) ) {
1165 $wp_filesystem->delete( $dest, true );
1166 }
1167
1168 // Move to the temporary backup directory.
1169 $result = move_dir( $src, $dest, true );
1170 if ( is_wp_error( $result ) ) {
1171 return new WP_Error( 'fs_temp_backup_move', $this->strings['temp_backup_move_failed'] );
1172 }
1173
1174 return true;
1175 }
1176
1177 /**
1178 * Restores the plugin or theme from temporary backup.
1179 *
1180 * @since 6.3.0
1181 * @since 6.6.0 Added the `$temp_backups` parameter.
1182 *
1183 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
1184 *
1185 * @param array[] $temp_backups {
1186 * Optional. An array of temporary backups.
1187 *
1188 * @type array ...$0 {
1189 * Information about the backup.
1190 *
1191 * @type string $dir The temporary backup location in the upgrade-temp-backup directory.
1192 * @type string $slug The item's slug.
1193 * @type string $src The directory where the original is stored. For example, `WP_PLUGIN_DIR`.
1194 * }
1195 * }
1196 * @return bool|WP_Error True on success, false on early exit, otherwise WP_Error.
1197 */
1198 public function restore_temp_backup( array $temp_backups = array() ) {
1199 global $wp_filesystem;
1200
1201 $errors = new WP_Error();
1202
1203 if ( empty( $temp_backups ) ) {
1204 $temp_backups = $this->temp_restores;
1205 }
1206
1207 foreach ( $temp_backups as $args ) {
1208 if ( empty( $args['slug'] ) || empty( $args['src'] ) || empty( $args['dir'] ) ) {
1209 return false;
1210 }
1211
1212 if ( ! $wp_filesystem->wp_content_dir() ) {
1213 $errors->add( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] );
1214 return $errors;
1215 }
1216
1217 $src = $wp_filesystem->wp_content_dir() . 'upgrade-temp-backup/' . $args['dir'] . '/' . $args['slug'];
1218 $dest_dir = $wp_filesystem->find_folder( $args['src'] );
1219 $dest = trailingslashit( $dest_dir ) . $args['slug'];
1220
1221 if ( $wp_filesystem->is_dir( $src ) ) {
1222 // Cleanup.
1223 if ( $wp_filesystem->is_dir( $dest ) && ! $wp_filesystem->delete( $dest, true ) ) {
1224 $errors->add(
1225 'fs_temp_backup_delete',
1226 sprintf( $this->strings['temp_backup_restore_failed'], $args['slug'] )
1227 );
1228 continue;
1229 }
1230
1231 // Move it.
1232 $result = move_dir( $src, $dest, true );
1233 if ( is_wp_error( $result ) ) {
1234 $errors->add(
1235 'fs_temp_backup_delete',
1236 sprintf( $this->strings['temp_backup_restore_failed'], $args['slug'] )
1237 );
1238 continue;
1239 }
1240 }
1241 }
1242
1243 return $errors->has_errors() ? $errors : true;
1244 }
1245
1246 /**
1247 * Deletes a temporary backup.
1248 *
1249 * @since 6.3.0
1250 * @since 6.6.0 Added the `$temp_backups` parameter.
1251 *
1252 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
1253 *
1254 * @param array[] $temp_backups {
1255 * Optional. An array of temporary backups.
1256 *
1257 * @type array ...$0 {
1258 * Information about the backup.
1259 *
1260 * @type string $dir The temporary backup location in the upgrade-temp-backup directory.
1261 * @type string $slug The item's slug.
1262 * @type string $src The directory where the original is stored. For example, `WP_PLUGIN_DIR`.
1263 * }
1264 * }
1265 * @return bool|WP_Error True on success, false on early exit, otherwise WP_Error.
1266 */
1267 public function delete_temp_backup( array $temp_backups = array() ) {
1268 global $wp_filesystem;
1269
1270 $errors = new WP_Error();
1271
1272 if ( empty( $temp_backups ) ) {
1273 $temp_backups = $this->temp_backups;
1274 }
1275
1276 foreach ( $temp_backups as $args ) {
1277 if ( empty( $args['slug'] ) || empty( $args['dir'] ) ) {
1278 return false;
1279 }
1280
1281 if ( ! $wp_filesystem->wp_content_dir() ) {
1282 $errors->add( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] );
1283 return $errors;
1284 }
1285
1286 $temp_backup_dir = $wp_filesystem->wp_content_dir() . "upgrade-temp-backup/{$args['dir']}/{$args['slug']}";
1287
1288 if ( ! $wp_filesystem->delete( $temp_backup_dir, true ) ) {
1289 $errors->add(
1290 'temp_backup_delete_failed',
1291 sprintf( $this->strings['temp_backup_delete_failed'], $args['slug'] )
1292 );
1293 continue;
1294 }
1295 }
1296
1297 return $errors->has_errors() ? $errors : true;
1298 }
1299}
1300
1301/** Plugin_Upgrader class */
1302require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
1303
1304/** Theme_Upgrader class */
1305require_once ABSPATH . 'wp-admin/includes/class-theme-upgrader.php';
1306
1307/** Language_Pack_Upgrader class */
1308require_once ABSPATH . 'wp-admin/includes/class-language-pack-upgrader.php';
1309
1310/** Core_Upgrader class */
1311require_once ABSPATH . 'wp-admin/includes/class-core-upgrader.php';
1312
1313/** File_Upload_Upgrader class */
1314require_once ABSPATH . 'wp-admin/includes/class-file-upload-upgrader.php';
1315
1316/** WP_Automatic_Updater class */
1317require_once ABSPATH . 'wp-admin/includes/class-wp-automatic-updater.php';
1318
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1