PluginProbe
Parse.ly / 3.8.4
Parse.ly v3.8.4
3.24.2 3.24.1 3.24.0 3.23.7 3.23.6 3.23.5 3.23.4 3.23.3 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.17.0 3.18.0 3.18.1 3.19.0 3.19.1 3.19.2 3.19.3 3.2.0 3.2.1 3.20.0 3.20.1 3.20.2 All 106 releases
← All changes | src/UI/class-settings-page.php +214 -618 3.17.0 → 3.8.4 View file →
@@ -9,14 +9,12 @@
9 9 declare(strict_types=1);
10 10
11 11 namespace Parsely\UI;
12 12
13 -use Parsely\Content_Helper\Excerpt_Suggestions;
14 13 use Parsely\Parsely;
15 -use Parsely\Permissions;
16 -use Parsely\Utils\Utils;
17 -use Parsely\Validator;
18 14
15 +use function Parsely\Utils\get_asset_info;
16 +
19 17 use const Parsely\PARSELY_FILE;
20 18
21 19 /**
22 20 * Renders the wp-admin Parse.ly plugin settings page.
@@ -22,11 +20,11 @@
22 20 * Renders the wp-admin Parse.ly plugin settings page.
23 21 *
24 22 * @since 3.0.0
25 23 *
24 + * @phpstan-import-type Parsely_Options from Parsely
25 + *
26 26 * @phpstan-type Setting_Arguments array{
27 - * add_fieldset?: bool,
28 - * legend?: string,
29 27 * option_key: string,
30 28 * label_for: string,
31 29 * title?: string,
32 30 * help_text?: string,
@@ -55,32 +53,17 @@
55 53 * disable_amp?: bool,
56 54 * track_post_types_as?: array<string, string>,
57 55 * track_post_types: string[],
58 56 * track_page_types: string[],
59 - * full_metadata_in_non_posts: ?bool,
60 57 * content_id_prefix?: string,
61 58 * use_top_level_cats?:bool|string,
62 59 * custom_taxonomy_section?: string,
63 60 * cats_as_tags?: bool|string,
64 - * content_helper: Parsely_Settings_Options_Content_Helper,
65 61 * lowercase_tags?: bool,
66 62 * force_https_canonicals?: bool,
67 63 * disable_autotrack?: bool|string,
64 + * parsely_wipe_metadata_cache: bool,
68 65 * }
69 - *
70 - * @phpstan-type Parsely_Settings_Options_Content_Helper array{
71 - * ai_features_enabled?: bool,
72 - * smart_linking?: Parsely_Settings_Options_Content_Helper_Feature,
73 - * title_suggestions?: Parsely_Settings_Options_Content_Helper_Feature,
74 - * excerpt_suggestions?: Parsely_Settings_Options_Content_Helper_Feature,
75 - * }
76 - *
77 - * @phpstan-type Parsely_Settings_Options_Content_Helper_Feature array{
78 - * enabled?: bool,
79 - * allowed_user_roles?: array<string, string>|array<string, bool>
80 - * }
81 - *
82 - * @phpstan-import-type Parsely_Options from Parsely
83 66 */
84 67 final class Settings_Page {
85 68 /**
86 69 * Instance of Parsely class.
@@ -98,30 +81,8 @@
98 81 */
99 82 private $hook_suffix;
100 83
101 84 /**
102 - * Options for badges that are displayed for managed options.
103 - *
104 - * @since 3.9.0
105 - *
106 - * @var array<string, mixed>
107 - */
108 - private $managed_options_badge = array();
109 -
110 - /**
111 - * The Content Helper features that can be configured in the settings page.
112 - *
113 - * @since 3.16.0
114 - *
115 - * @var string[]
116 - */
117 - private $configurable_pch_features = array(
118 - 'smart_linking',
119 - 'title_suggestions',
120 - 'excerpt_suggestions',
121 - );
122 -
123 - /**
124 85 * Constructor.
125 86 *
126 87 * @param Parsely $parsely Instance of Parsely class.
127 88 */
@@ -126,20 +87,8 @@
126 87 * @param Parsely $parsely Instance of Parsely class.
127 88 */
128 89 public function __construct( Parsely $parsely ) {
129 90 $this->parsely = $parsely;
130 -
131 - $managed_options_badge = apply_filters(
132 - 'wp_parsely_managed_options_badge',
133 - array(
134 - 'text' => __( 'Upgrade', 'wp-parsely' ),
135 - 'url' => 'https://www.parse.ly/getdemo/',
136 - )
137 - );
138 -
139 - if ( is_array( $managed_options_badge ) ) {
140 - $this->managed_options_badge = $managed_options_badge;
141 - }
142 91 }
143 92
144 93 /**
145 94 * Registers settings page.
@@ -154,35 +103,33 @@
154 103
155 104 /**
156 105 * Enqueues all needed scripts and styles for Parse.ly plugin settings page.
157 106 *
158 - * @param string|null $hook_suffix The current page being loaded.
107 + * @param string $hook_suffix The current page being loaded.
159 108 */
160 - public function enqueue_settings_assets( ?string $hook_suffix ): void {
161 - if ( ! is_string( $hook_suffix ) || 'settings_page_parsely' !== $hook_suffix ) {
162 - return;
163 - }
109 + public function enqueue_settings_assets( string $hook_suffix ): void {
110 + if ( 'settings_page_parsely' === $hook_suffix ) {
111 + add_filter( 'media_library_months_with_files', '__return_empty_array' );
112 + wp_enqueue_media();
164 113
165 - add_filter( 'media_library_months_with_files', '__return_empty_array' );
166 - wp_enqueue_media();
114 + $admin_settings_asset = get_asset_info( 'build/admin-settings.asset.php' );
115 + $built_assets_url = plugin_dir_url( PARSELY_FILE ) . '/build/';
167 116
168 - $admin_settings_asset = Utils::get_asset_info( 'build/admin-settings.asset.php' );
169 - $built_assets_url = plugin_dir_url( PARSELY_FILE ) . '/build/';
117 + wp_enqueue_script(
118 + 'parsely-admin-settings',
119 + $built_assets_url . 'admin-settings.js',
120 + $admin_settings_asset['dependencies'],
121 + $admin_settings_asset['version'],
122 + true
123 + );
170 124
171 - wp_enqueue_script(
172 - 'parsely-admin-settings',
173 - $built_assets_url . 'admin-settings.js',
174 - $admin_settings_asset['dependencies'],
175 - $admin_settings_asset['version'],
176 - true
177 - );
178 -
179 - wp_enqueue_style(
180 - 'parsely-admin-settings',
181 - $built_assets_url . 'admin-settings.css',
182 - $admin_settings_asset['dependencies'],
183 - $admin_settings_asset['version']
184 - );
125 + wp_enqueue_style(
126 + 'parsely-admin-settings',
127 + $built_assets_url . 'admin-settings.css',
128 + $admin_settings_asset['dependencies'],
129 + $admin_settings_asset['version']
130 + );
131 + }
185 132 }
186 133
187 134 /**
188 135 * Adds the Parse.ly settings page in WordPress settings menu.
@@ -190,9 +137,9 @@
190 137 public function add_settings_sub_menu(): void {
191 138 $suffix = add_options_page(
192 139 __( 'Parse.ly Settings', 'wp-parsely' ),
193 140 __( 'Parse.ly', 'wp-parsely' ),
194 - Parsely::CAPABILITY, // phpcs:ignore WordPress.WP.Capabilities.Undetermined
141 + Parsely::CAPABILITY,
195 142 Parsely::MENU_SLUG,
196 143 array( $this, 'display_settings' )
197 144 );
198 145
@@ -228,14 +175,13 @@
228 175 /**
229 176 * Displays the Parse.ly settings screen (options-general.php?page=[SLUG]).
230 177 */
231 178 public function display_settings(): void {
232 - // phpcs:ignore WordPress.WP.Capabilities.Undetermined
233 179 if ( ! current_user_can( Parsely::CAPABILITY ) ) {
234 180 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-parsely' ) );
235 181 }
236 182
237 - include_once plugin_dir_path( PARSELY_FILE ) . 'src/UI/settings-page.php';
183 + include_once plugin_dir_path( PARSELY_FILE ) . 'views/parsely-settings.php';
238 184 }
239 185
240 186 /**
241 187 * Initializes the settings for Parse.ly.
@@ -240,25 +186,16 @@
240 186 /**
241 187 * Initializes the settings for Parse.ly.
242 188 */
243 189 public function initialize_settings(): void {
244 - // Add the option first, to prevent double sanitization of the uninitialized option as reported
245 - // in https://core.trac.wordpress.org/ticket/21989.
246 - // The option will be initialized with the default values.
247 - add_option( Parsely::OPTIONS_KEY, $this->parsely->get_options() );
248 -
249 190 // All our options are actually stored in one single array to reduce DB queries.
250 191 register_setting(
251 192 Parsely::OPTIONS_KEY,
252 193 Parsely::OPTIONS_KEY,
253 - array(
254 - 'type' => 'array',
255 - 'sanitize_callback' => array( $this, 'validate_options' ),
256 - )
194 + array( $this, 'validate_options' )
257 195 );
258 196
259 197 $this->initialize_basic_section();
260 - $this->initialize_content_helper_section();
261 198 $this->initialize_recrawl_section();
262 199 $this->initialize_advanced_section();
263 200 }
264 201
@@ -267,10 +204,9 @@
267 204 *
268 205 * @since 3.2.0
269 206 */
270 207 private function initialize_basic_section(): void {
271 - $are_credentials_managed = $this->parsely->are_credentials_managed;
272 - $section_key = 'basic-section';
208 + $section_key = 'basic-section';
273 209
274 210 add_settings_section(
275 211 $section_key,
276 212 __( 'Basic', 'wp-parsely' ),
@@ -286,15 +222,14 @@
286 222 'label_for' => $field_id,
287 223 'optional_args' => array(
288 224 'required' => 'required',
289 225 'placeholder' => 'mydomain.com',
290 - 'disabled' => $are_credentials_managed,
291 226 ),
292 227
293 228 );
294 229 add_settings_field(
295 230 $field_id,
296 - __( 'Site ID <em>(required)</em>', 'wp-parsely' ),
231 + __( 'Parse.ly Site ID <em>(required)</em>', 'wp-parsely' ),
297 232 array( $this, 'print_text_tag' ),
298 233 Parsely::MENU_SLUG,
299 234 $section_key,
300 235 $field_args
@@ -299,36 +234,54 @@
299 234 $section_key,
300 235 $field_args
301 236 );
302 237
303 - if ( ! $are_credentials_managed ) {
304 - // API Secret.
305 - $field_id = 'api_secret';
306 - $field_args = array(
307 - 'option_key' => $field_id,
308 - 'help_text' => __( 'Your API secret is your secret code to <a href="https://docs.parse.ly/the-parsely-api/">access our API</a>. It can be found at <code>dash.parsely.com/<var>yoursitedomain</var>/settings/api</code> (replace <var>yoursitedomain</var> with your domain name, e.g. <samp>mydomain.com</samp>).<br />If you haven\'t purchased access to the API and would like to do so, email your account manager or <a href="mailto:[email protected]">[email protected]</a>.', 'wp-parsely' ),
309 - 'label_for' => $field_id,
310 - 'optional_args' => array(
311 - 'type' => 'password',
312 - 'is_obfuscated_value' => true,
313 - ),
314 - );
315 - add_settings_field(
316 - $field_id,
317 - __( 'API Secret', 'wp-parsely' ),
318 - array( $this, 'print_text_tag' ),
319 - Parsely::MENU_SLUG,
320 - $section_key,
321 - $field_args
322 - );
323 - }
238 + // API Secret.
239 + $field_id = 'api_secret';
240 + $field_args = array(
241 + 'option_key' => $field_id,
242 + 'help_text' => __( 'Your API secret is your secret code to <a href="https://www.parse.ly/help/api/analytics/">access our API</a>. It can be found at <code>dash.parsely.com/<var>yoursitedomain</var>/settings/api</code> (replace <var>yoursitedomain</var> with your domain name, e.g. <samp>mydomain.com</samp>).<br />If you haven\'t purchased access to the API and would like to do so, email your account manager or <a href="mailto:[email protected]">[email protected]</a>.', 'wp-parsely' ),
243 + 'label_for' => $field_id,
244 + 'optional_args' => array(
245 + 'type' => 'password',
246 + 'is_obfuscated_value' => true,
247 + ),
248 + );
249 + add_settings_field(
250 + $field_id,
251 + __( 'Parse.ly API Secret', 'wp-parsely' ),
252 + array( $this, 'print_text_tag' ),
253 + Parsely::MENU_SLUG,
254 + $section_key,
255 + $field_args
256 + );
324 257
258 + // Metadata Secret.
259 + $field_id = 'metadata_secret';
260 + $field_args = array(
261 + 'option_key' => $field_id,
262 + 'help_text' => __( 'Your metadata secret is given to you by Parse.ly support. DO NOT enter anything here unless given to you by Parse.ly support!', 'wp-parsely' ),
263 + 'label_for' => $field_id,
264 + 'optional_args' => array(
265 + 'type' => 'password',
266 + 'is_obfuscated_value' => true,
267 + ),
268 + );
269 + add_settings_field(
270 + $field_id,
271 + __( 'Parse.ly Metadata Secret', 'wp-parsely' ),
272 + array( $this, 'print_text_tag' ),
273 + Parsely::MENU_SLUG,
274 + $section_key,
275 + $field_args
276 + );
277 +
325 278 // Metadata Format.
326 279 $field_id = 'meta_type';
327 280 $field_args = array(
328 281 'title' => __( 'Metadata Format', 'wp-parsely' ),
329 282 'option_key' => $field_id,
330 - 'help_text' => __( 'Choose the metadata format for our crawlers to access. Most publishers are fine with <a href="https://docs.parse.ly/metadata-jsonld/">JSON-LD</a>, but if you prefer to use our proprietary metadata format then you can do so here.', 'wp-parsely' ),
283 + 'help_text' => __( 'Choose the metadata format for our crawlers to access. Most publishers are fine with <a href="https://www.parse.ly/help/integration/jsonld/">JSON-LD</a>, but if you prefer to use our proprietary metadata format then you can do so here.', 'wp-parsely' ),
331 284 'radio_options' => array(
332 285 'json_ld' => 'json_ld',
333 286 'repeated_metas' => 'repeated_metas',
334 287 ),
@@ -336,9 +289,9 @@
336 289 'filter' => 'wp_parsely_metadata',
337 290 );
338 291 add_settings_field(
339 292 $field_id,
340 - $this->set_field_label_contents( __( 'Metadata Format', 'wp-parsely' ), $field_id ),
293 + __( 'Metadata Format', 'wp-parsely' ),
341 294 array( $this, 'print_radio_tags' ),
342 295 Parsely::MENU_SLUG,
343 296 $section_key,
344 297 $field_args
@@ -348,9 +301,9 @@
348 301 $field_help = __( 'Here you can specify your logo\'s URL by using the "Browse" button or typing the URL manually.', 'wp-parsely' );
349 302 $field_id = 'logo';
350 303 add_settings_field(
351 304 $field_id,
352 - $this->set_field_label_contents( __( 'Logo', 'wp-parsely' ), $field_id ),
305 + __( 'Logo', 'wp-parsely' ),
353 306 array( $this, 'print_media_single_image' ),
354 307 Parsely::MENU_SLUG,
355 308 $section_key,
356 309 array(
@@ -361,18 +314,17 @@
361 314 )
362 315 );
363 316
364 317 // Track logged-in users.
365 - $field_id = 'track_authenticated_users';
366 318 add_settings_field(
367 - $field_id,
368 - $this->set_field_label_contents( __( 'Track Logged-in Users', 'wp-parsely' ), $field_id ),
319 + 'track_authenticated_users',
320 + __( 'Track Logged-in Users', 'wp-parsely' ),
369 321 array( $this, 'print_radio_tags' ),
370 322 Parsely::MENU_SLUG,
371 323 $section_key,
372 324 array(
373 325 'title' => __( 'Track Logged-in Users', 'wp-parsely' ), // Passed for legend element.
374 - 'option_key' => $field_id,
326 + 'option_key' => 'track_authenticated_users',
375 327 'radio_options' => array(
376 328 'true' => __( 'Yes, track logged-in users.', 'wp-parsely' ),
377 329 'false' => __( 'No, do not track logged-in users. I do not want to see the Parse.ly tracking code on my site when browsing while logged in.', 'wp-parsely' ),
378 330 ),
@@ -384,23 +336,22 @@
384 336 )
385 337 );
386 338
387 339 // Disable JavaScript.
388 - $field_id = 'disable_javascript';
389 340 add_settings_field(
390 - $field_id,
391 - $this->set_field_label_contents( __( 'Disable JavaScript', 'wp-parsely' ), $field_id ),
341 + 'disable_javascript',
342 + __( 'Disable JavaScript', 'wp-parsely' ),
392 343 array( $this, 'print_radio_tags' ),
393 344 Parsely::MENU_SLUG,
394 345 $section_key,
395 346 array(
396 347 'title' => __( 'Disable JavaScript', 'wp-parsely' ), // Passed for legend element.
397 - 'option_key' => $field_id,
348 + 'option_key' => 'disable_javascript',
398 349 'radio_options' => array(
399 350 'true' => __( 'Yes, disable JavaScript tracking. I want to use a separate system for tracking instead of the Parse.ly plugin.', 'wp-parsely' ),
400 351 'false' => __( 'No, do not disable JavaScript tracking. I want the Parse.ly plugin to load the tracker.', 'wp-parsely' ),
401 352 ),
402 - 'help_text' => __( '<span style="color:#d63638">WARNING:</span> Changing this setting to "Yes" will prevent the plugin from injecting the Parse.ly tracker. Only do so if you are setting the tracker elsewhere (e.g. hardcoded, GTM, another tag manager, etc.).', 'wp-parsely' ),
353 + 'help_text' => __( '<span style="color:#d63638">WARNING:</span> We highly recommend choosing "No." Disabling the JavaScript tracker will also disable the "Personalize Results" section of the recommendation widget.', 'wp-parsely' ),
403 354 'filter' => 'wp_parsely_load_js_tracker',
404 355 )
405 356 );
406 357
@@ -405,18 +356,17 @@
405 356 );
406 357
407 358 if ( defined( 'AMP__VERSION' ) ) {
408 359 // Disable AMP tracking.
409 - $field_id = 'disable_amp';
410 360 add_settings_field(
411 - $field_id,
412 - $this->set_field_label_contents( __( 'Disable AMP Tracking', 'wp-parsely' ), $field_id ),
361 + 'disable_amp',
362 + __( 'Disable AMP Tracking', 'wp-parsely' ),
413 363 array( $this, 'print_radio_tags' ),
414 364 Parsely::MENU_SLUG,
415 365 $section_key,
416 366 array(
417 367 'title' => __( 'Disable AMP Tracking', 'wp-parsely' ), // Passed for legend element.
418 - 'option_key' => $field_id,
368 + 'option_key' => 'disable_amp',
419 369 'radio_options' => array(
420 370 'true' => __( 'Yes, disable Parse.ly tracking on AMP pages. I use a different system for JavaScript tracking on AMP pages.', 'wp-parsely' ),
421 371 'false' => __( 'No, do not disable Parse.ly tracking on AMP pages.', 'wp-parsely' ),
422 372 ),
@@ -425,91 +375,8 @@
425 375 }
426 376 }
427 377
428 378 /**
429 - * Registers the Content Helper section and its settings.
430 - *
431 - * @since 3.16.0
432 - */
433 - private function initialize_content_helper_section(): void {
434 - $section_key = 'content-helper-section';
435 -
436 - add_settings_section(
437 - $section_key,
438 - __( 'Content Helper', 'wp-parsely' ),
439 - '__return_null',
440 - Parsely::MENU_SLUG
441 - );
442 -
443 - // AI Features.
444 - $field_id = 'content_helper[ai_features_enabled]';
445 - $field_args = array(
446 - 'option_key' => $field_id,
447 - 'label_for' => $field_id,
448 - 'yes_text' => __( 'Enabled', 'wp-parsely' ),
449 - 'add_fieldset' => true,
450 - 'legend' => __( 'AI Features', 'wp-parsely' ),
451 - );
452 - add_settings_field(
453 - $field_id,
454 - __( 'AI Features', 'wp-parsely' ),
455 - array( $this, 'print_checkbox_tag' ),
456 - Parsely::MENU_SLUG,
457 - $section_key,
458 - $field_args
459 - );
460 -
461 - // Smart Linking.
462 - $field_id = 'content_helper[smart_linking]';
463 - $field_args = array(
464 - 'option_key' => $field_id,
465 - 'label_for' => $field_id,
466 - 'legend' => __( 'Smart Linking', 'wp-parsely' ),
467 - );
468 - add_settings_field(
469 - $field_id,
470 - __( 'Smart Linking', 'wp-parsely' ),
471 - array( $this, 'print_content_helper_ai_feature_section' ),
472 - Parsely::MENU_SLUG,
473 - $section_key,
474 - $field_args
475 - );
476 -
477 - // Title Suggestions.
478 - $field_id = 'content_helper[title_suggestions]';
479 - $field_args = array(
480 - 'option_key' => $field_id,
481 - 'label_for' => $field_id,
482 - 'legend' => __( 'Title Suggestions', 'wp-parsely' ),
483 - );
484 - add_settings_field(
485 - $field_id,
486 - __( 'Title Suggestions', 'wp-parsely' ),
487 - array( $this, 'print_content_helper_ai_feature_section' ),
488 - Parsely::MENU_SLUG,
489 - $section_key,
490 - $field_args
491 - );
492 -
493 - // Excerpt Suggestions.
494 - $field_id = 'content_helper[excerpt_suggestions]';
495 - $field_args = array(
496 - 'option_key' => $field_id,
497 - 'label_for' => $field_id,
498 - 'legend' => __( 'Excerpt Suggestions', 'wp-parsely' ),
499 - 'filter' => Excerpt_Suggestions::get_feature_filter_name(),
500 - );
501 - add_settings_field(
502 - $field_id,
503 - __( 'Excerpt Suggestions', 'wp-parsely' ),
504 - array( $this, 'print_content_helper_ai_feature_section' ),
505 - Parsely::MENU_SLUG,
506 - $section_key,
507 - $field_args
508 - );
509 - }
510 -
511 - /**
512 379 * Registers section and settings for Recrawl section.
513 380 *
514 381 * @since 3.2.0
515 382 */
@@ -546,27 +413,8 @@
546 413 'filter' => 'wp_parsely_trackable_statuses',
547 414 )
548 415 );
549 416
550 - // Use full metadata in non-posts.
551 - $field_id = 'full_metadata_in_non_posts';
552 - add_settings_field(
553 - $field_id,
554 - $this->set_field_label_contents( __( 'Use Full Metadata in Non-Posts', 'wp-parsely' ), $field_id ),
555 - array( $this, 'print_radio_tags' ),
556 - Parsely::MENU_SLUG,
557 - $section_key,
558 - array(
559 - 'title' => __( 'Use Full Metadata in Non-Posts', 'wp-parsely' ), // Passed for legend element.
560 - 'option_key' => $field_id,
561 - 'radio_options' => array(
562 - 'true' => __( 'Yes, add full metadata to Post Types being tracked as Non-Posts.', 'wp-parsely' ),
563 - 'false' => __( 'No, we have code that modifies metadata and that has not been tested for compatibility yet.', 'wp-parsely' ),
564 - ),
565 - 'help_text' => __( '<strong><span style="color:#d63638">Important: This setting will be removed in the future, force-enabling this behavior.</span></strong> If you\'re using any code that modifies metadata in a way that conflicts with this setting when it is enabled, please apply any needed fixes.', 'wp-parsely' ),
566 - )
567 - );
568 -
569 417 // Content ID Prefix.
570 418 $field_id = 'content_id_prefix';
571 419 $field_args = array(
572 420 'option_key' => $field_id,
@@ -577,9 +425,9 @@
577 425 'label_for' => $field_id,
578 426 );
579 427 add_settings_field(
580 428 $field_id,
581 - $this->set_field_label_contents( __( 'Content ID Prefix', 'wp-parsely' ), $field_id ),
429 + __( 'Content ID Prefix', 'wp-parsely' ),
582 430 array( $this, 'print_text_tag' ),
583 431 Parsely::MENU_SLUG,
584 432 $section_key,
585 433 $field_args
@@ -585,18 +433,17 @@
585 433 $field_args
586 434 );
587 435
588 436 // Use top-level categories.
589 - $field_id = 'use_top_level_cats';
590 437 add_settings_field(
591 - $field_id,
592 - $this->set_field_label_contents( __( 'Use Top-Level Categories for Section', 'wp-parsely' ), $field_id ),
438 + 'use_top_level_cats',
439 + __( 'Use Top-Level Categories for Section', 'wp-parsely' ),
593 440 array( $this, 'print_radio_tags' ),
594 441 Parsely::MENU_SLUG,
595 442 $section_key,
596 443 array(
597 444 'title' => __( 'Use Top-Level Categories for Section', 'wp-parsely' ), // Passed for legend element.
598 - 'option_key' => $field_id,
445 + 'option_key' => 'use_top_level_cats',
599 446 'radio_options' => array(
600 447 'true' => __( 'Yes, use the first category assigned to a post as the section name.', 'wp-parsely' ),
601 448 'false' => __( 'No, do not use the first category assigned to a post as the section name.', 'wp-parsely' ),
602 449 ),
@@ -608,14 +455,24 @@
608 455 $field_id = 'custom_taxonomy_section';
609 456 $field_args = array(
610 457 'option_key' => $field_id,
611 458 'help_text' => __( 'By default, the section value in your Parse.ly dashboard maps to a post\'s category. You can optionally choose a custom taxonomy, if you\'ve created one, to populate the section value instead.', 'wp-parsely' ),
612 - 'select_options' => self::get_section_taxonomies(),
459 + // filter WordPress taxonomies under the hood that should not appear in dropdown.
460 + 'select_options' => array_diff(
461 + get_taxonomies(),
462 + array(
463 + 'post_tag',
464 + 'nav_menu',
465 + 'author',
466 + 'link_category',
467 + 'post_format',
468 + )
469 + ),
613 470 'label_for' => Parsely::OPTIONS_KEY . "[$field_id]",
614 471 );
615 472 add_settings_field(
616 473 $field_id,
617 - $this->set_field_label_contents( __( 'Use Custom Taxonomy for Section', 'wp-parsely' ), $field_id ),
474 + __( 'Use Custom Taxonomy for Section', 'wp-parsely' ),
618 475 array( $this, 'print_select_tag' ),
619 476 Parsely::MENU_SLUG,
620 477 $section_key,
621 478 $field_args
@@ -621,18 +478,17 @@
621 478 $field_args
622 479 );
623 480
624 481 // Use categories and custom taxonomies as tags.
625 - $field_id = 'cats_as_tags';
626 482 add_settings_field(
627 - $field_id,
628 - $this->set_field_label_contents( __( 'Add Categories to Tags', 'wp-parsely' ), $field_id ),
483 + 'cats_as_tags',
484 + __( 'Add Categories to Tags', 'wp-parsely' ),
629 485 array( $this, 'print_radio_tags' ),
630 486 Parsely::MENU_SLUG,
631 487 $section_key,
632 488 array(
633 489 'title' => __( 'Add Categories to Tags', 'wp-parsely' ), // Passed for legend element.
634 - 'option_key' => $field_id,
490 + 'option_key' => 'cats_as_tags',
635 491 'radio_options' => array(
636 492 'true' => __( 'Yes, add all assigned categories and taxonomies to my tags.', 'wp-parsely' ),
637 493 'false' => __( 'No, do not add all assigned categories and taxonomies to my tags.', 'wp-parsely' ),
638 494 ),
@@ -640,18 +496,17 @@
640 496 )
641 497 );
642 498
643 499 // Lowercase all tags.
644 - $field_id = 'lowercase_tags';
645 500 add_settings_field(
646 - $field_id,
647 - $this->set_field_label_contents( __( 'Lowercase All Tags', 'wp-parsely' ), $field_id ),
501 + 'lowercase_tags',
502 + __( 'Lowercase All Tags', 'wp-parsely' ),
648 503 array( $this, 'print_radio_tags' ),
649 504 Parsely::MENU_SLUG,
650 505 $section_key,
651 506 array(
652 507 'title' => __( 'Lowercase All Tags', 'wp-parsely' ), // Passed for legend element.
653 - 'option_key' => $field_id,
508 + 'option_key' => 'lowercase_tags',
654 509 'radio_options' => array(
655 510 'true' => __( 'Yes, use lowercase versions of my tags to correct for potential misspellings.', 'wp-parsely' ),
656 511 'false' => __( 'No, do not use lowercase versions of my tags to correct for potential misspellings.', 'wp-parsely' ),
657 512 ),
@@ -657,18 +512,17 @@
657 512 ),
658 513 )
659 514 );
660 515
661 - $field_id = 'force_https_canonicals';
662 516 add_settings_field(
663 - $field_id,
664 - $this->set_field_label_contents( __( 'Force HTTPS Canonicals', 'wp-parsely' ), $field_id ),
517 + 'force_https_canonicals',
518 + __( 'Force HTTPS Canonicals', 'wp-parsely' ),
665 519 array( $this, 'print_radio_tags' ),
666 520 Parsely::MENU_SLUG,
667 521 $section_key,
668 522 array(
669 523 'title' => __( 'Force HTTPS Canonicals', 'wp-parsely' ), // Passed for legend element.
670 - 'option_key' => $field_id,
524 + 'option_key' => 'force_https_canonicals',
671 525 'radio_options' => array(
672 526 'true' => __( 'Yes, force <code>https</code> canonical URLs by default.', 'wp-parsely' ),
673 527 'false' => __( 'No, I want to use <code>http</code>.', 'wp-parsely' ),
674 528 ),
@@ -682,10 +536,9 @@
682 536 *
683 537 * @since 3.2.0
684 538 */
685 539 private function initialize_advanced_section(): void {
686 - $are_credentials_managed = $this->parsely->are_credentials_managed;
687 - $section_key = 'advanced-section';
540 + $section_key = 'advanced-section';
688 541
689 542 add_settings_section(
690 543 $section_key,
691 544 __( 'Advanced', 'wp-parsely' ),
@@ -692,41 +545,18 @@
692 545 '__return_null',
693 546 Parsely::MENU_SLUG
694 547 );
695 548
696 - if ( ! $are_credentials_managed ) {
697 - // Metadata Secret.
698 - $field_id = 'metadata_secret';
699 - $field_args = array(
700 - 'option_key' => $field_id,
701 - 'help_text' => __( 'Your metadata secret is given to you by Parse.ly support. DO NOT enter anything here unless given to you by Parse.ly support!', 'wp-parsely' ),
702 - 'label_for' => $field_id,
703 - 'optional_args' => array(
704 - 'type' => 'password',
705 - 'is_obfuscated_value' => true,
706 - ),
707 - );
708 - add_settings_field(
709 - $field_id,
710 - __( 'Metadata Secret', 'wp-parsely' ),
711 - array( $this, 'print_text_tag' ),
712 - Parsely::MENU_SLUG,
713 - $section_key,
714 - $field_args
715 - );
716 - }
717 -
718 549 // Disable autotrack.
719 - $field_id = 'disable_autotrack';
720 550 add_settings_field(
721 - $field_id,
722 - $this->set_field_label_contents( __( 'Disable Autotracking', 'wp-parsely' ), $field_id ),
551 + 'disable_autotrack',
552 + __( 'Disable Autotracking', 'wp-parsely' ),
723 553 array( $this, 'print_radio_tags' ),
724 554 Parsely::MENU_SLUG,
725 555 $section_key,
726 556 array(
727 557 'title' => __( 'Disable Autotracking', 'wp-parsely' ), // Passed for legend element.
728 - 'option_key' => $field_id,
558 + 'option_key' => 'disable_autotrack',
729 559 'radio_options' => array(
730 560 'true' => __( 'Yes, disable autotracking. I do not want the tracking code to report an event as soon as the script has finished loading. I plan to implement Dynamic Tracking myself.', 'wp-parsely' ),
731 561 'false' => __( 'No, do not disable autotracking. I want to make sure the default behavior of the tracking code is in place. The tracking code should report an event as soon as the script has finished loading.', 'wp-parsely' ),
732 562 ),
@@ -731,8 +561,22 @@
731 561 'false' => __( 'No, do not disable autotracking. I want to make sure the default behavior of the tracking code is in place. The tracking code should report an event as soon as the script has finished loading.', 'wp-parsely' ),
732 562 ),
733 563 )
734 564 );
565 +
566 + // Clear metadata.
567 + add_settings_field(
568 + 'parsely_wipe_metadata_cache',
569 + __( 'Wipe Parse.ly Metadata Info', 'wp-parsely' ),
570 + array( $this, 'print_checkbox_tag' ),
571 + Parsely::MENU_SLUG,
572 + $section_key,
573 + array(
574 + 'option_key' => 'parsely_wipe_metadata_cache',
575 + 'yes_text' => __( 'Yes, clear all metadata information for Parse.ly posts and re-send all metadata to Parse.ly.', 'wp-parsely' ),
576 + 'help_text' => __( '<span style="color:#d63638">WARNING:</span> Do not do this unless explicitly instructed by Parse.ly Staff!', 'wp-parsely' ),
577 + )
578 + );
735 579 }
736 580
737 581 /**
738 582 * Shows setting tabs.
@@ -829,14 +673,12 @@
829 673 $id = esc_attr( $name );
830 674 $name = Parsely::OPTIONS_KEY . "[$id]";
831 675 $is_obfuscated_value = $optional_args['is_obfuscated_value'] ?? false;
832 676 $value = $is_obfuscated_value ? $this->get_obfuscated_value( $value ) : esc_attr( $value );
833 - $accepted_args = array( 'placeholder', 'required', 'disabled' );
677 + $accepted_args = array( 'placeholder', 'required' );
834 678 $type = $optional_args['type'] ?? 'text';
835 679
836 - $is_managed = key_exists( $id, $this->parsely->managed_options );
837 - echo '<fieldset', $is_managed ? ' disabled>' : '>';
838 - printf( "<input type='%s' name='%s' id='%s' value='%s'", esc_attr( $type ), esc_attr( $name ), esc_attr( $id ), esc_attr( $value ) );
680 + echo sprintf( "<input type='%s' name='%s' id='%s' value='%s'", esc_attr( $type ), esc_attr( $name ), esc_attr( $id ), esc_attr( $value ) );
839 681
840 682 if ( isset( $args['help_text'] ) ) {
841 683 echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
842 684 }
@@ -841,148 +683,41 @@
841 683 echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
842 684 }
843 685
844 686 foreach ( $optional_args as $key => $val ) {
845 - if ( \in_array( $key, $accepted_args, true ) && false !== $val ) {
846 - // Don't add a placeholder to managed option fields.
847 - if ( $is_managed && 'placeholder' === $key ) {
848 - continue;
849 - }
850 -
851 - // Support attributes without values.
852 - echo ' ' . esc_attr( $key );
853 - if ( true !== $val ) {
854 - echo '="' . esc_attr( $val ) . '"';
855 - }
687 + if ( \in_array( $key, $accepted_args, true ) ) {
688 + echo ' ' . esc_attr( $key ) . '="' . esc_attr( (string) $val ) . '"';
856 689 }
857 690 }
858 - echo ' /></fieldset>';
691 + echo ' />';
859 692
860 693 $this->print_description_text( $args );
861 694 }
862 695
863 696 /**
864 - * Prints a checkbox tag.
697 + * Prints a checkbox tag in the settings page.
865 698 *
866 - * @since 3.16.0
867 - *
868 - * @param Setting_Arguments $args Arguments for the checkbox tag.
869 - * @param Parsely_Options|null $options The options to use.
699 + * @param Setting_Arguments $args Arguments to print to checkbox tag.
870 700 */
871 - public function print_checkbox_tag( $args, $options = null ): void {
872 - $options = $options ?? $this->parsely->get_options();
873 - $name = $args['option_key'];
874 - $has_fieldset = isset( $args['add_fieldset'] ) && true === $args['add_fieldset'];
875 - $html_id = rtrim( str_replace( array( '[', ']', '__' ), '_', $name ), '_' );
876 - $html_name = str_replace(
877 - 'content_helper',
878 - '[content_helper]',
879 - Parsely::OPTIONS_KEY . esc_attr( $name )
880 - );
881 - $yes_text = $args['yes_text'] ?? '';
701 + public function print_checkbox_tag( $args ): void {
702 + $options = $this->parsely->get_options();
703 + $name = $args['option_key'];
704 + $value = $options[ $name ];
705 + $id = esc_attr( $name );
706 + $name = Parsely::OPTIONS_KEY . "[$id]";
707 + $yes_text = $args['yes_text'] ?? '';
882 708
883 - // Get option value.
884 - if ( false === strpos( $name, '[' ) ) {
885 - $value = $options[ $name ];
886 - } else {
887 - $value = Parsely::get_nested_option_value( $name, $options );
888 - }
889 -
890 - // Fieldset start.
891 - if ( $has_fieldset ) {
892 - echo '<fieldset>';
893 - if ( isset( $args['legend'] ) ) {
894 - echo '<legend class="screen-reader-text"><span>';
895 - echo esc_html( $args['legend'] ) . '</span></legend>';
896 - }
897 - }
898 -
899 - // Label and contained checkbox.
900 - printf( '<label for="%s">', esc_attr( $html_id ) );
901 - printf(
902 - '<input type="checkbox" name="%s" id="%s" value="true" ',
903 - esc_attr( $html_name ),
904 - esc_attr( $html_id )
905 - );
709 + echo sprintf( "<input type='checkbox' name='%s' id='%s_true' value='true' ", esc_attr( $name ), esc_attr( $id ) );
906 710 if ( isset( $args['help_text'] ) ) {
907 - echo ' aria-describedby="' . esc_attr( $html_id ) . '-description"';
711 + echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
908 712 }
909 - checked( true === $value, true );
910 - printf( ' /> %s</label>', esc_html( $yes_text ) );
713 + echo checked( true === $value, true, false );
714 + echo sprintf( " /> <label for='%s_true'>%s</label>", esc_attr( $id ), esc_html( $yes_text ) );
911 715
912 - // Fieldset end.
913 - if ( $has_fieldset ) {
914 - echo '</fieldset>';
915 - }
916 -
917 716 $this->print_description_text( $args );
918 717 }
919 718
920 719 /**
921 - * Prints a Content Helper AI feature section.
922 - *
923 - * @since 3.16.0
924 - *
925 - * @param Setting_Arguments $args The arguments for the section.
926 - */
927 - public function print_content_helper_ai_feature_section( $args ): void {
928 - $options = $this->parsely->get_options();
929 - $feature_id = str_replace(
930 - array( 'content_helper[', ']' ),
931 - '',
932 - $args['option_key']
933 - );
934 -
935 - // Convert user role settings to make them work in the settings page.
936 - $option = &$options['content_helper'][ $feature_id ];
937 - if ( isset( $option['allowed_user_roles'] ) ) {
938 - foreach ( $option['allowed_user_roles'] as $role ) {
939 - $option['allowed_user_roles'][ $role ] = true;
940 - }
941 - }
942 -
943 - // Feature "Enabled" checkbox.
944 - $enabled_args = array(
945 - 'option_key' => $args['option_key'] . '[enabled]',
946 - 'label_for' => $args['option_key'] . '[enabled]',
947 - 'yes_text' => __( 'Enabled', 'wp-parsely' ),
948 - 'add_fieldset' => true,
949 - );
950 - if ( isset( $args['legend'] ) ) {
951 - $enabled_args['legend'] = $args['legend'];
952 - }
953 - $this->print_checkbox_tag( $enabled_args, $options );
954 -
955 - // User role permissions fieldset.
956 - echo '<p>' . esc_html__( 'User Permissions', 'wp-parsely' ) . '</p>';
957 - echo '<fieldset class="user-role-permissions">';
958 - echo '<legend class="screen-reader-text"><span>';
959 - printf(
960 - /* translators: %s: Feature name */
961 - esc_html__( '%s User Permissions', 'wp-parsely' ),
962 - esc_html( $args['legend'] ?? __( 'Feature', 'wp-parsely' ) )
963 - );
964 - echo '</span></legend>';
965 -
966 - // User role checkboxes.
967 - $user_roles = Permissions::get_user_roles_with_edit_posts_cap();
968 - foreach ( $user_roles as $key => $name ) {
969 - $option_key = $args['option_key'] . '[allowed_user_roles][' . $key . ']';
970 - $role_args = array(
971 - 'option_key' => $option_key,
972 - 'label_for' => $option_key,
973 - 'yes_text' => translate_user_role( $name ),
974 - );
975 -
976 - $this->print_checkbox_tag( $role_args, $options );
977 - }
978 -
979 - echo '</fieldset>';
980 -
981 - $this->print_filter_text( $args );
982 - }
983 -
984 - /**
985 720 * Prints out the select tags
986 721 *
987 722 * @param Setting_Arguments $args The arguments for the select dropdowns.
988 723 */
@@ -993,11 +728,9 @@
993 728 $selected = $options[ $name ] ?? null;
994 729 $id = esc_attr( $name );
995 730 $name = Parsely::OPTIONS_KEY . "[$id]";
996 731
997 - $is_managed = key_exists( $id, $this->parsely->managed_options );
998 - echo '<fieldset', $is_managed ? ' disabled>' : '>';
999 - printf( "<select name='%s' id='%s'", esc_attr( $name ), esc_attr( $name ) );
732 + echo sprintf( "<select name='%s' id='%s'", esc_attr( $name ), esc_attr( $name ) );
1000 733 if ( isset( $args['help_text'] ) ) {
1001 734 echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
1002 735 }
1003 736 echo '>';
@@ -1007,9 +740,9 @@
1007 740 echo selected( $selected, $key, false ) . '>';
1008 741 echo esc_html( $val );
1009 742 echo '</option>';
1010 743 }
1011 - echo '</select></fieldset>';
744 + echo '</select>';
1012 745
1013 746 $this->print_filter_text( $args );
1014 747 $this->print_description_text( $args );
1015 748 }
@@ -1030,11 +763,10 @@
1030 763 // Converting boolean to string so that we have string type keys for all cases.
1031 764 $selected = $selected ? 'true' : 'false';
1032 765 }
1033 766
1034 - $is_managed = key_exists( $name, $this->parsely->managed_options );
1035 767 ?>
1036 - <fieldset <?php echo $is_managed ? 'disabled' : ''; ?>>
768 + <fieldset>
1037 769 <legend class="screen-reader-text"><span><?php echo esc_html( $title ); ?></span></legend>
1038 770 <p>
1039 771 <?php foreach ( $radio_options as $value => $text ) { ?>
1040 772 <label for="<?php echo esc_attr( "{$id}_{$value}" ); ?>">
@@ -1072,13 +804,11 @@
1072 804 */
1073 805 $input_value = $this->parsely->get_options()[ $key ];
1074 806 $input_name = Parsely::OPTIONS_KEY . "[$key]";
1075 807 $button_text = __( 'Browse', 'wp-parsely' );
1076 -
1077 - $is_managed = key_exists( $key, $this->parsely->managed_options );
1078 808 ?>
1079 809
1080 - <fieldset class="media-single-image" id="media-single-image-<?php echo esc_attr( $key ); ?>"<?php echo $is_managed ? ' disabled' : ''; ?>>
810 + <fieldset class="media-single-image" id="media-single-image-<?php echo esc_attr( $key ); ?>">
1081 811 <legend class="screen-reader-text"><span><?php echo esc_html( $title ); ?></span></legend>
1082 812 <input class="file-path" type="text" name="<?php echo esc_attr( $input_name ); ?>" id="logo" value="<?php echo esc_attr( $input_value ); ?>" />
1083 813 <button data-option="<?php echo esc_attr( $key ); ?>" class="browse button" type="button"><?php echo esc_html( $button_text ); ?></button>
1084 814 </fieldset>
@@ -1180,13 +910,13 @@
1180 910 /**
1181 911 * Validates the options provided by the user.
1182 912 *
1183 913 * @param ParselySettingOptions $input Options from the settings page.
914 + *
1184 915 * @return ParselySettingOptions
1185 916 */
1186 917 public function validate_options( $input ) {
1187 918 $input = $this->validate_basic_section( $input );
1188 - $input = $this->validate_content_helper_section( $input );
1189 919 $input = $this->validate_recrawl_section( $input );
1190 920 $input = $this->validate_advanced_section( $input );
1191 921
1192 922 return $input;
@@ -1195,46 +925,50 @@
1195 925 /**
1196 926 * Validates fields of Basic Section.
1197 927 *
1198 928 * @param ParselySettingOptions $input Options from the settings page.
929 + *
1199 930 * @return ParselySettingOptions Validated inputs.
1200 931 */
1201 - private function validate_basic_section( $input ): array {
1202 - $are_credentials_managed = $this->parsely->are_credentials_managed;
1203 - $options = $this->parsely->get_options();
932 + private function validate_basic_section( $input ) {
933 + $options = $this->parsely->get_options();
1204 934
1205 - if ( $are_credentials_managed ) {
1206 - $input['apikey'] = '';
1207 - $input['api_secret'] = '';
935 + if ( '' === $input['apikey'] ) {
936 + add_settings_error(
937 + Parsely::OPTIONS_KEY,
938 + 'apikey',
939 + __( 'Please specify the Site ID', 'wp-parsely' )
940 + );
1208 941 } else {
1209 - $site_id = $this->sanitize_site_id( $input['apikey'] );
1210 - $api_secret = $this->get_unobfuscated_value( $input['api_secret'], $this->parsely->get_api_secret() );
942 + $site_id = $this->sanitize_site_id( $input['apikey'] );
943 + if ( false === $this->validate_site_id( $site_id ) ) {
944 + add_settings_error(
945 + Parsely::OPTIONS_KEY,
946 + 'apikey',
947 + __( 'Your Parse.ly Site ID looks incorrect, it should look like "example.com".', 'wp-parsely' )
948 + );
949 + } else {
950 + $input['apikey'] = $site_id;
951 + }
952 + }
1211 953
1212 - $valid_credentials = Validator::validate_api_credentials( $this->parsely, $site_id, $api_secret );
954 + $input['api_secret'] = $this->get_unobfuscated_value( $input['api_secret'], $this->parsely->get_api_secret() );
1213 955
1214 - // When running e2e tests, we need to update the API keys without validating them, as they will be invalid.
1215 - // phpcs:ignore WordPress.Security.NonceVerification.Missing
1216 - if ( isset( $_POST['e2e_parsely_skip_api_validate'] ) && 'y' === $_POST['e2e_parsely_skip_api_validate'] ) {
1217 - $valid_credentials = true;
1218 - }
1219 -
1220 - if (
1221 - ( is_wp_error( $valid_credentials ) && Validator::INVALID_API_CREDENTIALS === $valid_credentials->get_error_code() ) ||
1222 - ( is_bool( $valid_credentials ) && ! $valid_credentials )
1223 - ) {
956 + $input['metadata_secret'] = $this->get_unobfuscated_value( $input['metadata_secret'], $this->parsely->get_options()['metadata_secret'] );
957 + if ( '' !== $input['metadata_secret'] ) {
958 + if ( strlen( $input['metadata_secret'] ) !== 10 ) {
1224 959 add_settings_error(
1225 960 Parsely::OPTIONS_KEY,
1226 - 'api_secret',
1227 - __( 'The Site ID and API Secret weren\'t saved as they failed to authenticate with the Parse.ly API. Try again with different credentials or contact Parse.ly support', 'wp-parsely' )
961 + 'metadata_secret',
962 + __( 'Metadata secret is incorrect. Please contact Parse.ly support!', 'wp-parsely' )
1228 963 );
1229 - $input['apikey'] = $options['apikey'];
1230 - $input['api_secret'] = $options['api_secret'];
1231 - }
964 + } elseif (
965 + isset( $input['parsely_wipe_metadata_cache'] ) && 'true' === $input['parsely_wipe_metadata_cache'] // @phpstan-ignore-line
966 + ) {
967 + delete_post_meta_by_key( 'parsely_metadata_last_updated' );
1232 968
1233 - // Since the API secret is obfuscated, we need to make sure that the value
1234 - // is not changed when the credentials are valid.
1235 - if ( true === $valid_credentials && $input['api_secret'] !== $api_secret ) {
1236 - $input['api_secret'] = $api_secret;
969 + wp_schedule_event( time() + 100, 'everytenminutes', 'parsely_bulk_metas_update' );
970 + $input['parsely_wipe_metadata_cache'] = false;
1237 971 }
1238 972 }
1239 973
1240 974 if ( ! isset( $input['meta_type'] ) ) {
@@ -1290,92 +1024,12 @@
1290 1024 return $input;
1291 1025 }
1292 1026
1293 1027 /**
1294 - * Validates the fields of the Content Helper section.
1295 - *
1296 - * @since 3.16.0
1297 - *
1298 - * @param ParselySettingOptions $input The settings page options.
1299 - * @return ParselySettingOptions The validated input.
1300 - */
1301 - private function validate_content_helper_section( $input ) {
1302 - /**
1303 - * Sanitizes the Content Helper data.
1304 - *
1305 - * @since 3.16.0
1306 - */
1307 - $sanitize = function ( $input ) use ( &$sanitize ) {
1308 - foreach ( $input as $key => $value ) {
1309 - if ( is_array( $value ) ) {
1310 - if ( 'allowed_user_roles' === $key && count( $input[ $key ] ) > 0 ) {
1311 - $passed_roles = array_keys( $input[ $key ] );
1312 - $valid_roles = array_keys(
1313 - Permissions::get_user_roles_with_edit_posts_cap()
1314 - );
1315 - $sanitized_roles = array();
1316 -
1317 - // Sanitize passed user roles and remove invalid ones.
1318 - foreach ( $passed_roles as $user_role ) {
1319 - if ( ! is_string( $user_role ) ) {
1320 - continue;
1321 - }
1322 -
1323 - $user_role = sanitize_text_field( $user_role );
1324 - if ( in_array( $user_role, $valid_roles, true ) ) {
1325 - $sanitized_roles[] = $user_role;
1326 - }
1327 - }
1328 -
1329 - $input[ $key ] = $sanitized_roles;
1330 - } else {
1331 - // Recurse when we have an array that's not user roles.
1332 - $input[ $key ] = $sanitize( $value );
1333 - }
1334 - } else {
1335 - // Enforce a boolean value.
1336 - $input[ $key ] = 'true' === $value || true === $value;
1337 - }
1338 - }
1339 -
1340 - return $input;
1341 - };
1342 -
1343 - // Add any missing data due to unchecked checkboxes.
1344 - if ( ! isset( $input['content_helper']['ai_features_enabled'] ) ) {
1345 - $input['content_helper']['ai_features_enabled'] = false;
1346 - }
1347 - foreach ( $this->configurable_pch_features as $feature_id ) {
1348 - if ( ! isset( $input['content_helper'][ $feature_id ] ) ) {
1349 - $input['content_helper'][ $feature_id ] = array(
1350 - 'enabled' => false,
1351 - 'allowed_user_roles' => array(),
1352 - );
1353 - } else {
1354 - if ( ! isset( $input['content_helper'][ $feature_id ]['enabled'] ) ) {
1355 - // @phpstan-ignore-next-line
1356 - $input['content_helper'][ $feature_id ]['enabled'] = false;
1357 - }
1358 - if ( ! isset( $input['content_helper'][ $feature_id ]['allowed_user_roles'] ) ) {
1359 - // @phpstan-ignore-next-line
1360 - $input['content_helper'][ $feature_id ]['allowed_user_roles'] = array();
1361 - }
1362 - }
1363 - }
1364 -
1365 - // Produce the final array.
1366 - $options = $this->parsely->get_options()['content_helper'];
1367 - $merged = array_merge( $options, $input['content_helper'] );
1368 -
1369 - $input['content_helper'] = $sanitize( $merged );
1370 -
1371 - return $input;
1372 - }
1373 -
1374 - /**
1375 1028 * Validates fields of Recrawl Section.
1376 1029 *
1377 1030 * @param ParselySettingOptions $input Options from the settings page.
1031 + *
1378 1032 * @return ParselySettingOptions Validated inputs.
1379 1033 */
1380 1034 private function validate_recrawl_section( $input ) {
1381 1035 $options = $this->parsely->get_options();
@@ -1388,16 +1042,8 @@
1388 1042 } else {
1389 1043 $input['content_id_prefix'] = sanitize_text_field( $input['content_id_prefix'] );
1390 1044 }
1391 1045
1392 - // Full metadata in non-posts.
1393 - if ( ! isset( $input['full_metadata_in_non_posts'] ) ) {
1394 - $input['full_metadata_in_non_posts'] = true;
1395 - } else {
1396 - // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
1397 - $input['full_metadata_in_non_posts'] = 'true' == $input['full_metadata_in_non_posts'];
1398 - }
1399 -
1400 1046 // Allow for Top-level categories setting to be conditionally included on the page.
1401 1047 // If it's not shown, then set the value as what was previously saved.
1402 1048 if ( ! isset( $input['use_top_level_cats'] ) ) {
1403 1049 $input['use_top_level_cats'] = 'true';
@@ -1489,30 +1135,14 @@
1489 1135 /**
1490 1136 * Validates fields of Advanced Section.
1491 1137 *
1492 1138 * @param ParselySettingOptions $input Options from the settings page.
1139 + *
1493 1140 * @return ParselySettingOptions Validated inputs.
1494 1141 */
1495 1142 private function validate_advanced_section( $input ) {
1496 - $are_credentials_managed = $this->parsely->are_credentials_managed;
1497 - $options = $this->parsely->get_options();
1143 + $options = $this->parsely->get_options();
1498 1144
1499 - if ( $are_credentials_managed ) {
1500 - $input['metadata_secret'] = '';
1501 - } else {
1502 - $input['metadata_secret'] = $this->get_unobfuscated_value( $input['metadata_secret'], $this->parsely->get_options()['metadata_secret'] );
1503 - $metadata_secret_length = strlen( $input['metadata_secret'] );
1504 - if ( $metadata_secret_length > 0 &&
1505 - false === Validator::validate_metadata_secret( $input['metadata_secret'] ) ) {
1506 - add_settings_error(
1507 - Parsely::OPTIONS_KEY,
1508 - 'metadata_secret',
1509 - __( 'The Metadata Secret was not saved because it is incorrect. Please contact Parse.ly support!', 'wp-parsely' )
1510 - );
1511 - $input['metadata_secret'] = $options['metadata_secret'];
1512 - }
1513 - }
1514 -
1515 1145 if ( ! isset( $input['disable_autotrack'] ) ) {
1516 1146 $input['disable_autotrack'] = $options['disable_autotrack'];
1517 1147 } elseif ( 'true' !== $input['disable_autotrack'] && 'false' !== $input['disable_autotrack'] ) {
1518 1148 add_settings_error(
@@ -1527,8 +1157,30 @@
1527 1157 return $input;
1528 1158 }
1529 1159
1530 1160 /**
1161 + * Validates the passed Site ID.
1162 + *
1163 + * Accepts a www prefix and up to 3 periods.
1164 + *
1165 + * Valid examples: 'test.com', 'www.test.com', 'subdomain.test.com',
1166 + * 'www.subdomain.test.com', 'subdomain.subdomain.test.com'.
1167 + *
1168 + * Invalid examples: 'test', 'test.com/', 'http://test.com', 'https://test.com',
1169 + * 'www.subdomain.subdomain.test.com'.
1170 + *
1171 + * @since 3.3.0
1172 + *
1173 + * @param string $site_id The Site ID to be validated.
1174 + * @return bool
1175 + */
1176 + private function validate_site_id( string $site_id ): bool {
1177 + $key_format = '/^((\w+)\.)?(([\w-]+)?)(\.[\w-]+){1,2}$/';
1178 +
1179 + return 1 === preg_match( $key_format, $site_id );
1180 + }
1181 +
1182 + /**
1531 1183 * Sanitizes the passed Site ID.
1532 1184 *
1533 1185 * @since 3.3.0
1534 1186 *
@@ -1612,18 +1264,17 @@
1612 1264
1613 1265 /**
1614 1266 * Sanitizes all elements in an option array.
1615 1267 *
1616 - * @param array<int, string> $options Array of options to be sanitized.
1268 + * @param array<int, string> $array Array of options to be sanitized.
1617 1269 * @return array<int, string>
1618 1270 */
1619 - private static function sanitize_option_array( array $options ): array {
1620 - $sanitized_options = $options;
1621 - foreach ( $options as $key => $val ) {
1622 - $sanitized_options[ $key ] = sanitize_text_field( $val );
1271 + private static function sanitize_option_array( array $array ): array {
1272 + $new_array = $array;
1273 + foreach ( $array as $key => $val ) {
1274 + $new_array[ $key ] = sanitize_text_field( $val );
1623 1275 }
1624 -
1625 - return $sanitized_options;
1276 + return $new_array;
1626 1277 }
1627 1278
1628 1279 /**
1629 1280 * Gets obfuscated value.
@@ -1628,8 +1279,9 @@
1628 1279 /**
1629 1280 * Gets obfuscated value.
1630 1281 *
1631 1282 * @param string $current_value Current value of the field.
1283 + *
1632 1284 * @return string
1633 1285 */
1634 1286 private function get_obfuscated_value( $current_value ): string {
1635 1287 return str_repeat( '*', strlen( $current_value ) );
@@ -1640,8 +1292,9 @@
1640 1292 *
1641 1293 * @param string $current_value Current value of the field.
1642 1294 * @param string $previous_value Previous value of the field. If current
1643 1295 * value is obfuscated then we will use this.
1296 + *
1644 1297 * @return string
1645 1298 */
1646 1299 private function get_unobfuscated_value( $current_value, $previous_value ): string {
1647 1300 if ( $current_value === $this->get_obfuscated_value( $current_value ) ) {
@@ -1648,63 +1301,6 @@
1648 1301 return '' === $current_value ? $current_value : $previous_value;
1649 1302 }
1650 1303
1651 1304 return $current_value;
1652 - }
1653 -
1654 - /**
1655 - * Returns the field label's title. If the option is managed, a badge is
1656 - * also included.
1657 - *
1658 - * @since 3.9.0
1659 - *
1660 - * @param string $title The field's title.
1661 - * @param string $option_id The option's ID.
1662 - * @return string The resulting content.
1663 - */
1664 - public function set_field_label_contents( string $title, string $option_id ): string {
1665 - $is_managed = key_exists( $option_id, $this->parsely->managed_options );
1666 -
1667 - if ( $is_managed ) {
1668 - $text = $this->managed_options_badge['text'] ?? '';
1669 - if ( ! is_string( $text ) || '' === $text ) {
1670 - return $title;
1671 - }
1672 -
1673 - $url = filter_var( $this->managed_options_badge['url'] ?? '', FILTER_VALIDATE_URL );
1674 -
1675 - if ( false === $url ) {
1676 - $badge = '<span class="managed-option-badge">' . esc_html( $text ) . '</span>';
1677 - } else {
1678 - $badge = '<a class="managed-option-badge" href="' . esc_url( $url ) .
1679 - '" target="_blank" rel="noopener">' . esc_html( $text ) . '</a>';
1680 - }
1681 -
1682 - return "{$title}&nbsp;&nbsp;{$badge}";
1683 - }
1684 -
1685 - return $title;
1686 - }
1687 -
1688 - /**
1689 - * Returns the taxonomies allowed to be used as custom_taxonomy_section
1690 - * option values.
1691 - *
1692 - * @since 3.9.0
1693 - *
1694 - * @return array<string> Array of taxonomies.
1695 - */
1696 - public static function get_section_taxonomies(): array {
1697 - return array_diff(
1698 - get_taxonomies(),
1699 - array(
1700 - 'post_tag',
1701 - 'nav_menu',
1702 - 'author',
1703 - 'link_category',
1704 - 'post_format',
1705 - 'wp_theme',
1706 - 'wp_template_part_area',
1707 - )
1708 - );
1709 1305 }
1710 1306 }