| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Upgrader API: Theme_Installer_Skin class 4 * 5 * @package WordPress 6 * @subpackage Upgrader 7 * @since 4.6.0 8 */ 9 10 /** 11 * Theme Installer Skin for the WordPress Theme Installer. 12 * 13 * @since 2.8.0 14 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader-skins.php. 15 * 16 * @see WP_Upgrader_Skin 17 */ 18 class Theme_Installer_Skin extends WP_Upgrader_Skin { 19 public $api; 20 public $type; 21 public $url; 22 public $overwrite; 23 24 private $is_downgrading = false; 25 26 /** 27 * Constructor. 28 * 29 * Sets up the theme installer skin. 30 * 31 * @since 2.8.0 32 * 33 * @param array $args 34 */ 35 public function __construct( $args = array() ) { 36 $defaults = array( 37 'type' => 'web', 38 'url' => '', 39 'theme' => '', 40 'nonce' => '', 41 'title' => '', 42 'overwrite' => '', 43 ); 44 $args = wp_parse_args( $args, $defaults ); 45 46 $this->type = $args['type']; 47 $this->url = $args['url']; 48 $this->api = $args['api'] ?? array(); 49 $this->overwrite = $args['overwrite']; 50 51 parent::__construct( $args ); 52 } 53 54 /** 55 * Performs an action before installing a theme. 56 * 57 * @since 2.8.0 58 */ 59 public function before() { 60 if ( ! empty( $this->api ) ) { 61 $this->upgrader->strings['process_success'] = sprintf( 62 $this->upgrader->strings['process_success_specific'], 63 $this->api->name, 64 $this->api->version 65 ); 66 } 67 } 68 69 /** 70 * Hides the `process_failed` error when updating a theme by uploading a zip file. 71 * 72 * @since 5.5.0 73 * 74 * @param WP_Error $wp_error WP_Error object. 75 * @return bool True if the error should be hidden, false otherwise. 76 */ 77 public function hide_process_failed( $wp_error ) { 78 if ( 79 'upload' === $this->type && 80 '' === $this->overwrite && 81 $wp_error->get_error_code() === 'folder_exists' 82 ) { 83 return true; 84 } 85 86 return false; 87 } 88 89 /** 90 * Performs an action following a single theme install. 91 * 92 * @since 2.8.0 93 */ 94 public function after() { 95 if ( $this->do_overwrite() ) { 96 return; 97 } 98 99 if ( empty( $this->upgrader->result['destination_name'] ) ) { 100 return; 101 } 102 103 $theme_info = $this->upgrader->theme_info(); 104 if ( empty( $theme_info ) ) { 105 return; 106 } 107 108 $name = $theme_info->display( 'Name' ); 109 $stylesheet = $this->upgrader->result['destination_name']; 110 $template = $theme_info->get_template(); 111 112 $activate_link = add_query_arg( 113 array( 114 'action' => 'activate', 115 'template' => urlencode( $template ), 116 'stylesheet' => urlencode( $stylesheet ), 117 ), 118 admin_url( 'themes.php' ) 119 ); 120 $activate_link = wp_nonce_url( $activate_link, 'switch-theme_' . $stylesheet ); 121 122 $install_actions = array(); 123 124 if ( current_user_can( 'edit_theme_options' ) && ( $theme_info->is_block_theme() || current_user_can( 'customize' ) ) ) { 125 if ( $theme_info->is_block_theme() ) { 126 $customize_url = add_query_arg( 127 array( 128 'wp_theme_preview' => urlencode( $stylesheet ), 129 'return' => urlencode( admin_url( 'web' === $this->type ? 'theme-install.php' : 'themes.php' ) ), 130 ), 131 admin_url( 'site-editor.php' ) 132 ); 133 } else { 134 $customize_url = add_query_arg( 135 array( 136 'theme' => urlencode( $stylesheet ), 137 'return' => urlencode( admin_url( 'web' === $this->type ? 'theme-install.php' : 'themes.php' ) ), 138 ), 139 admin_url( 'customize.php' ) 140 ); 141 } 142 143 $install_actions['preview'] = sprintf( 144 '<a href="%s" class="hide-if-no-customize load-customize">' . 145 '<span aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>', 146 esc_url( $customize_url ), 147 __( 'Live Preview' ), 148 /* translators: Hidden accessibility text. %s: Theme name. */ 149 sprintf( __( 'Live Preview “%s”' ), $name ) 150 ); 151 } 152 153 $install_actions['activate'] = sprintf( 154 '<a href="%s" class="activatelink">' . 155 '<span aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>', 156 esc_url( $activate_link ), 157 _x( 'Activate', 'theme' ), 158 /* translators: Hidden accessibility text. %s: Theme name. */ 159 sprintf( _x( 'Activate “%s”', 'theme' ), $name ) 160 ); 161 162 if ( is_network_admin() && current_user_can( 'manage_network_themes' ) ) { 163 $install_actions['network_enable'] = sprintf( 164 '<a href="%s" target="_parent">%s</a>', 165 esc_url( wp_nonce_url( 'themes.php?action=enable&theme=' . urlencode( $stylesheet ), 'enable-theme_' . $stylesheet ) ), 166 __( 'Network Enable' ) 167 ); 168 } 169 170 if ( 'web' === $this->type ) { 171 $install_actions['themes_page'] = sprintf( 172 '<a href="%s" target="_parent">%s</a>', 173 self_admin_url( 'theme-install.php' ), 174 __( 'Go to Theme Installer' ) 175 ); 176 } elseif ( current_user_can( 'switch_themes' ) || current_user_can( 'edit_theme_options' ) ) { 177 $install_actions['themes_page'] = sprintf( 178 '<a href="%s" target="_parent">%s</a>', 179 self_admin_url( 'themes.php' ), 180 __( 'Go to Themes page' ) 181 ); 182 } 183 184 if ( ! $this->result || is_wp_error( $this->result ) || is_network_admin() || ! current_user_can( 'switch_themes' ) ) { 185 unset( $install_actions['activate'], $install_actions['preview'] ); 186 } elseif ( get_option( 'template' ) === $stylesheet ) { 187 unset( $install_actions['activate'] ); 188 } 189 190 /** 191 * Filters the list of action links available following a single theme installation. 192 * 193 * @since 2.8.0 194 * 195 * @param string[] $install_actions Array of theme action links. 196 * @param object $api Object containing WordPress.org API theme data. 197 * @param string $stylesheet Theme directory name. 198 * @param WP_Theme $theme_info Theme object. 199 */ 200 $install_actions = apply_filters( 'install_theme_complete_actions', $install_actions, $this->api, $stylesheet, $theme_info ); 201 if ( ! empty( $install_actions ) ) { 202 $this->feedback( implode( ' | ', (array) $install_actions ) ); 203 } 204 } 205 206 /** 207 * Checks if the theme can be overwritten and outputs the HTML for overwriting a theme on upload. 208 * 209 * @since 5.5.0 210 * 211 * @return bool Whether the theme can be overwritten and HTML was outputted. 212 */ 213 private function do_overwrite() { 214 if ( 'upload' !== $this->type || ! is_wp_error( $this->result ) || 'folder_exists' !== $this->result->get_error_code() ) { 215 return false; 216 } 217 218 $folder = $this->result->get_error_data( 'folder_exists' ); 219 $folder = rtrim( $folder, '/' ); 220 221 $current_theme_data = false; 222 $all_themes = wp_get_themes( array( 'errors' => null ) ); 223 224 foreach ( $all_themes as $theme ) { 225 $stylesheet_dir = wp_normalize_path( $theme->get_stylesheet_directory() ); 226 227 if ( rtrim( $stylesheet_dir, '/' ) !== $folder ) { 228 continue; 229 } 230 231 $current_theme_data = $theme; 232 } 233 234 $new_theme_data = $this->upgrader->new_theme_data; 235 236 if ( ! $current_theme_data || ! $new_theme_data ) { 237 return false; 238 } 239 240 echo '<h2 class="update-from-upload-heading">' . esc_html__( 'This theme is already installed.' ) . '</h2>'; 241 242 // Check errors for active theme. 243 if ( is_wp_error( $current_theme_data->errors() ) ) { 244 $this->feedback( 'current_theme_has_errors', $current_theme_data->errors()->get_error_message() ); 245 } 246 247 $this->is_downgrading = version_compare( $current_theme_data['Version'], $new_theme_data['Version'], '>' ); 248 249 $is_invalid_parent = false; 250 if ( ! empty( $new_theme_data['Template'] ) ) { 251 $is_invalid_parent = ! in_array( $new_theme_data['Template'], array_keys( $all_themes ), true ); 252 } 253 254 $rows = array( 255 'Name' => __( 'Theme name' ), 256 'Version' => __( 'Version' ), 257 'Author' => __( 'Author' ), 258 'RequiresWP' => __( 'Required WordPress version' ), 259 'RequiresPHP' => __( 'Required PHP version' ), 260 'Template' => __( 'Parent theme' ), 261 ); 262 263 $table = '<table class="update-from-upload-comparison"><tbody>'; 264 $table .= '<tr><th></th><th>' . esc_html_x( 'Installed', 'theme' ) . '</th><th>' . esc_html_x( 'Uploaded', 'theme' ) . '</th></tr>'; 265 266 $is_same_theme = true; // Let's consider only these rows. 267 268 foreach ( $rows as $field => $label ) { 269 $old_value = $current_theme_data->display( $field, false ); 270 $old_value = $old_value ? (string) $old_value : '-'; 271 272 $new_value = ! empty( $new_theme_data[ $field ] ) ? (string) $new_theme_data[ $field ] : '-'; 273 274 if ( $old_value === $new_value && '-' === $new_value && 'Template' === $field ) { 275 continue; 276 } 277 278 $is_same_theme = $is_same_theme && ( $old_value === $new_value ); 279 280 $diff_field = ( 'Version' !== $field && $new_value !== $old_value ); 281 $diff_version = ( 'Version' === $field && $this->is_downgrading ); 282 $invalid_parent = false; 283 284 if ( 'Template' === $field && $is_invalid_parent ) { 285 $invalid_parent = true; 286 $new_value .= ' ' . __( '(not found)' ); 287 } 288 289 $table .= '<tr><td class="name-label">' . $label . '</td><td>' . wp_strip_all_tags( $old_value ) . '</td>'; 290 $table .= ( $diff_field || $diff_version || $invalid_parent ) ? '<td class="warning">' : '<td>'; 291 $table .= wp_strip_all_tags( $new_value ) . '</td></tr>'; 292 } 293 294 $table .= '</tbody></table>'; 295 296 /** 297 * Filters the compare table output for overwriting a theme package on upload. 298 * 299 * @since 5.5.0 300 * 301 * @param string $table The output table with Name, Version, Author, RequiresWP, and RequiresPHP info. 302 * @param WP_Theme $current_theme_data Active theme data. 303 * @param array $new_theme_data Array with uploaded theme data. 304 */ 305 echo apply_filters( 'install_theme_overwrite_comparison', $table, $current_theme_data, $new_theme_data ); 306 307 $install_actions = array(); 308 $can_update = true; 309 310 $blocked_message = '<p>' . esc_html__( 'The theme cannot be updated due to the following:' ) . '</p>'; 311 $blocked_message .= '<ul class="ul-disc">'; 312 313 $requires_php = $new_theme_data['RequiresPHP'] ?? null; 314 $requires_wp = $new_theme_data['RequiresWP'] ?? null; 315 316 if ( ! is_php_version_compatible( $requires_php ) ) { 317 $error = sprintf( 318 /* translators: 1: Current PHP version, 2: Version required by the uploaded theme. */ 319 __( 'The PHP version on your server is %1$s, however the uploaded theme requires %2$s.' ), 320 PHP_VERSION, 321 $requires_php 322 ); 323 324 $blocked_message .= '<li>' . esc_html( $error ) . '</li>'; 325 $can_update = false; 326 } 327 328 if ( ! is_wp_version_compatible( $requires_wp ) ) { 329 $error = sprintf( 330 /* translators: 1: Current WordPress version, 2: Version required by the uploaded theme. */ 331 __( 'Your WordPress version is %1$s, however the uploaded theme requires %2$s.' ), 332 esc_html( wp_get_wp_version() ), 333 $requires_wp 334 ); 335 336 $blocked_message .= '<li>' . esc_html( $error ) . '</li>'; 337 $can_update = false; 338 } 339 340 $blocked_message .= '</ul>'; 341 342 if ( $can_update ) { 343 if ( $this->is_downgrading ) { 344 $warning = sprintf( 345 /* translators: %s: Documentation URL. */ 346 __( 'You are uploading an older version of the installed theme. You can continue to install the older version, but be sure to <a href="%s">back up your database and files</a> first.' ), 347 __( 'https://developer.wordpress.org/advanced-administration/security/backup/' ) 348 ); 349 } else { 350 $warning = sprintf( 351 /* translators: %s: Documentation URL. */ 352 __( 'You are updating a theme. Be sure to <a href="%s">back up your database and files</a> first.' ), 353 __( 'https://developer.wordpress.org/advanced-administration/security/backup/' ) 354 ); 355 } 356 357 echo '<p class="update-from-upload-notice">' . $warning . '</p>'; 358 359 $overwrite = $this->is_downgrading ? 'downgrade-theme' : 'update-theme'; 360 361 $install_actions['overwrite_theme'] = sprintf( 362 '<a class="button button-primary update-from-upload-overwrite" href="%s" target="_parent">%s</a>', 363 wp_nonce_url( add_query_arg( 'overwrite', $overwrite, $this->url ), 'theme-upload' ), 364 _x( 'Replace installed with uploaded', 'theme' ) 365 ); 366 } else { 367 echo $blocked_message; 368 } 369 370 $cancel_url = add_query_arg( 'action', 'upload-theme-cancel-overwrite', $this->url ); 371 372 $install_actions['themes_page'] = sprintf( 373 '<a class="button" href="%s" target="_parent">%s</a>', 374 wp_nonce_url( $cancel_url, 'theme-upload-cancel-overwrite' ), 375 __( 'Cancel and go back' ) 376 ); 377 378 /** 379 * Filters the list of action links available following a single theme installation failure 380 * when overwriting is allowed. 381 * 382 * @since 5.5.0 383 * 384 * @param string[] $install_actions Array of theme action links. 385 * @param object $api Object containing WordPress.org API theme data. 386 * @param array $new_theme_data Array with uploaded theme data. 387 */ 388 $install_actions = apply_filters( 'install_theme_overwrite_actions', $install_actions, $this->api, $new_theme_data ); 389 390 if ( ! empty( $install_actions ) ) { 391 printf( 392 '<p class="update-from-upload-expired hidden">%s</p>', 393 __( 'The uploaded file has expired. Please go back and upload it again.' ) 394 ); 395 echo '<p class="update-from-upload-actions">' . implode( ' ', (array) $install_actions ) . '</p>'; 396 } 397 398 return true; 399 } 400 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sat Apr 25 08:20:11 2026 | Cross-referenced by PHPXref |