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 +230 -1061 3.24.03.8.4 View file →
@@ -9,15 +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 -use Parsely\Content_Helper\Suggestion_Defaults;
15 13 use Parsely\Parsely;
16 -use Parsely\Permissions;
17 -use Parsely\Utils\Utils;
18 -use Parsely\Validator;
19 14
15 +use function Parsely\Utils\get_asset_info;
16 +
20 17 use const Parsely\PARSELY_FILE;
21 18
22 19 /**
23 20 * Renders the wp-admin Parse.ly plugin settings page.
@@ -23,11 +20,11 @@
23 20 * Renders the wp-admin Parse.ly plugin settings page.
24 21 *
25 22 * @since 3.0.0
26 23 *
24 + * @phpstan-import-type Parsely_Options from Parsely
25 + *
27 26 * @phpstan-type Setting_Arguments array{
28 - * add_fieldset?: bool,
29 - * legend?: string,
30 27 * option_key: string,
31 28 * label_for: string,
32 29 * title?: string,
33 30 * help_text?: string,
@@ -56,38 +53,17 @@
56 53 * disable_amp?: bool,
57 54 * track_post_types_as?: array<string, string>,
58 55 * track_post_types: string[],
59 56 * track_page_types: string[],
60 - * full_metadata_in_non_posts: ?bool,
61 57 * content_id_prefix?: string,
62 58 * use_top_level_cats?:bool|string,
63 59 * custom_taxonomy_section?: string,
64 60 * cats_as_tags?: bool|string,
65 - * content_helper: Parsely_Settings_Options_Content_Helper,
66 - * headline_testing?: Parsely_Options_Headline_Testing,
67 61 * lowercase_tags?: bool,
68 62 * force_https_canonicals?: bool,
69 63 * disable_autotrack?: bool|string,
64 + * parsely_wipe_metadata_cache: bool,
70 65 * }
71 - *
72 - * @phpstan-type Parsely_Settings_Options_Content_Helper array{
73 - * ai_features_enabled?: bool,
74 - * smart_linking?: Parsely_Settings_Options_Content_Helper_Feature,
75 - * title_suggestions?: Parsely_Settings_Options_Content_Helper_Feature,
76 - * excerpt_suggestions?: Parsely_Settings_Options_Content_Helper_Feature,
77 - * traffic_boost?: Parsely_Settings_Options_Content_Helper_Feature,
78 - * }
79 - *
80 - * @phpstan-type Parsely_Settings_Options_Content_Helper_Feature array{
81 - * enabled?: bool,
82 - * allowed_user_roles?: array<string, string>|array<string, bool>,
83 - * default_length?: mixed,
84 - * default_tone?: mixed,
85 - * default_persona?: mixed
86 - * }
87 - *
88 - * @phpstan-import-type Parsely_Options from Parsely
89 - * @phpstan-import-type Parsely_Options_Headline_Testing from Parsely
90 66 */
91 67 final class Settings_Page {
92 68 /**
93 69 * Instance of Parsely class.
@@ -105,45 +81,8 @@
105 81 */
106 82 private $hook_suffix;
107 83
108 84 /**
109 - * Options for badges that are displayed for managed options.
110 - *
111 - * @since 3.9.0
112 - *
113 - * @var array<string, mixed>
114 - */
115 - private $managed_options_badge = array();
116 -
117 - /**
118 - * The Content Intelligence features that expose site-wide generation
119 - * defaults in the settings page.
120 - *
121 - * @since 3.24.0
122 - *
123 - * @var string[]
124 - */
125 - private const FEATURES_WITH_DEFAULTS = array(
126 - 'title_suggestions',
127 - 'excerpt_suggestions',
128 - );
129 -
130 - /**
131 - * The Content Intelligence features that can be configured in the settings
132 - * page.
133 - *
134 - * @since 3.16.0
135 - *
136 - * @var string[]
137 - */
138 - private $configurable_pch_features = array(
139 - 'smart_linking',
140 - 'title_suggestions',
141 - 'excerpt_suggestions',
142 - 'traffic_boost',
143 - );
144 -
145 - /**
146 85 * Constructor.
147 86 *
148 87 * @param Parsely $parsely Instance of Parsely class.
149 88 */
@@ -148,20 +87,8 @@
148 87 * @param Parsely $parsely Instance of Parsely class.
149 88 */
150 89 public function __construct( Parsely $parsely ) {
151 90 $this->parsely = $parsely;
152 -
153 - $managed_options_badge = apply_filters(
154 - 'wp_parsely_managed_options_badge',
155 - array(
156 - 'text' => __( 'Upgrade', 'wp-parsely' ),
157 - 'url' => 'https://www.parse.ly/getdemo/',
158 - )
159 - );
160 -
161 - if ( is_array( $managed_options_badge ) ) {
162 - $this->managed_options_badge = $managed_options_badge;
163 - }
164 91 }
165 92
166 93 /**
167 94 * Registers settings page.
@@ -176,35 +103,33 @@
176 103
177 104 /**
178 105 * Enqueues all needed scripts and styles for Parse.ly plugin settings page.
179 106 *
180 - * @param string|null $hook_suffix The current page being loaded.
107 + * @param string $hook_suffix The current page being loaded.
181 108 */
182 - public function enqueue_settings_assets( ?string $hook_suffix ): void {
183 - if ( ! is_string( $hook_suffix ) || $this->hook_suffix !== $hook_suffix ) {
184 - return;
185 - }
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();
186 113
187 - add_filter( 'media_library_months_with_files', '__return_empty_array' );
188 - 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/';
189 116
190 - $admin_settings_asset = Utils::get_asset_info( 'build/admin-settings.asset.php' );
191 - $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 + );
192 124
193 - wp_enqueue_script(
194 - 'parsely-admin-settings',
195 - $built_assets_url . 'admin-settings.js',
196 - $admin_settings_asset['dependencies'],
197 - $admin_settings_asset['version'],
198 - true
199 - );
200 -
201 - wp_enqueue_style(
202 - 'parsely-admin-settings',
203 - $built_assets_url . 'admin-settings.css',
204 - array(),
205 - $admin_settings_asset['version']
206 - );
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 + }
207 132 }
208 133
209 134 /**
210 135 * Adds the Parse.ly settings page in WordPress settings menu.
@@ -209,13 +134,12 @@
209 134 /**
210 135 * Adds the Parse.ly settings page in WordPress settings menu.
211 136 */
212 137 public function add_settings_sub_menu(): void {
213 - $suffix = add_submenu_page(
214 - 'parsely-dashboard-page',
138 + $suffix = add_options_page(
215 139 __( 'Parse.ly Settings', 'wp-parsely' ),
216 - __( 'Settings', 'wp-parsely' ),
217 - Parsely::CAPABILITY, // phpcs:ignore WordPress.WP.Capabilities.Undetermined
140 + __( 'Parse.ly', 'wp-parsely' ),
141 + Parsely::CAPABILITY,
218 142 Parsely::MENU_SLUG,
219 143 array( $this, 'display_settings' )
220 144 );
221 145
@@ -248,17 +172,16 @@
248 172 );
249 173 }
250 174
251 175 /**
252 - * Displays the Parse.ly settings screen (admin.php?page=[SLUG]).
176 + * Displays the Parse.ly settings screen (options-general.php?page=[SLUG]).
253 177 */
254 178 public function display_settings(): void {
255 - // phpcs:ignore WordPress.WP.Capabilities.Undetermined
256 179 if ( ! current_user_can( Parsely::CAPABILITY ) ) {
257 180 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-parsely' ) );
258 181 }
259 182
260 - include_once plugin_dir_path( PARSELY_FILE ) . 'src/UI/settings-page.php';
183 + include_once plugin_dir_path( PARSELY_FILE ) . 'views/parsely-settings.php';
261 184 }
262 185
263 186 /**
264 187 * Initializes the settings for Parse.ly.
@@ -263,26 +186,16 @@
263 186 /**
264 187 * Initializes the settings for Parse.ly.
265 188 */
266 189 public function initialize_settings(): void {
267 - // Add the option first, to prevent double sanitization of the uninitialized option as reported
268 - // in https://core.trac.wordpress.org/ticket/21989.
269 - // The option will be initialized with the default values.
270 - add_option( Parsely::OPTIONS_KEY, $this->parsely->get_options() );
271 -
272 190 // All our options are actually stored in one single array to reduce DB queries.
273 191 register_setting(
274 192 Parsely::OPTIONS_KEY,
275 193 Parsely::OPTIONS_KEY,
276 - array(
277 - 'type' => 'array',
278 - 'sanitize_callback' => array( $this, 'validate_options' ),
279 - )
194 + array( $this, 'validate_options' )
280 195 );
281 196
282 197 $this->initialize_basic_section();
283 - $this->initialize_content_helper_section();
284 - $this->initialize_headline_testing_section();
285 198 $this->initialize_recrawl_section();
286 199 $this->initialize_advanced_section();
287 200 }
288 201
@@ -291,10 +204,9 @@
291 204 *
292 205 * @since 3.2.0
293 206 */
294 207 private function initialize_basic_section(): void {
295 - $are_credentials_managed = $this->parsely->are_credentials_managed;
296 - $section_key = 'basic-section';
208 + $section_key = 'basic-section';
297 209
298 210 add_settings_section(
299 211 $section_key,
300 212 __( 'Basic', 'wp-parsely' ),
@@ -310,15 +222,14 @@
310 222 'label_for' => $field_id,
311 223 'optional_args' => array(
312 224 'required' => 'required',
313 225 'placeholder' => 'mydomain.com',
314 - 'disabled' => $are_credentials_managed,
315 226 ),
316 227
317 228 );
318 229 add_settings_field(
319 230 $field_id,
320 - __( 'Site ID <em>(required)</em>', 'wp-parsely' ),
231 + __( 'Parse.ly Site ID <em>(required)</em>', 'wp-parsely' ),
321 232 array( $this, 'print_text_tag' ),
322 233 Parsely::MENU_SLUG,
323 234 $section_key,
324 235 $field_args
@@ -323,36 +234,54 @@
323 234 $section_key,
324 235 $field_args
325 236 );
326 237
327 - if ( ! $are_credentials_managed ) {
328 - // API Secret.
329 - $field_id = 'api_secret';
330 - $field_args = array(
331 - 'option_key' => $field_id,
332 - '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' ),
333 - 'label_for' => $field_id,
334 - 'optional_args' => array(
335 - 'type' => 'password',
336 - 'is_obfuscated_value' => true,
337 - ),
338 - );
339 - add_settings_field(
340 - $field_id,
341 - __( 'API Secret', 'wp-parsely' ),
342 - array( $this, 'print_text_tag' ),
343 - Parsely::MENU_SLUG,
344 - $section_key,
345 - $field_args
346 - );
347 - }
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 + );
348 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 +
349 278 // Metadata Format.
350 279 $field_id = 'meta_type';
351 280 $field_args = array(
352 281 'title' => __( 'Metadata Format', 'wp-parsely' ),
353 282 'option_key' => $field_id,
354 - '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' ),
355 284 'radio_options' => array(
356 285 'json_ld' => 'json_ld',
357 286 'repeated_metas' => 'repeated_metas',
358 287 ),
@@ -360,9 +289,9 @@
360 289 'filter' => 'wp_parsely_metadata',
361 290 );
362 291 add_settings_field(
363 292 $field_id,
364 - $this->set_field_label_contents( __( 'Metadata Format', 'wp-parsely' ), $field_id ),
293 + __( 'Metadata Format', 'wp-parsely' ),
365 294 array( $this, 'print_radio_tags' ),
366 295 Parsely::MENU_SLUG,
367 296 $section_key,
368 297 $field_args
@@ -372,9 +301,9 @@
372 301 $field_help = __( 'Here you can specify your logo\'s URL by using the "Browse" button or typing the URL manually.', 'wp-parsely' );
373 302 $field_id = 'logo';
374 303 add_settings_field(
375 304 $field_id,
376 - $this->set_field_label_contents( __( 'Logo', 'wp-parsely' ), $field_id ),
305 + __( 'Logo', 'wp-parsely' ),
377 306 array( $this, 'print_media_single_image' ),
378 307 Parsely::MENU_SLUG,
379 308 $section_key,
380 309 array(
@@ -385,18 +314,17 @@
385 314 )
386 315 );
387 316
388 317 // Track logged-in users.
389 - $field_id = 'track_authenticated_users';
390 318 add_settings_field(
391 - $field_id,
392 - $this->set_field_label_contents( __( 'Track Logged-in Users', 'wp-parsely' ), $field_id ),
319 + 'track_authenticated_users',
320 + __( 'Track Logged-in Users', 'wp-parsely' ),
393 321 array( $this, 'print_radio_tags' ),
394 322 Parsely::MENU_SLUG,
395 323 $section_key,
396 324 array(
397 325 'title' => __( 'Track Logged-in Users', 'wp-parsely' ), // Passed for legend element.
398 - 'option_key' => $field_id,
326 + 'option_key' => 'track_authenticated_users',
399 327 'radio_options' => array(
400 328 'true' => __( 'Yes, track logged-in users.', 'wp-parsely' ),
401 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' ),
402 330 ),
@@ -408,23 +336,22 @@
408 336 )
409 337 );
410 338
411 339 // Disable JavaScript.
412 - $field_id = 'disable_javascript';
413 340 add_settings_field(
414 - $field_id,
415 - $this->set_field_label_contents( __( 'Disable JavaScript', 'wp-parsely' ), $field_id ),
341 + 'disable_javascript',
342 + __( 'Disable JavaScript', 'wp-parsely' ),
416 343 array( $this, 'print_radio_tags' ),
417 344 Parsely::MENU_SLUG,
418 345 $section_key,
419 346 array(
420 347 'title' => __( 'Disable JavaScript', 'wp-parsely' ), // Passed for legend element.
421 - 'option_key' => $field_id,
348 + 'option_key' => 'disable_javascript',
422 349 'radio_options' => array(
423 350 'true' => __( 'Yes, disable JavaScript tracking. I want to use a separate system for tracking instead of the Parse.ly plugin.', 'wp-parsely' ),
424 351 'false' => __( 'No, do not disable JavaScript tracking. I want the Parse.ly plugin to load the tracker.', 'wp-parsely' ),
425 352 ),
426 - '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' ),
427 354 'filter' => 'wp_parsely_load_js_tracker',
428 355 )
429 356 );
430 357
@@ -429,18 +356,17 @@
429 356 );
430 357
431 358 if ( defined( 'AMP__VERSION' ) ) {
432 359 // Disable AMP tracking.
433 - $field_id = 'disable_amp';
434 360 add_settings_field(
435 - $field_id,
436 - $this->set_field_label_contents( __( 'Disable AMP Tracking', 'wp-parsely' ), $field_id ),
361 + 'disable_amp',
362 + __( 'Disable AMP Tracking', 'wp-parsely' ),
437 363 array( $this, 'print_radio_tags' ),
438 364 Parsely::MENU_SLUG,
439 365 $section_key,
440 366 array(
441 367 'title' => __( 'Disable AMP Tracking', 'wp-parsely' ), // Passed for legend element.
442 - 'option_key' => $field_id,
368 + 'option_key' => 'disable_amp',
443 369 'radio_options' => array(
444 370 'true' => __( 'Yes, disable Parse.ly tracking on AMP pages. I use a different system for JavaScript tracking on AMP pages.', 'wp-parsely' ),
445 371 'false' => __( 'No, do not disable Parse.ly tracking on AMP pages.', 'wp-parsely' ),
446 372 ),
@@ -449,239 +375,8 @@
449 375 }
450 376 }
451 377
452 378 /**
453 - * Registers section and settings for Content Intelligence section.
454 - *
455 - * @since 3.16.0
456 - */
457 - private function initialize_content_helper_section(): void {
458 - $section_key = 'content-intelligence-section';
459 -
460 - add_settings_section(
461 - $section_key,
462 - __( 'Content Intelligence', 'wp-parsely' ),
463 - '__return_null',
464 - Parsely::MENU_SLUG
465 - );
466 -
467 - // AI Features.
468 - $field_id = 'content_helper[ai_features_enabled]';
469 - $field_args = array(
470 - 'option_key' => $field_id,
471 - 'label_for' => $field_id,
472 - 'yes_text' => __( 'Enabled', 'wp-parsely' ),
473 - 'add_fieldset' => true,
474 - 'legend' => __( 'AI Features', 'wp-parsely' ),
475 - );
476 - add_settings_field(
477 - $field_id,
478 - __( 'AI Features', 'wp-parsely' ),
479 - array( $this, 'print_checkbox_tag' ),
480 - Parsely::MENU_SLUG,
481 - $section_key,
482 - $field_args
483 - );
484 -
485 - // Smart Linking.
486 - $field_id = 'content_helper[smart_linking]';
487 - $field_args = array(
488 - 'option_key' => $field_id,
489 - 'label_for' => $field_id,
490 - 'legend' => __( 'Smart Linking', 'wp-parsely' ),
491 - );
492 - add_settings_field(
493 - $field_id,
494 - __( 'Smart Linking', 'wp-parsely' ),
495 - array( $this, 'print_content_helper_ai_feature_section' ),
496 - Parsely::MENU_SLUG,
497 - $section_key,
498 - $field_args
499 - );
500 -
501 - // Title Suggestions.
502 - $field_id = 'content_helper[title_suggestions]';
503 - $field_args = array(
504 - 'option_key' => $field_id,
505 - 'label_for' => $field_id,
506 - 'legend' => __( 'Title Suggestions', 'wp-parsely' ),
507 - );
508 - add_settings_field(
509 - $field_id,
510 - __( 'Title Suggestions', 'wp-parsely' ),
511 - array( $this, 'print_content_helper_ai_feature_section' ),
512 - Parsely::MENU_SLUG,
513 - $section_key,
514 - $field_args
515 - );
516 -
517 - // Excerpt Suggestions.
518 - $field_id = 'content_helper[excerpt_suggestions]';
519 - $field_args = array(
520 - 'option_key' => $field_id,
521 - 'label_for' => $field_id,
522 - 'legend' => __( 'Excerpt Suggestions', 'wp-parsely' ),
523 - 'filter' => Excerpt_Suggestions::get_feature_filter_name(),
524 - );
525 - add_settings_field(
526 - $field_id,
527 - __( 'Excerpt Suggestions', 'wp-parsely' ),
528 - array( $this, 'print_content_helper_ai_feature_section' ),
529 - Parsely::MENU_SLUG,
530 - $section_key,
531 - $field_args
532 - );
533 -
534 - // Traffic Boost.
535 - $field_id = 'content_helper[traffic_boost]';
536 - $field_args = array(
537 - 'option_key' => $field_id,
538 - 'label_for' => $field_id,
539 - 'legend' => __( 'Engagement Boost (beta)', 'wp-parsely' ),
540 - );
541 - add_settings_field(
542 - $field_id,
543 - __( 'Engagement Boost (beta)', 'wp-parsely' ),
544 - array( $this, 'print_content_helper_ai_feature_section' ),
545 - Parsely::MENU_SLUG,
546 - $section_key,
547 - $field_args
548 - );
549 - }
550 -
551 - /**
552 - * Registers section and settings for Headline Testing section.
553 - *
554 - * @since 3.21.0
555 - */
556 - private function initialize_headline_testing_section(): void {
557 - $section_key = 'headline-testing-section';
558 -
559 - add_settings_section(
560 - $section_key,
561 - __( 'Headline Testing', 'wp-parsely' ),
562 - function (): void {
563 - echo '<p>' . esc_html__( 'Configure Parse.ly Headline Testing to automatically test different headline variations and optimize for engagement.', 'wp-parsely' ) . '</p>';
564 - echo '<p><a href="https://docs.parse.ly/dashboard/optimization-menu/headline-testing-tab/" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Learn more about Headline Testing', 'wp-parsely' ) . '</a></p>';
565 - },
566 - Parsely::MENU_SLUG
567 - );
568 -
569 - // Enable Headline Testing.
570 - $field_id = 'headline_testing[enabled]';
571 - $field_args = array(
572 - 'option_key' => $field_id,
573 - 'label_for' => $field_id,
574 - 'yes_text' => __( 'Enabled', 'wp-parsely' ),
575 - 'add_fieldset' => true,
576 - 'legend' => __( 'Headline Testing', 'wp-parsely' ),
577 - 'help_text' => __( 'Enable Parse.ly Headline Testing to automatically test different headline variations.', 'wp-parsely' ),
578 - );
579 - add_settings_field(
580 - $field_id,
581 - __( 'Headline Testing', 'wp-parsely' ),
582 - array( $this, 'print_checkbox_tag' ),
583 - Parsely::MENU_SLUG,
584 - $section_key,
585 - $field_args
586 - );
587 -
588 - // Installation Method.
589 - $field_id = 'headline_testing[installation_method]';
590 - $field_args = array(
591 - 'option_key' => $field_id,
592 - 'label_for' => $field_id,
593 - 'help_text' => __( 'Choose how you want to install the Headline Testing script. One-line snippet is recommended for most sites.', 'wp-parsely' ),
594 - 'radio_options' => array(
595 - 'one_line' => __( 'One-line Snippet (Recommended)', 'wp-parsely' ),
596 - 'advanced' => __( 'Advanced Installation', 'wp-parsely' ),
597 - ),
598 - );
599 - add_settings_field(
600 - $field_id,
601 - __( 'Installation Method', 'wp-parsely' ),
602 - array( $this, 'print_radio_tags' ),
603 - Parsely::MENU_SLUG,
604 - $section_key,
605 - $field_args
606 - );
607 -
608 - // Enable Flicker Control (Advanced only).
609 - $field_id = 'headline_testing[enable_flicker_control]';
610 - $field_args = array(
611 - 'option_key' => $field_id,
612 - 'label_for' => $field_id,
613 - 'yes_text' => __( 'Enabled', 'wp-parsely' ),
614 - 'help_text' => __( 'Hide page body for up to 500ms to prevent flickering when headlines are replaced. Only available with Advanced installation.', 'wp-parsely' ),
615 - );
616 - add_settings_field(
617 - $field_id,
618 - __( 'Enable Flicker Control', 'wp-parsely' ),
619 - array( $this, 'print_checkbox_tag' ),
620 - Parsely::MENU_SLUG,
621 - $section_key,
622 - $field_args
623 - );
624 -
625 - // Enable Live Updates.
626 - $field_id = 'headline_testing[enable_live_updates]';
627 - $field_args = array(
628 - 'option_key' => $field_id,
629 - 'label_for' => $field_id,
630 - 'yes_text' => __( 'Enabled', 'wp-parsely' ),
631 - 'help_text' => __( 'Watch for new content and automatically update headlines for newly added anchors.', 'wp-parsely' ),
632 - );
633 - add_settings_field(
634 - $field_id,
635 - __( 'Enable Live Updates', 'wp-parsely' ),
636 - array( $this, 'print_checkbox_tag' ),
637 - Parsely::MENU_SLUG,
638 - $section_key,
639 - $field_args
640 - );
641 -
642 - // Live Update Timeout.
643 - $field_id = 'headline_testing[live_update_timeout]';
644 - $field_args = array(
645 - 'option_key' => $field_id,
646 - 'label_for' => $field_id,
647 - 'help_text' => __( 'How long to watch for new content (in milliseconds). Default: 30000 (30 seconds).', 'wp-parsely' ),
648 - 'optional_args' => array(
649 - 'type' => 'number',
650 - 'placeholder' => '30000',
651 - 'min' => '1000',
652 - 'max' => '60000',
653 - 'step' => '1000',
654 - ),
655 - );
656 - add_settings_field(
657 - $field_id,
658 - __( 'Live Update Timeout (ms)', 'wp-parsely' ),
659 - array( $this, 'print_text_tag' ),
660 - Parsely::MENU_SLUG,
661 - $section_key,
662 - $field_args
663 - );
664 -
665 - // Allow After Content Load.
666 - $field_id = 'headline_testing[allow_after_content_load]';
667 - $field_args = array(
668 - 'option_key' => $field_id,
669 - 'label_for' => $field_id,
670 - 'yes_text' => __( 'Enabled', 'wp-parsely' ),
671 - 'help_text' => __( 'Allow headline swapping even after the main content has loaded. May cause flickering. Highly recommended if you are loading your script asynchronously.', 'wp-parsely' ),
672 - );
673 - add_settings_field(
674 - $field_id,
675 - __( 'Allow After Content Load', 'wp-parsely' ),
676 - array( $this, 'print_checkbox_tag' ),
677 - Parsely::MENU_SLUG,
678 - $section_key,
679 - $field_args
680 - );
681 - }
682 -
683 - /**
684 379 * Registers section and settings for Recrawl section.
685 380 *
686 381 * @since 3.2.0
687 382 */
@@ -714,30 +409,12 @@
714 409 array(
715 410 'title' => __( 'Track Post Types as', 'wp-parsely' ),
716 411 'option_key' => $field_id,
717 412 'help_text' => $field_help,
413 + 'filter' => 'wp_parsely_trackable_statuses',
718 414 )
719 415 );
720 416
721 - // Use full metadata in non-posts.
722 - $field_id = 'full_metadata_in_non_posts';
723 - add_settings_field(
724 - $field_id,
725 - $this->set_field_label_contents( __( 'Use Full Metadata in Non-Posts', 'wp-parsely' ), $field_id ),
726 - array( $this, 'print_radio_tags' ),
727 - Parsely::MENU_SLUG,
728 - $section_key,
729 - array(
730 - 'title' => __( 'Use Full Metadata in Non-Posts', 'wp-parsely' ), // Passed for legend element.
731 - 'option_key' => $field_id,
732 - 'radio_options' => array(
733 - 'true' => __( 'Yes, add full metadata to Post Types being tracked as Non-Posts.', 'wp-parsely' ),
734 - 'false' => __( 'No, we have code that modifies metadata and that has not been tested for compatibility yet.', 'wp-parsely' ),
735 - ),
736 - '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' ),
737 - )
738 - );
739 -
740 417 // Content ID Prefix.
741 418 $field_id = 'content_id_prefix';
742 419 $field_args = array(
743 420 'option_key' => $field_id,
@@ -748,9 +425,9 @@
748 425 'label_for' => $field_id,
749 426 );
750 427 add_settings_field(
751 428 $field_id,
752 - $this->set_field_label_contents( __( 'Content ID Prefix', 'wp-parsely' ), $field_id ),
429 + __( 'Content ID Prefix', 'wp-parsely' ),
753 430 array( $this, 'print_text_tag' ),
754 431 Parsely::MENU_SLUG,
755 432 $section_key,
756 433 $field_args
@@ -756,18 +433,17 @@
756 433 $field_args
757 434 );
758 435
759 436 // Use top-level categories.
760 - $field_id = 'use_top_level_cats';
761 437 add_settings_field(
762 - $field_id,
763 - $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' ),
764 440 array( $this, 'print_radio_tags' ),
765 441 Parsely::MENU_SLUG,
766 442 $section_key,
767 443 array(
768 444 'title' => __( 'Use Top-Level Categories for Section', 'wp-parsely' ), // Passed for legend element.
769 - 'option_key' => $field_id,
445 + 'option_key' => 'use_top_level_cats',
770 446 'radio_options' => array(
771 447 'true' => __( 'Yes, use the first category assigned to a post as the section name.', 'wp-parsely' ),
772 448 'false' => __( 'No, do not use the first category assigned to a post as the section name.', 'wp-parsely' ),
773 449 ),
@@ -779,14 +455,24 @@
779 455 $field_id = 'custom_taxonomy_section';
780 456 $field_args = array(
781 457 'option_key' => $field_id,
782 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' ),
783 - '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 + ),
784 470 'label_for' => Parsely::OPTIONS_KEY . "[$field_id]",
785 471 );
786 472 add_settings_field(
787 473 $field_id,
788 - $this->set_field_label_contents( __( 'Use Custom Taxonomy for Section', 'wp-parsely' ), $field_id ),
474 + __( 'Use Custom Taxonomy for Section', 'wp-parsely' ),
789 475 array( $this, 'print_select_tag' ),
790 476 Parsely::MENU_SLUG,
791 477 $section_key,
792 478 $field_args
@@ -792,18 +478,17 @@
792 478 $field_args
793 479 );
794 480
795 481 // Use categories and custom taxonomies as tags.
796 - $field_id = 'cats_as_tags';
797 482 add_settings_field(
798 - $field_id,
799 - $this->set_field_label_contents( __( 'Add Categories to Tags', 'wp-parsely' ), $field_id ),
483 + 'cats_as_tags',
484 + __( 'Add Categories to Tags', 'wp-parsely' ),
800 485 array( $this, 'print_radio_tags' ),
801 486 Parsely::MENU_SLUG,
802 487 $section_key,
803 488 array(
804 489 'title' => __( 'Add Categories to Tags', 'wp-parsely' ), // Passed for legend element.
805 - 'option_key' => $field_id,
490 + 'option_key' => 'cats_as_tags',
806 491 'radio_options' => array(
807 492 'true' => __( 'Yes, add all assigned categories and taxonomies to my tags.', 'wp-parsely' ),
808 493 'false' => __( 'No, do not add all assigned categories and taxonomies to my tags.', 'wp-parsely' ),
809 494 ),
@@ -811,18 +496,17 @@
811 496 )
812 497 );
813 498
814 499 // Lowercase all tags.
815 - $field_id = 'lowercase_tags';
816 500 add_settings_field(
817 - $field_id,
818 - $this->set_field_label_contents( __( 'Lowercase All Tags', 'wp-parsely' ), $field_id ),
501 + 'lowercase_tags',
502 + __( 'Lowercase All Tags', 'wp-parsely' ),
819 503 array( $this, 'print_radio_tags' ),
820 504 Parsely::MENU_SLUG,
821 505 $section_key,
822 506 array(
823 507 'title' => __( 'Lowercase All Tags', 'wp-parsely' ), // Passed for legend element.
824 - 'option_key' => $field_id,
508 + 'option_key' => 'lowercase_tags',
825 509 'radio_options' => array(
826 510 'true' => __( 'Yes, use lowercase versions of my tags to correct for potential misspellings.', 'wp-parsely' ),
827 511 'false' => __( 'No, do not use lowercase versions of my tags to correct for potential misspellings.', 'wp-parsely' ),
828 512 ),
@@ -828,18 +512,17 @@
828 512 ),
829 513 )
830 514 );
831 515
832 - $field_id = 'force_https_canonicals';
833 516 add_settings_field(
834 - $field_id,
835 - $this->set_field_label_contents( __( 'Force HTTPS Canonicals', 'wp-parsely' ), $field_id ),
517 + 'force_https_canonicals',
518 + __( 'Force HTTPS Canonicals', 'wp-parsely' ),
836 519 array( $this, 'print_radio_tags' ),
837 520 Parsely::MENU_SLUG,
838 521 $section_key,
839 522 array(
840 523 'title' => __( 'Force HTTPS Canonicals', 'wp-parsely' ), // Passed for legend element.
841 - 'option_key' => $field_id,
524 + 'option_key' => 'force_https_canonicals',
842 525 'radio_options' => array(
843 526 'true' => __( 'Yes, force <code>https</code> canonical URLs by default.', 'wp-parsely' ),
844 527 'false' => __( 'No, I want to use <code>http</code>.', 'wp-parsely' ),
845 528 ),
@@ -853,10 +536,9 @@
853 536 *
854 537 * @since 3.2.0
855 538 */
856 539 private function initialize_advanced_section(): void {
857 - $are_credentials_managed = $this->parsely->are_credentials_managed;
858 - $section_key = 'advanced-section';
540 + $section_key = 'advanced-section';
859 541
860 542 add_settings_section(
861 543 $section_key,
862 544 __( 'Advanced', 'wp-parsely' ),
@@ -863,41 +545,18 @@
863 545 '__return_null',
864 546 Parsely::MENU_SLUG
865 547 );
866 548
867 - if ( ! $are_credentials_managed ) {
868 - // Metadata Secret.
869 - $field_id = 'metadata_secret';
870 - $field_args = array(
871 - 'option_key' => $field_id,
872 - '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' ),
873 - 'label_for' => $field_id,
874 - 'optional_args' => array(
875 - 'type' => 'password',
876 - 'is_obfuscated_value' => true,
877 - ),
878 - );
879 - add_settings_field(
880 - $field_id,
881 - __( 'Metadata Secret', 'wp-parsely' ),
882 - array( $this, 'print_text_tag' ),
883 - Parsely::MENU_SLUG,
884 - $section_key,
885 - $field_args
886 - );
887 - }
888 -
889 549 // Disable autotrack.
890 - $field_id = 'disable_autotrack';
891 550 add_settings_field(
892 - $field_id,
893 - $this->set_field_label_contents( __( 'Disable Autotracking', 'wp-parsely' ), $field_id ),
551 + 'disable_autotrack',
552 + __( 'Disable Autotracking', 'wp-parsely' ),
894 553 array( $this, 'print_radio_tags' ),
895 554 Parsely::MENU_SLUG,
896 555 $section_key,
897 556 array(
898 557 'title' => __( 'Disable Autotracking', 'wp-parsely' ), // Passed for legend element.
899 - 'option_key' => $field_id,
558 + 'option_key' => 'disable_autotrack',
900 559 'radio_options' => array(
901 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' ),
902 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' ),
903 562 ),
@@ -902,8 +561,22 @@
902 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' ),
903 562 ),
904 563 )
905 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 + );
906 579 }
907 580
908 581 /**
909 582 * Shows setting tabs.
@@ -987,261 +660,64 @@
987 660 *
988 661 * @param Setting_Arguments $args The arguments for text tag.
989 662 */
990 663 public function print_text_tag( $args ): void {
991 - $options = $this->parsely->get_options();
992 - $name = $args['option_key'];
993 - $raw_value = $this->get_option_value( $name, $options );
994 - $value_as_string = is_scalar( $raw_value ) ? (string) $raw_value : '';
664 + $options = $this->parsely->get_options();
665 + $name = $args['option_key'];
666 + /**
667 + * Variable.
668 + *
669 + * @var string
670 + */
671 + $value = $options[ $name ] ?? '';
995 672 $optional_args = $args['optional_args'] ?? array();
996 673 $id = esc_attr( $name );
997 - $html_name = $this->get_html_name_attribute( $name );
674 + $name = Parsely::OPTIONS_KEY . "[$id]";
998 675 $is_obfuscated_value = $optional_args['is_obfuscated_value'] ?? false;
999 - $value = $is_obfuscated_value ? $this->get_obfuscated_value( $value_as_string ) : esc_attr( $value_as_string );
676 + $value = $is_obfuscated_value ? $this->get_obfuscated_value( $value ) : esc_attr( $value );
677 + $accepted_args = array( 'placeholder', 'required' );
1000 678 $type = $optional_args['type'] ?? 'text';
1001 - $accepted_args = array( 'placeholder', 'required', 'disabled' );
1002 679
1003 - if ( 'number' === $type ) {
1004 - $accepted_args = array_merge( $accepted_args, array( 'min', 'max', 'step' ) );
1005 - }
680 + echo sprintf( "<input type='%s' name='%s' id='%s' value='%s'", esc_attr( $type ), esc_attr( $name ), esc_attr( $id ), esc_attr( $value ) );
1006 681
1007 - $is_managed = key_exists( $id, $this->parsely->managed_options );
1008 - echo '<fieldset', $is_managed ? ' disabled>' : '>';
1009 - printf( "<input type='%s' name='%s' id='%s' value='%s'", esc_attr( $type ), esc_attr( $html_name ), esc_attr( $id ), esc_attr( $value ) );
1010 -
1011 682 if ( isset( $args['help_text'] ) ) {
1012 683 echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
1013 684 }
1014 685
1015 686 foreach ( $optional_args as $key => $val ) {
1016 - if ( \in_array( $key, $accepted_args, true ) && false !== $val ) {
1017 - // Don't add a placeholder to managed option fields.
1018 - if ( $is_managed && 'placeholder' === $key ) {
1019 - continue;
1020 - }
1021 -
1022 - // Support attributes without values.
1023 - echo ' ' . esc_attr( $key );
1024 - if ( true !== $val ) {
1025 - echo '="' . esc_attr( $val ) . '"';
1026 - }
687 + if ( \in_array( $key, $accepted_args, true ) ) {
688 + echo ' ' . esc_attr( $key ) . '="' . esc_attr( (string) $val ) . '"';
1027 689 }
1028 690 }
1029 - echo ' /></fieldset>';
691 + echo ' />';
1030 692
1031 693 $this->print_description_text( $args );
1032 694 }
1033 695
1034 696 /**
1035 - * Prints a checkbox tag.
697 + * Prints a checkbox tag in the settings page.
1036 698 *
1037 - * @since 3.16.0
1038 - *
1039 - * @param Setting_Arguments $args Arguments for the checkbox tag.
1040 - * @param Parsely_Options|null $options The options to use.
699 + * @param Setting_Arguments $args Arguments to print to checkbox tag.
1041 700 */
1042 - public function print_checkbox_tag( $args, $options = null ): void {
1043 - $options = $options ?? $this->parsely->get_options();
1044 - $name = $args['option_key'];
1045 - $has_fieldset = isset( $args['add_fieldset'] ) && true === $args['add_fieldset'];
1046 - $html_id = rtrim( str_replace( array( '[', ']', '__' ), '_', $name ), '_' );
1047 - $html_name = $this->get_html_name_attribute( $name );
1048 - $yes_text = $args['yes_text'] ?? '';
1049 - $value = $this->get_option_value( $name, $options );
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'] ?? '';
1050 708
1051 - // Fieldset start.
1052 - if ( $has_fieldset ) {
1053 - echo '<fieldset>';
1054 - if ( isset( $args['legend'] ) ) {
1055 - echo '<legend class="screen-reader-text"><span>';
1056 - echo esc_html( $args['legend'] ) . '</span></legend>';
1057 - }
1058 - }
1059 -
1060 - // Label and contained checkbox.
1061 - printf( '<label for="%s">', esc_attr( $html_id ) );
1062 - printf(
1063 - '<input type="checkbox" name="%s" id="%s" value="true" ',
1064 - esc_attr( $html_name ),
1065 - esc_attr( $html_id )
1066 - );
709 + echo sprintf( "<input type='checkbox' name='%s' id='%s_true' value='true' ", esc_attr( $name ), esc_attr( $id ) );
1067 710 if ( isset( $args['help_text'] ) ) {
1068 - echo ' aria-describedby="' . esc_attr( $html_id ) . '-description"';
711 + echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
1069 712 }
1070 - checked( true === $value, true );
1071 - 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 ) );
1072 715
1073 - // Fieldset end.
1074 - if ( $has_fieldset ) {
1075 - echo '</fieldset>';
1076 - }
1077 -
1078 716 $this->print_description_text( $args );
1079 717 }
1080 718
1081 719 /**
1082 - * Prints a Content Intelligence AI feature section.
1083 - *
1084 - * @since 3.16.0
1085 - *
1086 - * @param Setting_Arguments $args The arguments for the section.
1087 - */
1088 - public function print_content_helper_ai_feature_section( $args ): void {
1089 - $options = $this->parsely->get_options();
1090 - $feature_id = str_replace(
1091 - array( 'content_helper[', ']' ),
1092 - '',
1093 - $args['option_key']
1094 - );
1095 -
1096 - // Convert user role settings to make them work in the settings page.
1097 - $option = &$options['content_helper'][ $feature_id ];
1098 - if ( isset( $option['allowed_user_roles'] ) ) {
1099 - foreach ( $option['allowed_user_roles'] as $role ) {
1100 - $option['allowed_user_roles'][ $role ] = true;
1101 - }
1102 - }
1103 -
1104 - // Feature "Enabled" checkbox.
1105 - $enabled_args = array(
1106 - 'option_key' => $args['option_key'] . '[enabled]',
1107 - 'label_for' => $args['option_key'] . '[enabled]',
1108 - 'yes_text' => __( 'Enabled', 'wp-parsely' ),
1109 - 'add_fieldset' => true,
1110 - );
1111 - if ( isset( $args['legend'] ) ) {
1112 - $enabled_args['legend'] = $args['legend'];
1113 - }
1114 - $this->print_checkbox_tag( $enabled_args, $options );
1115 -
1116 - // User role permissions fieldset.
1117 - echo '<p>' . esc_html__( 'User Permissions', 'wp-parsely' ) . '</p>';
1118 - echo '<fieldset class="user-role-permissions">';
1119 - echo '<legend class="screen-reader-text"><span>';
1120 - printf(
1121 - /* translators: %s: Feature name */
1122 - esc_html__( '%s User Permissions', 'wp-parsely' ),
1123 - esc_html( $args['legend'] ?? __( 'Feature', 'wp-parsely' ) )
1124 - );
1125 - echo '</span></legend>';
1126 -
1127 - // User role checkboxes.
1128 - $user_roles = Permissions::get_user_roles_with_edit_posts_cap();
1129 - foreach ( $user_roles as $key => $name ) {
1130 - $option_key = $args['option_key'] . '[allowed_user_roles][' . $key . ']';
1131 - $role_args = array(
1132 - 'option_key' => $option_key,
1133 - 'label_for' => $option_key,
1134 - 'yes_text' => translate_user_role( $name ),
1135 - );
1136 -
1137 - $this->print_checkbox_tag( $role_args, $options );
1138 - }
1139 -
1140 - echo '</fieldset>';
1141 -
1142 - if ( in_array( $feature_id, self::FEATURES_WITH_DEFAULTS, true ) ) {
1143 - $this->print_suggestion_defaults( $args, $options, $feature_id );
1144 - }
1145 -
1146 - $this->print_filter_text( $args );
1147 - }
1148 -
1149 - /**
1150 - * Prints out a feature's site-wide generation defaults.
1151 - *
1152 - * These apply to users who have not yet set their own values in the editor.
1153 - * The desired length is printed for Excerpt Suggestions only, as Title
1154 - * Suggestions has no equivalent setting.
1155 - *
1156 - * @since 3.24.0
1157 - *
1158 - * @param Setting_Arguments $args The arguments for the fieldset.
1159 - * @param Parsely_Options $options The plugin's options.
1160 - * @param string $feature_id The feature's name.
1161 - */
1162 - private function print_suggestion_defaults( $args, $options, string $feature_id ): void {
1163 - /** @var array<string, mixed> $feature_options */
1164 - $feature_options = $options['content_helper'][ $feature_id ] ?? array();
1165 - $is_managed = key_exists( 'content_helper', $this->parsely->managed_options );
1166 -
1167 - echo '<fieldset class="', esc_attr( str_replace( '_', '-', $feature_id ) ), '-defaults"',
1168 - $is_managed ? ' disabled>' : '>';
1169 - echo '<legend class="screen-reader-text"><span>';
1170 - printf(
1171 - /* translators: %s: Feature name */
1172 - esc_html__( '%s Default Settings', 'wp-parsely' ),
1173 - esc_html( $args['legend'] ?? __( 'Feature', 'wp-parsely' ) )
1174 - );
1175 - echo '</span></legend>';
1176 -
1177 - if ( Excerpt_Suggestions::get_feature_name() === $feature_id ) {
1178 - $length_key = $args['option_key'] . '[default_length]';
1179 - printf(
1180 - '<p><label for="%1$s">%2$s</label><br />' .
1181 - '<input type="number" name="%3$s" id="%1$s" value="%4$d" min="%5$d" max="%6$d" step="1" class="small-text" /></p>',
1182 - esc_attr( $length_key ),
1183 - esc_html__( 'Default length (characters)', 'wp-parsely' ),
1184 - esc_attr( $this->get_html_name_attribute( $length_key ) ),
1185 - absint( Suggestion_Defaults::get_default_length( $feature_options ) ),
1186 - absint( Suggestion_Defaults::MIN_LENGTH ),
1187 - absint( Suggestion_Defaults::MAX_LENGTH )
1188 - );
1189 - }
1190 -
1191 - $this->print_suggestion_defaults_select(
1192 - $args['option_key'] . '[default_tone]',
1193 - __( 'Default tone', 'wp-parsely' ),
1194 - Suggestion_Defaults::get_tones(),
1195 - Suggestion_Defaults::get_default_tone( $feature_options )
1196 - );
1197 -
1198 - $this->print_suggestion_defaults_select(
1199 - $args['option_key'] . '[default_persona]',
1200 - __( 'Default persona', 'wp-parsely' ),
1201 - Suggestion_Defaults::get_personas(),
1202 - Suggestion_Defaults::get_default_persona( $feature_options )
1203 - );
1204 -
1205 - echo '</fieldset>';
1206 - }
1207 -
1208 - /**
1209 - * Prints out a select tag for a feature's default generation setting.
1210 - *
1211 - * @since 3.24.0
1212 - *
1213 - * @param string $option_key The nested option key.
1214 - * @param string $label The visible label.
1215 - * @param array<string, string> $choices The choices, as value => label pairs.
1216 - * @param string $selected The currently selected value.
1217 - */
1218 - private function print_suggestion_defaults_select(
1219 - string $option_key,
1220 - string $label,
1221 - array $choices,
1222 - string $selected
1223 - ): void {
1224 - printf(
1225 - '<p><label for="%1$s">%2$s</label><br /><select name="%3$s" id="%1$s">',
1226 - esc_attr( $option_key ),
1227 - esc_html( $label ),
1228 - esc_attr( $this->get_html_name_attribute( $option_key ) )
1229 - );
1230 -
1231 - foreach ( $choices as $value => $choice_label ) {
1232 - printf(
1233 - '<option value="%s"%s>%s</option>',
1234 - esc_attr( $value ),
1235 - selected( $selected, $value, false ),
1236 - esc_html( $choice_label )
1237 - );
1238 - }
1239 -
1240 - echo '</select></p>';
1241 - }
1242 -
1243 - /**
1244 720 * Prints out the select tags
1245 721 *
1246 722 * @param Setting_Arguments $args The arguments for the select dropdowns.
1247 723 */
@@ -1252,11 +728,9 @@
1252 728 $selected = $options[ $name ] ?? null;
1253 729 $id = esc_attr( $name );
1254 730 $name = Parsely::OPTIONS_KEY . "[$id]";
1255 731
1256 - $is_managed = key_exists( $id, $this->parsely->managed_options );
1257 - echo '<fieldset', $is_managed ? ' disabled>' : '>';
1258 - 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 ) );
1259 733 if ( isset( $args['help_text'] ) ) {
1260 734 echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
1261 735 }
1262 736 echo '>';
@@ -1266,9 +740,9 @@
1266 740 echo selected( $selected, $key, false ) . '>';
1267 741 echo esc_html( $val );
1268 742 echo '</option>';
1269 743 }
1270 - echo '</select></fieldset>';
744 + echo '</select>';
1271 745
1272 746 $this->print_filter_text( $args );
1273 747 $this->print_description_text( $args );
1274 748 }
@@ -1278,13 +752,11 @@
1278 752 *
1279 753 * @param Setting_Arguments $args The arguments for the radio buttons.
1280 754 */
1281 755 public function print_radio_tags( $args ): void {
1282 - $options = $this->parsely->get_options();
1283 756 $name = $args['option_key'];
1284 757 $id = esc_attr( $name );
1285 - $selected = $this->get_option_value( $name, $options );
1286 - $html_name = $this->get_html_name_attribute( $name );
758 + $selected = $this->parsely->get_options()[ $name ];
1287 759 $title = $args['title'] ?? '';
1288 760 $radio_options = $args['radio_options'] ?? array();
1289 761
1290 762 if ( is_bool( $selected ) ) {
@@ -1291,11 +763,10 @@
1291 763 // Converting boolean to string so that we have string type keys for all cases.
1292 764 $selected = $selected ? 'true' : 'false';
1293 765 }
1294 766
1295 - $is_managed = key_exists( $name, $this->parsely->managed_options );
1296 767 ?>
1297 - <fieldset <?php echo $is_managed ? 'disabled' : ''; ?>>
768 + <fieldset>
1298 769 <legend class="screen-reader-text"><span><?php echo esc_html( $title ); ?></span></legend>
1299 770 <p>
1300 771 <?php foreach ( $radio_options as $value => $text ) { ?>
1301 772 <label for="<?php echo esc_attr( "{$id}_{$value}" ); ?>">
@@ -1300,9 +771,9 @@
1300 771 <?php foreach ( $radio_options as $value => $text ) { ?>
1301 772 <label for="<?php echo esc_attr( "{$id}_{$value}" ); ?>">
1302 773 <input
1303 774 type="radio"
1304 - name="<?php echo esc_attr( $html_name ); ?>"
775 + name="<?php echo esc_attr( Parsely::OPTIONS_KEY . "[$id]" ); ?>"
1305 776 id="<?php echo esc_attr( "{$id}_{$value}" ); ?>"
1306 777 value="<?php echo esc_attr( $value ); ?>"
1307 778 <?php checked( $selected, $value ); ?>
1308 779 />
@@ -1333,13 +804,11 @@
1333 804 */
1334 805 $input_value = $this->parsely->get_options()[ $key ];
1335 806 $input_name = Parsely::OPTIONS_KEY . "[$key]";
1336 807 $button_text = __( 'Browse', 'wp-parsely' );
1337 -
1338 - $is_managed = key_exists( $key, $this->parsely->managed_options );
1339 808 ?>
1340 809
1341 - <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 ); ?>">
1342 811 <legend class="screen-reader-text"><span><?php echo esc_html( $title ); ?></span></legend>
1343 812 <input class="file-path" type="text" name="<?php echo esc_attr( $input_name ); ?>" id="logo" value="<?php echo esc_attr( $input_value ); ?>" />
1344 813 <button data-option="<?php echo esc_attr( $key ); ?>" class="browse button" type="button"><?php echo esc_html( $button_text ); ?></button>
1345 814 </fieldset>
@@ -1348,55 +817,8 @@
1348 817 $this->print_description_text( $args );
1349 818 }
1350 819
1351 820 /**
1352 - * Generates the HTML name attribute for a form field.
1353 - *
1354 - * Handles nested options (content_helper, headline_testing) and regular
1355 - * options, properly escaping and formatting the attribute value.
1356 - *
1357 - * @since 3.21.0
1358 - *
1359 - * @param string $name The option key/name.
1360 - * @return string The properly formatted and escaped HTML name attribute value.
1361 - */
1362 - private function get_html_name_attribute( string $name ): string {
1363 - if ( strpos( $name, 'content_helper' ) === 0 ) {
1364 - return Parsely::OPTIONS_KEY . str_replace(
1365 - 'content_helper',
1366 - '[content_helper]',
1367 - esc_attr( $name )
1368 - );
1369 - } elseif ( strpos( $name, 'headline_testing' ) === 0 ) {
1370 - return Parsely::OPTIONS_KEY . str_replace(
1371 - 'headline_testing',
1372 - '[headline_testing]',
1373 - esc_attr( $name )
1374 - );
1375 - } else {
1376 - return Parsely::OPTIONS_KEY . '[' . esc_attr( $name ) . ']';
1377 - }
1378 - }
1379 -
1380 - /**
1381 - * Gets the value of an option, handling both flat and nested option keys.
1382 - *
1383 - * @since 3.21.0
1384 - *
1385 - * @param string $name The option key name (may contain brackets for nested options).
1386 - * @param Parsely_Options $options The options array to retrieve from.
1387 - * @return mixed The option value, or null if not found.
1388 - */
1389 - private function get_option_value( string $name, $options ) {
1390 - // Get raw value based on whether it's a nested option or not.
1391 - if ( false === strpos( $name, '[' ) ) {
1392 - return $options[ $name ] ?? null;
1393 - } else {
1394 - return Parsely::get_nested_option_value( $name, $options ) ?? null;
1395 - }
1396 - }
1397 -
1398 - /**
1399 821 * Prints out the post tracking options table.
1400 822 *
1401 823 * @since 3.2.0
1402 824 *
@@ -1488,14 +910,13 @@
1488 910 /**
1489 911 * Validates the options provided by the user.
1490 912 *
1491 913 * @param ParselySettingOptions $input Options from the settings page.
914 + *
1492 915 * @return ParselySettingOptions
1493 916 */
1494 917 public function validate_options( $input ) {
1495 918 $input = $this->validate_basic_section( $input );
1496 - $input = $this->validate_content_helper_section( $input );
1497 - $input = $this->validate_headline_testing_section( $input );
1498 919 $input = $this->validate_recrawl_section( $input );
1499 920 $input = $this->validate_advanced_section( $input );
1500 921
1501 922 return $input;
@@ -1504,46 +925,50 @@
1504 925 /**
1505 926 * Validates fields of Basic Section.
1506 927 *
1507 928 * @param ParselySettingOptions $input Options from the settings page.
929 + *
1508 930 * @return ParselySettingOptions Validated inputs.
1509 931 */
1510 - private function validate_basic_section( $input ): array {
1511 - $are_credentials_managed = $this->parsely->are_credentials_managed;
1512 - $options = $this->parsely->get_options();
932 + private function validate_basic_section( $input ) {
933 + $options = $this->parsely->get_options();
1513 934
1514 - if ( $are_credentials_managed ) {
1515 - $input['apikey'] = '';
1516 - $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 + );
1517 941 } else {
1518 - $site_id = $this->sanitize_site_id( $input['apikey'] );
1519 - $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 + }
1520 953
1521 - $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() );
1522 955
1523 - // When running e2e tests, we need to update the API keys without validating them, as they will be invalid.
1524 - // phpcs:ignore WordPress.Security.NonceVerification.Missing
1525 - if ( isset( $_POST['e2e_parsely_skip_api_validate'] ) && 'y' === $_POST['e2e_parsely_skip_api_validate'] ) {
1526 - $valid_credentials = true;
1527 - }
1528 -
1529 - if (
1530 - ( is_wp_error( $valid_credentials ) && Validator::INVALID_API_CREDENTIALS === $valid_credentials->get_error_code() ) ||
1531 - ( is_bool( $valid_credentials ) && ! $valid_credentials )
1532 - ) {
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 ) {
1533 959 add_settings_error(
1534 960 Parsely::OPTIONS_KEY,
1535 - 'api_secret',
1536 - __( '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' )
1537 963 );
1538 - $input['apikey'] = $options['apikey'];
1539 - $input['api_secret'] = $options['api_secret'];
1540 - }
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' );
1541 968
1542 - // Since the API secret is obfuscated, we need to make sure that the value
1543 - // is not changed when the credentials are valid.
1544 - if ( true === $valid_credentials && $input['api_secret'] !== $api_secret ) {
1545 - $input['api_secret'] = $api_secret;
969 + wp_schedule_event( time() + 100, 'everytenminutes', 'parsely_bulk_metas_update' );
970 + $input['parsely_wipe_metadata_cache'] = false;
1546 971 }
1547 972 }
1548 973
1549 974 if ( ! isset( $input['meta_type'] ) ) {
@@ -1599,210 +1024,12 @@
1599 1024 return $input;
1600 1025 }
1601 1026
1602 1027 /**
1603 - * Validates the fields of the Content Intelligence section.
1028 + * Validates fields of Recrawl Section.
1604 1029 *
1605 - * @since 3.16.0
1606 - *
1607 - * @param ParselySettingOptions $input The settings page options.
1608 - * @return ParselySettingOptions The validated input.
1609 - */
1610 - private function validate_content_helper_section( $input ) {
1611 - /**
1612 - * Sanitizes the Content Intelligence data.
1613 - *
1614 - * @since 3.16.0
1615 - */
1616 - $sanitize = function ( $input ) use ( &$sanitize ) {
1617 - foreach ( $input as $key => $value ) {
1618 - if ( is_array( $value ) ) {
1619 - if ( 'allowed_user_roles' === $key && count( $input[ $key ] ) > 0 ) {
1620 - $passed_roles = array_keys( $input[ $key ] );
1621 - $valid_roles = array_keys(
1622 - Permissions::get_user_roles_with_edit_posts_cap()
1623 - );
1624 - $sanitized_roles = array();
1625 -
1626 - // Sanitize passed user roles and remove invalid ones.
1627 - foreach ( $passed_roles as $user_role ) {
1628 - if ( ! is_string( $user_role ) ) {
1629 - continue;
1630 - }
1631 -
1632 - $user_role = sanitize_text_field( $user_role );
1633 - if ( in_array( $user_role, $valid_roles, true ) ) {
1634 - $sanitized_roles[] = $user_role;
1635 - }
1636 - }
1637 -
1638 - $input[ $key ] = $sanitized_roles;
1639 - } else {
1640 - // Recurse when we have an array that's not user roles.
1641 - $input[ $key ] = $sanitize( $value );
1642 - }
1643 - } else {
1644 - // Enforce a boolean value.
1645 - $input[ $key ] = 'true' === $value || true === $value;
1646 - }
1647 - }
1648 -
1649 - return $input;
1650 - };
1651 -
1652 - // Add any missing data due to unchecked checkboxes.
1653 - if ( ! isset( $input['content_helper']['ai_features_enabled'] ) ) {
1654 - $input['content_helper']['ai_features_enabled'] = false;
1655 - }
1656 - foreach ( $this->configurable_pch_features as $feature_id ) {
1657 - if ( ! isset( $input['content_helper'][ $feature_id ] ) ) {
1658 - $input['content_helper'][ $feature_id ] = array(
1659 - 'enabled' => false,
1660 - 'allowed_user_roles' => array(),
1661 - );
1662 - } else {
1663 - if ( ! isset( $input['content_helper'][ $feature_id ]['enabled'] ) ) {
1664 - // @phpstan-ignore-next-line
1665 - $input['content_helper'][ $feature_id ]['enabled'] = false;
1666 - }
1667 - if ( ! isset( $input['content_helper'][ $feature_id ]['allowed_user_roles'] ) ) {
1668 - // @phpstan-ignore-next-line
1669 - $input['content_helper'][ $feature_id ]['allowed_user_roles'] = array();
1670 - }
1671 - }
1672 - }
1673 -
1674 - // Produce the final array.
1675 - $options = $this->parsely->get_options()['content_helper'];
1676 -
1677 - // Validate the generation defaults separately, as the sanitizer above
1678 - // coerces every scalar into a boolean. Values that were not submitted
1679 - // fall back to the stored ones, and anything invalid falls back to the
1680 - // shipped default.
1681 - $validated_defaults = array();
1682 - foreach ( self::FEATURES_WITH_DEFAULTS as $feature_id ) {
1683 - /** @var array<string, mixed> $submitted */
1684 - $submitted = $input['content_helper'][ $feature_id ] ?? array();
1685 - /** @var array<string, mixed> $stored */
1686 - $stored = $options[ $feature_id ];
1687 -
1688 - $defaults = array(
1689 - 'default_tone' => Suggestion_Defaults::get_default_tone(
1690 - array( 'default_tone' => $submitted['default_tone'] ?? $stored['default_tone'] ?? null )
1691 - ),
1692 - 'default_persona' => Suggestion_Defaults::get_default_persona(
1693 - array( 'default_persona' => $submitted['default_persona'] ?? $stored['default_persona'] ?? null )
1694 - ),
1695 - );
1696 -
1697 - // Only Excerpt Suggestions has a desired length.
1698 - if ( Excerpt_Suggestions::get_feature_name() === $feature_id ) {
1699 - $length = $submitted['default_length'] ?? $stored['default_length'] ?? null;
1700 -
1701 - // Anything that is not an integer is invalid, rather than
1702 - // something to coerce into one.
1703 - $length = is_scalar( $length ) ? filter_var( $length, FILTER_VALIDATE_INT ) : false;
1704 -
1705 - $defaults['default_length'] = Suggestion_Defaults::get_default_length(
1706 - array( 'default_length' => false === $length ? null : $length )
1707 - );
1708 - }
1709 -
1710 - $validated_defaults[ $feature_id ] = $defaults;
1711 - }
1712 -
1713 - $merged = array_merge( $options, $input['content_helper'] );
1714 -
1715 - $input['content_helper'] = $sanitize( $merged );
1716 -
1717 - foreach ( $validated_defaults as $feature_id => $defaults ) {
1718 - $input['content_helper'][ $feature_id ] = array_merge(
1719 - $input['content_helper'][ $feature_id ],
1720 - $defaults
1721 - );
1722 - }
1723 -
1724 - return $input;
1725 - }
1726 -
1727 - /**
1728 - * Validates fields of Headline Testing Section.
1729 - *
1730 - * @since 3.21.0
1731 - *
1732 1030 * @param ParselySettingOptions $input Options from the settings page.
1733 - * @return ParselySettingOptions Validated inputs.
1734 - */
1735 - private function validate_headline_testing_section( $input ) {
1736 - /**
1737 - * Sanitizes the Headline Testing data.
1738 - *
1739 - * @since 3.21.0
1740 - */
1741 - $sanitize = function ( $input ) use ( &$sanitize ) {
1742 - foreach ( $input as $key => $value ) {
1743 - if ( is_array( $value ) ) {
1744 - // Recurse for nested arrays.
1745 - $input[ $key ] = $sanitize( $value );
1746 - } else {
1747 - $input[ $key ] = $this->sanitize_headline_testing_field( $key, $value );
1748 - }
1749 - }
1750 -
1751 - return $input;
1752 - };
1753 -
1754 - // Initialize headline_testing array if it doesn't exist.
1755 - if ( ! isset( $input['headline_testing'] ) ) {
1756 - $input['headline_testing'] = array();
1757 - }
1758 -
1759 - // Ensure all required keys exist with defaults.
1760 - $input['headline_testing'] = array_merge(
1761 - $this->parsely->get_default_options()['headline_testing'],
1762 - $input['headline_testing']
1763 - );
1764 -
1765 - // Produce the final array.
1766 - $options = $this->parsely->get_options()['headline_testing'];
1767 - $merged = array_merge( $options, $input['headline_testing'] );
1768 -
1769 - $input['headline_testing'] = $sanitize( $merged );
1770 -
1771 - return $input;
1772 - }
1773 -
1774 - /**
1775 - * Sanitizes headline testing field values.
1776 1031 *
1777 - * @since 3.21.0
1778 - *
1779 - * @param string $key The field key.
1780 - * @param mixed $value The field value.
1781 - * @return mixed The sanitized value.
1782 - */
1783 - private function sanitize_headline_testing_field( string $key, $value ) {
1784 - switch ( $key ) {
1785 - case 'enabled':
1786 - case 'enable_flicker_control':
1787 - case 'enable_live_updates':
1788 - case 'allow_after_content_load':
1789 - return 'true' === $value || true === $value;
1790 - case 'installation_method':
1791 - $valid_methods = array( 'one_line', 'advanced' );
1792 - return in_array( $value, $valid_methods, true ) ? $value : 'one_line';
1793 - case 'live_update_timeout':
1794 - $timeout = intval( is_scalar( $value ) ? (string) $value : '0' );
1795 - return ( $timeout >= 1000 && $timeout <= 60000 ) ? $timeout : 30000;
1796 - default:
1797 - return sanitize_text_field( is_scalar( $value ) ? (string) $value : '' );
1798 - }
1799 - }
1800 -
1801 - /**
1802 - * Validates fields of Recrawl Section.
1803 - *
1804 - * @param ParselySettingOptions $input Options from the settings page.
1805 1032 * @return ParselySettingOptions Validated inputs.
1806 1033 */
1807 1034 private function validate_recrawl_section( $input ) {
1808 1035 $options = $this->parsely->get_options();
@@ -1815,16 +1042,8 @@
1815 1042 } else {
1816 1043 $input['content_id_prefix'] = sanitize_text_field( $input['content_id_prefix'] );
1817 1044 }
1818 1045
1819 - // Full metadata in non-posts.
1820 - if ( ! isset( $input['full_metadata_in_non_posts'] ) ) {
1821 - $input['full_metadata_in_non_posts'] = true;
1822 - } else {
1823 - // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
1824 - $input['full_metadata_in_non_posts'] = 'true' == $input['full_metadata_in_non_posts'];
1825 - }
1826 -
1827 1046 // Allow for Top-level categories setting to be conditionally included on the page.
1828 1047 // If it's not shown, then set the value as what was previously saved.
1829 1048 if ( ! isset( $input['use_top_level_cats'] ) ) {
1830 1049 $input['use_top_level_cats'] = 'true';
@@ -1916,30 +1135,14 @@
1916 1135 /**
1917 1136 * Validates fields of Advanced Section.
1918 1137 *
1919 1138 * @param ParselySettingOptions $input Options from the settings page.
1139 + *
1920 1140 * @return ParselySettingOptions Validated inputs.
1921 1141 */
1922 1142 private function validate_advanced_section( $input ) {
1923 - $are_credentials_managed = $this->parsely->are_credentials_managed;
1924 - $options = $this->parsely->get_options();
1143 + $options = $this->parsely->get_options();
1925 1144
1926 - if ( $are_credentials_managed ) {
1927 - $input['metadata_secret'] = '';
1928 - } else {
1929 - $input['metadata_secret'] = $this->get_unobfuscated_value( $input['metadata_secret'], $this->parsely->get_options()['metadata_secret'] );
1930 - $metadata_secret_length = strlen( $input['metadata_secret'] );
1931 - if ( $metadata_secret_length > 0 &&
1932 - false === Validator::validate_metadata_secret( $input['metadata_secret'] ) ) {
1933 - add_settings_error(
1934 - Parsely::OPTIONS_KEY,
1935 - 'metadata_secret',
1936 - __( 'The Metadata Secret was not saved because it is incorrect. Please contact Parse.ly support!', 'wp-parsely' )
1937 - );
1938 - $input['metadata_secret'] = $options['metadata_secret'];
1939 - }
1940 - }
1941 -
1942 1145 if ( ! isset( $input['disable_autotrack'] ) ) {
1943 1146 $input['disable_autotrack'] = $options['disable_autotrack'];
1944 1147 } elseif ( 'true' !== $input['disable_autotrack'] && 'false' !== $input['disable_autotrack'] ) {
1945 1148 add_settings_error(
@@ -1954,8 +1157,30 @@
1954 1157 return $input;
1955 1158 }
1956 1159
1957 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 + /**
1958 1183 * Sanitizes the passed Site ID.
1959 1184 *
1960 1185 * @since 3.3.0
1961 1186 *
@@ -2039,18 +1264,17 @@
2039 1264
2040 1265 /**
2041 1266 * Sanitizes all elements in an option array.
2042 1267 *
2043 - * @param array<int, string> $options Array of options to be sanitized.
1268 + * @param array<int, string> $array Array of options to be sanitized.
2044 1269 * @return array<int, string>
2045 1270 */
2046 - private static function sanitize_option_array( array $options ): array {
2047 - $sanitized_options = $options;
2048 - foreach ( $options as $key => $val ) {
2049 - $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 );
2050 1275 }
2051 -
2052 - return $sanitized_options;
1276 + return $new_array;
2053 1277 }
2054 1278
2055 1279 /**
2056 1280 * Gets obfuscated value.
@@ -2055,8 +1279,9 @@
2055 1279 /**
2056 1280 * Gets obfuscated value.
2057 1281 *
2058 1282 * @param string $current_value Current value of the field.
1283 + *
2059 1284 * @return string
2060 1285 */
2061 1286 private function get_obfuscated_value( $current_value ): string {
2062 1287 return str_repeat( '*', strlen( $current_value ) );
@@ -2067,8 +1292,9 @@
2067 1292 *
2068 1293 * @param string $current_value Current value of the field.
2069 1294 * @param string $previous_value Previous value of the field. If current
2070 1295 * value is obfuscated then we will use this.
1296 + *
2071 1297 * @return string
2072 1298 */
2073 1299 private function get_unobfuscated_value( $current_value, $previous_value ): string {
2074 1300 if ( $current_value === $this->get_obfuscated_value( $current_value ) ) {
@@ -2075,63 +1301,6 @@
2075 1301 return '' === $current_value ? $current_value : $previous_value;
2076 1302 }
2077 1303
2078 1304 return $current_value;
2079 - }
2080 -
2081 - /**
2082 - * Returns the field label's title. If the option is managed, a badge is
2083 - * also included.
2084 - *
2085 - * @since 3.9.0
2086 - *
2087 - * @param string $title The field's title.
2088 - * @param string $option_id The option's ID.
2089 - * @return string The resulting content.
2090 - */
2091 - public function set_field_label_contents( string $title, string $option_id ): string {
2092 - $is_managed = key_exists( $option_id, $this->parsely->managed_options );
2093 -
2094 - if ( $is_managed ) {
2095 - $text = $this->managed_options_badge['text'] ?? '';
2096 - if ( ! is_string( $text ) || '' === $text ) {
2097 - return $title;
2098 - }
2099 -
2100 - $url = filter_var( $this->managed_options_badge['url'] ?? '', FILTER_VALIDATE_URL );
2101 -
2102 - if ( false === $url ) {
2103 - $badge = '<span class="managed-option-badge">' . esc_html( $text ) . '</span>';
2104 - } else {
2105 - $badge = '<a class="managed-option-badge" href="' . esc_url( $url ) .
2106 - '" target="_blank" rel="noopener">' . esc_html( $text ) . '</a>';
2107 - }
2108 -
2109 - return "{$title}&nbsp;&nbsp;{$badge}";
2110 - }
2111 -
2112 - return $title;
2113 - }
2114 -
2115 - /**
2116 - * Returns the taxonomies allowed to be used as custom_taxonomy_section
2117 - * option values.
2118 - *
2119 - * @since 3.9.0
2120 - *
2121 - * @return array<string> Array of taxonomies.
2122 - */
2123 - public static function get_section_taxonomies(): array {
2124 - return array_diff(
2125 - get_taxonomies(),
2126 - array(
2127 - 'post_tag',
2128 - 'nav_menu',
2129 - 'author',
2130 - 'link_category',
2131 - 'post_format',
2132 - 'wp_theme',
2133 - 'wp_template_part_area',
2134 - )
2135 - );
2136 1305 }
2137 1306 }