[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-admin/includes/ -> class-theme-installer-skin.php (source)

   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 &#8220;%s&#8221;' ), $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 &#8220;%s&#8221;', '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&amp;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  }


Generated : Fri Aug 28 08:20:23 2026 Cross-referenced by PHPXref