Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.41% covered (success)
98.41%
62 / 63
85.71% covered (warning)
85.71%
6 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Schema_Settings_Controller
98.41% covered (success)
98.41%
62 / 63
85.71% covered (warning)
85.71%
6 / 7
10
0.00% covered (danger)
0.00%
0 / 1
 register_routes
100.00% covered (success)
100.00%
42 / 42
100.00% covered (success)
100.00%
1 / 1
1
 permissions_check
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 write_permissions_check
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
3.01
 get_item
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 update_item
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 get_author_profile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 update_author_profile
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * REST controller for the site-level Schema settings.
4 *
5 * Exposes {@see Schema_Settings} over a dedicated `jetpack/v4/seo/schema-settings`
6 * route rather than `/jetpack/v4/settings`, which rejects the nested schema
7 * container. GET returns the editing payload; the write method sanitizes,
8 * persists, and returns the new payload. Gated on `manage_options`.
9 *
10 * Also serves the signed-in user's author profile on `jetpack/v4/seo/author-profile`.
11 * Simple's loader registers only the controllers it names, so that route lives here.
12 *
13 * @package automattic/jetpack-seo-package
14 */
15
16namespace Automattic\Jetpack\SEO;
17
18use WP_REST_Request;
19use WP_REST_Server;
20
21/**
22 * Registers and serves the Schema settings and author profile REST routes.
23 */
24class Schema_Settings_Controller {
25
26    /**
27     * REST namespace, shared with the package's other Jetpack routes.
28     *
29     * @var string
30     */
31    const REST_NAMESPACE = 'jetpack/v4';
32
33    /**
34     * Route, relative to the namespace.
35     *
36     * @var string
37     */
38    const REST_BASE = '/seo/schema-settings';
39
40    /**
41     * Author profile route, relative to the namespace.
42     *
43     * @var string
44     */
45    const AUTHOR_PROFILE_BASE = '/seo/author-profile';
46
47    /**
48     * Register the GET (read) and write (sanitize + persist) routes.
49     *
50     * @return void
51     */
52    public static function register_routes() {
53        register_rest_route(
54            self::REST_NAMESPACE,
55            self::REST_BASE,
56            array(
57                array(
58                    'methods'             => WP_REST_Server::READABLE,
59                    'callback'            => array( __CLASS__, 'get_item' ),
60                    'permission_callback' => array( __CLASS__, 'permissions_check' ),
61                ),
62                array(
63                    'methods'             => WP_REST_Server::EDITABLE,
64                    'callback'            => array( __CLASS__, 'update_item' ),
65                    'permission_callback' => array( __CLASS__, 'write_permissions_check' ),
66                ),
67            )
68        );
69
70        register_rest_route(
71            self::REST_NAMESPACE,
72            self::AUTHOR_PROFILE_BASE,
73            array(
74                array(
75                    'methods'             => WP_REST_Server::READABLE,
76                    'callback'            => array( __CLASS__, 'get_author_profile' ),
77                    'permission_callback' => array( __CLASS__, 'permissions_check' ),
78                ),
79                array(
80                    'methods'             => WP_REST_Server::EDITABLE,
81                    'callback'            => array( __CLASS__, 'update_author_profile' ),
82                    'permission_callback' => array( __CLASS__, 'write_permissions_check' ),
83                    'args'                => array(
84                        'name'        => array( 'type' => 'string' ),
85                        'description' => array( 'type' => 'string' ),
86                        'url'         => array( 'type' => 'string' ),
87                        'jobTitle'    => array( 'type' => 'string' ),
88                        'sameAs'      => array(
89                            'type'  => 'array',
90                            'items' => array( 'type' => 'string' ),
91                        ),
92                    ),
93                ),
94            )
95        );
96    }
97
98    /**
99     * Only administrators, the SEO page's audience, can use these routes.
100     *
101     * @return bool
102     */
103    public static function permissions_check() {
104        return current_user_can( 'manage_options' );
105    }
106
107    /**
108     * Require administrator access and Jetpack ownership before saving SEO settings.
109     *
110     * @since $$next-version$$
111     *
112     * @return bool|\WP_Error
113     */
114    public static function write_permissions_check() {
115        if ( ! self::permissions_check() ) {
116            return false;
117        }
118
119        if ( apply_filters( 'jetpack_disable_seo_tools', false ) ) {
120            return new \WP_Error(
121                'jetpack_seo_conflicting_plugin',
122                __( 'Another SEO plugin is managing your structured data. Your changes were not saved. Reload this page to see the current settings.', 'jetpack-seo' ),
123                array( 'status' => 409 )
124            );
125        }
126
127        return true;
128    }
129
130    /**
131     * GET: the editing payload (stored overrides plus placeholder defaults).
132     *
133     * @return \WP_REST_Response
134     */
135    public static function get_item() {
136        return rest_ensure_response( Schema_Settings::get_editable() );
137    }
138
139    /**
140     * POST/PUT: sanitize and persist the submission, then return the new payload.
141     * Unknown keys are ignored and omitted schema sections retain their stored values.
142     *
143     * @param WP_REST_Request $request The REST request.
144     * @return \WP_REST_Response
145     */
146    public static function update_item( WP_REST_Request $request ) {
147        $params = $request->get_json_params();
148        if ( ! is_array( $params ) ) {
149            $params = $request->get_params();
150        }
151
152        return rest_ensure_response( Schema_Settings::update( $params ) );
153    }
154
155    /**
156     * GET: the signed-in user's author profile.
157     *
158     * @return \WP_REST_Response
159     */
160    public static function get_author_profile() {
161        return rest_ensure_response( Author_Schema_Node::get_profile( get_current_user_id() ) );
162    }
163
164    /**
165     * POST/PUT: update the signed-in user's author profile and return it as stored.
166     *
167     * @param WP_REST_Request $request The REST request.
168     * @return \WP_REST_Response|\WP_Error
169     */
170    public static function update_author_profile( WP_REST_Request $request ) {
171        $fields = array_intersect_key(
172            $request->get_params(),
173            array_flip( array( 'name', 'description', 'url', 'jobTitle', 'sameAs' ) )
174        );
175
176        return rest_ensure_response( Author_Schema_Node::update_profile( get_current_user_id(), $fields ) );
177    }
178}