exists(); } /** * Whether this site may switch to a theme. * * Always on a single site. On a network, only a theme enabled for the * network or for this site, as Appearance > Themes offers, unless the * current user can enable themes for the network themselves. * * @param \WP_Theme $theme Theme. * @return bool */ public static function allowed_here( \WP_Theme $theme ) { if ( ! is_multisite() ) { return true; } return $theme->is_allowed() || current_user_can( 'manage_network_themes' ); } /** * What to say when the theme is installed but this site may not use it. * * @param string $name Theme name. * @return string */ public static function not_allowed_message( $name ) { return sprintf( /* translators: %s: theme name. */ __( 'The %s theme is installed but not enabled for this site. A network administrator can enable it under Network Admin, Themes.', 'pagecore' ), $name ); } /** * Everything the Settings screen needs to show the right button. * * @return array Keys: state (active|installed|missing), canFix, name, and * detail, which says why when canFix is false for a reason * of its own. */ public static function state() { if ( self::is_active() ) { $state = 'active'; } elseif ( self::is_installed() ) { $state = 'installed'; } else { $state = 'missing'; } $name = __( 'PageCore FSE', 'pagecore' ); $allowed = 'installed' !== $state || self::allowed_here( wp_get_theme( PAGECORE_THEME_SLUG ) ); if ( 'installed' === $state ) { $can_fix = $allowed && Capabilities::can( 'switch_themes' ); } else { $can_fix = Capabilities::can( 'install_themes' ) && Capabilities::can( 'switch_themes' ); } return array( 'state' => $state, 'canFix' => 'active' !== $state && $can_fix, 'name' => $name, 'detail' => $allowed ? '' : self::not_allowed_message( $name ), ); } /** * Install the theme if it is missing, then activate it. * * @return true|\WP_Error */ public static function install_and_activate() { if ( self::is_active() ) { return true; } if ( ! Capabilities::can( 'switch_themes' ) ) { return new \WP_Error( 'pagecore_cannot_switch_themes', __( 'Your account cannot change the theme.', 'pagecore' ), array( 'status' => 403 ) ); } if ( ! self::is_installed() ) { $installed = self::install(); if ( is_wp_error( $installed ) ) { return $installed; } } // On a network, a site may only use themes the network allows it, // unless the person asking is the one who decides that. if ( ! self::allowed_here( wp_get_theme( PAGECORE_THEME_SLUG ) ) ) { return new \WP_Error( 'pagecore_theme_not_allowed', self::not_allowed_message( __( 'PageCore FSE', 'pagecore' ) ), array( 'status' => 403 ) ); } switch_theme( PAGECORE_THEME_SLUG ); if ( ! self::is_active() ) { return new \WP_Error( 'pagecore_theme_switch_failed', __( 'The PageCore FSE theme could not be activated.', 'pagecore' ), array( 'status' => 500 ) ); } return true; } /** * Install the PageCore theme from WordPress.org. * * @return true|\WP_Error */ private static function install() { // The skin is built here, before install_from_directory() loads the // upgrader classes, and a REST or admin-post request has not loaded // them either. if ( ! class_exists( 'WP_Ajax_Upgrader_Skin' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; } return self::install_from_directory( PAGECORE_THEME_SLUG, new \WP_Ajax_Upgrader_Skin() ); } /** * Install a theme from the WordPress.org theme directory. * * The download address comes from WordPress.org's own Themes API, the same * way Appearance > Themes > Add New installs one, so no theme code ever * comes from anywhere else. * * @param string $slug Theme slug on WordPress.org. * @param \WP_Upgrader_Skin $skin Upgrader skin. * @return true|\WP_Error */ public static function install_from_directory( $slug, $skin ) { $slug = sanitize_key( $slug ); if ( ! Capabilities::can( 'install_themes' ) ) { return new \WP_Error( 'pagecore_cannot_install_themes', __( 'Your account cannot install themes.', 'pagecore' ), array( 'status' => 403 ) ); } if ( ! class_exists( 'WP_Upgrader' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; } require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/misc.php'; require_once ABSPATH . 'wp-admin/includes/theme.php'; $information = themes_api( 'theme_information', array( 'slug' => $slug, 'fields' => array( 'sections' => false ), ) ); if ( is_wp_error( $information ) || empty( $information->download_link ) ) { return new \WP_Error( 'pagecore_theme_not_found', sprintf( /* translators: %s: theme slug. */ __( 'The %s theme could not be found on WordPress.org. Install it from Appearance, Themes.', 'pagecore' ), $slug ), array( 'status' => 404 ) ); } // WordPress refuses a theme this server cannot run, but only says the // install failed. WordPress.org already said what it needs. $needs_php = isset( $information->requires_php ) ? (string) $information->requires_php : ''; $needs_wp = isset( $information->requires ) ? (string) $information->requires : ''; if ( '' !== $needs_php && ! is_php_version_compatible( $needs_php ) ) { return new \WP_Error( 'pagecore_theme_requirements', sprintf( /* translators: 1: theme slug, 2: required PHP version, 3: PHP version of this site. */ __( 'The %1$s theme needs PHP %2$s or newer, and this site runs PHP %3$s.', 'pagecore' ), $slug, $needs_php, PHP_VERSION ), array( 'status' => 409 ) ); } if ( '' !== $needs_wp && ! is_wp_version_compatible( $needs_wp ) ) { return new \WP_Error( 'pagecore_theme_requirements', sprintf( /* translators: 1: theme slug, 2: required WordPress version, 3: WordPress version of this site. */ __( 'The %1$s theme needs WordPress %2$s or newer, and this site runs WordPress %3$s.', 'pagecore' ), $slug, $needs_wp, get_bloginfo( 'version' ) ), array( 'status' => 409 ) ); } $upgrader = new \Theme_Upgrader( $skin ); $result = $upgrader->install( $information->download_link ); if ( is_wp_error( $result ) ) { return $result; } if ( ! $result || ! wp_get_theme( $slug )->exists() ) { return new \WP_Error( 'pagecore_theme_install_failed', sprintf( /* translators: %s: theme slug. */ __( 'The %s theme could not be installed.', 'pagecore' ), $slug ), array( 'status' => 500 ) ); } return true; } }