Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 390
0.00% covered (danger)
0.00%
0 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 6
WPCOM_JSON_API_Sharing_Button_Endpoint
0.00% covered (danger)
0.00%
0 / 83
0.00% covered (danger)
0.00%
0 / 8
2970
0.00% covered (danger)
0.00%
0 / 1
 setup
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
56
 format_sharing_button
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
42
 get_button_visibility
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 is_button_enabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 is_button_input_for_custom
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
56
 validate_button_input
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
56
 create_custom_button
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
42
 update_button
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
306
WPCOM_JSON_API_Get_Sharing_Buttons_Endpoint
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
182
0.00% covered (danger)
0.00%
0 / 1
 callback
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
182
WPCOM_JSON_API_Get_Sharing_Button_Endpoint
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 1
 callback
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
WPCOM_JSON_API_Update_Sharing_Buttons_Endpoint
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
182
0.00% covered (danger)
0.00%
0 / 1
 callback
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
182
WPCOM_JSON_API_Update_Sharing_Button_Endpoint
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
72
0.00% covered (danger)
0.00%
0 / 1
 callback
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
72
WPCOM_JSON_API_Delete_Sharing_Button_Endpoint
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 1
 callback
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
1<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3// phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
4
5if ( ! defined( 'ABSPATH' ) ) {
6    exit( 0 );
7}
8
9/**
10 * Sharing button endpoint class.
11 */
12abstract class WPCOM_JSON_API_Sharing_Button_Endpoint extends WPCOM_JSON_API_Endpoint {
13
14    /**
15     * All visibilties.
16     *
17     * @var array
18     */
19    public static $all_visibilities = array( 'visible', 'hidden' );
20
21    /**
22     * Sharing service.
23     *
24     * @var Sharing_Service
25     */
26    protected $sharing_service;
27
28    /**
29     * Setup function.
30     *
31     * @return null|WP_Error
32     */
33    protected function setup() {
34        if ( class_exists( 'Sharing_Service' ) ) {
35            $this->sharing_service = new Sharing_Service();
36        }
37
38        if ( ! current_user_can( 'manage_options' ) ) {
39            return new WP_Error( 'forbidden', 'You do not have the capability to manage sharing buttons for this site', 403 );
40        } elseif ( ! class_exists( 'Sharing_Service' ) || ! class_exists( 'Sharing_Source' ) ||
41                ( method_exists( 'Jetpack', 'is_module_active' ) && ! Jetpack::is_module_active( 'sharedaddy' ) ) ) {
42            return new WP_Error( 'missing_jetpack_module', 'The Sharing module must be activated in order to use this endpoint', 400 );
43        }
44    }
45
46    /**
47     * Format the sharing button.
48     *
49     * @param object $button - the button object.
50     * @return array
51     */
52    public function format_sharing_button( $button ) {
53        $response = array(
54            'ID'        => $button->get_id(),
55            'name'      => $button->get_name(),
56            'shortname' => $button->shortname,
57            'custom'    => is_a( $button, 'Share_Custom' ),
58            'enabled'   => $this->is_button_enabled( $button ),
59        );
60
61        if ( $response['enabled'] ) {
62            // Status is either "disabled" or the visibility value
63            $response['visibility'] = $this->get_button_visibility( $button );
64        }
65
66        if ( ! empty( $button->icon ) ) {
67            // Only pre-defined sharing buttons include genericon
68            $response['genericon'] = $button->icon;
69        }
70
71        if ( method_exists( $button, 'get_options' ) ) {
72            // merge get_options() values into response, primarily to account
73            // for custom sharing button values
74            foreach ( $button->get_options() as $key => $value ) {
75                // Capitalize URL property
76                if ( 'url' === strtolower( $key ) ) {
77                    $key = strtoupper( $key );
78                }
79
80                $response[ $key ] = $value;
81            }
82        }
83
84        return $response;
85    }
86
87    /**
88     * Get the button visibility.
89     *
90     * @param object $button - the button object.
91     *
92     * @return string|false
93     */
94    public function get_button_visibility( $button ) {
95        $services     = $this->sharing_service->get_blog_services();
96        $visibilities = self::$all_visibilities;
97        $button_id    = $button->get_id();
98
99        foreach ( $visibilities as $visibility ) {
100            if ( isset( $services[ $visibility ][ $button_id ] ) ) {
101                return $visibility;
102            }
103        }
104
105        return false;
106    }
107
108    /**
109     * Check if the button is enabled.
110     *
111     * @param object $button - the button object.
112     *
113     * @return bool
114     */
115    public function is_button_enabled( $button ) {
116        return false !== $this->get_button_visibility( $button );
117    }
118
119    /**
120     * Check if button is for custom (?).
121     *
122     * @param array $button - the button array.
123     *
124     * @return bool
125     */
126    protected function is_button_input_for_custom( $button ) {
127        return ( isset( $button['custom'] ) && $button['custom'] ) ||
128            ( isset( $button['ID'] ) && 1 === preg_match( '/^custom-/', $button['ID'] ) ) ||
129            ! empty( $button['name'] ) || ! empty( $button['URL'] ) || ! empty( $button['icon'] );
130    }
131
132    /**
133     * Validate the button input.
134     *
135     * @param array $button - the button array.
136     * @param bool  $is_new - if the button is new.
137     *
138     * @return null|WP_Error
139     */
140    protected function validate_button_input( $button, $is_new = false ) {
141        if ( ! empty( $button['visibility'] ) && ! in_array( $button['visibility'], self::$all_visibilities, true ) ) {
142            return new WP_Error( 'invalid_visibility', sprintf( 'The visibility field must be one of the following values: %s', implode( ', ', self::$all_visibilities ) ), 400 );
143        } elseif ( $is_new && empty( $button['URL'] ) ) {
144            return new WP_Error( 'invalid_request', 'The URL field is required', 400 );
145        } elseif ( $is_new && empty( $button['icon'] ) ) {
146            return new WP_Error( 'invalid_request', 'The icon field is required', 400 );
147        }
148    }
149
150    /**
151     * Create a custom button.
152     *
153     * @param array $button - the button array.
154     *
155     * @return Share_Custom|false
156     */
157    public function create_custom_button( $button ) {
158        // Default visibility to 'visible' if enabled
159        if ( empty( $button['visibility'] ) && true === $button['enabled'] ) {
160            $button['visibility'] = 'visible';
161        }
162
163        $updated_service = $this->sharing_service->new_service( $button['name'], $button['URL'], $button['icon'] );
164        if ( false !== $updated_service && ( true === $button['enabled'] || ! empty( $button['visibility'] ) ) ) {
165            $blog_services = $this->sharing_service->get_blog_services();
166            $blog_services[ $button['visibility'] ][ (string) $updated_service->get_id() ] = $updated_service;
167            $this->sharing_service->set_blog_services( array_keys( $blog_services['visible'] ), array_keys( $blog_services['hidden'] ) );
168        }
169
170        return $updated_service;
171    }
172
173    /**
174     * Update the button.
175     *
176     * @param int   $button_id - the button id.
177     * @param array $button - the button array.
178     *
179     * @return Share_Custom|WP_Error
180     */
181    public function update_button( $button_id, $button ) {
182        $blog_services = $this->sharing_service->get_blog_services();
183
184        // Find existing button
185        $all_buttons = $this->sharing_service->get_all_services_blog();
186        if ( ! array_key_exists( $button_id, $all_buttons ) ) {
187            // Button doesn't exist
188            return new WP_Error( 'not_found', 'The specified sharing button was not found', 404 );
189        }
190
191        $updated_service = $all_buttons[ $button_id ];
192        $service_id      = $updated_service->get_id();
193        if ( is_a( $all_buttons[ $button_id ], 'Share_Custom' ) ) {
194            // Replace options for existing custom button
195            $options         = $updated_service->get_options();
196            $name            = isset( $button['name'] ) ? $button['name'] : $options['name'];
197            $url             = isset( $button['URL'] ) ? $button['URL'] : $options['url'];
198            $icon            = isset( $button['icon'] ) ? $button['icon'] : $options['icon'];
199            $updated_service = new Share_Custom(
200                $service_id,
201                array(
202                    'name' => $name,
203                    'url'  => $url,
204                    'icon' => $icon,
205                )
206            );
207            $this->sharing_service->set_service( $button_id, $updated_service );
208        }
209
210        // Default visibility to 'visible' if enabled
211        if ( empty( $button['visibility'] ) && true === $button['enabled'] ) {
212            $button['visibility'] = 'visible';
213        } elseif ( false === $button['enabled'] ) {
214            unset( $button['visibility'] );
215        }
216
217        // Update button visibility and enabled status
218        $visibility_changed = ( isset( $button['visibility'] ) || true === $button['enabled'] ) && ! array_key_exists( $service_id, $blog_services[ $button['visibility'] ] );
219        $is_disabling       = false === $button['enabled'];
220        if ( $visibility_changed || $is_disabling ) {
221            // Remove from all other visibilities
222            foreach ( $blog_services as $service_visibility => $services ) {
223                if ( $is_disabling || $service_visibility !== $button['visibility'] ) {
224                    unset( $blog_services[ $service_visibility ][ $service_id ] );
225                }
226            }
227
228            if ( $visibility_changed ) {
229                $blog_services[ $button['visibility'] ][ $service_id ] = $updated_service;
230            }
231
232            $this->sharing_service->set_blog_services( array_keys( $blog_services['visible'] ), array_keys( $blog_services['hidden'] ) );
233        }
234
235        return $updated_service;
236    }
237}
238
239new WPCOM_JSON_API_Get_Sharing_Buttons_Endpoint(
240    array(
241        'description'          => 'Get a list of a site\'s sharing buttons.',
242        'group'                => 'sharing',
243        'stat'                 => 'sharing-buttons',
244        'method'               => 'GET',
245        'path'                 => '/sites/%s/sharing-buttons/',
246        'path_labels'          => array(
247            '$site' => '(int|string) Site ID or domain',
248        ),
249        'query_parameters'     => array(
250            'enabled_only' => '(bool) If true, only enabled sharing buttons are included in the response',
251            'visibility'   => '(string) The type of enabled sharing buttons to filter by, either "visible" or "hidden"',
252        ),
253        'response_format'      => array(
254            'found'           => '(int) The total number of sharing buttons found that match the request.',
255            'sharing_buttons' => '(array:object) Array of sharing button objects',
256        ),
257        'example_request'      => 'https://public-api.wordpress.com/rest/v1/sites/30434183/sharing-buttons/',
258        'example_request_data' => array(
259            'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ),
260        ),
261        'example_response'     => '
262{
263    "found": 2,
264    "sharing_buttons": [
265        {
266            "ID": "twitter",
267            "name": "Twitter",
268            "shortname": "twitter",
269            "custom": false,
270            "enabled": true,
271            "visibility": "visible",
272            "genericon": "\\f202"
273        },
274        {
275            "ID": "facebook",
276            "name": "Facebook",
277            "shortname": "facebook",
278            "custom": false,
279            "enabled": true,
280            "visibility": "visible",
281            "genericon": "\\f203"
282        }
283    ]
284}',
285    )
286);
287
288/**
289 * Get sharing buttons endpoint class.
290 *
291 * GET /sites/%s/sharing-buttons -> $blog_id
292 *
293 * @phan-constructor-used-for-side-effects
294 */
295class WPCOM_JSON_API_Get_Sharing_Buttons_Endpoint extends WPCOM_JSON_API_Sharing_Button_Endpoint {
296
297    /**
298     * API Callback.
299     *
300     * @param string $path - the path.
301     * @param int    $blog_id - the blog ID.
302     * @return array|WP_Error
303     */
304    public function callback( $path = '', $blog_id = 0 ) {
305        $args = $this->query_args();
306
307        // Validate request
308        $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
309        if ( is_wp_error( $blog_id ) ) {
310            return $blog_id;
311        }
312
313        $continue = $this->setup();
314        if ( is_wp_error( $continue ) ) {
315            return $continue;
316        }
317
318        if ( ! empty( $args['visibility'] ) && ! in_array( $args['visibility'], self::$all_visibilities, true ) ) {
319            return new WP_Error( 'invalid_visibility', sprintf( 'The visibility field must be one of the following values: %s', implode( ', ', self::$all_visibilities ) ), 400 );
320        }
321
322        // Determine which visibilities to include based on request
323        $visibilities = empty( $args['visibility'] ) ? self::$all_visibilities : array( $args['visibility'] );
324
325        // Discover enabled services
326        $buttons          = array();
327        $enabled_services = $this->sharing_service->get_blog_services();
328        $all_services     = $this->sharing_service->get_all_services_blog();
329
330        // Include buttons of desired visibility
331        foreach ( $visibilities as $visibility ) {
332            $buttons = array_merge( $buttons, $enabled_services[ $visibility ] );
333        }
334
335        // Unless `enabled_only` or `visibility` is specified, append the
336        // remaining buttons to the end of the array
337        if ( ( ! isset( $args['enabled_only'] ) || ! $args['enabled_only'] ) && empty( $args['visibility'] ) ) {
338            foreach ( $all_services as $id => $button ) {
339                if ( ! array_key_exists( $id, $buttons ) ) {
340                    $buttons[ $id ] = $button;
341                }
342            }
343        }
344
345        // Format each button in the response
346        $response = array();
347        foreach ( $buttons as $button ) {
348            $response[] = $this->format_sharing_button( $button );
349        }
350
351        return array(
352            'found'           => count( $response ),
353            'sharing_buttons' => $response,
354        );
355    }
356}
357
358new WPCOM_JSON_API_Get_Sharing_Button_Endpoint(
359    array(
360        'description'          => 'Get information about a single sharing button.',
361        'group'                => '__do_not_document',
362        'stat'                 => 'sharing-buttons:1',
363        'method'               => 'GET',
364        'path'                 => '/sites/%s/sharing-buttons/%s',
365        'path_labels'          => array(
366            '$site'      => '(int|string) Site ID or domain',
367            '$button_id' => '(string) The button ID',
368        ),
369        'response_format'      => array(
370            'ID'         => '(int) Sharing button ID',
371            'name'       => '(string) Sharing button name, used as a label on the button itself',
372            'shortname'  => '(string) A generated short name for the sharing button',
373            'URL'        => '(string) The URL pattern defined for a custom sharing button',
374            'icon'       => '(string) URL to the 16x16 icon defined for a custom sharing button',
375            'genericon'  => '(string) Icon character in Genericons icon set',
376            'custom'     => '(bool) Is the button a user-created custom sharing button?',
377            'enabled'    => '(bool) Is the button currently enabled for the site?',
378            'visibility' => '(string) If enabled, the current visibility of the sharing button, either "visible" or "hidden"',
379        ),
380        'example_request'      => 'https://public-api.wordpress.com/rest/v1/sites/30434183/sharing-buttons/facebook',
381        'example_request_data' => array(
382            'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ),
383        ),
384        'example_response'     => '{
385    "ID": "facebook",
386    "name": "Facebook",
387    "shortname": "facebook",
388    "custom": false,
389    "enabled": true,
390    "visibility": "visible",
391    "genericon": "\\f203"
392}',
393    )
394);
395
396/**
397 * Get sharing button endpoint class.
398 *
399 * GET /sites/%s/sharing-buttons/%s -> $blog_id, $button_id
400 *
401 * @phan-constructor-used-for-side-effects
402 */
403class WPCOM_JSON_API_Get_Sharing_Button_Endpoint extends WPCOM_JSON_API_Sharing_Button_Endpoint {
404
405    /**
406     * API Callback.
407     *
408     * @param string $path - the path.
409     * @param int    $blog_id - the blog ID.
410     * @param int    $button_id - the button id.
411     * @return array|WP_Error
412     */
413    public function callback( $path = '', $blog_id = 0, $button_id = 0 ) {
414        // Validate request
415        $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
416        if ( is_wp_error( $blog_id ) ) {
417            return $blog_id;
418        }
419
420        $continue = $this->setup();
421        if ( is_wp_error( $continue ) ) {
422            return $continue;
423        }
424
425        // Search existing services for button
426        $all_buttons = $this->sharing_service->get_all_services_blog();
427        if ( ! array_key_exists( $button_id, $all_buttons ) ) {
428            return new WP_Error( 'not_found', 'The specified sharing button was not found', 404 );
429        } else {
430            return $this->format_sharing_button( $all_buttons[ $button_id ] );
431        }
432    }
433}
434
435new WPCOM_JSON_API_Update_Sharing_Buttons_Endpoint(
436    array(
437        'description'          => 'Edit all sharing buttons for a site.',
438        'group'                => 'sharing',
439        'stat'                 => 'sharing-buttons:X:POST',
440        'method'               => 'POST',
441        'path'                 => '/sites/%s/sharing-buttons',
442        'path_labels'          => array(
443            '$site' => '(int|string) Site ID or domain',
444        ),
445        'request_format'       => array(
446            'sharing_buttons' => '(array:sharing_button) An array of sharing button objects',
447        ),
448        'response_format'      => array(
449            'success' => '(bool) Confirmation that all sharing buttons were updated as specified',
450            'updated' => '(array) An array of updated sharing buttons',
451        ),
452        'example_request'      => 'https://public-api.wordpress.com/rest/v1/sites/30434183/sharing-buttons',
453        'example_request_data' => array(
454            'headers' => array(
455                'authorization' => 'Bearer YOUR_API_TOKEN',
456            ),
457            'body'    => array(
458                'sharing_buttons' => array(
459                    array(
460                        'ID'         => 'facebook',
461                        'visibility' => 'hidden',
462                    ),
463                ),
464            ),
465        ),
466        'example_response'     => '{
467    "success": true,
468    "updated": [
469        {
470            "ID": "facebook",
471            "name": "Facebook",
472            "shortname": "facebook",
473            "custom": false,
474            "enabled": true,
475            "visibility": "hidden",
476            "genericon": "\\f204"
477        }
478    ]
479}',
480    )
481);
482
483/**
484 * Update sharing buttons endpoint.
485 *
486 * POST /sites/%s/sharing-buttons -> $blog_id
487 *
488 * @phan-constructor-used-for-side-effects
489 */
490class WPCOM_JSON_API_Update_Sharing_Buttons_Endpoint extends WPCOM_JSON_API_Sharing_Button_Endpoint {
491
492    /**
493     * API Callback.
494     *
495     * @param string $path - the path.
496     * @param int    $blog_id - the blog ID.
497     *
498     * @return array|WP_Error
499     */
500    public function callback( $path = '', $blog_id = 0 ) {
501        $input = $this->input();
502
503        // Validate request
504        $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
505        if ( is_wp_error( $blog_id ) ) {
506            return $blog_id;
507        }
508
509        $continue = $this->setup();
510        if ( is_wp_error( $continue ) ) {
511            return $continue;
512        }
513
514        $all_buttons = $this->sharing_service->get_all_services_blog();
515
516        if ( ! isset( $input['sharing_buttons'] ) ) {
517            $input['sharing_buttons'] = array();
518        }
519
520        // We do a first pass of all buttons to verify that no validation
521        // issues exist before continuing to update
522        foreach ( $input['sharing_buttons'] as $button ) {
523            $button_exists = isset( $button['ID'] ) && array_key_exists( $button['ID'], $all_buttons );
524            $is_custom     = $this->is_button_input_for_custom( $button );
525
526            // If neither custom nor existing, bail
527            if ( ! $button_exists && ! $is_custom ) {
528                return new WP_Error( 'not_found', 'The specified sharing button was not found', 404 );
529            }
530
531            // Validate input, only testing custom values if the button doesn't
532            // already exist
533            $validation_error = $this->validate_button_input( $button, ! $button_exists );
534            if ( is_wp_error( $validation_error ) ) {
535                return $validation_error;
536            }
537        }
538
539        // Reset all existing buttons
540        $this->sharing_service->set_blog_services( array(), array() );
541
542        // Finally, we iterate over each button and update or create
543        $success = true;
544        $updated = array();
545        foreach ( $input['sharing_buttons'] as $button ) {
546            $button_exists = isset( $button['ID'] ) && array_key_exists( $button['ID'], $all_buttons );
547            if ( $button_exists ) {
548                $updated_service = $this->update_button( $button['ID'], $button );
549            } else {
550                $updated_service = $this->create_custom_button( $button );
551            }
552
553            // We'll allow the request to continue if a failure occurred, but
554            // log it for the response
555            if ( false === $updated_service ) {
556                $success = false;
557            } else {
558                $updated[] = $this->format_sharing_button( $updated_service );
559            }
560        }
561
562        return array(
563            'success' => $success,
564            'updated' => $updated,
565        );
566    }
567}
568
569new WPCOM_JSON_API_Update_Sharing_Button_Endpoint(
570    array(
571        'description'          => 'Create a new custom sharing button.',
572        'group'                => '__do_not_document',
573        'stat'                 => 'sharing-buttons:new',
574        'method'               => 'POST',
575        'path'                 => '/sites/%s/sharing-buttons/new',
576        'path_labels'          => array(
577            '$site' => '(int|string) Site ID or domain',
578        ),
579        'request_format'       => array(
580            'name'       => '(string) The name for your custom sharing button, used as a label on the button itself',
581            'URL'        => '(string) The URL to use for share links, including optional placeholders (%post_id%, %post_title%, %post_slug%, %post_url%, %post_full_url%, %post_excerpt%, %post_tags%, %home_url%)',
582            'icon'       => '(string) The full URL to a 16x16 icon to display on the sharing button',
583            'enabled'    => '(bool) Is the button currently enabled for the site?',
584            'visibility' => '(string) If enabled, the visibility of the sharing button, either "visible" (default) or "hidden"',
585        ),
586        'response_format'      => array(
587            'ID'         => '(string) Sharing button ID',
588            'name'       => '(string) Sharing button name, used as a label on the button itself',
589            'shortname'  => '(string) A generated short name for the sharing button',
590            'URL'        => '(string) The URL pattern defined for a custom sharing button',
591            'icon'       => '(string) URL to the 16x16 icon defined for a custom sharing button',
592            'genericon'  => '(string) Icon character in Genericons icon set',
593            'custom'     => '(bool) Is the button a user-created custom sharing button?',
594            'enabled'    => '(bool) Is the button currently enabled for the site?',
595            'visibility' => '(string) If enabled, the current visibility of the sharing button, either "visible" or "hidden"',
596        ),
597        'example_request'      => 'https://public-api.wordpress.com/rest/v1/sites/30434183/sharing-buttons/new/',
598        'example_request_data' => array(
599            'headers' => array(
600                'authorization' => 'Bearer YOUR_API_TOKEN',
601            ),
602            'body'    => array(
603                'name'       => 'Custom',
604                'URL'        => 'https://www.wordpress.com/%post_name%',
605                'icon'       => 'https://en.wordpress.com/i/stats-icon.gif',
606                'enabled'    => true,
607                'visibility' => 'visible',
608            ),
609        ),
610        'example_response'     => '{
611    "ID": "custom-123456789",
612    "name": "Custom",
613    "shortname": "custom",
614    "url": "https://www.wordpress.com/%post_name%",
615    "icon": "https://en.wordpress.com/i/stats-icon.gif",
616    "custom": true,
617    "enabled": true,
618    "visibility": "visible"
619}',
620    )
621);
622
623new WPCOM_JSON_API_Update_Sharing_Button_Endpoint(
624    array(
625        'description'          => 'Edit a sharing button.',
626        'group'                => '__do_not_document',
627        'stat'                 => 'sharing-buttons:1:POST',
628        'method'               => 'POST',
629        'path'                 => '/sites/%s/sharing-buttons/%s',
630        'path_labels'          => array(
631            '$site'      => '(int|string) Site ID or domain',
632            '$button_id' => '(string) The button ID',
633        ),
634        'request_format'       => array(
635            'name'       => '(string) Only if a custom sharing button, a new name used as a label on the button itself',
636            'URL'        => '(string) Only if a custom sharing button, the URL to use for share links, including optional placeholders (%post_title%, %post_url%, %post_full_url%, %post_excerpt%, %post_tags%)',
637            'icon'       => '(string) Only if a custom sharing button, the full URL to a 16x16 icon to display on the sharing button',
638            'enabled'    => '(bool) Is the button currently enabled for the site?',
639            'visibility' => '(string) If enabled, the visibility of the sharing button, either "visible" (default) or "hidden"',
640        ),
641        'response_format'      => array(
642            'ID'         => '(string) Sharing button ID',
643            'name'       => '(string) Sharing button name, used as a label on the button itself',
644            'shortname'  => '(string) A generated short name for the sharing button',
645            'URL'        => '(string) The URL pattern defined for a custom sharing button',
646            'icon'       => '(string) URL to the 16x16 icon defined for a custom sharing button',
647            'genericon'  => '(string) Icon character in Genericons icon set',
648            'custom'     => '(bool) Is the button a user-created custom sharing button?',
649            'enabled'    => '(bool) Is the button currently enabled for the site?',
650            'visibility' => '(string) If enabled, the current visibility of the sharing button, either "visible" or "hidden"',
651        ),
652        'example_request'      => 'https://public-api.wordpress.com/rest/v1/sites/30434183/sharing-buttons/custom-123456789/',
653        'example_request_data' => array(
654            'headers' => array(
655                'authorization' => 'Bearer YOUR_API_TOKEN',
656            ),
657            'body'    => array(
658                'enabled' => false,
659            ),
660        ),
661        'example_response'     => '{
662    "ID": "custom-123456789",
663    "name": "Custom",
664    "shortname": "custom",
665    "custom": true,
666    "enabled": false,
667    "icon": "https://en.wordpress.com/i/stats-icon.gif",
668    "url": "https://www.wordpress.com/%post_name%"
669}',
670    )
671);
672
673/**
674 * Sharing button endpoint class.
675 *
676 * POST /sites/%s/sharing-buttons/new -> $blog_id
677 * POST /sites/%s/sharing-buttons/%s -> $blog_id, $button_id
678 *
679 * @phan-constructor-used-for-side-effects
680 */
681class WPCOM_JSON_API_Update_Sharing_Button_Endpoint extends WPCOM_JSON_API_Sharing_Button_Endpoint {
682
683    /**
684     * API Callback.
685     *
686     * @param string $path - the path.
687     * @param int    $blog_id - the blog ID.
688     * @param int    $button_id - the button ID.
689     *
690     * @return array|WP_Error
691     */
692    public function callback( $path = '', $blog_id = 0, $button_id = 0 ) {
693        $new   = $this->api->ends_with( $path, '/new' );
694        $input = $this->input();
695
696        // Validate request
697        $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
698        if ( is_wp_error( $blog_id ) ) {
699            return $blog_id;
700        }
701
702        $continue = $this->setup();
703        if ( is_wp_error( $continue ) ) {
704            return $continue;
705        }
706
707        $validation_error = $this->validate_button_input( $input, $new );
708        if ( is_wp_error( $validation_error ) ) {
709            return $validation_error;
710        }
711
712        // Update or create button
713        if ( $new ) {
714            $updated_service = $this->create_custom_button( $input );
715        } else {
716            $updated_service = $this->update_button( $button_id, $input );
717        }
718
719        if ( false === $updated_service ) {
720            return new WP_Error( 'invalid_request', sprintf( 'The sharing button was not %s', $new ? 'created' : 'updated' ), 400 );
721        } elseif ( is_wp_error( $updated_service ) ) {
722            return $updated_service;
723        } else {
724            return $this->format_sharing_button( $updated_service );
725        }
726    }
727}
728
729new WPCOM_JSON_API_Delete_Sharing_Button_Endpoint(
730    array(
731        'description'          => 'Delete a custom sharing button.',
732        'group'                => '__do_not_document',
733        'stat'                 => 'sharing-buttons:1:delete',
734        'method'               => 'POST',
735        'path'                 => '/sites/%s/sharing-buttons/%s/delete',
736        'path_labels'          => array(
737            '$site'      => '(int|string) Site ID or domain',
738            '$button_id' => '(string) The button ID',
739        ),
740        'response_format'      => array(
741            'ID'      => '(int) The ID of the deleted sharing button',
742            'success' => '(bool) Confirmation that the sharing button has been removed',
743        ),
744        'example_request'      => 'https://public-api.wordpress.com/rest/v1/sites/30434183/sharing-buttons/custom-123456789/delete',
745        'example_request_data' => array(
746            'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ),
747        ),
748        'example_response'     => '{
749    "ID": "custom-123456789",
750    "success": "true"
751}',
752    )
753);
754
755/**
756 * Delete sharing button endpoint class.
757 *
758 * POST /sites/%s/sharing-buttons/%s/delete -> $blog_id, $button_id
759 *
760 * @phan-constructor-used-for-side-effects
761 */
762class WPCOM_JSON_API_Delete_Sharing_Button_Endpoint extends WPCOM_JSON_API_Sharing_Button_Endpoint {
763    /**
764     * API Callback.
765     *
766     * @param string $path - the path.
767     * @param int    $blog_id - the blog ID.
768     * @param int    $button_id - the button ID.
769     *
770     * @return array|WP_Error
771     */
772    public function callback( $path = '', $blog_id = 0, $button_id = 0 ) {
773        // Validate request
774        $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
775        if ( is_wp_error( $blog_id ) ) {
776            return $blog_id;
777        }
778
779        $continue = $this->setup();
780        if ( is_wp_error( $continue ) ) {
781            return $continue;
782        }
783
784        // Find existing button
785        $all_buttons = $this->sharing_service->get_all_services_blog();
786        if ( ! array_key_exists( $button_id, $all_buttons ) ) {
787            // Button doesn't exist
788            return new WP_Error( 'not_found', 'The specified sharing button was not found', 404 );
789        }
790
791        // Verify button is custom
792        if ( ! is_a( $all_buttons[ $button_id ], 'Share_Custom' ) ) {
793            return new WP_Error( 'invalid_request', 'Only custom sharing buttons can be deleted', 400 );
794        }
795
796        $success = $this->sharing_service->delete_service( $button_id );
797        return array(
798            'ID'      => $button_id,
799            'success' => $success,
800        );
801    }
802}