Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 86
0.00% covered (danger)
0.00%
0 / 12
CRAP
n/a
0 / 0
wpcomsh_get_wpcom_themes_service_instance
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
wpcomsh_remove_dot_org_themes_based_on_plan
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
wpcomsh_popular_wpcom_themes_api_result
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
wpcomsh_latest_wpcom_themes_api_result
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
wpcomsh_block_themes_wpcom_themes_api_result
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
wpcomsh_search_wpcom_themes_api_result
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
wpcomsh_feature_filter_wpcom_themes_api_result
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
wpcomsh_theme_information_wpcom_themes_api_result
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
wpcomsh_remove_symlink_wp_error
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
wpcomsh_theme_install_by_symlink
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
56
wpcomsh_delete_managed_wpcom_theme
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
wpcomsh_include_themes_creation_date
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Registers a filter for the themes API result to add WPCom themes.
4 *
5 * @package wpcom-themes
6 */
7
8/**
9 * Loads the WPCom themes service instance.
10 *
11 * @return WPCom_Themes_Service
12 */
13function wpcomsh_get_wpcom_themes_service_instance(): WPCom_Themes_Service {
14    static $instance;
15    if ( ! $instance ) {
16        // Load dependencies.
17        require_once __DIR__ . '/includes/class-wpcom-themes-mapper.php';
18        require_once __DIR__ . '/includes/class-wpcom-themes-merger.php';
19        require_once __DIR__ . '/includes/class-wpcom-themes-cache.php';
20        require_once __DIR__ . '/includes/class-wpcom-themes-service.php';
21        require_once __DIR__ . '/includes/class-wpcom-themes-api.php';
22
23        // Resolve and Inject dependencies.
24        $mapper               = new WPCom_Themes_Mapper();
25        $merger               = new WPCom_Themes_Merger();
26        $cache                = new WPCom_Themes_Cache();
27        $api                  = new WPCom_Themes_Api( $cache );
28        $wpcom_themes_service = new WPCom_Themes_Service( $api, $mapper, $merger );
29
30        $instance = $wpcom_themes_service;
31    }
32
33    return $instance;
34}
35
36/**
37 * Perform necessary checks to remove dot org themes based on the plan and other features.
38 *
39 * @param mixed $res The result object.
40 *
41 * @return mixed|stdClass
42 */
43function wpcomsh_remove_dot_org_themes_based_on_plan( $res ) {
44    if ( ! wpcom_site_has_feature( WPCOM_Features::COMMUNITY_THEMES ) ) {
45        $res->info['results'] = 0;
46        $res->themes          = array();
47    }
48
49    return $res;
50}
51add_filter( 'themes_api_result', 'wpcomsh_remove_dot_org_themes_based_on_plan', 0, 1 );
52
53/**
54 * Process the themes API result and add the recommended WPCom themes to the Popular tab.
55 *
56 * @param mixed  $res    The result object.
57 * @param string $action The action.
58 * @param mixed  $args   The arguments.
59 *
60 * @return mixed|stdClass
61 */
62function wpcomsh_popular_wpcom_themes_api_result( $res, string $action, $args ) {
63    // Pre-requisites checks.
64    $browse = $args->browse ?? '';
65    if ( 'query_themes' !== $action || 'popular' !== $browse ) {
66        return $res;
67    }
68
69    $wpcom_themes_service = wpcomsh_get_wpcom_themes_service_instance();
70
71    // Add results to the resulting array.
72    return $wpcom_themes_service->filter_themes_api_result_recommended( $res );
73}
74add_filter( 'themes_api_result', 'wpcomsh_popular_wpcom_themes_api_result', 1, 3 );
75
76/**
77 * Process the themes API result and add the latest WPCom themes to the Latest tab.
78 *
79 * @param mixed  $res    The result object.
80 * @param string $action The action.
81 * @param mixed  $args   The arguments.
82 *
83 * @return mixed|stdClass
84 */
85function wpcomsh_latest_wpcom_themes_api_result( $res, string $action, $args ) {
86    // Pre-requisites checks.
87    $browse = $args->browse ?? '';
88    if ( 'query_themes' !== $action || 'new' !== $browse ) {
89        return $res;
90    }
91
92    $wpcom_themes_service = wpcomsh_get_wpcom_themes_service_instance();
93
94    // Add results to the resulting array.
95    return $wpcom_themes_service->filter_themes_api_result_latest( $res );
96}
97add_filter( 'themes_api_result', 'wpcomsh_latest_wpcom_themes_api_result', 1, 3 );
98
99/**
100 * Process the themes API result and add the WPCom block themes to the Block themes tab.
101 *
102 * @param mixed  $res    The result object.
103 * @param string $action The action.
104 * @param mixed  $args   The arguments.
105 *
106 * @return mixed|stdClass
107 */
108function wpcomsh_block_themes_wpcom_themes_api_result( $res, string $action, $args ) {
109    // Pre-requisites checks.
110    $tag = $args->tag ?? '';
111    if ( 'query_themes' !== $action || 'full-site-editing' !== $tag ) {
112        return $res;
113    }
114
115    $wpcom_themes_service = wpcomsh_get_wpcom_themes_service_instance();
116
117    // Add results to the resulting array.
118    return $wpcom_themes_service->filter_themes_api_result_block_themes( $res );
119}
120add_filter( 'themes_api_result', 'wpcomsh_block_themes_wpcom_themes_api_result', 1, 3 );
121
122/**
123 * Process the themes API result and add WPCom themes when using the search.
124 *
125 * @param mixed  $res    The result object.
126 * @param string $action The action.
127 * @param mixed  $args   The arguments.
128 *
129 * @return mixed|stdClass
130 */
131function wpcomsh_search_wpcom_themes_api_result( $res, string $action, $args ) {
132    // Pre-requisites checks.
133    $search = $args->search ?? '';
134    if ( 'query_themes' !== $action || '' === $search ) {
135        return $res;
136    }
137
138    $wpcom_themes_service = wpcomsh_get_wpcom_themes_service_instance();
139
140    // Add results to the resulting array.
141    return $wpcom_themes_service->filter_themes_api_result_search( $res, $search );
142}
143add_filter( 'themes_api_result', 'wpcomsh_search_wpcom_themes_api_result', 1, 3 );
144
145/**
146 * Process the themes API result and add WPCom themes when using the filter feature.
147 *
148 * @param mixed  $res    The result object.
149 * @param string $action The action.
150 * @param mixed  $args   The arguments.
151 *
152 * @return mixed|stdClass
153 */
154function wpcomsh_feature_filter_wpcom_themes_api_result( $res, string $action, $args ) {
155    // Pre-requisites checks.
156    $tags = $args->tag ?? array();
157    if ( 'query_themes' !== $action || ! $tags ) {
158        return $res;
159    }
160
161    if ( ! is_array( $tags ) ) {
162        $tags = array( $tags );
163    }
164
165    $wpcom_themes_service = wpcomsh_get_wpcom_themes_service_instance();
166
167    // Add results to the resulting array.
168    return $wpcom_themes_service->filter_themes_api_result_feature_filter( $res, $tags );
169}
170add_filter( 'themes_api_result', 'wpcomsh_feature_filter_wpcom_themes_api_result', 1, 3 );
171
172/**
173 * Process the themes API result theme_information for WPCom themes if needed.
174 *
175 * @param mixed  $res    The result object.
176 * @param string $action The action.
177 * @param mixed  $args   The arguments.
178 *
179 * @return mixed|stdClass|WP_Error|null
180 */
181function wpcomsh_theme_information_wpcom_themes_api_result( $res, string $action, $args ) {
182    // Pre-requisites checks.
183    if ( 'theme_information' !== $action ) {
184        return $res;
185    }
186
187    $wpcom_themes_service = wpcomsh_get_wpcom_themes_service_instance();
188
189    return $wpcom_themes_service->get_theme( $args->slug ) ?? $res;
190}
191add_filter( 'themes_api_result', 'wpcomsh_theme_information_wpcom_themes_api_result', 1, 3 );
192
193/**
194 * Remove the WP_Error object from the WP_Error object.
195 *
196 * @param string   $code    The error code.
197 * @param string   $message The error message.
198 * @param mixed    $data    The error data.
199 * @param WP_Error $error   The WP_Error object.
200 *
201 * @return void
202 */
203function wpcomsh_remove_symlink_wp_error( $code, $message, $data, WP_Error $error ) {
204    if ( 'wpcomsh_theme_install_symlink' === $code ) {
205        $error->remove( 'wpcomsh_theme_install_symlink' );
206    }
207}
208
209/**
210 * Install WPCom themes by creating a symlink instead of downloading the theme package.
211 *
212 * @param mixed  $reply    The result object.
213 * @param string $package  The package to install.
214 * @param mixed  $upgrader The upgrader component.
215 *
216 * @return bool|mixed|WP_Error
217 */
218function wpcomsh_theme_install_by_symlink( $reply, $package, $upgrader ) {
219    // Pre-requisites checks.
220    if ( ! $package || ! is_string( $package ) || ! $upgrader instanceof Theme_Upgrader ) {
221        return $reply;
222    }
223
224    $wpcom_themes_service = wpcomsh_get_wpcom_themes_service_instance();
225    $wpcom_theme          = $wpcom_themes_service->get_theme( $package );
226
227    if ( ! $wpcom_theme ) {
228        return $reply;
229    }
230
231    $upgrader->skin->feedback( 'installing_package' );
232    $result = wpcomsh_jetpack_wpcom_theme_skip_download( false, $wpcom_theme->slug );
233
234    if ( is_wp_error( $result ) && $result->get_error_code() !== 'wpcom_theme_already_installed' ) {
235        $upgrader->skin->feedback( 'process_failed' );
236        // The internal state and return values are WP_Errors.
237        $upgrader->result = $result;
238
239        return $result;
240    }
241
242    // Set the result to the theme slug to indicate success and so that the skin can reference it.
243    $result           = array(
244        'destination_name' => $wpcom_theme->slug,
245    );
246    $upgrader->result = $result;
247    // Skin uses both the upgrader and its own result which should be the same.
248    $upgrader->skin->result = $result;
249    $upgrader->skin->feedback( 'process_success' );
250
251    // Symlink errors are not really errors so let's remove them. Some clients inspect the skin errors to determine the success of the operation.
252    add_action( 'wp_error_added', 'wpcomsh_remove_symlink_wp_error', 0, 4 );
253
254    // We need to return a WP_Error to short circuit the installation process.
255    return new WP_Error( 'wpcomsh_theme_install_symlink' );
256}
257add_filter( 'upgrader_pre_download', 'wpcomsh_theme_install_by_symlink', 0, 4 );
258
259/**
260 * Remove the symlink created for the WPCom theme when the theme is deleted.
261 *
262 * @param string $stylesheet The theme stylesheet.
263 *
264 * @return void Return early if the theme is not a WPCom theme.
265 */
266function wpcomsh_delete_managed_wpcom_theme( string $stylesheet ) {
267    if ( wpcomsh_is_theme_symlinked( $stylesheet ) ) {
268        wpcomsh_jetpack_wpcom_theme_delete( false, $stylesheet );
269    }
270}
271add_action( 'delete_theme', 'wpcomsh_delete_managed_wpcom_theme' );
272
273/**
274 * Include the creation date in the themes API response.
275 *
276 * @param array $args The arguments.
277 *
278 * @return array The arguments with the creation time included.
279 */
280function wpcomsh_include_themes_creation_date( $args ) {
281    $args['fields']['creation_time'] = true;
282
283    return $args;
284}
285add_filter( 'install_themes_table_api_args_new', 'wpcomsh_include_themes_creation_date' );
286add_filter( 'install_themes_table_api_args_search', 'wpcomsh_include_themes_creation_date' );