Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
79.75% |
63 / 79 |
|
77.78% |
7 / 9 |
CRAP | n/a |
0 / 0 |
|
| wpcom_themes_tab_register_flag | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
| wpcom_themes_tab_enabled | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
| wpcom_themes_tab_enqueue_script | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
2 | |||
| wpcom_themes_tab_is_default_view | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
| wpcom_themes_tab_redirect_to_default_view | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
| wpcom_themes_tab_api | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
| wpcom_themes_tab_mapper | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
| wpcom_themes_tab_catalog | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
| wpcom_themes_tab_serve_themes_api | |
100.00% |
26 / 26 |
|
100.00% |
1 / 1 |
11 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * Lists WordPress.com themes as a tab on the core Add Themes screen. |
| 4 | * |
| 5 | * @package automattic/jetpack-mu-wpcom |
| 6 | */ |
| 7 | |
| 8 | use Automattic\Jetpack\Feature_Flags\Feature_Flags; |
| 9 | use Automattic\Jetpack\Jetpack_Mu_Wpcom; |
| 10 | |
| 11 | /** |
| 12 | * The tab's `data-sort` value, which core's theme.js sends to themes_api() as `browse`. |
| 13 | */ |
| 14 | const WPCOM_THEMES_TAB = 'wpcom'; |
| 15 | |
| 16 | /** |
| 17 | * Feature flag gating the tab. |
| 18 | */ |
| 19 | const WPCOM_THEMES_TAB_FLAG = 'wpcom-themes-marketplace-tab'; |
| 20 | |
| 21 | /** |
| 22 | * Themes per request, which is also the grid's page size. |
| 23 | */ |
| 24 | const WPCOM_THEMES_TAB_PER_PAGE = 100; |
| 25 | |
| 26 | /** |
| 27 | * Registers the feature flag. |
| 28 | * |
| 29 | * @return void |
| 30 | */ |
| 31 | function wpcom_themes_tab_register_flag() { |
| 32 | Feature_Flags::register( |
| 33 | WPCOM_THEMES_TAB_FLAG, |
| 34 | array( |
| 35 | 'default' => false, |
| 36 | 'description' => 'Show WordPress.com themes as a tab on the Add Themes screen.', |
| 37 | 'owner' => 'jetpack-mu-wpcom', |
| 38 | ) |
| 39 | ); |
| 40 | } |
| 41 | wpcom_themes_tab_register_flag(); |
| 42 | |
| 43 | /** |
| 44 | * Whether to show the tab on this site. |
| 45 | * |
| 46 | * Atomic only for now: Simple has no Add Themes screen to add a tab to, and this package ships |
| 47 | * there too, so the host is checked rather than left to the screen's absence. |
| 48 | * |
| 49 | * @return bool |
| 50 | */ |
| 51 | function wpcom_themes_tab_enabled() { |
| 52 | return is_woa_site() && Feature_Flags::is_enabled( WPCOM_THEMES_TAB_FLAG ); |
| 53 | } |
| 54 | |
| 55 | /** |
| 56 | * Loads the script that adds the tab. |
| 57 | * |
| 58 | * Core hard-codes this screen's tabs with no filter (trac #65979), so the script inserts ours |
| 59 | * into the markup before core's theme.js reads it on DOM ready. |
| 60 | * |
| 61 | * @return void |
| 62 | */ |
| 63 | function wpcom_themes_tab_enqueue_script() { |
| 64 | if ( ! wpcom_themes_tab_enabled() ) { |
| 65 | return; |
| 66 | } |
| 67 | |
| 68 | wp_enqueue_script( |
| 69 | 'wpcom-themes-tab', |
| 70 | plugins_url( 'js/themes-tab.js', __FILE__ ), |
| 71 | array(), |
| 72 | Jetpack_Mu_Wpcom::PACKAGE_VERSION, |
| 73 | array( 'in_footer' => true ) |
| 74 | ); |
| 75 | wp_localize_script( |
| 76 | 'wpcom-themes-tab', |
| 77 | 'wpcomThemesTab', |
| 78 | array( |
| 79 | 'sort' => WPCOM_THEMES_TAB, |
| 80 | 'label' => _x( 'Marketplace', 'Theme Installer', 'jetpack-mu-wpcom' ), |
| 81 | ) |
| 82 | ); |
| 83 | } |
| 84 | add_action( 'load-theme-install.php', 'wpcom_themes_tab_enqueue_script' ); |
| 85 | |
| 86 | /** |
| 87 | * Whether a request to the Add Themes screen should land on this tab. |
| 88 | * |
| 89 | * @param array $query The request's query args. |
| 90 | * @return bool |
| 91 | */ |
| 92 | function wpcom_themes_tab_is_default_view( array $query ) { |
| 93 | if ( ! wpcom_themes_tab_enabled() ) { |
| 94 | return false; |
| 95 | } |
| 96 | |
| 97 | // An explicit view, a search, an upload or a preview is left as asked for. |
| 98 | return ! array_intersect( array( 'browse', 'theme', 'search', 'upload' ), array_keys( $query ) ); |
| 99 | } |
| 100 | |
| 101 | /** |
| 102 | * Sends a bare Add Themes request to this tab. |
| 103 | * |
| 104 | * Core's theme.js routes a bare `theme-install.php` to `popular` and rewrites the URL to match, so |
| 105 | * the view has to be chosen before that runs rather than by ordering the tabs. |
| 106 | * |
| 107 | * @return void |
| 108 | */ |
| 109 | function wpcom_themes_tab_redirect_to_default_view() { |
| 110 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reads which view was asked for, nothing more. |
| 111 | if ( ! wpcom_themes_tab_is_default_view( $_GET ) ) { |
| 112 | return; |
| 113 | } |
| 114 | |
| 115 | wp_safe_redirect( admin_url( 'theme-install.php?browse=' . WPCOM_THEMES_TAB ) ); |
| 116 | exit; |
| 117 | } |
| 118 | add_action( 'load-theme-install.php', 'wpcom_themes_tab_redirect_to_default_view' ); |
| 119 | /** |
| 120 | * The shared WordPress.com themes reader, which wpcomsh also uses for core's own tabs. |
| 121 | * |
| 122 | * @return WPCom_Themes_Api |
| 123 | */ |
| 124 | function wpcom_themes_tab_api() { |
| 125 | static $api; |
| 126 | if ( ! $api ) { |
| 127 | $api = new WPCom_Themes_Api( new WPCom_Themes_Cache() ); |
| 128 | } |
| 129 | |
| 130 | return $api; |
| 131 | } |
| 132 | |
| 133 | /** |
| 134 | * The shared mapper, which shapes a WordPress.com theme the way core's grid reads it. |
| 135 | * |
| 136 | * @return WPCom_Themes_Mapper |
| 137 | */ |
| 138 | function wpcom_themes_tab_mapper() { |
| 139 | static $mapper; |
| 140 | if ( ! $mapper ) { |
| 141 | $mapper = new WPCom_Themes_Mapper(); |
| 142 | } |
| 143 | |
| 144 | return $mapper; |
| 145 | } |
| 146 | |
| 147 | /** |
| 148 | * Every theme the tab lists, in the order WordPress.com returns them. |
| 149 | * |
| 150 | * The shared reader fetches and caches the whole catalog, so the tab pages through it here rather |
| 151 | * than asking for a page at a time. |
| 152 | * |
| 153 | * @return array<stdClass> |
| 154 | */ |
| 155 | function wpcom_themes_tab_catalog() { |
| 156 | return array_values( |
| 157 | array_filter( |
| 158 | wpcom_themes_tab_api()->fetch_all_non_delisted_themes(), |
| 159 | static function ( $theme ) { |
| 160 | return is_object( $theme ) && ! empty( $theme->id ) && is_string( $theme->id ); |
| 161 | } |
| 162 | ) |
| 163 | ); |
| 164 | } |
| 165 | |
| 166 | /** |
| 167 | * Answers the tab's query in place of the WordPress.org themes API. |
| 168 | * |
| 169 | * @param false|object|array $result The result object or array. Default false. |
| 170 | * @param string $action The type of information being requested. |
| 171 | * @param object $args Theme API arguments. |
| 172 | * @return false|object|array |
| 173 | */ |
| 174 | function wpcom_themes_tab_serve_themes_api( $result, $action, $args ) { |
| 175 | if ( false !== $result || 'query_themes' !== $action || ! wpcom_themes_tab_enabled() ) { |
| 176 | return $result; |
| 177 | } |
| 178 | |
| 179 | // A `?theme=<slug>` preview link, loaded directly, looks its theme up by slug rather than by tab. |
| 180 | if ( ! isset( $args->browse ) && isset( $args->theme ) && is_string( $args->theme ) ) { |
| 181 | $theme = wpcom_themes_tab_api()->fetch_theme( $args->theme ); |
| 182 | |
| 183 | return null === $theme ? $result : (object) array( |
| 184 | 'info' => array( |
| 185 | 'page' => 1, |
| 186 | 'pages' => 1, |
| 187 | 'results' => 1, |
| 188 | ), |
| 189 | 'themes' => array( wpcom_themes_tab_mapper()->map_wpcom_to_wporg( $theme ) ), |
| 190 | ); |
| 191 | } |
| 192 | |
| 193 | if ( ! isset( $args->browse ) || WPCOM_THEMES_TAB !== $args->browse ) { |
| 194 | return $result; |
| 195 | } |
| 196 | |
| 197 | $themes = wpcom_themes_tab_catalog(); |
| 198 | $page = isset( $args->page ) ? max( 1, (int) $args->page ) : 1; |
| 199 | $mapper = wpcom_themes_tab_mapper(); |
| 200 | |
| 201 | // Only the page core asked for is mapped: mapping reads each theme from disk to say whether it |
| 202 | // is installed. |
| 203 | $shown = array_slice( $themes, ( $page - 1 ) * WPCOM_THEMES_TAB_PER_PAGE, WPCOM_THEMES_TAB_PER_PAGE ); |
| 204 | |
| 205 | return (object) array( |
| 206 | 'info' => array( |
| 207 | 'page' => $page, |
| 208 | 'pages' => (int) ceil( count( $themes ) / WPCOM_THEMES_TAB_PER_PAGE ), |
| 209 | 'results' => count( $themes ), |
| 210 | ), |
| 211 | 'themes' => array_values( array_map( array( $mapper, 'map_wpcom_to_wporg' ), $shown ) ), |
| 212 | ); |
| 213 | } |
| 214 | add_filter( 'themes_api', 'wpcom_themes_tab_serve_themes_api', 10, 3 ); |