Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 115
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
SettingsController
0.00% covered (danger)
0.00%
0 / 115
0.00% covered (danger)
0.00%
0 / 6
1260
0.00% covered (danger)
0.00%
0 / 1
 register_routes
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
2
 get_item
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 update_item
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 1
272
 get_current_settings
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
72
 initialize
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
30
 validate_twitter_id
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2namespace NewfoldLabs\WP\Module\Onboarding\RestApi;
3
4use NewfoldLabs\WP\Module\Onboarding\Permissions;
5use NewfoldLabs\WP\Module\Onboarding\Data\Options;
6use NewfoldLabs\WP\Module\Onboarding\Data\Config;
7use NewfoldLabs\WP\Module\Onboarding\WP_Config;
8
9/**
10 * Class SettingsController
11 */
12class SettingsController {
13
14    /**
15     * The namespace of this controller's route.
16     *
17     * @var string
18     */
19    protected $namespace = 'newfold-onboarding/v1';
20
21    /**
22     * The endpoint base
23     *
24     * @var string
25     */
26    protected $rest_base = '/settings';
27
28    /**
29     * Array of defaults for the option.
30     *
31     * Shouldn't be requested directly, use $this->get_defaults();
32     *
33     * @var array
34     */
35    protected $defaults = array(
36        'facebook_site'         => '', // Text field.
37        'instagram_url'         => '',
38        'linkedin_url'          => '',
39        'myspace_url'           => '',
40        'og_default_image'      => '', // Text field.
41        'og_default_image_id'   => '',
42        'og_frontpage_title'    => '', // Text field.
43        'og_frontpage_desc'     => '', // Text field.
44        'og_frontpage_image'    => '', // Text field.
45        'og_frontpage_image_id' => '',
46        'opengraph'             => true,
47        'pinterest_url'         => '',
48        'pinterestverify'       => '',
49        'twitter'               => true,
50        'twitter_site'          => '', // Text field.
51        'twitter_card_type'     => 'summary_large_image',
52        'youtube_url'           => '',
53        'wikipedia_url'         => '',
54        'other_social_urls'     => array(),
55        'mastodon_url'          => '',
56    );
57
58    /**
59     * Validate these URL keys in the data provided
60     *
61     * @var array
62     */
63    protected $social_urls_to_validate = array(
64        'facebook_site',
65        'instagram_url',
66        'linkedin_url',
67        'twitter_site',
68        'myspace_url',
69        'pinterest_url',
70        'youtube_url',
71        'wikipedia_url',
72        'other_social_urls',
73        'mastodon_url',
74    );
75
76    /**
77     * Store for invalid urls
78     *
79     * @var array
80     */
81    protected $invalid_urls = array();
82
83    /**
84     * Registers the settings route
85     */
86    public function register_routes() {
87        \register_rest_route(
88            $this->namespace,
89            $this->rest_base,
90            array(
91                array(
92                    'methods'             => \WP_REST_Server::READABLE,
93                    'callback'            => array( $this, 'get_item' ),
94                    'permission_callback' => array( Permissions::class, 'rest_is_authorized_admin' ),
95                ),
96                array(
97                    'methods'             => \WP_REST_Server::EDITABLE,
98                    'callback'            => array( $this, 'update_item' ),
99                    'permission_callback' => array( Permissions::class, 'rest_is_authorized_admin' ),
100                ),
101            )
102        );
103
104        \register_rest_route(
105            $this->namespace,
106            $this->rest_base . '/initialize',
107            array(
108                array(
109                    'methods'             => \WP_REST_Server::EDITABLE,
110                    'callback'            => array( $this, 'initialize' ),
111                    'permission_callback' => array( Permissions::class, 'rest_is_authorized_admin' ),
112                ),
113            )
114        );
115    }
116
117    /**
118     * Retrieves the settings handled by the plugin.
119     *
120     * @return \WP_REST_Response
121     */
122    public function get_item() {
123        return new \WP_REST_Response( $this->get_current_settings() );
124    }
125
126    /**
127     * Updates settings for the settings object.
128     *
129     * @param \WP_REST_Request $request Full details about the request.
130     *
131     * @return \WP_REST_Response|\WP_Error
132     */
133    public function update_item( \WP_REST_Request $request ) {
134        $settings = $this->get_current_settings();
135        $params   = $request->get_json_params();
136
137        // Check if $params is an array, else return the current settings that we have.
138        if ( ! is_array( $params ) ) {
139            return $settings;
140        }
141
142        // check if all the param keys are present in the yoast social keys
143        foreach ( $params as $param_key => $param_value ) {
144            if ( ! array_key_exists( $param_key, $this->defaults ) ) {
145                $this->invalid_urls[] = $param_key;
146                unset( $params[ $param_key ] );
147                continue;
148            }
149
150            // check for proper url
151            if ( in_array( $param_key, $this->social_urls_to_validate, true ) ) {
152                switch ( $param_key ) {
153                    case 'twitter_site':
154                        if ( ! empty( $params['twitter_site'] ) ) {
155                            $twitter_id = $this->validate_twitter_id( $params['twitter_site'] );
156                            if ( false === $twitter_id ) {
157                                $this->invalid_urls[] = 'twitter_site';
158                                unset( $params['twitter_site'] );
159                            } else {
160                                $params['twitter_site'] = $twitter_id;
161                            }
162                        }
163                        break;
164                    case 'other_social_urls':
165                        $filtered_social_urls = array();
166                        foreach ( $param_value as $param_key_osu => $param_url ) {
167                            if ( ! empty( $param_url ) ) {
168                                if ( \wp_http_validate_url( $param_url ) ) {
169                                    $filtered_social_urls[] = \sanitize_text_field( $param_url );
170                                } else {
171                                    $this->invalid_urls[] = $param_key_osu;
172                                }
173                            }
174                        }
175                        $params[ $param_key ] = $filtered_social_urls;
176                        break;
177                    default:
178                        $param[ $param_key ] = \sanitize_text_field( $param_value );
179                        if ( ! empty( $param_value ) && ! \wp_http_validate_url( $param_value ) ) {
180                            $this->invalid_urls[] = $param_key;
181                            unset( $params[ $param_key ] );
182                        }
183                        break;
184                }
185            }
186        }
187        $settings = array_merge( $settings, $params );
188
189        \update_option( Options::get_option_name( 'wpseo_social', false ), $settings );
190
191        if ( ! empty( $this->invalid_urls ) ) {
192            $error_keys = implode( ', ', $this->invalid_urls );
193            return new \WP_Error(
194                'invalid_urls',
195                "Invalid url(s) provided for {$error_keys}.",
196                array( 'status' => 400 )
197            );
198        }
199        return $this->get_item();
200    }
201
202    /**
203     * Retrieve the existing saved array of settings
204     *
205     * @return array $settings List of the settings and their values
206     */
207    public function get_current_settings() {
208
209        $social_data = \get_option( Options::get_option_name( 'wpseo_social', false ), false );
210        // incase yoast plugin is not installed then we need to save the values in the yoast_wp_options_key
211        if ( false === $social_data ) {
212
213            // initialize an array with default values
214            $social_data = $this->defaults;
215
216            // update database
217            \add_option( Options::get_option_name( 'wpseo_social', false ), $social_data );
218        }
219        // add the full url for twitter cause only the handle is saved in the database
220        $twitter_handle = $this->validate_twitter_id( $social_data['twitter_site'] );
221        if ( ( ! empty( $social_data['twitter_site'] ) ) && ( false !== $twitter_handle ) ) {
222            $social_data['twitter_site'] = 'https://www.twitter.com/' . $twitter_handle;
223        }
224
225        $filtered_social_urls = array();
226        // handle other social urls for onboarding
227        foreach ( $social_data['other_social_urls'] as $index => $social_url ) {
228            if ( ! empty( $social_url ) ) {
229                if ( preg_match( '/(?:https?:\/\/)?(www\.)?yelp\.com/', $social_url ) ) {
230                    $filtered_social_urls['yelp_url'] = $social_url;
231                } elseif ( preg_match( '/(?:https?:\/\/)?(www\.)?tiktok\.com/', $social_url ) ) {
232                    $filtered_social_urls['tiktok_url'] = $social_url;
233                } else {
234                    $filtered_social_urls[ 'social_url_' . $index ] = $social_url;
235                }
236            }
237        }
238        $social_data['other_social_urls'] = $filtered_social_urls;
239        return $social_data;
240    }
241
242    /**
243     * Initialize WordPress Options, Permalinks and Configuration.
244     *
245     * @return \WP_REST_Response
246     */
247    public function initialize() {
248
249        if ( \get_option( Options::get_option_name( 'settings_initialized' ), false ) ) {
250            return new \WP_REST_Response(
251                array(),
252                200
253            );
254        }
255
256        // Update wp_options
257        $init_options = Options::get_initialization_options();
258        foreach ( $init_options as $option_key => $option_value ) {
259            \update_option( Options::get_option_name( $option_key, false ), $option_value );
260        }
261        // Can't be part of initialization constants as they are static.
262        \update_option( Options::get_option_name( 'start_date' ), gmdate( 'U' ) );
263
264        // Flush permalinks
265        flush_rewrite_rules();
266
267        // Add constants to the WordPress configuration (wp-config.php)
268        $wp_config_constants = Config::get_wp_config_initialization_constants();
269        $wp_config           = new WP_Config();
270        foreach ( $wp_config_constants as $constant_key => $constant_value ) {
271            if ( $wp_config->constant_exists( $constant_key ) ) {
272                $wp_config->update_constant( $constant_key, $constant_value );
273                continue;
274            }
275            $wp_config->add_constant( $constant_key, $constant_value );
276        }
277
278        \update_option( Options::get_option_name( 'settings_initialized' ), true );
279
280        return new \WP_REST_Response(
281            array(),
282            201
283        );
284    }
285
286    /**
287     * Validates a twitter id.
288     *
289     * @param string $twitter_id    The twitter id to be validated.
290     * @param bool   $strip_at_sign Whether or not to strip the `@` sign.
291     *
292     * @return string|false The validated twitter id or false if it is not valid.
293     */
294    private function validate_twitter_id( $twitter_id, $strip_at_sign = true ) {
295        $twitter_id = ( $strip_at_sign ) ? sanitize_text_field( ltrim( $twitter_id, '@' ) ) : sanitize_text_field( $twitter_id );
296
297        /*
298         * From the Twitter documentation about twitter screen names:
299         * Typically a maximum of 15 characters long, but some historical accounts may exist with longer names.
300         * A username can only contain alphanumeric characters (letters A-Z, numbers 0-9) with the exception of underscores.
301         *
302         * @link https://support.twitter.com/articles/101299-why-can-t-i-register-certain-usernames
303         */
304        if ( preg_match( '`^[A-Za-z0-9_]{1,25}$`', $twitter_id ) ) {
305            return $twitter_id;
306        }
307
308        if ( preg_match( '`^http(?:s)?://(?:www\.)?twitter\.com/(?P<handle>[A-Za-z0-9_]{1,25})/?$`', $twitter_id, $matches ) ) {
309            return $matches['handle'];
310        }
311
312        return false;
313    }
314}