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 +544 -422 3.2.03.8.4 View file →
@@ -1,10 +1,10 @@
1 1 <?php
2 2 /**
3 - * Parsely settings page class
3 + * UI: Settings page class
4 4 *
5 5 * @package Parsely
6 - * @since 3.0.0
6 + * @since 3.0.0
7 7 */
8 8
9 9 declare(strict_types=1);
10 10
@@ -11,15 +11,59 @@
11 11 namespace Parsely\UI;
12 12
13 13 use Parsely\Parsely;
14 14
15 -use WP_Screen;
15 +use function Parsely\Utils\get_asset_info;
16 +
16 17 use const Parsely\PARSELY_FILE;
17 18
18 19 /**
19 - * Render the wp-admin Parse.ly plugin settings page
20 + * Renders the wp-admin Parse.ly plugin settings page.
20 21 *
21 22 * @since 3.0.0
23 + *
24 + * @phpstan-import-type Parsely_Options from Parsely
25 + *
26 + * @phpstan-type Setting_Arguments array{
27 + * option_key: string,
28 + * label_for: string,
29 + * title?: string,
30 + * help_text?: string,
31 + * yes_text?: string,
32 + * filter?: string,
33 + * optional_args?: Setting_Optional_Args,
34 + * select_options?: array<string, string>,
35 + * radio_options?: array<string, string>,
36 + * }
37 + *
38 + * @phpstan-type Setting_Optional_Args array{
39 + * type?: string,
40 + * placeholder?: string,
41 + * required?: string,
42 + * is_obfuscated_value: bool,
43 + * }
44 + *
45 + * @phpstan-type ParselySettingOptions array{
46 + * apikey: string,
47 + * api_secret: string,
48 + * metadata_secret: string,
49 + * meta_type?: string,
50 + * logo: string,
51 + * track_authenticated_users: bool|string,
52 + * disable_javascript: bool|string,
53 + * disable_amp?: bool,
54 + * track_post_types_as?: array<string, string>,
55 + * track_post_types: string[],
56 + * track_page_types: string[],
57 + * content_id_prefix?: string,
58 + * use_top_level_cats?:bool|string,
59 + * custom_taxonomy_section?: string,
60 + * cats_as_tags?: bool|string,
61 + * lowercase_tags?: bool,
62 + * force_https_canonicals?: bool,
63 + * disable_autotrack?: bool|string,
64 + * parsely_wipe_metadata_cache: bool,
65 + * }
22 66 */
23 67 final class Settings_Page {
24 68 /**
25 69 * Instance of Parsely class.
@@ -37,19 +81,8 @@
37 81 */
38 82 private $hook_suffix;
39 83
40 84 /**
41 - * Screen options name.
42 - *
43 - * Name must end in `_page` so that set-screen-option hook is triggered for WP < 5.4.2.
44 - *
45 - * @since 3.2.0
46 - *
47 - * @var string
48 - */
49 - private $screen_options_name = 'wp_parsely_page';
50 -
51 - /**
52 85 * Constructor.
53 86 *
54 87 * @param Parsely $parsely Instance of Parsely class.
55 88 */
@@ -57,36 +90,29 @@
57 90 $this->parsely = $parsely;
58 91 }
59 92
60 93 /**
61 - * Register settings page.
94 + * Registers settings page.
62 95 *
63 96 * @since 3.0.0
64 - *
65 - * @return void
66 97 */
67 98 public function run(): void {
68 99 add_action( 'admin_menu', array( $this, 'add_settings_sub_menu' ) );
69 100 add_action( 'admin_init', array( $this, 'initialize_settings' ) );
70 101 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_settings_assets' ) );
71 -
72 - // Handle saving of screen options.
73 - add_filter( 'set-screen-option', array( $this, 'set_screen_option' ), 11, 3 );
74 - // Render screen options.
75 - add_filter( 'screen_settings', array( $this, 'screen_settings' ), 10, 2 );
76 102 }
77 103
78 104 /**
79 - * Enqueue all needed scripts and styles for Parse.ly plugin settings page.
105 + * Enqueues all needed scripts and styles for Parse.ly plugin settings page.
80 106 *
81 107 * @param string $hook_suffix The current page being loaded.
82 - * @return void
83 108 */
84 109 public function enqueue_settings_assets( string $hook_suffix ): void {
85 110 if ( 'settings_page_parsely' === $hook_suffix ) {
111 + add_filter( 'media_library_months_with_files', '__return_empty_array' );
86 112 wp_enqueue_media();
87 113
88 - $admin_settings_asset = require plugin_dir_path( PARSELY_FILE ) . 'build/admin-settings.asset.php';
114 + $admin_settings_asset = get_asset_info( 'build/admin-settings.asset.php' );
89 115 $built_assets_url = plugin_dir_url( PARSELY_FILE ) . '/build/';
90 116
91 117 wp_enqueue_script(
92 118 'parsely-admin-settings',
@@ -105,11 +131,9 @@
105 131 }
106 132 }
107 133
108 134 /**
109 - * Parse.ly settings page in WordPress settings menu.
110 - *
111 - * @return void
135 + * Adds the Parse.ly settings page in WordPress settings menu.
112 136 */
113 137 public function add_settings_sub_menu(): void {
114 138 $suffix = add_options_page(
115 139 __( 'Parse.ly Settings', 'wp-parsely' ),
@@ -123,143 +147,34 @@
123 147 $this->hook_suffix = $suffix;
124 148
125 149 // Adds help text when admin page loads.
126 150 add_action( 'load-' . $this->hook_suffix, array( $this, 'add_help_text' ) );
127 - // Adds screen options when admin page loads.
128 - add_action( 'load-' . $this->hook_suffix, array( $this, 'add_screen_options' ) );
129 151 }
130 152 }
131 153
132 154 /**
133 - * Save the screen option setting.
155 + * Adds the help tab to the settings page.
134 156 *
135 - * Nonce is already checked in set_screen_options() - no need to check here.
136 - *
137 - * @since 3.2.0
138 - *
139 - * @param mixed $screen_option The value to save instead of the option value. Default false (to skip saving the current option).
140 - * @param string $option The option name.
141 - * @param mixed $value The option value.
142 - * @return mixed Updated option value.
143 - */
144 - public function set_screen_option( $screen_option, string $option, $value ) {
145 - if ( $this->screen_options_name === $option ) {
146 - // phpcs:ignore WordPress.Security.NonceVerification.Missing
147 - if ( isset( $_POST[ $this->screen_options_name ] ) && is_array( $_POST[ $this->screen_options_name ] ) ) {
148 - // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
149 - $unslashed = wp_unslash( $_POST[ $this->screen_options_name ] );
150 - $data = array_map( 'sanitize_text_field', $unslashed );
151 - }
152 - $value = $data ?? array();
153 - }
154 - return $value;
155 - }
156 -
157 - /**
158 - * Register screen options.
159 - *
160 - * @since 3.2.0
161 - *
162 - * @return void
163 - */
164 - public function add_screen_options(): void {
165 - add_screen_option(
166 - 'wp_parsely_screen_options_requires_recrawl',
167 - array(
168 - 'label' => __( 'Requires Recrawl Settings', 'wp-parsely' ),
169 - 'option' => 'requires-recrawl',
170 - )
171 - );
172 - add_screen_option(
173 - 'wp_parsely_screen_options_advanced',
174 - array(
175 - 'label' => __( 'Advanced Settings', 'wp-parsely' ),
176 - 'option' => 'advanced',
177 - )
178 - );
179 - }
180 -
181 - /**
182 - * Render the screen options block.
183 - *
184 - * @since 3.2.0
185 - *
186 - * @param string $screen_settings Screen settings.
187 - * @param WP_Screen $screen WP_Screen object.
188 - *
189 - * @return string The filtered screen settings.
190 - */
191 - public function screen_settings( string $screen_settings, WP_Screen $screen ): string {
192 - if ( $this->hook_suffix !== $screen->base ) {
193 - return $screen_settings;
194 - }
195 -
196 - $current_screen = get_current_screen();
197 - if ( null === $current_screen ) {
198 - return $screen_settings;
199 - }
200 -
201 - $user_meta = get_user_meta( get_current_user_id(), $this->screen_options_name, true );
202 -
203 - ob_start();
204 - ?>
205 - <fieldset>
206 - <legend><?php esc_html_e( 'Show on screen', 'wp-parsely' ); ?></legend>
207 - <input type="hidden" name="wp_screen_options[option]" value="<?php echo esc_attr( $this->screen_options_name ); ?>" />
208 - <input type="hidden" name="wp_screen_options[value]" value="yes" />
209 - <?php
210 - foreach ( $current_screen->get_options() as $option ) {
211 - $checked = isset( $user_meta[ $option['option'] ] );
212 - $name = $this->screen_options_name . '[' . $option['option'] . ']';
213 - ?>
214 - <label><input class="hide-section-tog" name="<?php echo esc_attr( $name ); ?>" type="checkbox" id="<?php echo esc_attr( $option['option'] ); ?>" value="true"<?php checked( $checked ); ?>><?php echo esc_html( $option['label'] ); ?></label>
215 - <?php
216 - }
217 - ?>
218 - </fieldset>
219 - <?php
220 - submit_button( __( 'Apply', 'wp-parsely' ), 'primary', 'screen-options-apply' );
221 -
222 - return ob_get_clean();
223 - }
224 -
225 - /**
226 - * Add help tab to the settings page.
227 - *
228 157 * @since 3.1.0
229 158 */
230 159 public function add_help_text(): void {
231 160 $screen = get_current_screen();
161 + if ( null === $screen ) {
162 + return;
163 + }
164 +
232 165 $screen->add_help_tab(
233 166 array(
234 167 'id' => 'overview',
235 168 'title' => __( 'Overview', 'wp-parsely' ),
236 169 'content' => '<p>' . __( 'The only required setting on this page is the Site ID. All of the other settings are optional.', 'wp-parsely' ) . '</p>' .
237 - '<p>' . __( 'You must click the Save Changes button at the bottom of the screen for new settings to take effect.', 'wp-parsely' ) . '</p>' .
238 - '<p>' . __( 'This plugin does not currently support dynamic tracking (the tracking of multiple pageviews on a single page). Some common use-cases for dynamic tracking are slideshows or articles loaded via AJAX calls in single-page applications — situations in which new content is loaded without a full page refresh. Tracking these events requires manually implementing additional JavaScript above <a href="https://www.parsely.com/help/integration/basic/">the standard Parse.ly include</a> that the plugin injects into your page source. Please consult <a href="https://www.parsely.com/help/integration/dynamic/">the Parse.ly documentation on dynamic tracking</a> for instructions on implementing dynamic tracking, or contact Parse.ly support (<a href="mailto:support@parsely.com">support@parsely.com</a>) for additional assistance.', 'wp-parsely' ) . '</p>',
170 + '<p>' . __( 'You must click the Save Changes button at the bottom of the screen for new settings to take effect.', 'wp-parsely' ) . '</p>',
239 171 )
240 172 );
241 - $screen->add_help_tab(
242 - array(
243 - 'id' => 'requires_recrawl',
244 - 'title' => __( 'Requires Recrawl', 'wp-parsely' ),
245 - 'content' => '<p>' . __(
246 - 'Important: changing any of the values in the Requires Recrawl section on a site currently tracked with Parse.ly will require reprocessing of your Parse.ly data.
247 -Once you have changed a value and saved, please contact support@parsely.com to request a recrawl.',
248 - 'wp-parsely'
249 - ) . '</p>' .
250 - '<p>' . __(
251 - 'If you can\'t see these settings, you will need to enable the Requires Recrawl Settings in the Screen Options.',
252 - 'wp-parsely'
253 - ) . '</p>',
254 - )
255 - );
256 173 }
257 174
258 175 /**
259 - * Parse.ly settings screen ( options-general.php?page=[MENU_SLUG] ).
260 - *
261 - * @return void
176 + * Displays the Parse.ly settings screen (options-general.php?page=[SLUG]).
262 177 */
263 178 public function display_settings(): void {
264 179 if ( ! current_user_can( Parsely::CAPABILITY ) ) {
265 180 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-parsely' ) );
@@ -264,15 +179,13 @@
264 179 if ( ! current_user_can( Parsely::CAPABILITY ) ) {
265 180 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-parsely' ) );
266 181 }
267 182
268 - include plugin_dir_path( PARSELY_FILE ) . 'views/parsely-settings.php';
183 + include_once plugin_dir_path( PARSELY_FILE ) . 'views/parsely-settings.php';
269 184 }
270 185
271 186 /**
272 - * Initialize the settings for Parsely.
273 - *
274 - * @return void
187 + * Initializes the settings for Parse.ly.
275 188 */
276 189 public function initialize_settings(): void {
277 190 // All our options are actually stored in one single array to reduce DB queries.
278 191 register_setting(
@@ -280,45 +193,37 @@
280 193 Parsely::OPTIONS_KEY,
281 194 array( $this, 'validate_options' )
282 195 );
283 196
284 - $user_meta = array_keys( (array) get_user_meta( get_current_user_id(), $this->screen_options_name, true ) );
285 -
286 197 $this->initialize_basic_section();
287 -
288 - if ( in_array( 'requires-recrawl', $user_meta, true ) ) {
289 - $this->initialize_requires_recrawl_section();
290 - }
291 -
292 - if ( in_array( 'advanced', $user_meta, true ) ) {
293 - $this->initialize_advanced_section();
294 - }
198 + $this->initialize_recrawl_section();
199 + $this->initialize_advanced_section();
295 200 }
296 201
297 202 /**
298 - * Register section and settings for Basic section.
203 + * Registers section and settings for Basic section.
299 204 *
300 205 * @since 3.2.0
301 - *
302 - * @return void
303 206 */
304 207 private function initialize_basic_section(): void {
208 + $section_key = 'basic-section';
209 +
305 210 add_settings_section(
306 - 'basic_settings',
307 - __( 'Basic Settings', 'wp-parsely' ),
211 + $section_key,
212 + __( 'Basic', 'wp-parsely' ),
308 213 '__return_null',
309 214 Parsely::MENU_SLUG
310 215 );
311 216
312 - // Get the API Key.
313 - $h = __( 'Your Site ID is your own site domain (e.g. `mydomain.com`).', 'wp-parsely' );
217 + // Site ID.
314 218 $field_id = 'apikey';
315 219 $field_args = array(
316 220 'option_key' => $field_id,
317 - 'help_text' => $h,
221 + 'help_text' => __( 'Your Site ID is typically your own site domain without <code>http(s)://</code> prefixes or trailing <code>/</code> (e.g. <code>mydomain.com</code>).', 'wp-parsely' ),
318 222 'label_for' => $field_id,
319 223 'optional_args' => array(
320 - 'required' => 'required',
224 + 'required' => 'required',
225 + 'placeholder' => 'mydomain.com',
321 226 ),
322 227
323 228 );
324 229 add_settings_field(
@@ -325,19 +230,22 @@
325 230 $field_id,
326 231 __( 'Parse.ly Site ID <em>(required)</em>', 'wp-parsely' ),
327 232 array( $this, 'print_text_tag' ),
328 233 Parsely::MENU_SLUG,
329 - 'basic_settings',
234 + $section_key,
330 235 $field_args
331 236 );
332 237
333 - /* translators: 1: Opening anchor tag markup, 2: Documentation URL, 3: Opening anchor tag markup continued, 4: Closing anchor tag */
334 - $h = __( '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' );
238 + // API Secret.
335 239 $field_id = 'api_secret';
336 240 $field_args = array(
337 - 'option_key' => $field_id,
338 - 'help_text' => $h,
339 - 'label_for' => $field_id,
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 + ),
340 248 );
341 249 add_settings_field(
342 250 $field_id,
343 251 __( 'Parse.ly API Secret', 'wp-parsely' ),
@@ -342,18 +250,22 @@
342 250 $field_id,
343 251 __( 'Parse.ly API Secret', 'wp-parsely' ),
344 252 array( $this, 'print_text_tag' ),
345 253 Parsely::MENU_SLUG,
346 - 'basic_settings',
254 + $section_key,
347 255 $field_args
348 256 );
349 257
350 - $h = __( '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' );
258 + // Metadata Secret.
351 259 $field_id = 'metadata_secret';
352 260 $field_args = array(
353 - 'option_key' => $field_id,
354 - 'help_text' => $h,
355 - 'label_for' => $field_id,
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 + ),
356 268 );
357 269 add_settings_field(
358 270 $field_id,
359 271 __( 'Parse.ly Metadata Secret', 'wp-parsely' ),
@@ -358,12 +270,34 @@
358 270 $field_id,
359 271 __( 'Parse.ly Metadata Secret', 'wp-parsely' ),
360 272 array( $this, 'print_text_tag' ),
361 273 Parsely::MENU_SLUG,
362 - 'basic_settings',
274 + $section_key,
363 275 $field_args
364 276 );
365 277
278 + // Metadata Format.
279 + $field_id = 'meta_type';
280 + $field_args = array(
281 + 'title' => __( 'Metadata Format', 'wp-parsely' ),
282 + 'option_key' => $field_id,
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' ),
284 + 'radio_options' => array(
285 + 'json_ld' => 'json_ld',
286 + 'repeated_metas' => 'repeated_metas',
287 + ),
288 + 'label_for' => Parsely::OPTIONS_KEY . "[$field_id]",
289 + 'filter' => 'wp_parsely_metadata',
290 + );
291 + add_settings_field(
292 + $field_id,
293 + __( 'Metadata Format', 'wp-parsely' ),
294 + array( $this, 'print_radio_tags' ),
295 + Parsely::MENU_SLUG,
296 + $section_key,
297 + $field_args
298 + );
299 +
366 300 // Logo.
367 301 $field_help = __( 'Here you can specify your logo\'s URL by using the "Browse" button or typing the URL manually.', 'wp-parsely' );
368 302 $field_id = 'logo';
369 303 add_settings_field(
@@ -370,9 +304,9 @@
370 304 $field_id,
371 305 __( 'Logo', 'wp-parsely' ),
372 306 array( $this, 'print_media_single_image' ),
373 307 Parsely::MENU_SLUG,
374 - 'basic_settings',
308 + $section_key,
375 309 array(
376 310 'title' => __( 'Logo', 'wp-parsely' ), // Passed for legend element.
377 311 'option_key' => $field_id,
378 312 'label_for' => $field_id,
@@ -380,50 +314,63 @@
380 314 )
381 315 );
382 316
383 317 // Track logged-in users.
384 - $h = __( 'By default, the plugin will track the activity of users that are logged into this site. You can change this setting to only track the activity of anonymous visitors. Note: You will no longer see the Parse.ly tracking code on your site if you browse while logged in.', 'wp-parsely' );
385 318 add_settings_field(
386 319 'track_authenticated_users',
387 320 __( 'Track Logged-in Users', 'wp-parsely' ),
388 - array( $this, 'print_binary_radio_tag' ),
321 + array( $this, 'print_radio_tags' ),
389 322 Parsely::MENU_SLUG,
390 - 'basic_settings',
323 + $section_key,
391 324 array(
392 - 'title' => __( 'Track Logged-in Users', 'wp-parsely' ), // Passed for legend element.
393 - 'option_key' => 'track_authenticated_users',
394 - 'help_text' => $h,
325 + 'title' => __( 'Track Logged-in Users', 'wp-parsely' ), // Passed for legend element.
326 + 'option_key' => 'track_authenticated_users',
327 + 'radio_options' => array(
328 + 'true' => __( 'Yes, track logged-in users.', 'wp-parsely' ),
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' ),
330 + ),
331 + 'help_text' => (
332 + is_multisite() ?
333 + __( ' Note: For WordPress multisite, a user must be logged-in to the current site to be considered logged-in.', 'wp-parsely' ) :
334 + null
335 + ),
395 336 )
396 337 );
397 338
398 339 // Disable JavaScript.
399 - $h = __( 'If you use a separate system for JavaScript tracking (Tealium / Segment / Google Tag Manager / other tag manager solution) you may want to use that instead of having the plugin load the tracker. <span style="color:#d63638">WARNING:</span> disabling this option will also disable the "Personalize Results" section of the recommended widget! We highly recommend leaving this option set to "No".', 'wp-parsely' );
400 340 add_settings_field(
401 341 'disable_javascript',
402 342 __( 'Disable JavaScript', 'wp-parsely' ),
403 - array( $this, 'print_binary_radio_tag' ),
343 + array( $this, 'print_radio_tags' ),
404 344 Parsely::MENU_SLUG,
405 - 'basic_settings',
345 + $section_key,
406 346 array(
407 - 'title' => __( 'Disable JavaScript', 'wp-parsely' ), // Passed for legend element.
408 - 'option_key' => 'disable_javascript',
409 - 'help_text' => $h,
347 + 'title' => __( 'Disable JavaScript', 'wp-parsely' ), // Passed for legend element.
348 + 'option_key' => 'disable_javascript',
349 + 'radio_options' => array(
350 + 'true' => __( 'Yes, disable JavaScript tracking. I want to use a separate system for tracking instead of the Parse.ly plugin.', 'wp-parsely' ),
351 + 'false' => __( 'No, do not disable JavaScript tracking. I want the Parse.ly plugin to load the tracker.', 'wp-parsely' ),
352 + ),
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' ),
354 + 'filter' => 'wp_parsely_load_js_tracker',
410 355 )
411 356 );
412 357
413 358 if ( defined( 'AMP__VERSION' ) ) {
414 359 // Disable AMP tracking.
415 - $h = __( 'If you use a separate system for JavaScript tracking on AMP pages (Tealium / Segment / Google Tag Manager / other tag manager solution) you may want to use that instead of having the plugin load the tracker.', 'wp-parsely' );
416 360 add_settings_field(
417 361 'disable_amp',
418 362 __( 'Disable AMP Tracking', 'wp-parsely' ),
419 - array( $this, 'print_binary_radio_tag' ),
363 + array( $this, 'print_radio_tags' ),
420 364 Parsely::MENU_SLUG,
421 - 'basic_settings',
365 + $section_key,
422 366 array(
423 - 'title' => __( 'Disable AMP Tracking', 'wp-parsely' ), // Passed for legend element.
424 - 'option_key' => 'disable_amp',
425 - 'help_text' => $h,
367 + 'title' => __( 'Disable AMP Tracking', 'wp-parsely' ), // Passed for legend element.
368 + 'option_key' => 'disable_amp',
369 + 'radio_options' => array(
370 + 'true' => __( 'Yes, disable Parse.ly tracking on AMP pages. I use a different system for JavaScript tracking on AMP pages.', 'wp-parsely' ),
371 + 'false' => __( 'No, do not disable Parse.ly tracking on AMP pages.', 'wp-parsely' ),
372 + ),
426 373 )
427 374 );
428 375 }
429 376 }
@@ -428,24 +375,24 @@
428 375 }
429 376 }
430 377
431 378 /**
432 - * Register section and settings for Requires Recrawl section.
379 + * Registers section and settings for Recrawl section.
433 380 *
434 381 * @since 3.2.0
435 - *
436 - * @return void
437 382 */
438 - private function initialize_requires_recrawl_section(): void {
383 + private function initialize_recrawl_section(): void {
384 + $section_key = 'recrawl-section';
385 +
439 386 add_settings_section(
440 - 'requires_recrawl_settings',
441 - __( 'Requires Recrawl Settings', 'wp-parsely' ),
387 + $section_key,
388 + __( 'Recrawl', 'wp-parsely' ),
442 389 function (): void {
443 - echo '<strong>' . wp_kses_post( __( '<span style="color:#d63638">Important:</span> Changing any of these values below on a site currently tracked with Parse.ly will require reprocessing of your Parse.ly data.', 'wp-parsely' ) ) . '</strong><br />';
390 + echo '<br /><strong>' . wp_kses_post( __( '<span style="color:#d63638">Important:</span> Changing any of these values below on a site currently tracked with Parse.ly will require reprocessing of your Parse.ly data.', 'wp-parsely' ) ) . '</strong><br />';
444 391 printf(
445 392 /* translators: Mailto link */
446 - esc_html__( 'Once you have changed a value and and saved, please contact %s to request a recrawl.', 'wp-parsely' ),
447 - wp_kses_post( '<a href="mailto:support@parsely.com?subject=' . rawurlencode( 'Please reprocess ' . $this->parsely->get_api_key() ) . '">support@parsely.com</a>' )
393 + esc_html__( 'Once you have changed a value and saved, please contact %s to request a recrawl.', 'wp-parsely' ),
394 + wp_kses_post( '<a href="mailto:support@parsely.com?subject=' . rawurlencode( 'Please reprocess ' . $this->parsely->get_site_id() ) . '">support@parsely.com</a>' )
448 395 );
449 396 },
450 397 Parsely::MENU_SLUG
451 398 );
@@ -457,41 +404,18 @@
457 404 $field_id,
458 405 __( 'Track Post Types as', 'wp-parsely' ),
459 406 array( $this, 'print_track_post_types_table' ),
460 407 Parsely::MENU_SLUG,
461 - 'requires_recrawl_settings',
408 + $section_key,
462 409 array(
463 410 'title' => __( 'Track Post Types as', 'wp-parsely' ),
464 411 'option_key' => $field_id,
465 412 'help_text' => $field_help,
413 + 'filter' => 'wp_parsely_trackable_statuses',
466 414 )
467 415 );
468 416
469 - // Metadata Format.
470 - /* translators: 1: Opening anchor tag markup, 2: Documentation URL, 3: Opening anchor tag markup continued, 4: Closing anchor tag */
471 - $h = __( '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' );
472 - $field_id = 'meta_type';
473 - $field_args = array(
474 - 'option_key' => $field_id,
475 - 'help_text' => $h,
476 - // filter WordPress taxonomies under the hood that should not appear in dropdown.
477 - 'select_options' => array(
478 - 'json_ld' => 'json_ld',
479 - 'repeated_metas' => 'repeated_metas',
480 - ),
481 - 'label_for' => Parsely::OPTIONS_KEY . "[$field_id]",
482 - );
483 - add_settings_field(
484 - $field_id,
485 - __( 'Metadata Format', 'wp-parsely' ),
486 - array( $this, 'print_select_tag' ),
487 - Parsely::MENU_SLUG,
488 - 'requires_recrawl_settings',
489 - $field_args
490 - );
491 -
492 417 // Content ID Prefix.
493 - $h = __( 'If you use more than one content management system (e.g. WordPress and Drupal), you may end up with duplicate content IDs. Adding a Content ID Prefix will ensure the content IDs from WordPress will not conflict with other content management systems. We recommend using "WP-" for your prefix.', 'wp-parsely' );
494 418 $field_id = 'content_id_prefix';
495 419 $field_args = array(
496 420 'option_key' => $field_id,
497 421 'optional_args' => array(
@@ -496,9 +420,9 @@
496 420 'option_key' => $field_id,
497 421 'optional_args' => array(
498 422 'placeholder' => 'WP-',
499 423 ),
500 - 'help_text' => $h,
424 + 'help_text' => __( 'If you use more than one content management system (e.g. WordPress and Drupal), you may end up with duplicate content IDs. Adding a Content ID Prefix will ensure the content IDs from WordPress will not conflict with other content management systems. We recommend using "WP-" for your prefix.', 'wp-parsely' ),
501 425 'label_for' => $field_id,
502 426 );
503 427 add_settings_field(
504 428 $field_id,
@@ -504,33 +428,35 @@
504 428 $field_id,
505 429 __( 'Content ID Prefix', 'wp-parsely' ),
506 430 array( $this, 'print_text_tag' ),
507 431 Parsely::MENU_SLUG,
508 - 'requires_recrawl_settings',
432 + $section_key,
509 433 $field_args
510 434 );
511 435
512 436 // Use top-level categories.
513 - $h = __( 'The plugin will use the first category assigned to a post. With this option selected, if you post a story to News > National > Florida, the plugin will use the "News" for the section name in your dashboard instead of "Florida".', 'wp-parsely' );
514 437 add_settings_field(
515 438 'use_top_level_cats',
516 439 __( 'Use Top-Level Categories for Section', 'wp-parsely' ),
517 - array( $this, 'print_binary_radio_tag' ),
440 + array( $this, 'print_radio_tags' ),
518 441 Parsely::MENU_SLUG,
519 - 'requires_recrawl_settings',
442 + $section_key,
520 443 array(
521 - 'title' => __( 'Use Top-Level Categories for Section', 'wp-parsely' ), // Passed for legend element.
522 - 'option_key' => 'use_top_level_cats',
523 - 'help_text' => $h,
444 + 'title' => __( 'Use Top-Level Categories for Section', 'wp-parsely' ), // Passed for legend element.
445 + 'option_key' => 'use_top_level_cats',
446 + 'radio_options' => array(
447 + 'true' => __( 'Yes, use the first category assigned to a post as the section name.', 'wp-parsely' ),
448 + 'false' => __( 'No, do not use the first category assigned to a post as the section name.', 'wp-parsely' ),
449 + ),
450 + 'help_text' => __( 'If you choose Yes, and post a story to News > National > Florida, the plugin will use "News" for the section name in your dashboard instead of "Florida".', 'wp-parsely' ),
524 451 )
525 452 );
526 453
527 454 // Allow use of custom taxonomy to populate articleSection in parselyPage; defaults to category.
528 - $h = __( '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' );
529 455 $field_id = 'custom_taxonomy_section';
530 456 $field_args = array(
531 457 'option_key' => $field_id,
532 - 'help_text' => $h,
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' ),
533 459 // filter WordPress taxonomies under the hood that should not appear in dropdown.
534 460 'select_options' => array_diff(
535 461 get_taxonomies(),
536 462 array(
@@ -547,122 +473,220 @@
547 473 $field_id,
548 474 __( 'Use Custom Taxonomy for Section', 'wp-parsely' ),
549 475 array( $this, 'print_select_tag' ),
550 476 Parsely::MENU_SLUG,
551 - 'requires_recrawl_settings',
477 + $section_key,
552 478 $field_args
553 479 );
554 480
555 481 // Use categories and custom taxonomies as tags.
556 - $h = __( 'You can use this option to add all assigned categories and taxonomies to your tags. For example, if you had a post assigned to the categories: "Business/Tech", "Business/Social", your tags would include "Business/Tech" and "Business/Social" in addition to your other tags.', 'wp-parsely' );
557 482 add_settings_field(
558 483 'cats_as_tags',
559 484 __( 'Add Categories to Tags', 'wp-parsely' ),
560 - array( $this, 'print_binary_radio_tag' ),
485 + array( $this, 'print_radio_tags' ),
561 486 Parsely::MENU_SLUG,
562 - 'requires_recrawl_settings',
487 + $section_key,
563 488 array(
564 - 'title' => __( 'Add Categories to Tags', 'wp-parsely' ), // Passed for legend element.
565 - 'option_key' => 'cats_as_tags',
566 - 'help_text' => $h,
489 + 'title' => __( 'Add Categories to Tags', 'wp-parsely' ), // Passed for legend element.
490 + 'option_key' => 'cats_as_tags',
491 + 'radio_options' => array(
492 + 'true' => __( 'Yes, add all assigned categories and taxonomies to my tags.', 'wp-parsely' ),
493 + 'false' => __( 'No, do not add all assigned categories and taxonomies to my tags.', 'wp-parsely' ),
494 + ),
495 + 'help_text' => __( 'If you choose Yes, then a post that has been assigned the categories "Business/Tech" and "Business/Social" will automatically include "Business/Tech" and "Business/Social" as tags, too.', 'wp-parsely' ),
567 496 )
568 497 );
569 498
570 499 // Lowercase all tags.
571 - $h = __( 'By default, the plugin will use lowercase versions of your tags to correct for potential misspellings. You can change this setting to ensure that tag names are used verbatim.', 'wp-parsely' );
572 500 add_settings_field(
573 501 'lowercase_tags',
574 502 __( 'Lowercase All Tags', 'wp-parsely' ),
575 - array( $this, 'print_binary_radio_tag' ),
503 + array( $this, 'print_radio_tags' ),
576 504 Parsely::MENU_SLUG,
577 - 'requires_recrawl_settings',
505 + $section_key,
578 506 array(
579 - 'title' => __( 'Lowercase All Tags', 'wp-parsely' ), // Passed for legend element.
580 - 'option_key' => 'lowercase_tags',
581 - 'help_text' => $h,
507 + 'title' => __( 'Lowercase All Tags', 'wp-parsely' ), // Passed for legend element.
508 + 'option_key' => 'lowercase_tags',
509 + 'radio_options' => array(
510 + 'true' => __( 'Yes, use lowercase versions of my tags to correct for potential misspellings.', 'wp-parsely' ),
511 + 'false' => __( 'No, do not use lowercase versions of my tags to correct for potential misspellings.', 'wp-parsely' ),
512 + ),
582 513 )
583 514 );
584 515
585 - $h = __( 'The plugin uses <code>http</code> canonical URLs by default. If this needs to be forced to use <code>https</code>, set this option to true. Note: the default is fine for almost all publishers, it\'s unlikely you\'ll have to change this unless directed to do so by a Parse.ly support rep.', 'wp-parsely' );
586 516 add_settings_field(
587 517 'force_https_canonicals',
588 518 __( 'Force HTTPS Canonicals', 'wp-parsely' ),
589 - array( $this, 'print_binary_radio_tag' ),
519 + array( $this, 'print_radio_tags' ),
590 520 Parsely::MENU_SLUG,
591 - 'requires_recrawl_settings',
521 + $section_key,
592 522 array(
593 - 'title' => __( 'Force HTTPS Canonicals', 'wp-parsely' ), // Passed for legend element.
594 - 'option_key' => 'force_https_canonicals',
595 - 'help_text' => $h,
523 + 'title' => __( 'Force HTTPS Canonicals', 'wp-parsely' ), // Passed for legend element.
524 + 'option_key' => 'force_https_canonicals',
525 + 'radio_options' => array(
526 + 'true' => __( 'Yes, force <code>https</code> canonical URLs by default.', 'wp-parsely' ),
527 + 'false' => __( 'No, I want to use <code>http</code>.', 'wp-parsely' ),
528 + ),
529 + 'help_text' => __( 'Note: the plugin uses <code>http</code> by default, and this is fine for most publishers. It is unlikely you will have to change this unless directed to do so by a Parse.ly support representative.', 'wp-parsely' ),
596 530 )
597 531 );
598 532 }
599 533
600 534 /**
601 - * Register section and settings for Advanced section.
535 + * Registers section and settings for Advanced section.
602 536 *
603 537 * @since 3.2.0
604 - *
605 - * @return void
606 538 */
607 539 private function initialize_advanced_section(): void {
608 - // These are Advanced Settings.
540 + $section_key = 'advanced-section';
541 +
609 542 add_settings_section(
610 - 'advanced_settings',
611 - __( 'Advanced Settings', 'wp-parsely' ),
543 + $section_key,
544 + __( 'Advanced', 'wp-parsely' ),
612 545 '__return_null',
613 546 Parsely::MENU_SLUG
614 547 );
615 548
549 + // Disable autotrack.
550 + add_settings_field(
551 + 'disable_autotrack',
552 + __( 'Disable Autotracking', 'wp-parsely' ),
553 + array( $this, 'print_radio_tags' ),
554 + Parsely::MENU_SLUG,
555 + $section_key,
556 + array(
557 + 'title' => __( 'Disable Autotracking', 'wp-parsely' ), // Passed for legend element.
558 + 'option_key' => 'disable_autotrack',
559 + 'radio_options' => array(
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' ),
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' ),
562 + ),
563 + )
564 + );
565 +
616 566 // Clear metadata.
617 - $h = __( 'Check this radio button and hit "Save Changes" to clear all metadata information for Parse.ly posts and re-send all metadata to Parse.ly.<br /><span style="color:#d63638">WARNING:</span> Do not do this unless explicitly instructed by Parse.ly Staff!', 'wp-parsely' );
618 567 add_settings_field(
619 568 'parsely_wipe_metadata_cache',
620 569 __( 'Wipe Parse.ly Metadata Info', 'wp-parsely' ),
621 570 array( $this, 'print_checkbox_tag' ),
622 571 Parsely::MENU_SLUG,
623 - 'advanced_settings',
572 + $section_key,
624 573 array(
625 574 'option_key' => 'parsely_wipe_metadata_cache',
626 - 'help_text' => $h,
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' ),
627 577 )
628 578 );
629 579 }
630 580
631 581 /**
632 - * Print out the description text, if there is any.
582 + * Shows setting tabs.
633 583 *
584 + * @since 3.8.0
585 + */
586 + public function show_setting_tabs(): void {
587 + global $wp_settings_sections;
588 + ?>
589 +
590 + <nav class="nav-tab-wrapper">
591 + <?php foreach ( $wp_settings_sections[ Parsely::MENU_SLUG ] as $section ) { ?>
592 + <a
593 + class="nav-tab <?php echo esc_attr( $section['id'] . '-tab' ); ?>"
594 + href=<?php echo esc_url_raw( '?page=' . Parsely::MENU_SLUG . '#' . $section['id'] ); ?>
595 + >
596 + <?php echo esc_html( $section['title'] ); ?>
597 + </a>
598 + <?php } ?>
599 + </nav>
600 +
601 + <?php
602 + }
603 +
604 + /**
605 + * Shows content of setting tabs.
606 + *
607 + * @since 3.8.0
608 + */
609 + public function show_setting_tabs_content(): void {
610 + global $wp_settings_sections;
611 +
612 + foreach ( $wp_settings_sections[ Parsely::MENU_SLUG ] as $section ) {
613 + ?>
614 +
615 + <div class="tab-content <?php echo esc_attr( $section['id'] ); ?>">
616 + <?php
617 + if ( $section['callback'] ) {
618 + call_user_func( $section['callback'], $section );
619 + }
620 + ?>
621 +
622 + <table class="form-table" role="presentation">
623 + <?php do_settings_fields( Parsely::MENU_SLUG, $section['id'] ); ?>
624 + </table>
625 + </div>
626 +
627 + <?php
628 + }
629 + }
630 +
631 + /**
632 + * Prints out a warning if the filter for the setting is defined, if any.
633 + *
634 + * @since 3.4.0
635 + *
636 + * @param Setting_Arguments $args The arguments for the form field. May contain 'filter'.
637 + */
638 + private function print_filter_text( $args ): void {
639 + if ( isset( $args['filter'] ) && has_filter( $args['filter'] ) ) {
640 + echo '<p>';
641 + echo '<b><code>' . esc_html( $args['filter'] ) . '</code>' . esc_html__( 'filter hook is in use!', 'wp-parsely' ) . '</b> ';
642 + echo esc_html__( 'A callback is attached to the filter hook that might interfere and override this setting.', 'wp-parsely' );
643 + echo '</p>';
644 + }
645 + }
646 +
647 + /**
648 + * Prints out the description text, if there is any.
649 + *
634 650 * @since 3.1.0
635 651 *
636 - * @param array $args The arguments for the form field. May contain 'help_text'.
652 + * @param Setting_Arguments $args The arguments for the form field. May contain 'help_text'.
637 653 */
638 - public function print_description_text( $args ) {
654 + private function print_description_text( $args ): void {
639 655 echo isset( $args['help_text'] ) ? '<p class="description" id="' . esc_attr( $args['option_key'] ) . '-description">' . wp_kses_post( $args['help_text'] ) . '</p>' : '';
640 656 }
641 657
642 658 /**
643 - * Print out an input text tag.
659 + * Prints out an input text tag.
644 660 *
645 - * @param array $args The arguments for text tag.
646 - * @return void
661 + * @param Setting_Arguments $args The arguments for text tag.
647 662 */
648 - public function print_text_tag( array $args ): void {
649 - $options = $this->parsely->get_options();
650 - $name = $args['option_key'];
651 - $value = $options[ $name ] ?? '';
652 - $optional_args = $args['optional_args'] ?? array();
653 - $id = esc_attr( $name );
654 - $name = Parsely::OPTIONS_KEY . "[$id]";
655 - $value = esc_attr( $value );
656 - $accepted_args = array( 'placeholder', 'required' );
663 + public function print_text_tag( $args ): void {
664 + $options = $this->parsely->get_options();
665 + $name = $args['option_key'];
666 + /**
667 + * Variable.
668 + *
669 + * @var string
670 + */
671 + $value = $options[ $name ] ?? '';
672 + $optional_args = $args['optional_args'] ?? array();
673 + $id = esc_attr( $name );
674 + $name = Parsely::OPTIONS_KEY . "[$id]";
675 + $is_obfuscated_value = $optional_args['is_obfuscated_value'] ?? false;
676 + $value = $is_obfuscated_value ? $this->get_obfuscated_value( $value ) : esc_attr( $value );
677 + $accepted_args = array( 'placeholder', 'required' );
678 + $type = $optional_args['type'] ?? 'text';
657 679
658 - echo sprintf( "<input type='text' name='%s' id='%s' value='%s'", esc_attr( $name ), esc_attr( $id ), esc_attr( $value ) );
659 - if ( $args['help_text'] ) {
680 + echo sprintf( "<input type='%s' name='%s' id='%s' value='%s'", esc_attr( $type ), esc_attr( $name ), esc_attr( $id ), esc_attr( $value ) );
681 +
682 + if ( isset( $args['help_text'] ) ) {
660 683 echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
661 684 }
685 +
662 686 foreach ( $optional_args as $key => $val ) {
663 687 if ( \in_array( $key, $accepted_args, true ) ) {
664 - echo ' ' . esc_attr( $key ) . '="' . esc_attr( $val ) . '"';
688 + echo ' ' . esc_attr( $key ) . '="' . esc_attr( (string) $val ) . '"';
665 689 }
666 690 }
667 691 echo ' />';
668 692
@@ -671,44 +695,43 @@
671 695
672 696 /**
673 697 * Prints a checkbox tag in the settings page.
674 698 *
675 - * @param array $args Arguments to print to checkbox tag.
676 - * @return void
699 + * @param Setting_Arguments $args Arguments to print to checkbox tag.
677 700 */
678 - public function print_checkbox_tag( array $args ): void {
679 - $options = $this->parsely->get_options();
680 - $name = $args['option_key'];
681 - $value = $options[ $name ];
682 - $id = esc_attr( $name );
683 - $name = Parsely::OPTIONS_KEY . "[$id]";
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'] ?? '';
684 708
685 709 echo sprintf( "<input type='checkbox' name='%s' id='%s_true' value='true' ", esc_attr( $name ), esc_attr( $id ) );
686 - if ( $args['help_text'] ) {
710 + if ( isset( $args['help_text'] ) ) {
687 711 echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
688 712 }
689 713 echo checked( true === $value, true, false );
690 - echo sprintf( " /> <label for='%s_true'>%s</label>", esc_attr( $id ), esc_html__( 'Yes', 'wp-parsely' ) );
714 + echo sprintf( " /> <label for='%s_true'>%s</label>", esc_attr( $id ), esc_html( $yes_text ) );
691 715
692 716 $this->print_description_text( $args );
693 717 }
694 718
695 719 /**
696 - * Print out the select tags
720 + * Prints out the select tags
697 721 *
698 - * @param array $args The arguments for the select dropdowns.
699 - * @return void
722 + * @param Setting_Arguments $args The arguments for the select dropdowns.
700 723 */
701 - public function print_select_tag( array $args ): void {
724 + public function print_select_tag( $args ): void {
702 725 $options = $this->parsely->get_options();
703 726 $name = $args['option_key'];
704 - $select_options = $args['select_options'];
727 + $select_options = $args['select_options'] ?? array();
705 728 $selected = $options[ $name ] ?? null;
706 729 $id = esc_attr( $name );
707 730 $name = Parsely::OPTIONS_KEY . "[$id]";
708 731
709 732 echo sprintf( "<select name='%s' id='%s'", esc_attr( $name ), esc_attr( $name ) );
710 - if ( $args['help_text'] ) {
733 + if ( isset( $args['help_text'] ) ) {
711 734 echo ' aria-describedby="' . esc_attr( $id ) . '-description"';
712 735 }
713 736 echo '>';
714 737
@@ -719,79 +742,67 @@
719 742 echo '</option>';
720 743 }
721 744 echo '</select>';
722 745
746 + $this->print_filter_text( $args );
723 747 $this->print_description_text( $args );
724 748 }
725 749
726 750 /**
727 - * Print out the radio buttons
751 + * Prints the radio buttons.
728 752 *
729 - * @param array $args The arguments for the radio buttons.
730 - * @return void
753 + * @param Setting_Arguments $args The arguments for the radio buttons.
731 754 */
732 - public function print_binary_radio_tag( array $args ): void {
733 - $options = $this->parsely->get_options();
734 - $name = $args['option_key'];
735 - $value = $options[ $name ];
736 - $id = esc_attr( $name );
737 - $name = Parsely::OPTIONS_KEY . "[$id]";
755 + public function print_radio_tags( $args ): void {
756 + $name = $args['option_key'];
757 + $id = esc_attr( $name );
758 + $selected = $this->parsely->get_options()[ $name ];
759 + $title = $args['title'] ?? '';
760 + $radio_options = $args['radio_options'] ?? array();
761 +
762 + if ( is_bool( $selected ) ) {
763 + // Converting boolean to string so that we have string type keys for all cases.
764 + $selected = $selected ? 'true' : 'false';
765 + }
766 +
738 767 ?>
739 768 <fieldset>
740 - <legend class="screen-reader-text"><span><?php echo esc_html( $args['title'] ); ?></span></legend>
769 + <legend class="screen-reader-text"><span><?php echo esc_html( $title ); ?></span></legend>
741 770 <p>
742 - <label for="<?php echo esc_attr( "{$id}_true" ); ?>">
743 - <input type="radio" name="<?php echo esc_attr( $name ); ?>" id="<?php echo esc_attr( "{$id}_true" ); ?>" value="true"<?php checked( $value ); ?> /><?php echo esc_html__( 'Yes', 'wp-parsely' ); ?>
771 + <?php foreach ( $radio_options as $value => $text ) { ?>
772 + <label for="<?php echo esc_attr( "{$id}_{$value}" ); ?>">
773 + <input
774 + type="radio"
775 + name="<?php echo esc_attr( Parsely::OPTIONS_KEY . "[$id]" ); ?>"
776 + id="<?php echo esc_attr( "{$id}_{$value}" ); ?>"
777 + value="<?php echo esc_attr( $value ); ?>"
778 + <?php checked( $selected, $value ); ?>
779 + />
780 + <?php echo wp_kses_post( $text ); ?>
744 781 </label>
745 782 <br />
746 - <label for="<?php echo esc_attr( "{$id}_false" ); ?>">
747 - <input type="radio" name="<?php echo esc_attr( $name ); ?>" id="<?php echo esc_attr( "{$id}_false" ); ?>" value="false"<?php checked( $value, false ); ?> /><?php echo esc_html__( 'No', 'wp-parsely' ); ?>
748 - </label>
783 + <?php } ?>
749 784 </p>
750 785 </fieldset>
751 786 <?php
787 + $this->print_filter_text( $args );
752 788 $this->print_description_text( $args );
753 789 }
754 790
755 791 /**
756 - * Prints out multiple selection in the form of checkboxes
792 + * Prints out a "single-image browse control" which includes a text input to
793 + * store image path and a button to browse for images.
757 794 *
758 - * @param array $args The arguments for the checkboxes.
759 - * @return void
795 + * @param Setting_Arguments $args The arguments for the control.
760 796 */
761 - public function print_multiple_checkboxes( array $args ): void {
762 - $options = $this->parsely->get_options();
763 - $select_options = $args['select_options'];
764 - $id = esc_attr( $args['option_key'] );
765 - $name = Parsely::OPTIONS_KEY . "[$id]";
766 -
767 - ?>
768 - <fieldset>
769 - <legend class="screen-reader-text"><span><?php echo esc_html( $args['title'] ); ?></span></legend>
770 - <?php
771 - foreach ( $select_options as $key => $val ) {
772 - $selected = in_array( $val, $options[ $args['option_key'] ], true );
773 - printf(
774 - '<label for="%1$s-%2$s"><input type="checkbox" name="%1$s[]" id="%1$s-%2$s" value="%2$s" ',
775 - esc_attr( $name ),
776 - esc_attr( $key )
777 - );
778 - echo checked( $selected, true, false );
779 - echo sprintf( ' /> %s</label><br />', esc_attr( $val ) );
780 - }
781 -
782 - $this->print_description_text( $args );
783 - }
784 -
785 - /**
786 - * Print out a "single-image browse control" which includes a text
787 - * input to store image path and a button to browse for images.
788 - *
789 - * @param array $args The arguments for the control.
790 - * @return void
791 - */
792 - public function print_media_single_image( array $args ) {
793 - $key = $args['option_key'];
797 + public function print_media_single_image( $args ): void {
798 + $key = $args['option_key'];
799 + $title = $args['title'] ?? '';
800 + /**
801 + * Variable.
802 + *
803 + * @var string
804 + */
794 805 $input_value = $this->parsely->get_options()[ $key ];
795 806 $input_name = Parsely::OPTIONS_KEY . "[$key]";
796 807 $button_text = __( 'Browse', 'wp-parsely' );
797 808 ?>
@@ -796,13 +807,11 @@
796 807 $button_text = __( 'Browse', 'wp-parsely' );
797 808 ?>
798 809
799 810 <fieldset class="media-single-image" id="media-single-image-<?php echo esc_attr( $key ); ?>">
800 - <legend class="screen-reader-text"><span><?php echo esc_html( $args['title'] ); ?></span></legend>
801 - <input class="file-path" type="text" name="<?php echo esc_attr( $input_name ); ?>" value="<?php echo esc_attr( $input_value ); ?>" />
802 - <p>
803 - <button data-option="<?php echo esc_attr( $key ); ?>" class="browse button" type="button"><?php echo esc_html( $button_text ); ?></button>
804 - </p>
811 + <legend class="screen-reader-text"><span><?php echo esc_html( $title ); ?></span></legend>
812 + <input class="file-path" type="text" name="<?php echo esc_attr( $input_name ); ?>" id="logo" value="<?php echo esc_attr( $input_value ); ?>" />
813 + <button data-option="<?php echo esc_attr( $key ); ?>" class="browse button" type="button"><?php echo esc_html( $button_text ); ?></button>
805 814 </fieldset>
806 815
807 816 <?php
808 817 $this->print_description_text( $args );
@@ -808,29 +817,34 @@
808 817 $this->print_description_text( $args );
809 818 }
810 819
811 820 /**
812 - * Print out the post tracking options table.
821 + * Prints out the post tracking options table.
813 822 *
814 823 * @since 3.2.0
815 824 *
816 - * @param array<string, string> $args The arguments used in the output HTML elements.
817 - * @return void
825 + * @param Setting_Arguments $args The arguments used in the output HTML elements.
818 826 */
819 - public function print_track_post_types_table( array $args ): void {
827 + public function print_track_post_types_table( $args ): void {
820 828 $option_key = esc_attr( $args['option_key'] );
829 + $title = $args['title'] ?? '';
830 + /**
831 + * Variable.
832 + *
833 + * @var array<string>
834 + */
821 835 $post_types = get_post_types( array( 'public' => true ) );
822 836 $values = $this->get_tracking_values_for_display();
823 837 ?>
824 838 <fieldset>
825 - <legend class="screen-reader-text"><span><?php echo esc_html( $args['title'] ); ?></span></legend>
839 + <legend class="screen-reader-text"><span><?php echo esc_html( $title ); ?></span></legend>
826 840 <table class="form-table widefat striped" id="track-post-types">
827 - <caption class="screen-reader-text"><?php echo esc_html( $args['title'] ); ?></caption>
841 + <caption class="screen-reader-text"><?php echo esc_html( $title ); ?></caption>
828 842 <thead>
829 843 <tr>
830 844 <th scope="col"><?php echo esc_html__( 'Post Type', 'wp-parsely' ); ?></th>
831 845 <th id="track-post-types--post" scope="col"><?php echo esc_html__( 'Track as Post', 'wp-parsely' ); ?></th>
832 - <th id="track-post-types--page" scope="col"><?php echo esc_html__( 'Track as Page', 'wp-parsely' ); ?></th>
846 + <th id="track-post-types--page" scope="col"><?php echo esc_html__( 'Track as Non-Post', 'wp-parsely' ); ?></th>
833 847 <th id="track-post-types--none" scope="col"><?php echo esc_html__( 'Do not track', 'wp-parsely' ); ?></th>
834 848 </tr>
835 849 </thead>
836 850 <tbody>
@@ -864,18 +878,19 @@
864 878 </tbody>
865 879 </table>
866 880 </fieldset>
867 881 <?php
882 + $this->print_filter_text( $args );
868 883 $this->print_description_text( $args );
869 884 }
870 885
871 886 /**
872 - * Return the custom post type tracking values in a format that is easily
887 + * Returns the custom post type tracking values in a format that is easily
873 888 * consumable by the print_track_post_types_table() function.
874 889 *
875 890 * @since 3.2.0
876 891 *
877 - * @return array<string, string> Key-value pairs with post type and their 'track as' value.
892 + * @return array<string> Key-value pairs with post type and their 'track as' value.
878 893 */
879 894 public function get_tracking_values_for_display(): array {
880 895 $options = $this->parsely->get_options();
881 896 $types = array( 'post', 'page' );
@@ -881,13 +896,12 @@
881 896 $types = array( 'post', 'page' );
882 897 $result = array();
883 898
884 899 foreach ( $types as $type ) {
885 - $array_key = "track_{$type}_types";
886 - if ( array_key_exists( $array_key, $options ) ) {
887 - foreach ( $options[ $array_key ] as $post_type ) {
888 - $result[ $post_type ] = $type;
889 - }
900 + $array_value = $options[ "track_{$type}_types" ];
901 +
902 + foreach ( $array_value as $post_type ) {
903 + $result[ $post_type ] = $type;
890 904 }
891 905 }
892 906
893 907 return $result;
@@ -893,17 +907,33 @@
893 907 return $result;
894 908 }
895 909
896 910 /**
897 - * Validate the options provided by the user
911 + * Validates the options provided by the user.
898 912 *
899 - * @param array $input Options from the settings page.
900 - * @return array List of validated input settings.
913 + * @param ParselySettingOptions $input Options from the settings page.
914 + *
915 + * @return ParselySettingOptions
901 916 */
902 - public function validate_options( array $input ): array {
917 + public function validate_options( $input ) {
918 + $input = $this->validate_basic_section( $input );
919 + $input = $this->validate_recrawl_section( $input );
920 + $input = $this->validate_advanced_section( $input );
921 +
922 + return $input;
923 + }
924 +
925 + /**
926 + * Validates fields of Basic Section.
927 + *
928 + * @param ParselySettingOptions $input Options from the settings page.
929 + *
930 + * @return ParselySettingOptions Validated inputs.
931 + */
932 + private function validate_basic_section( $input ) {
903 933 $options = $this->parsely->get_options();
904 934
905 - if ( empty( $input['apikey'] ) ) {
935 + if ( '' === $input['apikey'] ) {
906 936 add_settings_error(
907 937 Parsely::OPTIONS_KEY,
908 938 'apikey',
909 939 __( 'Please specify the Site ID', 'wp-parsely' )
@@ -908,22 +938,24 @@
908 938 'apikey',
909 939 __( 'Please specify the Site ID', 'wp-parsely' )
910 940 );
911 941 } else {
912 - $input['apikey'] = strtolower( $input['apikey'] );
913 - $input['apikey'] = sanitize_text_field( $input['apikey'] );
914 - if ( strpos( $input['apikey'], '.' ) === false || strpos( $input['apikey'], ' ' ) !== false ) {
942 + $site_id = $this->sanitize_site_id( $input['apikey'] );
943 + if ( false === $this->validate_site_id( $site_id ) ) {
915 944 add_settings_error(
916 945 Parsely::OPTIONS_KEY,
917 946 'apikey',
918 947 __( 'Your Parse.ly Site ID looks incorrect, it should look like "example.com".', 'wp-parsely' )
919 948 );
949 + } else {
950 + $input['apikey'] = $site_id;
920 951 }
921 952 }
922 953
923 - $input['api_secret'] = sanitize_text_field( $input['api_secret'] );
954 + $input['api_secret'] = $this->get_unobfuscated_value( $input['api_secret'], $this->parsely->get_api_secret() );
924 955
925 - if ( ! empty( $input['metadata_secret'] ) ) {
956 + $input['metadata_secret'] = $this->get_unobfuscated_value( $input['metadata_secret'], $this->parsely->get_options()['metadata_secret'] );
957 + if ( '' !== $input['metadata_secret'] ) {
926 958 if ( strlen( $input['metadata_secret'] ) !== 10 ) {
927 959 add_settings_error(
928 960 Parsely::OPTIONS_KEY,
929 961 'metadata_secret',
@@ -928,9 +960,11 @@
928 960 Parsely::OPTIONS_KEY,
929 961 'metadata_secret',
930 962 __( 'Metadata secret is incorrect. Please contact Parse.ly support!', 'wp-parsely' )
931 963 );
932 - } elseif ( isset( $input['parsely_wipe_metadata_cache'] ) && 'true' === $input['parsely_wipe_metadata_cache'] ) {
964 + } elseif (
965 + isset( $input['parsely_wipe_metadata_cache'] ) && 'true' === $input['parsely_wipe_metadata_cache'] // @phpstan-ignore-line
966 + ) {
933 967 delete_post_meta_by_key( 'parsely_metadata_last_updated' );
934 968
935 969 wp_schedule_event( time() + 100, 'everytenminutes', 'parsely_bulk_metas_update' );
936 970 $input['parsely_wipe_metadata_cache'] = false;
@@ -936,15 +970,18 @@
936 970 $input['parsely_wipe_metadata_cache'] = false;
937 971 }
938 972 }
939 973
940 - if ( empty( $input['logo'] ) ) {
974 + if ( ! isset( $input['meta_type'] ) ) {
975 + $input['meta_type'] = $options['meta_type'];
976 + } else {
977 + $input['meta_type'] = sanitize_text_field( $input['meta_type'] );
978 + }
979 +
980 + if ( '' === $input['logo'] ) {
941 981 $input['logo'] = self::get_logo_default();
942 982 }
943 983
944 - // Validate 'Track post type as'.
945 - $this->validate_options_post_type_tracking( $input );
946 -
947 984 // Track authenticated users.
948 985 if ( 'true' !== $input['track_authenticated_users'] && 'false' !== $input['track_authenticated_users'] ) {
949 986 add_settings_error(
950 987 Parsely::OPTIONS_KEY,
@@ -958,9 +995,9 @@
958 995 if ( 'true' !== $input['disable_javascript'] && 'false' !== $input['disable_javascript'] ) {
959 996 add_settings_error(
960 997 Parsely::OPTIONS_KEY,
961 998 'disable_javascript',
962 - __( 'Value passed for disable_javascript must be either "true" or "false".', 'wp-parsely' )
999 + __( 'Value passed for disable_javascript must be either "Yes" or "No".', 'wp-parsely' )
963 1000 );
964 1001 } else {
965 1002 $input['disable_javascript'] = 'true' === $input['disable_javascript'];
966 1003 }
@@ -983,17 +1020,23 @@
983 1020 } else {
984 1021 $input['disable_amp'] = 'true' === $input['disable_amp'];
985 1022 }
986 1023
987 - $input['api_secret'] = sanitize_text_field( $input['api_secret'] );
1024 + return $input;
1025 + }
988 1026
989 - // Custom taxonomy as section.
990 - if ( ! isset( $input['meta_type'] ) ) {
991 - $input['meta_type'] = $options['meta_type'];
992 - } else {
993 - $input['meta_type'] = sanitize_text_field( $input['meta_type'] );
994 - }
1027 + /**
1028 + * Validates fields of Recrawl Section.
1029 + *
1030 + * @param ParselySettingOptions $input Options from the settings page.
1031 + *
1032 + * @return ParselySettingOptions Validated inputs.
1033 + */
1034 + private function validate_recrawl_section( $input ) {
1035 + $options = $this->parsely->get_options();
995 1036
1037 + $this->validate_options_post_type_tracking( $input );
1038 +
996 1039 // Content ID prefix.
997 1040 if ( ! isset( $input['content_id_prefix'] ) ) {
998 1041 $input['content_id_prefix'] = $options['content_id_prefix'];
999 1042 } else {
@@ -1089,17 +1132,77 @@
1089 1132 return $input;
1090 1133 }
1091 1134
1092 1135 /**
1093 - * Receive the $input array from the validate_options() function and validate post tracking options.
1136 + * Validates fields of Advanced Section.
1137 + *
1138 + * @param ParselySettingOptions $input Options from the settings page.
1139 + *
1140 + * @return ParselySettingOptions Validated inputs.
1141 + */
1142 + private function validate_advanced_section( $input ) {
1143 + $options = $this->parsely->get_options();
1144 +
1145 + if ( ! isset( $input['disable_autotrack'] ) ) {
1146 + $input['disable_autotrack'] = $options['disable_autotrack'];
1147 + } elseif ( 'true' !== $input['disable_autotrack'] && 'false' !== $input['disable_autotrack'] ) {
1148 + add_settings_error(
1149 + Parsely::OPTIONS_KEY,
1150 + 'disable_autotrack',
1151 + __( 'Value passed for disable_autotrack must be either "Yes" or "No".', 'wp-parsely' )
1152 + );
1153 + } else {
1154 + $input['disable_autotrack'] = 'true' === $input['disable_autotrack'];
1155 + }
1156 +
1157 + return $input;
1158 + }
1159 +
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 + /**
1183 + * Sanitizes the passed Site ID.
1184 + *
1185 + * @since 3.3.0
1186 + *
1187 + * @param string $site_id The Site ID to be sanitized.
1188 + * @return string
1189 + */
1190 + private function sanitize_site_id( string $site_id ): string {
1191 + return strtolower( sanitize_text_field( $site_id ) );
1192 + }
1193 +
1194 + /**
1195 + * Receives the $input array from the validate_options() function and
1196 + * validate post tracking options.
1197 + *
1094 1198 * This function will mutate the $input array.
1095 1199 *
1096 1200 * @since 3.2.0
1097 1201 *
1098 - * @param array $input Array passed to validate_options() function.
1099 - * @return void
1202 + * @param ParselySettingOptions $input Array passed to validate_options() function.
1100 1203 */
1101 - private function validate_options_post_type_tracking( array &$input ): void {
1204 + private function validate_options_post_type_tracking( &$input ): void {
1102 1205 $options = $this->parsely->get_options();
1103 1206 $posts = 'track_post_types';
1104 1207 $pages = 'track_page_types';
1105 1208 $track_as = 'track_post_types_as';
@@ -1105,8 +1208,9 @@
1105 1208 $track_as = 'track_post_types_as';
1106 1209 $input[ $posts ] = $options[ $posts ];
1107 1210 $input[ $pages ] = $options[ $pages ];
1108 1211
1212 + // @phpstan-ignore-next-line
1109 1213 if ( isset( $input[ $track_as ] ) && is_array( $input[ $track_as ] ) && 0 < count( $input[ $track_as ] ) ) {
1110 1214 $post_types = get_post_types( array( 'public' => true ) );
1111 1215 $temp_posts = array();
1112 1216 $temp_pages = array();
@@ -1134,32 +1238,22 @@
1134 1238 }
1135 1239 }
1136 1240
1137 1241 /**
1138 - * Show our note about dynamic tracking.
1139 - *
1140 - * @return void
1141 - */
1142 - public function print_dynamic_tracking_note(): void {
1143 - printf(
1144 - /* translators: 1: Documentation URL 2: Documentation URL */
1145 - wp_kses_post( __( 'This plugin does not currently support dynamic tracking ( the tracking of multiple pageviews on a single page). Some common use-cases for dynamic tracking are slideshows or articles loaded via AJAX calls in single-page applications -- situations in which new content is loaded without a full page refresh. Tracking these events requires manually implementing additional JavaScript above <a href="%1$s">the standard Parse.ly include</a> that the plugin injects into your page source. Please consult <a href="%2$s">the Parse.ly documentation on dynamic tracking</a> for instructions on implementing dynamic tracking, or contact Parse.ly support (<a href="%3$s">support@parsely.com</a> ) for additional assistance.', 'wp-parsely' ) ),
1146 - esc_url( 'http://www.parsely.com/help/integration/basic/' ),
1147 - esc_url( 'https://www.parsely.com/help/integration/dynamic/' ),
1148 - esc_url( 'mailto:support@parsely.com' )
1149 - );
1150 - }
1151 -
1152 - /**
1153 1242 * Returns default logo if one can be found.
1154 1243 *
1155 1244 * @return string
1156 1245 */
1157 1246 private static function get_logo_default(): string {
1247 + /**
1248 + * Variable.
1249 + *
1250 + * @var int
1251 + */
1158 1252 $custom_logo_id = get_theme_mod( 'custom_logo' );
1159 - if ( $custom_logo_id ) {
1253 + if ( (bool) $custom_logo_id ) {
1160 1254 $logo_attrs = wp_get_attachment_image_src( $custom_logo_id, 'full' );
1161 - if ( $logo_attrs ) {
1255 + if ( isset( $logo_attrs[0] ) ) {
1162 1256 return $logo_attrs[0];
1163 1257 }
1164 1258 }
1165 1259
@@ -1168,12 +1262,12 @@
1168 1262 return get_site_icon_url();
1169 1263 }
1170 1264
1171 1265 /**
1172 - * Sanitize all elements in an option array.
1266 + * Sanitizes all elements in an option array.
1173 1267 *
1174 - * @param array $array Array of options to be sanitized.
1175 - * @return array
1268 + * @param array<int, string> $array Array of options to be sanitized.
1269 + * @return array<int, string>
1176 1270 */
1177 1271 private static function sanitize_option_array( array $array ): array {
1178 1272 $new_array = $array;
1179 1273 foreach ( $array as $key => $val ) {
@@ -1179,6 +1273,34 @@
1179 1273 foreach ( $array as $key => $val ) {
1180 1274 $new_array[ $key ] = sanitize_text_field( $val );
1181 1275 }
1182 1276 return $new_array;
1277 + }
1278 +
1279 + /**
1280 + * Gets obfuscated value.
1281 + *
1282 + * @param string $current_value Current value of the field.
1283 + *
1284 + * @return string
1285 + */
1286 + private function get_obfuscated_value( $current_value ): string {
1287 + return str_repeat( '*', strlen( $current_value ) );
1288 + }
1289 +
1290 + /**
1291 + * Gets unobfuscated value.
1292 + *
1293 + * @param string $current_value Current value of the field.
1294 + * @param string $previous_value Previous value of the field. If current
1295 + * value is obfuscated then we will use this.
1296 + *
1297 + * @return string
1298 + */
1299 + private function get_unobfuscated_value( $current_value, $previous_value ): string {
1300 + if ( $current_value === $this->get_obfuscated_value( $current_value ) ) {
1301 + return '' === $current_value ? $current_value : $previous_value;
1302 + }
1303 +
1304 + return $current_value;
1183 1305 }
1184 1306 }