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