PluginProbe
Parse.ly / 3.8.4
Parse.ly v3.8.4
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 3.20.3 All 105 releases
← All changes | src/UI/class-settings-page.php +218 -640 3.20.03.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,33 +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 - * traffic_boost?: Parsely_Settings_Options_Content_Helper_Feature,
76 - * }
77 - *
78 - * @phpstan-type Parsely_Settings_Options_Content_Helper_Feature array{
79 - * enabled?: bool,
80 - * allowed_user_roles?: array<string, string>|array<string, bool>
81 - * }
82 - *
83 - * @phpstan-import-type Parsely_Options from Parsely
84 66 */
85 67 final class Settings_Page {
86 68 /**
87 69 * Instance of Parsely class.
@@ -99,31 +81,8 @@
99 81 */
100 82 private $hook_suffix;
101 83
102 84 /**
103 - * Options for badges that are displayed for managed options.
104 - *
105 - * @since 3.9.0
106 - *
107 - * @var array<string, mixed>
108 - */
109 - private $managed_options_badge = array();
110 -
111 - /**
112 - * The Content Helper features that can be configured in the settings page.
113 - *
114 - * @since 3.16.0
115 - *
116 - * @var string[]
117 - */
118 - private $configurable_pch_features = array(
119 - 'smart_linking',
120 - 'title_suggestions',
121 - 'excerpt_suggestions',
122 - 'traffic_boost',
123 - );
124 -
125 - /**
126 85 * Constructor.
127 86 *
128 87 * @param Parsely $parsely Instance of Parsely class.
129 88 */
@@ -128,20 +87,8 @@
128 87 * @param Parsely $parsely Instance of Parsely class.
129 88 */
130 89 public function __construct( Parsely $parsely ) {
131 90 $this->parsely = $parsely;
132 -
133 - $managed_options_badge = apply_filters(
134 - 'wp_parsely_managed_options_badge',
135 - array(
136 - 'text' => __( 'Upgrade', 'wp-parsely' ),
137 - 'url' => 'https://www.parse.ly/getdemo/',
138 - )
139 - );
140 -
141 - if ( is_array( $managed_options_badge ) ) {
142 - $this->managed_options_badge = $managed_options_badge;
143 - }
144 91 }
145 92
146 93 /**
147 94 * Registers settings page.
@@ -156,35 +103,33 @@
156 103
157 104 /**
158 105 * Enqueues all needed scripts and styles for Parse.ly plugin settings page.
159 106 *
160 - * @param string|null $hook_suffix The current page being loaded.
107 + * @param string $hook_suffix The current page being loaded.
161 108 */
162 - public function enqueue_settings_assets( ?string $hook_suffix ): void {
163 - if ( ! is_string( $hook_suffix ) || $this->hook_suffix !== $hook_suffix ) {
164 - return;
165 - }
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();
166 113
167 - add_filter( 'media_library_months_with_files', '__return_empty_array' );
168 - 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/';
169 116
170 - $admin_settings_asset = Utils::get_asset_info( 'build/admin-settings.asset.php' );
171 - $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 + );
172 124
173 - wp_enqueue_script(
174 - 'parsely-admin-settings',
175 - $built_assets_url . 'admin-settings.js',
176 - $admin_settings_asset['dependencies'],
177 - $admin_settings_asset['version'],
178 - true
179 - );
180 -
181 - wp_enqueue_style(
182 - 'parsely-admin-settings',
183 - $built_assets_url . 'admin-settings.css',
184 - array(),
185 - $admin_settings_asset['version']
186 - );
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 + }
187 132 }
188 133
189 134 /**
190 135 * Adds the Parse.ly settings page in WordPress settings menu.
@@ -189,13 +134,12 @@
189 134 /**
190 135 * Adds the Parse.ly settings page in WordPress settings menu.
191 136 */
192 137 public function add_settings_sub_menu(): void {
193 - $suffix = add_submenu_page(
194 - 'parsely-dashboard-page',
138 + $suffix = add_options_page(
195 139 __( 'Parse.ly Settings', 'wp-parsely' ),
196 - __( 'Settings', 'wp-parsely' ),
197 - Parsely::CAPABILITY, // phpcs:ignore WordPress.WP.Capabilities.Undetermined
140 + __( 'Parse.ly', 'wp-parsely' ),
141 + Parsely::CAPABILITY,
198 142 Parsely::MENU_SLUG,
199 143 array( $this, 'display_settings' )
200 144 );
201 145
@@ -228,17 +172,16 @@
228 172 );
229 173 }
230 174
231 175 /**
232 - * Displays the Parse.ly settings screen (admin.php?page=[SLUG]).
176 + * Displays the Parse.ly settings screen (options-general.php?page=[SLUG]).
233 177 */
234 178 public function display_settings(): void {
235 - // phpcs:ignore WordPress.WP.Capabilities.Undetermined
236 179 if ( ! current_user_can( Parsely::CAPABILITY ) ) {
237 180 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-parsely' ) );
238 181 }
239 182
240 - include_once plugin_dir_path( PARSELY_FILE ) . 'src/UI/settings-page.php';
183 + include_once plugin_dir_path( PARSELY_FILE ) . 'views/parsely-settings.php';
241 184 }
242 185
243 186 /**
244 187 * Initializes the settings for Parse.ly.
@@ -243,25 +186,16 @@
243 186 /**
244 187 * Initializes the settings for Parse.ly.
245 188 */
246 189 public function initialize_settings(): void {
247 - // Add the option first, to prevent double sanitization of the uninitialized option as reported
248 - // in https://core.trac.wordpress.org/ticket/21989.
249 - // The option will be initialized with the default values.
250 - add_option( Parsely::OPTIONS_KEY, $this->parsely->get_options() );
251 -
252 190 // All our options are actually stored in one single array to reduce DB queries.
253 191 register_setting(
254 192 Parsely::OPTIONS_KEY,
255 193 Parsely::OPTIONS_KEY,
256 - array(
257 - 'type' => 'array',
258 - 'sanitize_callback' => array( $this, 'validate_options' ),
259 - )
194 + array( $this, 'validate_options' )
260 195 );
261 196
262 197 $this->initialize_basic_section();
263 - $this->initialize_content_helper_section();
264 198 $this->initialize_recrawl_section();
265 199 $this->initialize_advanced_section();
266 200 }
267 201
@@ -270,10 +204,9 @@
270 204 *
271 205 * @since 3.2.0
272 206 */
273 207 private function initialize_basic_section(): void {
274 - $are_credentials_managed = $this->parsely->are_credentials_managed;
275 - $section_key = 'basic-section';
208 + $section_key = 'basic-section';
276 209
277 210 add_settings_section(
278 211 $section_key,
279 212 __( 'Basic', 'wp-parsely' ),
@@ -289,15 +222,14 @@
289 222 'label_for' => $field_id,
290 223 'optional_args' => array(
291 224 'required' => 'required',
292 225 'placeholder' => 'mydomain.com',
293 - 'disabled' => $are_credentials_managed,
294 226 ),
295 227
296 228 );
297 229 add_settings_field(
298 230 $field_id,
299 - __( 'Site ID <em>(required)</em>', 'wp-parsely' ),
231 + __( 'Parse.ly Site ID <em>(required)</em>', 'wp-parsely' ),
300 232 array( $this, 'print_text_tag' ),
301 233 Parsely::MENU_SLUG,
302 234 $section_key,
303 235 $field_args
@@ -302,36 +234,54 @@
302 234 $section_key,
303 235 $field_args
304 236 );
305 237
306 - if ( ! $are_credentials_managed ) {
307 - // API Secret.
308 - $field_id = 'api_secret';
309 - $field_args = array(
310 - 'option_key' => $field_id,
311 - '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:support@parsely.com">support@parsely.com</a>.', 'wp-parsely' ),
312 - 'label_for' => $field_id,
313 - 'optional_args' => array(
314 - 'type' => 'password',
315 - 'is_obfuscated_value' => true,
316 - ),
317 - );
318 - add_settings_field(
319 - $field_id,
320 - __( 'API Secret', 'wp-parsely' ),
321 - array( $this, 'print_text_tag' ),
322 - Parsely::MENU_SLUG,
323 - $section_key,
324 - $field_args
325 - );
326 - }
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:support@parsely.com">support@parsely.com</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 + );
327 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 +
328 278 // Metadata Format.
329 279 $field_id = 'meta_type';
330 280 $field_args = array(
331 281 'title' => __( 'Metadata Format', 'wp-parsely' ),
332 282 'option_key' => $field_id,
333 - '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' ),
334 284 'radio_options' => array(
335 285 'json_ld' => 'json_ld',
336 286 'repeated_metas' => 'repeated_metas',
337 287 ),
@@ -339,9 +289,9 @@
339 289 'filter' => 'wp_parsely_metadata',
340 290 );
341 291 add_settings_field(
342 292 $field_id,
343 - $this->set_field_label_contents( __( 'Metadata Format', 'wp-parsely' ), $field_id ),
293 + __( 'Metadata Format', 'wp-parsely' ),
344 294 array( $this, 'print_radio_tags' ),
345 295 Parsely::MENU_SLUG,
346 296 $section_key,
347 297 $field_args
@@ -351,9 +301,9 @@
351 301 $field_help = __( 'Here you can specify your logo\'s URL by using the "Browse" button or typing the URL manually.', 'wp-parsely' );
352 302 $field_id = 'logo';
353 303 add_settings_field(
354 304 $field_id,
355 - $this->set_field_label_contents( __( 'Logo', 'wp-parsely' ), $field_id ),
305 + __( 'Logo', 'wp-parsely' ),
356 306 array( $this, 'print_media_single_image' ),
357 307 Parsely::MENU_SLUG,
358 308 $section_key,
359 309 array(
@@ -364,18 +314,17 @@
364 314 )
365 315 );
366 316
367 317 // Track logged-in users.
368 - $field_id = 'track_authenticated_users';
369 318 add_settings_field(
370 - $field_id,
371 - $this->set_field_label_contents( __( 'Track Logged-in Users', 'wp-parsely' ), $field_id ),
319 + 'track_authenticated_users',
320 + __( 'Track Logged-in Users', 'wp-parsely' ),
372 321 array( $this, 'print_radio_tags' ),
373 322 Parsely::MENU_SLUG,
374 323 $section_key,
375 324 array(
376 325 'title' => __( 'Track Logged-in Users', 'wp-parsely' ), // Passed for legend element.
377 - 'option_key' => $field_id,
326 + 'option_key' => 'track_authenticated_users',
378 327 'radio_options' => array(
379 328 'true' => __( 'Yes, track logged-in users.', 'wp-parsely' ),
380 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' ),
381 330 ),
@@ -387,23 +336,22 @@
387 336 )
388 337 );
389 338
390 339 // Disable JavaScript.
391 - $field_id = 'disable_javascript';
392 340 add_settings_field(
393 - $field_id,
394 - $this->set_field_label_contents( __( 'Disable JavaScript', 'wp-parsely' ), $field_id ),
341 + 'disable_javascript',
342 + __( 'Disable JavaScript', 'wp-parsely' ),
395 343 array( $this, 'print_radio_tags' ),
396 344 Parsely::MENU_SLUG,
397 345 $section_key,
398 346 array(
399 347 'title' => __( 'Disable JavaScript', 'wp-parsely' ), // Passed for legend element.
400 - 'option_key' => $field_id,
348 + 'option_key' => 'disable_javascript',
401 349 'radio_options' => array(
402 350 'true' => __( 'Yes, disable JavaScript tracking. I want to use a separate system for tracking instead of the Parse.ly plugin.', 'wp-parsely' ),
403 351 'false' => __( 'No, do not disable JavaScript tracking. I want the Parse.ly plugin to load the tracker.', 'wp-parsely' ),
404 352 ),
405 - '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' ),
406 354 'filter' => 'wp_parsely_load_js_tracker',
407 355 )
408 356 );
409 357
@@ -408,18 +356,17 @@
408 356 );
409 357
410 358 if ( defined( 'AMP__VERSION' ) ) {
411 359 // Disable AMP tracking.
412 - $field_id = 'disable_amp';
413 360 add_settings_field(
414 - $field_id,
415 - $this->set_field_label_contents( __( 'Disable AMP Tracking', 'wp-parsely' ), $field_id ),
361 + 'disable_amp',
362 + __( 'Disable AMP Tracking', 'wp-parsely' ),
416 363 array( $this, 'print_radio_tags' ),
417 364 Parsely::MENU_SLUG,
418 365 $section_key,
419 366 array(
420 367 'title' => __( 'Disable AMP Tracking', 'wp-parsely' ), // Passed for legend element.
421 - 'option_key' => $field_id,
368 + 'option_key' => 'disable_amp',
422 369 'radio_options' => array(
423 370 'true' => __( 'Yes, disable Parse.ly tracking on AMP pages. I use a different system for JavaScript tracking on AMP pages.', 'wp-parsely' ),
424 371 'false' => __( 'No, do not disable Parse.ly tracking on AMP pages.', 'wp-parsely' ),
425 372 ),
@@ -428,107 +375,8 @@
428 375 }
429 376 }
430 377
431 378 /**
432 - * Registers the Content Helper section and its settings.
433 - *
434 - * @since 3.16.0
435 - */
436 - private function initialize_content_helper_section(): void {
437 - $section_key = 'content-helper-section';
438 -
439 - add_settings_section(
440 - $section_key,
441 - __( 'Content Helper', 'wp-parsely' ),
442 - '__return_null',
443 - Parsely::MENU_SLUG
444 - );
445 -
446 - // AI Features.
447 - $field_id = 'content_helper[ai_features_enabled]';
448 - $field_args = array(
449 - 'option_key' => $field_id,
450 - 'label_for' => $field_id,
451 - 'yes_text' => __( 'Enabled', 'wp-parsely' ),
452 - 'add_fieldset' => true,
453 - 'legend' => __( 'AI Features', 'wp-parsely' ),
454 - );
455 - add_settings_field(
456 - $field_id,
457 - __( 'AI Features', 'wp-parsely' ),
458 - array( $this, 'print_checkbox_tag' ),
459 - Parsely::MENU_SLUG,
460 - $section_key,
461 - $field_args
462 - );
463 -
464 - // Smart Linking.
465 - $field_id = 'content_helper[smart_linking]';
466 - $field_args = array(
467 - 'option_key' => $field_id,
468 - 'label_for' => $field_id,
469 - 'legend' => __( 'Smart Linking', 'wp-parsely' ),
470 - );
471 - add_settings_field(
472 - $field_id,
473 - __( 'Smart Linking', 'wp-parsely' ),
474 - array( $this, 'print_content_helper_ai_feature_section' ),
475 - Parsely::MENU_SLUG,
476 - $section_key,
477 - $field_args
478 - );
479 -
480 - // Title Suggestions.
481 - $field_id = 'content_helper[title_suggestions]';
482 - $field_args = array(
483 - 'option_key' => $field_id,
484 - 'label_for' => $field_id,
485 - 'legend' => __( 'Title Suggestions', 'wp-parsely' ),
486 - );
487 - add_settings_field(
488 - $field_id,
489 - __( 'Title Suggestions', 'wp-parsely' ),
490 - array( $this, 'print_content_helper_ai_feature_section' ),
491 - Parsely::MENU_SLUG,
492 - $section_key,
493 - $field_args
494 - );
495 -
496 - // Excerpt Suggestions.
497 - $field_id = 'content_helper[excerpt_suggestions]';
498 - $field_args = array(
499 - 'option_key' => $field_id,
500 - 'label_for' => $field_id,
501 - 'legend' => __( 'Excerpt Suggestions', 'wp-parsely' ),
502 - 'filter' => Excerpt_Suggestions::get_feature_filter_name(),
503 - );
504 - add_settings_field(
505 - $field_id,
506 - __( 'Excerpt Suggestions', 'wp-parsely' ),
507 - array( $this, 'print_content_helper_ai_feature_section' ),
508 - Parsely::MENU_SLUG,
509 - $section_key,
510 - $field_args
511 - );
512 -
513 - // Traffic Boost.
514 - $field_id = 'content_helper[traffic_boost]';
515 - $field_args = array(
516 - 'option_key' => $field_id,
517 - 'label_for' => $field_id,
518 - 'legend' => __( 'Traffic Boost (beta)', 'wp-parsely' ),
519 - );
520 - add_settings_field(
521 - $field_id,
522 - __( 'Traffic Boost (beta)', 'wp-parsely' ),
523 - array( $this, 'print_content_helper_ai_feature_section' ),
524 - Parsely::MENU_SLUG,
525 - $section_key,
526 - $field_args
527 - );
528 - }
529 -
530 - /**
531 379 * Registers section and settings for Recrawl section.
532 380 *
533 381 * @since 3.2.0
534 382 */
@@ -561,30 +409,12 @@
561 409 array(
562 410 'title' => __( 'Track Post Types as', 'wp-parsely' ),
563 411 'option_key' => $field_id,
564 412 'help_text' => $field_help,
413 + 'filter' => 'wp_parsely_trackable_statuses',
565 414 )
566 415 );
567 416
568 - // Use full metadata in non-posts.
569 - $field_id = 'full_metadata_in_non_posts';
570 - add_settings_field(
571 - $field_id,
572 - $this->set_field_label_contents( __( 'Use Full Metadata in Non-Posts', 'wp-parsely' ), $field_id ),
573 - array( $this, 'print_radio_tags' ),
574 - Parsely::MENU_SLUG,
575 - $section_key,
576 - array(
577 - 'title' => __( 'Use Full Metadata in Non-Posts', 'wp-parsely' ), // Passed for legend element.
578 - 'option_key' => $field_id,
579 - 'radio_options' => array(
580 - 'true' => __( 'Yes, add full metadata to Post Types being tracked as Non-Posts.', 'wp-parsely' ),
581 - 'false' => __( 'No, we have code that modifies metadata and that has not been tested for compatibility yet.', 'wp-parsely' ),
582 - ),
583 - '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' ),
584 - )
585 - );
586 -
587 417 // Content ID Prefix.
588 418 $field_id = 'content_id_prefix';
589 419 $field_args = array(
590 420 'option_key' => $field_id,
@@ -595,9 +425,9 @@
595 425 'label_for' => $field_id,
596 426 );
597 427 add_settings_field(
598 428 $field_id,
599 - $this->set_field_label_contents( __( 'Content ID Prefix', 'wp-parsely' ), $field_id ),
429 + __( 'Content ID Prefix', 'wp-parsely' ),
600 430 array( $this, 'print_text_tag' ),
601 431 Parsely::MENU_SLUG,
602 432 $section_key,
603 433 $field_args
@@ -603,18 +433,17 @@
603 433 $field_args
604 434 );
605 435
606 436 // Use top-level categories.
607 - $field_id = 'use_top_level_cats';
608 437 add_settings_field(
609 - $field_id,
610 - $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' ),
611 440 array( $this, 'print_radio_tags' ),
612 441 Parsely::MENU_SLUG,
613 442 $section_key,
614 443 array(
615 444 'title' => __( 'Use Top-Level Categories for Section', 'wp-parsely' ), // Passed for legend element.
616 - 'option_key' => $field_id,
445 + 'option_key' => 'use_top_level_cats',
617 446 'radio_options' => array(
618 447 'true' => __( 'Yes, use the first category assigned to a post as the section name.', 'wp-parsely' ),
619 448 'false' => __( 'No, do not use the first category assigned to a post as the section name.', 'wp-parsely' ),
620 449 ),
@@ -626,14 +455,24 @@
626 455 $field_id = 'custom_taxonomy_section';
627 456 $field_args = array(
628 457 'option_key' => $field_id,
629 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' ),
630 - '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 + ),
631 470 'label_for' => Parsely::OPTIONS_KEY . "[$field_id]",
632 471 );
633 472 add_settings_field(
634 473 $field_id,
635 - $this->set_field_label_contents( __( 'Use Custom Taxonomy for Section', 'wp-parsely' ), $field_id ),
474 + __( 'Use Custom Taxonomy for Section', 'wp-parsely' ),
636 475 array( $this, 'print_select_tag' ),
637 476 Parsely::MENU_SLUG,
638 477 $section_key,
639 478 $field_args
@@ -639,18 +478,17 @@
639 478 $field_args
640 479 );
641 480
642 481 // Use categories and custom taxonomies as tags.
643 - $field_id = 'cats_as_tags';
644 482 add_settings_field(
645 - $field_id,
646 - $this->set_field_label_contents( __( 'Add Categories to Tags', 'wp-parsely' ), $field_id ),
483 + 'cats_as_tags',
484 + __( 'Add Categories to Tags', 'wp-parsely' ),
647 485 array( $this, 'print_radio_tags' ),
648 486 Parsely::MENU_SLUG,
649 487 $section_key,
650 488 array(
651 489 'title' => __( 'Add Categories to Tags', 'wp-parsely' ), // Passed for legend element.
652 - 'option_key' => $field_id,
490 + 'option_key' => 'cats_as_tags',
653 491 'radio_options' => array(
654 492 'true' => __( 'Yes, add all assigned categories and taxonomies to my tags.', 'wp-parsely' ),
655 493 'false' => __( 'No, do not add all assigned categories and taxonomies to my tags.', 'wp-parsely' ),
656 494 ),
@@ -658,18 +496,17 @@
658 496 )
659 497 );
660 498
661 499 // Lowercase all tags.
662 - $field_id = 'lowercase_tags';
663 500 add_settings_field(
664 - $field_id,
665 - $this->set_field_label_contents( __( 'Lowercase All Tags', 'wp-parsely' ), $field_id ),
501 + 'lowercase_tags',
502 + __( 'Lowercase All Tags', 'wp-parsely' ),
666 503 array( $this, 'print_radio_tags' ),
667 504 Parsely::MENU_SLUG,
668 505 $section_key,
669 506 array(
670 507 'title' => __( 'Lowercase All Tags', 'wp-parsely' ), // Passed for legend element.
671 - 'option_key' => $field_id,
508 + 'option_key' => 'lowercase_tags',
672 509 'radio_options' => array(
673 510 'true' => __( 'Yes, use lowercase versions of my tags to correct for potential misspellings.', 'wp-parsely' ),
674 511 'false' => __( 'No, do not use lowercase versions of my tags to correct for potential misspellings.', 'wp-parsely' ),
675 512 ),
@@ -675,18 +512,17 @@
675 512 ),
676 513 )
677 514 );
678 515
679 - $field_id = 'force_https_canonicals';
680 516 add_settings_field(
681 - $field_id,
682 - $this->set_field_label_contents( __( 'Force HTTPS Canonicals', 'wp-parsely' ), $field_id ),
517 + 'force_https_canonicals',
518 + __( 'Force HTTPS Canonicals', 'wp-parsely' ),
683 519 array( $this, 'print_radio_tags' ),
684 520 Parsely::MENU_SLUG,
685 521 $section_key,
686 522 array(
687 523 'title' => __( 'Force HTTPS Canonicals', 'wp-parsely' ), // Passed for legend element.
688 - 'option_key' => $field_id,
524 + 'option_key' => 'force_https_canonicals',
689 525 'radio_options' => array(
690 526 'true' => __( 'Yes, force <code>https</code> canonical URLs by default.', 'wp-parsely' ),
691 527 'false' => __( 'No, I want to use <code>http</code>.', 'wp-parsely' ),
692 528 ),
@@ -700,10 +536,9 @@
700 536 *
701 537 * @since 3.2.0
702 538 */
703 539 private function initialize_advanced_section(): void {
704 - $are_credentials_managed = $this->parsely->are_credentials_managed;
705 - $section_key = 'advanced-section';
540 + $section_key = 'advanced-section';
706 541
707 542 add_settings_section(
708 543 $section_key,
709 544 __( 'Advanced', 'wp-parsely' ),
@@ -710,41 +545,18 @@
710 545 '__return_null',
711 546 Parsely::MENU_SLUG
712 547 );
713 548
714 - if ( ! $are_credentials_managed ) {
715 - // Metadata Secret.
716 - $field_id = 'metadata_secret';
717 - $field_args = array(
718 - 'option_key' => $field_id,
719 - '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' ),
720 - 'label_for' => $field_id,
721 - 'optional_args' => array(
722 - 'type' => 'password',
723 - 'is_obfuscated_value' => true,
724 - ),
725 - );
726 - add_settings_field(
727 - $field_id,
728 - __( 'Metadata Secret', 'wp-parsely' ),
729 - array( $this, 'print_text_tag' ),
730 - Parsely::MENU_SLUG,
731 - $section_key,
732 - $field_args
733 - );
734 - }
735 -
736 549 // Disable autotrack.
737 - $field_id = 'disable_autotrack';
738 550 add_settings_field(
739 - $field_id,
740 - $this->set_field_label_contents( __( 'Disable Autotracking', 'wp-parsely' ), $field_id ),
551 + 'disable_autotrack',
552 + __( 'Disable Autotracking', 'wp-parsely' ),
741 553 array( $this, 'print_radio_tags' ),
742 554 Parsely::MENU_SLUG,
743 555 $section_key,
744 556 array(
745 557 'title' => __( 'Disable Autotracking', 'wp-parsely' ), // Passed for legend element.
746 - 'option_key' => $field_id,
558 + 'option_key' => 'disable_autotrack',
747 559 'radio_options' => array(
748 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' ),
749 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' ),
750 562 ),
@@ -749,8 +561,22 @@
749 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' ),
750 562 ),
751 563 )
752 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 + );
753 579 }
754 580
755 581 /**
756 582 * Shows setting tabs.
@@ -847,14 +673,12 @@
847 673 $id = esc_attr( $name );
848 674 $name = Parsely::OPTIONS_KEY . "[$id]";
849 675 $is_obfuscated_value = $optional_args['is_obfuscated_value'] ?? false;
850 676 $value = $is_obfuscated_value ? $this->get_obfuscated_value( $value ) : esc_attr( $value );
851 - $accepted_args = array( 'placeholder', 'required', 'disabled' );
677 + $accepted_args = array( 'placeholder', 'required' );
852 678 $type = $optional_args['type'] ?? 'text';
853 679
854 - $is_managed = key_exists( $id, $this->parsely->managed_options );
855 - echo '<fieldset', $is_managed ? ' disabled>' : '>';
856 - 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 ) );
857 681
858 682 if ( isset( $args['help_text'] ) ) {
859 683 echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
860 684 }
@@ -859,148 +683,41 @@
859 683 echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
860 684 }
861 685
862 686 foreach ( $optional_args as $key => $val ) {
863 - if ( \in_array( $key, $accepted_args, true ) && false !== $val ) {
864 - // Don't add a placeholder to managed option fields.
865 - if ( $is_managed && 'placeholder' === $key ) {
866 - continue;
867 - }
868 -
869 - // Support attributes without values.
870 - echo ' ' . esc_attr( $key );
871 - if ( true !== $val ) {
872 - echo '="' . esc_attr( $val ) . '"';
873 - }
687 + if ( \in_array( $key, $accepted_args, true ) ) {
688 + echo ' ' . esc_attr( $key ) . '="' . esc_attr( (string) $val ) . '"';
874 689 }
875 690 }
876 - echo ' /></fieldset>';
691 + echo ' />';
877 692
878 693 $this->print_description_text( $args );
879 694 }
880 695
881 696 /**
882 - * Prints a checkbox tag.
697 + * Prints a checkbox tag in the settings page.
883 698 *
884 - * @since 3.16.0
885 - *
886 - * @param Setting_Arguments $args Arguments for the checkbox tag.
887 - * @param Parsely_Options|null $options The options to use.
699 + * @param Setting_Arguments $args Arguments to print to checkbox tag.
888 700 */
889 - public function print_checkbox_tag( $args, $options = null ): void {
890 - $options = $options ?? $this->parsely->get_options();
891 - $name = $args['option_key'];
892 - $has_fieldset = isset( $args['add_fieldset'] ) && true === $args['add_fieldset'];
893 - $html_id = rtrim( str_replace( array( '[', ']', '__' ), '_', $name ), '_' );
894 - $html_name = str_replace(
895 - 'content_helper',
896 - '[content_helper]',
897 - Parsely::OPTIONS_KEY . esc_attr( $name )
898 - );
899 - $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'] ?? '';
900 708
901 - // Get option value.
902 - if ( false === strpos( $name, '[' ) ) {
903 - $value = $options[ $name ];
904 - } else {
905 - $value = Parsely::get_nested_option_value( $name, $options );
906 - }
907 -
908 - // Fieldset start.
909 - if ( $has_fieldset ) {
910 - echo '<fieldset>';
911 - if ( isset( $args['legend'] ) ) {
912 - echo '<legend class="screen-reader-text"><span>';
913 - echo esc_html( $args['legend'] ) . '</span></legend>';
914 - }
915 - }
916 -
917 - // Label and contained checkbox.
918 - printf( '<label for="%s">', esc_attr( $html_id ) );
919 - printf(
920 - '<input type="checkbox" name="%s" id="%s" value="true" ',
921 - esc_attr( $html_name ),
922 - esc_attr( $html_id )
923 - );
709 + echo sprintf( "<input type='checkbox' name='%s' id='%s_true' value='true' ", esc_attr( $name ), esc_attr( $id ) );
924 710 if ( isset( $args['help_text'] ) ) {
925 - echo ' aria-describedby="' . esc_attr( $html_id ) . '-description"';
711 + echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
926 712 }
927 - checked( true === $value, true );
928 - 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 ) );
929 715
930 - // Fieldset end.
931 - if ( $has_fieldset ) {
932 - echo '</fieldset>';
933 - }
934 -
935 716 $this->print_description_text( $args );
936 717 }
937 718
938 719 /**
939 - * Prints a Content Helper AI feature section.
940 - *
941 - * @since 3.16.0
942 - *
943 - * @param Setting_Arguments $args The arguments for the section.
944 - */
945 - public function print_content_helper_ai_feature_section( $args ): void {
946 - $options = $this->parsely->get_options();
947 - $feature_id = str_replace(
948 - array( 'content_helper[', ']' ),
949 - '',
950 - $args['option_key']
951 - );
952 -
953 - // Convert user role settings to make them work in the settings page.
954 - $option = &$options['content_helper'][ $feature_id ];
955 - if ( isset( $option['allowed_user_roles'] ) ) {
956 - foreach ( $option['allowed_user_roles'] as $role ) {
957 - $option['allowed_user_roles'][ $role ] = true;
958 - }
959 - }
960 -
961 - // Feature "Enabled" checkbox.
962 - $enabled_args = array(
963 - 'option_key' => $args['option_key'] . '[enabled]',
964 - 'label_for' => $args['option_key'] . '[enabled]',
965 - 'yes_text' => __( 'Enabled', 'wp-parsely' ),
966 - 'add_fieldset' => true,
967 - );
968 - if ( isset( $args['legend'] ) ) {
969 - $enabled_args['legend'] = $args['legend'];
970 - }
971 - $this->print_checkbox_tag( $enabled_args, $options );
972 -
973 - // User role permissions fieldset.
974 - echo '<p>' . esc_html__( 'User Permissions', 'wp-parsely' ) . '</p>';
975 - echo '<fieldset class="user-role-permissions">';
976 - echo '<legend class="screen-reader-text"><span>';
977 - printf(
978 - /* translators: %s: Feature name */
979 - esc_html__( '%s User Permissions', 'wp-parsely' ),
980 - esc_html( $args['legend'] ?? __( 'Feature', 'wp-parsely' ) )
981 - );
982 - echo '</span></legend>';
983 -
984 - // User role checkboxes.
985 - $user_roles = Permissions::get_user_roles_with_edit_posts_cap();
986 - foreach ( $user_roles as $key => $name ) {
987 - $option_key = $args['option_key'] . '[allowed_user_roles][' . $key . ']';
988 - $role_args = array(
989 - 'option_key' => $option_key,
990 - 'label_for' => $option_key,
991 - 'yes_text' => translate_user_role( $name ),
992 - );
993 -
994 - $this->print_checkbox_tag( $role_args, $options );
995 - }
996 -
997 - echo '</fieldset>';
998 -
999 - $this->print_filter_text( $args );
1000 - }
1001 -
1002 - /**
1003 720 * Prints out the select tags
1004 721 *
1005 722 * @param Setting_Arguments $args The arguments for the select dropdowns.
1006 723 */
@@ -1011,11 +728,9 @@
1011 728 $selected = $options[ $name ] ?? null;
1012 729 $id = esc_attr( $name );
1013 730 $name = Parsely::OPTIONS_KEY . "[$id]";
1014 731
1015 - $is_managed = key_exists( $id, $this->parsely->managed_options );
1016 - echo '<fieldset', $is_managed ? ' disabled>' : '>';
1017 - 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 ) );
1018 733 if ( isset( $args['help_text'] ) ) {
1019 734 echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
1020 735 }
1021 736 echo '>';
@@ -1025,9 +740,9 @@
1025 740 echo selected( $selected, $key, false ) . '>';
1026 741 echo esc_html( $val );
1027 742 echo '</option>';
1028 743 }
1029 - echo '</select></fieldset>';
744 + echo '</select>';
1030 745
1031 746 $this->print_filter_text( $args );
1032 747 $this->print_description_text( $args );
1033 748 }
@@ -1048,11 +763,10 @@
1048 763 // Converting boolean to string so that we have string type keys for all cases.
1049 764 $selected = $selected ? 'true' : 'false';
1050 765 }
1051 766
1052 - $is_managed = key_exists( $name, $this->parsely->managed_options );
1053 767 ?>
1054 - <fieldset <?php echo $is_managed ? 'disabled' : ''; ?>>
768 + <fieldset>
1055 769 <legend class="screen-reader-text"><span><?php echo esc_html( $title ); ?></span></legend>
1056 770 <p>
1057 771 <?php foreach ( $radio_options as $value => $text ) { ?>
1058 772 <label for="<?php echo esc_attr( "{$id}_{$value}" ); ?>">
@@ -1090,13 +804,11 @@
1090 804 */
1091 805 $input_value = $this->parsely->get_options()[ $key ];
1092 806 $input_name = Parsely::OPTIONS_KEY . "[$key]";
1093 807 $button_text = __( 'Browse', 'wp-parsely' );
1094 -
1095 - $is_managed = key_exists( $key, $this->parsely->managed_options );
1096 808 ?>
1097 809
1098 - <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 ); ?>">
1099 811 <legend class="screen-reader-text"><span><?php echo esc_html( $title ); ?></span></legend>
1100 812 <input class="file-path" type="text" name="<?php echo esc_attr( $input_name ); ?>" id="logo" value="<?php echo esc_attr( $input_value ); ?>" />
1101 813 <button data-option="<?php echo esc_attr( $key ); ?>" class="browse button" type="button"><?php echo esc_html( $button_text ); ?></button>
1102 814 </fieldset>
@@ -1198,13 +910,13 @@
1198 910 /**
1199 911 * Validates the options provided by the user.
1200 912 *
1201 913 * @param ParselySettingOptions $input Options from the settings page.
914 + *
1202 915 * @return ParselySettingOptions
1203 916 */
1204 917 public function validate_options( $input ) {
1205 918 $input = $this->validate_basic_section( $input );
1206 - $input = $this->validate_content_helper_section( $input );
1207 919 $input = $this->validate_recrawl_section( $input );
1208 920 $input = $this->validate_advanced_section( $input );
1209 921
1210 922 return $input;
@@ -1213,46 +925,50 @@
1213 925 /**
1214 926 * Validates fields of Basic Section.
1215 927 *
1216 928 * @param ParselySettingOptions $input Options from the settings page.
929 + *
1217 930 * @return ParselySettingOptions Validated inputs.
1218 931 */
1219 - private function validate_basic_section( $input ): array {
1220 - $are_credentials_managed = $this->parsely->are_credentials_managed;
1221 - $options = $this->parsely->get_options();
932 + private function validate_basic_section( $input ) {
933 + $options = $this->parsely->get_options();
1222 934
1223 - if ( $are_credentials_managed ) {
1224 - $input['apikey'] = '';
1225 - $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 + );
1226 941 } else {
1227 - $site_id = $this->sanitize_site_id( $input['apikey'] );
1228 - $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 + }
1229 953
1230 - $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() );
1231 955
1232 - // When running e2e tests, we need to update the API keys without validating them, as they will be invalid.
1233 - // phpcs:ignore WordPress.Security.NonceVerification.Missing
1234 - if ( isset( $_POST['e2e_parsely_skip_api_validate'] ) && 'y' === $_POST['e2e_parsely_skip_api_validate'] ) {
1235 - $valid_credentials = true;
1236 - }
1237 -
1238 - if (
1239 - ( is_wp_error( $valid_credentials ) && Validator::INVALID_API_CREDENTIALS === $valid_credentials->get_error_code() ) ||
1240 - ( is_bool( $valid_credentials ) && ! $valid_credentials )
1241 - ) {
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 ) {
1242 959 add_settings_error(
1243 960 Parsely::OPTIONS_KEY,
1244 - 'api_secret',
1245 - __( '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' )
1246 963 );
1247 - $input['apikey'] = $options['apikey'];
1248 - $input['api_secret'] = $options['api_secret'];
1249 - }
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' );
1250 968
1251 - // Since the API secret is obfuscated, we need to make sure that the value
1252 - // is not changed when the credentials are valid.
1253 - if ( true === $valid_credentials && $input['api_secret'] !== $api_secret ) {
1254 - $input['api_secret'] = $api_secret;
969 + wp_schedule_event( time() + 100, 'everytenminutes', 'parsely_bulk_metas_update' );
970 + $input['parsely_wipe_metadata_cache'] = false;
1255 971 }
1256 972 }
1257 973
1258 974 if ( ! isset( $input['meta_type'] ) ) {
@@ -1308,92 +1024,12 @@
1308 1024 return $input;
1309 1025 }
1310 1026
1311 1027 /**
1312 - * Validates the fields of the Content Helper section.
1313 - *
1314 - * @since 3.16.0
1315 - *
1316 - * @param ParselySettingOptions $input The settings page options.
1317 - * @return ParselySettingOptions The validated input.
1318 - */
1319 - private function validate_content_helper_section( $input ) {
1320 - /**
1321 - * Sanitizes the Content Helper data.
1322 - *
1323 - * @since 3.16.0
1324 - */
1325 - $sanitize = function ( $input ) use ( &$sanitize ) {
1326 - foreach ( $input as $key => $value ) {
1327 - if ( is_array( $value ) ) {
1328 - if ( 'allowed_user_roles' === $key && count( $input[ $key ] ) > 0 ) {
1329 - $passed_roles = array_keys( $input[ $key ] );
1330 - $valid_roles = array_keys(
1331 - Permissions::get_user_roles_with_edit_posts_cap()
1332 - );
1333 - $sanitized_roles = array();
1334 -
1335 - // Sanitize passed user roles and remove invalid ones.
1336 - foreach ( $passed_roles as $user_role ) {
1337 - if ( ! is_string( $user_role ) ) {
1338 - continue;
1339 - }
1340 -
1341 - $user_role = sanitize_text_field( $user_role );
1342 - if ( in_array( $user_role, $valid_roles, true ) ) {
1343 - $sanitized_roles[] = $user_role;
1344 - }
1345 - }
1346 -
1347 - $input[ $key ] = $sanitized_roles;
1348 - } else {
1349 - // Recurse when we have an array that's not user roles.
1350 - $input[ $key ] = $sanitize( $value );
1351 - }
1352 - } else {
1353 - // Enforce a boolean value.
1354 - $input[ $key ] = 'true' === $value || true === $value;
1355 - }
1356 - }
1357 -
1358 - return $input;
1359 - };
1360 -
1361 - // Add any missing data due to unchecked checkboxes.
1362 - if ( ! isset( $input['content_helper']['ai_features_enabled'] ) ) {
1363 - $input['content_helper']['ai_features_enabled'] = false;
1364 - }
1365 - foreach ( $this->configurable_pch_features as $feature_id ) {
1366 - if ( ! isset( $input['content_helper'][ $feature_id ] ) ) {
1367 - $input['content_helper'][ $feature_id ] = array(
1368 - 'enabled' => false,
1369 - 'allowed_user_roles' => array(),
1370 - );
1371 - } else {
1372 - if ( ! isset( $input['content_helper'][ $feature_id ]['enabled'] ) ) {
1373 - // @phpstan-ignore-next-line
1374 - $input['content_helper'][ $feature_id ]['enabled'] = false;
1375 - }
1376 - if ( ! isset( $input['content_helper'][ $feature_id ]['allowed_user_roles'] ) ) {
1377 - // @phpstan-ignore-next-line
1378 - $input['content_helper'][ $feature_id ]['allowed_user_roles'] = array();
1379 - }
1380 - }
1381 - }
1382 -
1383 - // Produce the final array.
1384 - $options = $this->parsely->get_options()['content_helper'];
1385 - $merged = array_merge( $options, $input['content_helper'] );
1386 -
1387 - $input['content_helper'] = $sanitize( $merged );
1388 -
1389 - return $input;
1390 - }
1391 -
1392 - /**
1393 1028 * Validates fields of Recrawl Section.
1394 1029 *
1395 1030 * @param ParselySettingOptions $input Options from the settings page.
1031 + *
1396 1032 * @return ParselySettingOptions Validated inputs.
1397 1033 */
1398 1034 private function validate_recrawl_section( $input ) {
1399 1035 $options = $this->parsely->get_options();
@@ -1406,16 +1042,8 @@
1406 1042 } else {
1407 1043 $input['content_id_prefix'] = sanitize_text_field( $input['content_id_prefix'] );
1408 1044 }
1409 1045
1410 - // Full metadata in non-posts.
1411 - if ( ! isset( $input['full_metadata_in_non_posts'] ) ) {
1412 - $input['full_metadata_in_non_posts'] = true;
1413 - } else {
1414 - // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
1415 - $input['full_metadata_in_non_posts'] = 'true' == $input['full_metadata_in_non_posts'];
1416 - }
1417 -
1418 1046 // Allow for Top-level categories setting to be conditionally included on the page.
1419 1047 // If it's not shown, then set the value as what was previously saved.
1420 1048 if ( ! isset( $input['use_top_level_cats'] ) ) {
1421 1049 $input['use_top_level_cats'] = 'true';
@@ -1507,30 +1135,14 @@
1507 1135 /**
1508 1136 * Validates fields of Advanced Section.
1509 1137 *
1510 1138 * @param ParselySettingOptions $input Options from the settings page.
1139 + *
1511 1140 * @return ParselySettingOptions Validated inputs.
1512 1141 */
1513 1142 private function validate_advanced_section( $input ) {
1514 - $are_credentials_managed = $this->parsely->are_credentials_managed;
1515 - $options = $this->parsely->get_options();
1143 + $options = $this->parsely->get_options();
1516 1144
1517 - if ( $are_credentials_managed ) {
1518 - $input['metadata_secret'] = '';
1519 - } else {
1520 - $input['metadata_secret'] = $this->get_unobfuscated_value( $input['metadata_secret'], $this->parsely->get_options()['metadata_secret'] );
1521 - $metadata_secret_length = strlen( $input['metadata_secret'] );
1522 - if ( $metadata_secret_length > 0 &&
1523 - false === Validator::validate_metadata_secret( $input['metadata_secret'] ) ) {
1524 - add_settings_error(
1525 - Parsely::OPTIONS_KEY,
1526 - 'metadata_secret',
1527 - __( 'The Metadata Secret was not saved because it is incorrect. Please contact Parse.ly support!', 'wp-parsely' )
1528 - );
1529 - $input['metadata_secret'] = $options['metadata_secret'];
1530 - }
1531 - }
1532 -
1533 1145 if ( ! isset( $input['disable_autotrack'] ) ) {
1534 1146 $input['disable_autotrack'] = $options['disable_autotrack'];
1535 1147 } elseif ( 'true' !== $input['disable_autotrack'] && 'false' !== $input['disable_autotrack'] ) {
1536 1148 add_settings_error(
@@ -1545,8 +1157,30 @@
1545 1157 return $input;
1546 1158 }
1547 1159
1548 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 + /**
1549 1183 * Sanitizes the passed Site ID.
1550 1184 *
1551 1185 * @since 3.3.0
1552 1186 *
@@ -1630,18 +1264,17 @@
1630 1264
1631 1265 /**
1632 1266 * Sanitizes all elements in an option array.
1633 1267 *
1634 - * @param array<int, string> $options Array of options to be sanitized.
1268 + * @param array<int, string> $array Array of options to be sanitized.
1635 1269 * @return array<int, string>
1636 1270 */
1637 - private static function sanitize_option_array( array $options ): array {
1638 - $sanitized_options = $options;
1639 - foreach ( $options as $key => $val ) {
1640 - $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 );
1641 1275 }
1642 -
1643 - return $sanitized_options;
1276 + return $new_array;
1644 1277 }
1645 1278
1646 1279 /**
1647 1280 * Gets obfuscated value.
@@ -1646,8 +1279,9 @@
1646 1279 /**
1647 1280 * Gets obfuscated value.
1648 1281 *
1649 1282 * @param string $current_value Current value of the field.
1283 + *
1650 1284 * @return string
1651 1285 */
1652 1286 private function get_obfuscated_value( $current_value ): string {
1653 1287 return str_repeat( '*', strlen( $current_value ) );
@@ -1658,8 +1292,9 @@
1658 1292 *
1659 1293 * @param string $current_value Current value of the field.
1660 1294 * @param string $previous_value Previous value of the field. If current
1661 1295 * value is obfuscated then we will use this.
1296 + *
1662 1297 * @return string
1663 1298 */
1664 1299 private function get_unobfuscated_value( $current_value, $previous_value ): string {
1665 1300 if ( $current_value === $this->get_obfuscated_value( $current_value ) ) {
@@ -1666,63 +1301,6 @@
1666 1301 return '' === $current_value ? $current_value : $previous_value;
1667 1302 }
1668 1303
1669 1304 return $current_value;
1670 - }
1671 -
1672 - /**
1673 - * Returns the field label's title. If the option is managed, a badge is
1674 - * also included.
1675 - *
1676 - * @since 3.9.0
1677 - *
1678 - * @param string $title The field's title.
1679 - * @param string $option_id The option's ID.
1680 - * @return string The resulting content.
1681 - */
1682 - public function set_field_label_contents( string $title, string $option_id ): string {
1683 - $is_managed = key_exists( $option_id, $this->parsely->managed_options );
1684 -
1685 - if ( $is_managed ) {
1686 - $text = $this->managed_options_badge['text'] ?? '';
1687 - if ( ! is_string( $text ) || '' === $text ) {
1688 - return $title;
1689 - }
1690 -
1691 - $url = filter_var( $this->managed_options_badge['url'] ?? '', FILTER_VALIDATE_URL );
1692 -
1693 - if ( false === $url ) {
1694 - $badge = '<span class="managed-option-badge">' . esc_html( $text ) . '</span>';
1695 - } else {
1696 - $badge = '<a class="managed-option-badge" href="' . esc_url( $url ) .
1697 - '" target="_blank" rel="noopener">' . esc_html( $text ) . '</a>';
1698 - }
1699 -
1700 - return "{$title}&nbsp;&nbsp;{$badge}";
1701 - }
1702 -
1703 - return $title;
1704 - }
1705 -
1706 - /**
1707 - * Returns the taxonomies allowed to be used as custom_taxonomy_section
1708 - * option values.
1709 - *
1710 - * @since 3.9.0
1711 - *
1712 - * @return array<string> Array of taxonomies.
1713 - */
1714 - public static function get_section_taxonomies(): array {
1715 - return array_diff(
1716 - get_taxonomies(),
1717 - array(
1718 - 'post_tag',
1719 - 'nav_menu',
1720 - 'author',
1721 - 'link_category',
1722 - 'post_format',
1723 - 'wp_theme',
1724 - 'wp_template_part_area',
1725 - )
1726 - );
1727 1305 }
1728 1306 }