-
Notifications
You must be signed in to change notification settings - Fork 809
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Untangle theme screens #41374
base: trunk
Are you sure you want to change the base?
Untangle theme screens #41374
Changes from all commits
aec7d11
3415864
259aae7
1d7d077
ffec564
3edc01a
971a845
0c67014
6a10604
8342b7e
6b1d85e
ebbbef4
1be87f1
89d9ba1
2331013
e16a87d
0013535
cf77993
81d3c17
405cbb0
8884b04
9a83d3d
db48ebf
b76f129
39c2f50
47e5bca
f5f6f49
2ccf4b2
189c9a9
a25b393
3494763
dc0a8e2
08e7e23
26b7e86
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Significance: minor | ||
Type: added | ||
|
||
Add theme install page |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* Copied from Gutenberg's Badge component. See: | ||
* https://github.com/WordPress/gutenberg/blob/trunk/packages/components/src/badge/styles.scss | ||
|
||
* The scss code was translated to plain css to make it work in wp-admin | ||
*/ | ||
|
||
.components-badge { | ||
background-color: color-mix(in srgb, #fff 90%, var(--base-color)); | ||
color: color-mix(in srgb, #000 50%, var(--base-color)); | ||
padding: 0 8px; | ||
min-height: 24px; | ||
max-width: 100%; | ||
border-radius: 4px; | ||
font-size: 12px; | ||
font-weight: 400; | ||
line-height: 1.4; | ||
display: inline-flex; | ||
align-items: center; | ||
gap: 2px; | ||
} | ||
|
||
.components-badge.is-default { | ||
background-color: #f0f0f0; | ||
color: #2c3338; | ||
} | ||
|
||
.components-badge.has-icon { | ||
padding-inline-start: 4px; | ||
} | ||
|
||
/* Color variants */ | ||
.components-badge.is-info { | ||
--base-color: #3858e9; | ||
} | ||
|
||
.components-badge.is-warning { | ||
--base-color: #f0b849; | ||
} | ||
|
||
.components-badge.is-error { | ||
--base-color: #d63638; | ||
} | ||
|
||
.components-badge.is-success { | ||
--base-color: #00a32a; | ||
} | ||
|
||
.components-badge__icon { | ||
flex-shrink: 0; | ||
} | ||
|
||
.components-badge__content { | ||
overflow: hidden; | ||
white-space: nowrap; | ||
text-overflow: ellipsis; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
<?php | ||
/** | ||
* Class WPCom_Themes_Api. | ||
* Retrieves themes from the WordPress.com themes API. | ||
* | ||
* @package wpcom-themes | ||
*/ | ||
|
||
/** | ||
* Fetches themes from the WordPress.com themes API. | ||
*/ | ||
class WPCom_Themes_Api { | ||
|
||
/** | ||
* The URL of the WordPress.com themes API. | ||
*/ | ||
const WP_COM_THEMES_API_URL = 'https://public-api.wordpress.com/rest/v1.2/themes?http_envelope=1&page=1&number=1000'; | ||
|
||
/** | ||
* The URL of the WordPress.com theme API. | ||
*/ | ||
const WP_COM_THEME_API_URL = 'https://public-api.wordpress.com/rest/v1.2/themes/%s?http_envelope=1'; | ||
|
||
/** | ||
* Cache handler. | ||
* | ||
* @var WPCom_Themes_Cache $cache | ||
*/ | ||
private WPCom_Themes_Cache $cache; | ||
Check failure on line 29 in projects/packages/jetpack-mu-wpcom/src/features/wpcom-themes/includes/class-wpcom-themes-api.php
|
||
|
||
/** | ||
* Class constructor. | ||
* | ||
* @param WPCom_Themes_Cache $cache Cache handler. | ||
*/ | ||
public function __construct( WPCom_Themes_Cache $cache ) { | ||
$this->cache = $cache; | ||
} | ||
|
||
/** | ||
* Returns an array of themes fetched from the WordPress.com themes API. | ||
* Caching is used to avoid fetching the themes on every request. | ||
* | ||
* @param string $cache_key Key of the cache where the API response will be cached. | ||
* @param array $params Query params to pass to the API URL. | ||
* | ||
* @return array<stdClass> An array with all the WPCom themes. | ||
*/ | ||
protected function fetch_themes( string $cache_key, array $params = array() ): array { | ||
$url = add_query_arg( $params, self::WP_COM_THEMES_API_URL ); | ||
|
||
return $this->cache->run_cached( | ||
$cache_key, | ||
fn() => $this->handle_request( $url )->themes ?? array() | ||
Check failure on line 54 in projects/packages/jetpack-mu-wpcom/src/features/wpcom-themes/includes/class-wpcom-themes-api.php
|
||
); | ||
} | ||
|
||
/** | ||
* Fetches the response from the given URL. | ||
* | ||
* @param string $url URL to fetch. | ||
* | ||
* @return ?stdClass Response body. | ||
*/ | ||
protected function handle_request( string $url ): ?stdClass { | ||
$response = wp_remote_get( esc_url_raw( $url ) ); | ||
if ( 200 === wp_remote_retrieve_response_code( $response ) ) { | ||
$body_json = json_decode( wp_remote_retrieve_body( $response ) ); | ||
|
||
if ( isset( $body_json->body ) ) { | ||
return $body_json->body; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/** | ||
* Returns all the WP.com themes. | ||
* | ||
* @return array<stdClass> An array with all the WPCom themes. | ||
*/ | ||
public function fetch_all_non_delisted_themes(): array { | ||
return $this->fetch_themes( 'wpcom-themes-all' ); | ||
} | ||
|
||
/** | ||
* Returns the WP.com theme with the given slug. | ||
* | ||
* @param string $slug Theme slug. | ||
* | ||
* @return stdClass|null A WPCom theme object or null if not found. | ||
*/ | ||
public function fetch_theme( string $slug ): ?stdClass { | ||
$url = sprintf( self::WP_COM_THEME_API_URL, $slug ); | ||
|
||
$theme = $this->cache->run_cached( | ||
'wpcom-themes-' . $slug, | ||
fn() => $this->handle_request( $url ) | ||
Check failure on line 99 in projects/packages/jetpack-mu-wpcom/src/features/wpcom-themes/includes/class-wpcom-themes-api.php
|
||
); | ||
|
||
if ( ! $theme || isset( $theme->error ) ) { | ||
return null; | ||
} | ||
|
||
return $theme; | ||
} | ||
|
||
/** | ||
* Returns the collection of the recommended WP.com themes. | ||
* | ||
* @return array<stdClass> An array with all the recommended WPCom themes. | ||
*/ | ||
public function fetch_recommended_themes(): array { | ||
return $this->fetch_themes( | ||
'wpcom-themes-recommended', | ||
array( 'collection' => 'recommended' ) | ||
); | ||
} | ||
|
||
/** | ||
* Returns the WP.com themes that match the given search term. | ||
* | ||
* @param string $search Search term. | ||
* | ||
* @return array<stdClass> An array with all the matching WPCom themes. | ||
*/ | ||
public function search_themes( string $search ): array { | ||
return $this->fetch_themes( | ||
'wpcom-themes-search-' . md5( $search ), | ||
array( | ||
'search' => $search, | ||
'sort' => 'date', | ||
) | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?php | ||
/** | ||
* Class WPCom_Themes_Cache. | ||
* Caches WPCom themes. | ||
* | ||
* @package wpcom-themes | ||
*/ | ||
|
||
/** | ||
* Basic cache implementation for themes. | ||
*/ | ||
class WPCom_Themes_Cache { | ||
/** | ||
* The cache group. | ||
* | ||
* @var string | ||
*/ | ||
const CACHE_GROUP = 'wpcom-themes-cache'; | ||
|
||
/** | ||
* Executes the supplied callable and caches the result. | ||
* | ||
* @param string $cache_key The cache key. | ||
* @param callable $lambda Callable that returns theme data. | ||
* @param int $ttl Time to live in seconds. | ||
* | ||
* @return mixed Cached data. | ||
*/ | ||
public function run_cached( string $cache_key, callable $lambda, int $ttl = DAY_IN_SECONDS ) { | ||
$data = wp_cache_get( $cache_key, self::CACHE_GROUP ); | ||
|
||
if ( false === $data || defined( 'IGNORE_CACHED_WPCOM_THEMES' ) ) { | ||
$data = $lambda(); | ||
wp_cache_set( $cache_key, $data, self::CACHE_GROUP, $ttl ); | ||
} | ||
|
||
return $data; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
<?php | ||
/** | ||
* Class WPCom_Themes_Mapper. | ||
* Responsible for mapping theme objects between WPCom and WPOrg formats. | ||
* | ||
* @package wpcom-themes | ||
*/ | ||
|
||
/** | ||
* Maps theme objects between WPCom and WPOrg formats. | ||
*/ | ||
class WPCom_Themes_Mapper { | ||
|
||
/** | ||
* Maps a WPCom theme subject to .org theme subjects. | ||
*/ | ||
private const SUBJECT_MAP = array( | ||
'business' => 'e-commerce', | ||
'store' => 'e-commerce', | ||
'real-estate' => 'e-commerce', | ||
'health-wellness' => 'e-commerce', | ||
'restaurant' => 'food-and-drink', | ||
'travel-lifestyle' => 'holiday', | ||
'art-design' => 'photography', | ||
'about' => 'blog', | ||
'authors-writers' => 'blog', | ||
'newsletter' => 'news', | ||
'magazine' => 'news', | ||
'music' => 'portfolio', | ||
'fashion-beauty' => 'e-commerce', | ||
'community-non-profit' => 'blog', | ||
'podcast' => 'portfolio', | ||
); | ||
|
||
/** | ||
* Maps a WPCom theme object to a WPOrg theme object. | ||
* | ||
* @param stdClass $wpcom_theme WPCom theme object. | ||
* | ||
* @return stdClass WPOrg theme object. | ||
*/ | ||
public function map_wpcom_to_wporg( stdClass $wpcom_theme ): stdClass { | ||
$slug = $wpcom_theme->stylesheet; | ||
if ( defined( 'IS_ATOMIC' ) && IS_ATOMIC ) { | ||
$slug = $wpcom_theme->id; | ||
} | ||
|
||
$wp_theme = wp_get_theme( $slug ); | ||
$current_theme = wp_get_theme(); | ||
|
||
$theme = new stdClass(); | ||
$theme->name = $wpcom_theme->name; | ||
$theme->slug = $slug; | ||
$theme->preview_url = $wpcom_theme->demo_uri . '?demo=true&iframe=true&theme_preview=true'; | ||
$theme->author = array( 'display_name' => $wpcom_theme->author ); | ||
$theme->screenshot_url = $wpcom_theme->screenshot; | ||
$theme->homepage = "https://wordpress.com/theme/$wpcom_theme->id"; | ||
$theme->description = $wpcom_theme->description; | ||
|
||
// Some themes returned by the API do not have a download URI, but they are installable since they're managed by WP.com. | ||
// In those cases we generate a fake download url so that we can find the theme by download url even though it's not a real download link. | ||
$theme->download_link = $wpcom_theme->id; | ||
$theme->is_commercial = false; | ||
$theme->external_support_url = false; | ||
$theme->is_community = false; | ||
$theme->compatible_wp = true; | ||
$theme->compatible_php = true; | ||
$theme->num_ratings = 0; | ||
$theme->rating = 0; | ||
$theme->requires = '5.8'; | ||
$theme->requires_php = '7.4'; | ||
$theme->active = $slug === $current_theme->get_stylesheet(); | ||
$theme->installed = $wp_theme->exists(); | ||
$theme->block_theme = $wpcom_theme->block_theme; | ||
$theme->version = $wpcom_theme->version; | ||
$theme->creation_time = $wpcom_theme->date_added; | ||
$theme->is_wpcom_theme = true; | ||
$theme->tags = $this->build_theme_tags( $wpcom_theme ); | ||
$theme->tier = $wpcom_theme->theme_tier->slug; | ||
$theme->theme_type = $wpcom_theme->theme_type; | ||
$theme->product_details = property_exists( $wpcom_theme, 'product_details' ) ? $wpcom_theme->product_details : ''; | ||
|
||
return $theme; | ||
} | ||
|
||
/** | ||
* Creates an array of theme tags from a WPCom theme object. | ||
* | ||
* @param stdClass $theme WPCom theme object. | ||
* | ||
* @return array Theme tags. | ||
*/ | ||
private function build_theme_tags( $theme ) { | ||
$tags = array(); | ||
|
||
foreach ( $theme->taxonomies->theme_subject ?? array() as $subject ) { | ||
$tags[] = $this::SUBJECT_MAP[ $subject->slug ] ?? $subject->slug; | ||
} | ||
|
||
foreach ( (array) $theme->taxonomies as $taxonomy ) { | ||
foreach ( $taxonomy as $item ) { | ||
if ( isset( $item->slug ) ) { | ||
$tags[] = $item->slug; | ||
} | ||
} | ||
} | ||
|
||
return $tags; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function calls the API through HTTP. On Simple sites we can call the code directly, which means that we don't need to wait for an HTTP request. I think we can do this using Client::wpcom_json_api_request_as_blog
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 458df77 - we should now be able to modify the API and see the results when testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reverted this because it was broken, but it should give you an idea how to do it!