Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_JSON_API_Themes_Endpoint
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 6
702
0.00% covered (danger)
0.00%
0 / 1
 result
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 validate_input
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
56
 validate_themes
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 format_theme
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
56
 check_query_args
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 get_themes
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
1<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3use Automattic\Jetpack\Image_CDN\Image_CDN_Core;
4
5if ( ! defined( 'ABSPATH' ) ) {
6    exit( 0 );
7}
8
9/**
10 * Base class for working with themes, has useful helper functions.
11 */
12abstract class Jetpack_JSON_API_Themes_Endpoint extends Jetpack_JSON_API_Endpoint {
13
14    /**
15     * The themes.
16     *
17     * @var array
18     */
19    protected $themes = array();
20
21    /**
22     * If we're working in bulk.
23     *
24     * @var boolean
25     */
26    protected $bulk = true;
27
28    /**
29     * The log.
30     *
31     * @var array
32     */
33    protected $log;
34
35    /**
36     * The current theme ID.
37     *
38     * @var int
39     */
40    protected $current_theme_id;
41
42    /**
43     * The response format.
44     *
45     * @var array
46     */
47    public static $_response_format = array( // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
48        'id'                     => '(string) The theme\'s ID.',
49        'screenshot'             => '(string) A theme screenshot URL',
50        'name'                   => '(string) The name of the theme.',
51        'theme_uri'              => '(string) The URI of the theme\'s webpage.',
52        'description'            => '(string) A description of the theme.',
53        'author'                 => '(string) The author of the theme.',
54        'author_uri'             => '(string) The website of the theme author.',
55        'tags'                   => '(array) Tags indicating styles and features of the theme.',
56        'log'                    => '(array) An array of log strings',
57        'update'                 => '(array|null) An object containing information about the available update if there is an update available, null otherwise.',
58        'autoupdate'             => '(bool) Whether the theme is automatically updated',
59        'autoupdate_translation' => '(bool) Whether the theme is automatically updating translations',
60    );
61
62    /**
63     * The result.
64     */
65    protected function result() {
66
67        $themes = $this->get_themes();
68
69        if ( ! $this->bulk && ! empty( $themes ) ) {
70            return array_pop( $themes );
71        }
72
73        return array( 'themes' => $themes );
74    }
75
76    /**
77     * Walks through either the submitted theme or list of themes and creates the global array
78     *
79     * @param string $theme - the theme URL.
80     *
81     * @return bool|WP_Error
82     */
83    protected function validate_input( $theme ) {
84        $args = $this->input();
85        // lets set what themes were requested, and validate them
86        if ( ! isset( $theme ) || empty( $theme ) ) {
87
88            if ( ! $args['themes'] || empty( $args['themes'] ) ) {
89                return new WP_Error( 'missing_theme', __( 'You are required to specify a theme to update.', 'jetpack' ), 400 );
90            }
91            if ( is_array( $args['themes'] ) ) {
92                $this->themes = $args['themes'];
93            } else {
94                $this->themes[] = $args['themes'];
95            }
96        } else {
97            $this->themes[] = urldecode( $theme );
98            $this->bulk     = false;
99        }
100
101        $error = $this->validate_themes();
102        if ( is_wp_error( $error ) ) {
103            return $error;
104        }
105
106        return parent::validate_input( $theme );
107    }
108
109    /**
110     * Walks through submitted themes to make sure they are valid
111     *
112     * @return bool|WP_Error
113     */
114    protected function validate_themes() {
115        foreach ( $this->themes as $theme ) {
116            $error = wp_get_theme( $theme )->errors();
117            if ( is_wp_error( $error ) ) {
118                return new WP_Error( 'unknown_theme', $error->get_error_messages(), 404 );
119            }
120        }
121        return true;
122    }
123
124    /**
125     * Format a theme for the public API
126     *
127     * @param  object $theme WP_Theme object.
128     * @return array Named array of theme info used by the API
129     */
130    protected function format_theme( $theme ) {
131
132        if ( ! ( $theme instanceof WP_Theme ) ) {
133            $theme = wp_get_theme( $theme );
134        }
135
136        $fields = array(
137            'name'        => 'Name',
138            'theme_uri'   => 'ThemeURI',
139            'description' => 'Description',
140            'author'      => 'Author',
141            'author_uri'  => 'AuthorURI',
142            'tags'        => 'Tags',
143            'version'     => 'Version',
144        );
145
146        $id              = $theme->get_stylesheet();
147        $formatted_theme = array(
148            'id'         => $id,
149            'screenshot' => Image_CDN_Core::cdn_url( $theme->get_screenshot(), array(), 'network_path' ),
150            'active'     => $id === $this->current_theme_id,
151        );
152
153        foreach ( $fields as $key => $field ) {
154            $formatted_theme[ $key ] = $theme->get( $field );
155        }
156
157        $update_themes             = get_site_transient( 'update_themes' );
158        $formatted_theme['update'] = ( isset( $update_themes->response[ $id ] ) ) ? $update_themes->response[ $id ] : null;
159
160        $autoupdate                    = in_array( $id, Jetpack_Options::get_option( 'autoupdate_themes', array() ), true );
161        $formatted_theme['autoupdate'] = $autoupdate;
162
163        $autoupdate_translation                    = in_array( $id, Jetpack_Options::get_option( 'autoupdate_themes_translations', array() ), true );
164        $formatted_theme['autoupdate_translation'] = $autoupdate || $autoupdate_translation || Jetpack_Options::get_option( 'autoupdate_translations', false );
165
166        if ( isset( $this->log[ $id ] ) ) {
167            $formatted_theme['log'] = $this->log[ $id ];
168        }
169
170        /**
171         * Filter the array of theme information that will be returned per theme by the Jetpack theme APIs.
172         *
173         * @module json-api
174         *
175         * @since 4.7.0
176         *
177         * @param array $formatted_theme The theme info array.
178         */
179        return apply_filters( 'jetpack_format_theme_details', $formatted_theme );
180    }
181
182    /**
183     * Checks the query_args our collection endpoint was passed to ensure that it's in the proper bounds.
184     *
185     * @return bool|WP_Error a WP_Error object if the args are out of bounds, true if things are good.
186     */
187    protected function check_query_args() {
188        $args = $this->query_args();
189        if ( $args['offset'] < 0 ) {
190            return new WP_Error( 'invalid_offset', __( 'Offset must be greater than or equal to 0.', 'jetpack' ), 400 );
191        }
192        if ( $args['limit'] < 0 ) {
193            return new WP_Error( 'invalid_limit', __( 'Limit must be greater than or equal to 0.', 'jetpack' ), 400 );
194        }
195        return true;
196    }
197
198    /**
199     * Format a list of themes for public display, using the supplied offset and limit args
200     *
201     * @uses   WPCOM_JSON_API_Endpoint::query_args()
202     * @return array         Public API theme objects
203     */
204    protected function get_themes() {
205        // ditch keys
206        $themes = array_values( $this->themes );
207        // do offset & limit - we've already returned a 400 error if they're bad numbers
208        $args = $this->query_args();
209
210        if ( isset( $args['offset'] ) ) {
211            $themes = array_slice( $themes, (int) $args['offset'] );
212        }
213        if ( isset( $args['limit'] ) ) {
214            $themes = array_slice( $themes, 0, (int) $args['limit'] );
215        }
216
217        $this->current_theme_id = wp_get_theme()->get_stylesheet();
218
219        return array_map( array( $this, 'format_theme' ), $themes );
220    }
221}