Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 241
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
WPCOM_JSON_API_Update_Taxonomy_Endpoint
0.00% covered (danger)
0.00%
0 / 89
0.00% covered (danger)
0.00%
0 / 4
1806
0.00% covered (danger)
0.00%
0 / 1
 callback
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
 new_taxonomy
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
272
 update_taxonomy
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
210
 delete_taxonomy
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
56
1<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2/**
3 * Update site taxonomy API endpoints.
4 *
5 * Endpoints:
6 * Create a new category: /sites/%s/categories/new
7 * Create a new tag:      /sites/%s/tags/new
8 * Edit a category:       /sites/%s/categories/slug:%s
9 * Edit a tag:            /sites/%s/tags/slug:%s
10 * Delete a category:     /sites/%s/categories/slug:%s/delete
11 * Delete a tag:          /sites/%s/tags/slug:%s/delete
12 */
13
14if ( ! defined( 'ABSPATH' ) ) {
15    exit( 0 );
16}
17
18new WPCOM_JSON_API_Update_Taxonomy_Endpoint(
19    array(
20        'description'          => 'Create a new category.',
21        'group'                => 'taxonomy',
22        'stat'                 => 'categories:new',
23
24        'method'               => 'POST',
25        'path'                 => '/sites/%s/categories/new',
26        'path_labels'          => array(
27            '$site' => '(int|string) Site ID or domain',
28        ),
29
30        'request_format'       => array(
31            'name'        => '(string) Name of the category',
32            'description' => '(string) A description of the category',
33            'parent'      => '(int) ID of the parent category',
34        ),
35
36        'example_request'      => 'https://public-api.wordpress.com/rest/v1/sites/82974409/categories/new/',
37        'example_request_data' => array(
38            'headers' => array(
39                'authorization' => 'Bearer YOUR_API_TOKEN',
40            ),
41            'body'    => array(
42                'name' => 'Puppies',
43            ),
44        ),
45    )
46);
47
48new WPCOM_JSON_API_Update_Taxonomy_Endpoint(
49    array(
50        'description'          => 'Create a new tag.',
51        'group'                => 'taxonomy',
52        'stat'                 => 'tags:new',
53
54        'method'               => 'POST',
55        'path'                 => '/sites/%s/tags/new',
56        'path_labels'          => array(
57            '$site' => '(int|string) Site ID or domain',
58        ),
59
60        'request_format'       => array(
61            'name'        => '(string) Name of the tag',
62            'description' => '(string) A description of the tag',
63        ),
64
65        'example_request'      => 'https://public-api.wordpress.com/rest/v1/sites/82974409/tags/new/',
66        'example_request_data' => array(
67            'headers' => array(
68                'authorization' => 'Bearer YOUR_API_TOKEN',
69            ),
70            'body'    => array(
71                'name' => 'Kitties',
72            ),
73        ),
74    )
75);
76
77new WPCOM_JSON_API_Update_Taxonomy_Endpoint(
78    array(
79        'description'          => 'Edit a tag.',
80        'group'                => 'taxonomy',
81        'stat'                 => 'tags:1:POST',
82
83        'method'               => 'POST',
84        'path'                 => '/sites/%s/tags/slug:%s',
85        'path_labels'          => array(
86            '$site' => '(int|string) Site ID or domain',
87            '$tag'  => '(string) The tag slug',
88        ),
89
90        'request_format'       => array(
91            'name'        => '(string) Name of the tag',
92            'description' => '(string) A description of the tag',
93        ),
94
95        'example_request'      => 'https://public-api.wordpress.com/rest/v1/sites/82974409/tags/slug:testing-tag',
96        'example_request_data' => array(
97            'headers' => array(
98                'authorization' => 'Bearer YOUR_API_TOKEN',
99            ),
100            'body'    => array(
101                'description' => 'Kitties are awesome!',
102            ),
103        ),
104    )
105);
106
107new WPCOM_JSON_API_Update_Taxonomy_Endpoint(
108    array(
109        'description'          => 'Edit a category.',
110        'group'                => 'taxonomy',
111        'stat'                 => 'categories:1:POST',
112
113        'method'               => 'POST',
114        'path'                 => '/sites/%s/categories/slug:%s',
115        'path_labels'          => array(
116            '$site'     => '(int|string) Site ID or domain',
117            '$category' => '(string) The category slug',
118        ),
119
120        'request_format'       => array(
121            'name'        => '(string) Name of the category',
122            'description' => '(string) A description of the category',
123            'parent'      => '(int) ID of the parent category',
124        ),
125
126        'example_request'      => 'https://public-api.wordpress.com/rest/v1/sites/82974409/categories/slug:testing-category',
127        'example_request_data' => array(
128            'headers' => array(
129                'authorization' => 'Bearer YOUR_API_TOKEN',
130            ),
131            'body'    => array(
132                'description' => 'Puppies are great!',
133            ),
134        ),
135    )
136);
137
138new WPCOM_JSON_API_Update_Taxonomy_Endpoint(
139    array(
140        'description'          => 'Delete a category.',
141        'group'                => 'taxonomy',
142        'stat'                 => 'categories:1:delete',
143
144        'method'               => 'POST',
145        'path'                 => '/sites/%s/categories/slug:%s/delete',
146        'path_labels'          => array(
147            '$site'     => '(int|string) Site ID or domain',
148            '$category' => '(string) The category slug',
149        ),
150        'response_format'      => array(
151            'slug'    => '(string) The slug of the deleted category',
152            'success' => '(bool) Was the operation successful?',
153        ),
154
155        'example_request'      => 'https://public-api.wordpress.com/rest/v1/sites/82974409/categories/slug:$category/delete',
156        'example_request_data' => array(
157            'headers' => array(
158                'authorization' => 'Bearer YOUR_API_TOKEN',
159            ),
160        ),
161    )
162);
163
164new WPCOM_JSON_API_Update_Taxonomy_Endpoint(
165    array(
166        'description'          => 'Delete a tag.',
167        'group'                => 'taxonomy',
168        'stat'                 => 'tags:1:delete',
169
170        'method'               => 'POST',
171        'path'                 => '/sites/%s/tags/slug:%s/delete',
172        'path_labels'          => array(
173            '$site' => '(int|string) Site ID or domain',
174            '$tag'  => '(string) The tag slug',
175        ),
176        'response_format'      => array(
177            'slug'    => '(string) The slug of the deleted tag',
178            'success' => '(bool) Was the operation successful?',
179        ),
180
181        'example_request'      => 'https://public-api.wordpress.com/rest/v1/sites/82974409/tags/slug:$tag/delete',
182        'example_request_data' => array(
183            'headers' => array(
184                'authorization' => 'Bearer YOUR_API_TOKEN',
185            ),
186        ),
187    )
188);
189
190/**
191 * Update site taxonomy API class.
192 *
193 * @phan-constructor-used-for-side-effects
194 */
195class WPCOM_JSON_API_Update_Taxonomy_Endpoint extends WPCOM_JSON_API_Taxonomy_Endpoint {
196    /**
197     * Update site taxonomy API callback.
198     *
199     * - /sites/%s/tags|categories/new            -> $blog_id
200     * - /sites/%s/tags|categories/slug:%s        -> $blog_id, $taxonomy_id
201     * - /sites/%s/tags|categories/slug:%s/delete -> $blog_id, $taxonomy_id
202     *
203     * @param string     $path API path.
204     * @param int        $blog_id Blog ID.
205     * @param int|string $object_id Term.
206     */
207    public function callback( $path = '', $blog_id = 0, $object_id = 0 ) {
208        $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
209        if ( is_wp_error( $blog_id ) ) {
210            return $blog_id;
211        }
212
213        if ( preg_match( '#/tags/#i', $path ) ) {
214            $taxonomy_type = 'post_tag';
215        } else {
216            $taxonomy_type = 'category';
217        }
218
219        if ( $this->api->ends_with( $path, '/delete' ) ) {
220            return $this->delete_taxonomy( $path, $blog_id, $object_id, $taxonomy_type );
221        } elseif ( $this->api->ends_with( $path, '/new' ) ) {
222            return $this->new_taxonomy( $path, $blog_id, $taxonomy_type );
223        }
224
225        return $this->update_taxonomy( $path, $blog_id, $object_id, $taxonomy_type );
226    }
227
228    /**
229     * Create a new taxonomy.
230     *
231     * - /sites/%s/tags|categories/new -> $blog_id
232     *
233     * @param string $path API path.
234     * @param int    $blog_id Blog ID.
235     * @param string $taxonomy_type Taxonomy type (category, post_tag).
236     */
237    public function new_taxonomy( $path, $blog_id, $taxonomy_type ) {
238        $args  = $this->query_args();
239        $input = $this->input();
240        if ( ! is_array( $input ) || ! $input || ! strlen( $input['name'] ) ) {
241            return new WP_Error( 'invalid_input', 'Unknown data passed', 400 );
242        }
243
244        $user = wp_get_current_user();
245        if ( ! $user || is_wp_error( $user ) || ! $user->ID ) {
246            return new WP_Error( 'authorization_required', 'An active access token must be used to manage taxonomies.', 403 );
247        }
248
249        $tax = get_taxonomy( $taxonomy_type );
250        if ( ! current_user_can( $tax->cap->edit_terms ) ) {
251            return new WP_Error( 'unauthorized', 'User cannot edit taxonomy', 403 );
252        }
253
254        if ( 'category' !== $taxonomy_type || ! isset( $input['parent'] ) ) {
255            $input['parent'] = 0;
256        }
257
258        $term = get_term_by( 'name', $input['name'], $taxonomy_type );
259        if ( $term ) {
260            // the same name is allowed as long as the parents are different.
261            if ( $input['parent'] === $term->parent ) {
262                return new WP_Error( 'duplicate', 'A taxonomy with that name already exists', 400 );
263            }
264        }
265
266        $data = wp_insert_term(
267            addslashes( $input['name'] ),
268            $taxonomy_type,
269            array(
270                'description' => isset( $input['description'] ) ? addslashes( $input['description'] ) : '',
271                'parent'      => $input['parent'],
272            )
273        );
274
275        if ( is_wp_error( $data ) ) {
276            return $data;
277        }
278
279        $taxonomy = get_term_by( 'id', $data['term_id'], $taxonomy_type );
280
281        $return = $this->get_taxonomy( $taxonomy->slug, $taxonomy_type, $args['context'] );
282        if ( ! $return || is_wp_error( $return ) ) {
283            return $return;
284        }
285
286        /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
287        do_action( 'wpcom_json_api_objects', 'taxonomies' );
288        return $return;
289    }
290
291    /**
292     * Update a taxonomy.
293     *
294     * - /sites/%s/tags|categories/slug:%s -> $blog_id, $taxonomy_id
295     *
296     * @param string     $path API path.
297     * @param int        $blog_id Blog ID.
298     * @param int|string $object_id Term.
299     * @param string     $taxonomy_type Taxonomy type (category, post_tag).
300     */
301    public function update_taxonomy( $path, $blog_id, $object_id, $taxonomy_type ) {
302        $taxonomy = get_term_by( 'slug', $object_id, $taxonomy_type );
303        $tax      = get_taxonomy( $taxonomy_type );
304        if ( ! current_user_can( $tax->cap->edit_terms ) ) {
305            return new WP_Error( 'unauthorized', 'User cannot edit taxonomy', 403 );
306        }
307
308        if ( ! $taxonomy || is_wp_error( $taxonomy ) ) {
309            return new WP_Error( 'unknown_taxonomy', 'Unknown taxonomy', 404 );
310        }
311
312        if ( false === term_exists( $object_id, $taxonomy_type ) ) {
313            return new WP_Error( 'unknown_taxonomy', 'That taxonomy does not exist', 404 );
314        }
315
316        $args  = $this->query_args();
317        $input = $this->input( false );
318        if ( ! is_array( $input ) || ! $input ) {
319            return new WP_Error( 'invalid_input', 'Invalid request input', 400 );
320        }
321
322        $update = array();
323        if ( 'category' === $taxonomy_type && ! empty( $input['parent'] ) ) {
324            $update['parent'] = $input['parent'];
325        }
326
327        if ( ! empty( $input['description'] ) ) {
328            $update['description'] = addslashes( $input['description'] );
329        }
330
331        if ( ! empty( $input['name'] ) ) {
332            $update['name'] = addslashes( $input['name'] );
333        }
334
335        $data = wp_update_term( $taxonomy->term_id, $taxonomy_type, $update );
336        if ( is_wp_error( $data ) ) {
337            return $data;
338        }
339        $taxonomy = get_term_by( 'id', $data['term_id'], $taxonomy_type );
340
341        $return = $this->get_taxonomy( $taxonomy->slug, $taxonomy_type, $args['context'] );
342        if ( ! $return || is_wp_error( $return ) ) {
343            return $return;
344        }
345
346        /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
347        do_action( 'wpcom_json_api_objects', 'taxonomies' );
348        return $return;
349    }
350
351    /**
352     * Delete a taxonomy.
353     *
354     * - /sites/%s/tags|categories/%s/delete -> $blog_id, $taxonomy_id
355     *
356     * @param string     $path API path.
357     * @param int        $blog_id Blog ID.
358     * @param int|string $object_id Term.
359     * @param string     $taxonomy_type Taxonomy type (category, post_tag).
360     */
361    public function delete_taxonomy( $path, $blog_id, $object_id, $taxonomy_type ) {
362        $taxonomy = get_term_by( 'slug', $object_id, $taxonomy_type );
363        $tax      = get_taxonomy( $taxonomy_type );
364        if ( ! current_user_can( $tax->cap->delete_terms ) ) {
365            return new WP_Error( 'unauthorized', 'User cannot edit taxonomy', 403 );
366        }
367
368        if ( ! $taxonomy || is_wp_error( $taxonomy ) ) {
369            return new WP_Error( 'unknown_taxonomy', 'Unknown taxonomy', 404 );
370        }
371
372        if ( false === term_exists( $object_id, $taxonomy_type ) ) {
373            return new WP_Error( 'unknown_taxonomy', 'That taxonomy does not exist', 404 );
374        }
375
376        $args   = $this->query_args();
377        $return = $this->get_taxonomy( $taxonomy->slug, $taxonomy_type, $args['context'] );
378        if ( ! $return || is_wp_error( $return ) ) {
379            return $return;
380        }
381
382        /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
383        do_action( 'wpcom_json_api_objects', 'taxonomies' );
384
385        wp_delete_term( $taxonomy->term_id, $taxonomy_type );
386
387        return array(
388            'slug'    => (string) $taxonomy->slug,
389            'success' => 'true',
390        );
391    }
392}