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