PluginProbe ʕ •ᴥ•ʔ
Strong Testimonials / 3.3.2
Strong Testimonials v3.3.2
3.3.2 3.3.1 trunk 1.0.1 2.30.9 2.31.10 2.32 2.32.1 2.32.2 2.32.3 2.32.4 2.33 2.34 2.35 2.36 2.37 2.38 2.38.1 2.39 2.39.1 2.39.2 2.39.3 2.40.0 2.40.1 2.40.2 2.40.3 2.40.4 2.40.5 2.40.6 2.40.7 2.41.0 2.41.1 2.50.0 2.50.1 2.50.2 2.50.3 2.50.4 2.51.0 2.51.1 2.51.2 2.51.3 2.51.4 2.51.5 2.51.6 2.51.7 2.51.8 2.51.9 3.0.0 3.0.1 3.0.2 3.0.3 3.1.0 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 3.1.19 3.1.2 3.1.20 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.10 3.2.11 3.2.12 3.2.13 3.2.14 3.2.15 3.2.16 3.2.17 3.2.18 3.2.19 3.2.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 3.3.0
strong-testimonials / admin / settings / class-strong-testimonials-general-settings-react.php
strong-testimonials / admin / settings Last commit date
partials 1 month ago class-strong-testimonials-forms.php 1 month ago class-strong-testimonials-general-settings-react.php 1 week ago class-strong-testimonials-settings-form.php 1 month ago
class-strong-testimonials-general-settings-react.php
2936 lines
1 <?php
2 /**
3 * Class Strong_Testimonials_General_Settings_React
4 *
5 * Provides tabs, subtabs and field configuration for the React-based settings page.
6 * Follows the same structural pattern as Modula's settings class.
7 *
8 * @since 3.2.23
9 */
10 class Strong_Testimonials_General_Settings_React {
11
12 // =========================================================================
13 // Constants
14 // =========================================================================
15
16 /** WordPress option names */
17 const OPTION_GENERAL = 'wpmtst_options';
18 const OPTION_ADVANCED = 'strong_testimonials_advanced_settings';
19 const OPTION_ROLES = 'wpmtst_access_options';
20 const OPTION_MAILCHIMP = 'wpmtst_mailchimp_options';
21 const OPTION_ASSIGNMENT = 'wpmtst_assignment';
22 const OPTION_PROPERTIES = 'wpmtst_properties';
23 const OPTION_REVIEW_MARKUP = 'wpmtst_review_markup';
24
25 /** Field type identifiers (must match React FieldRenderer switch cases) */
26 const FIELD_TYPE_TOGGLE = 'toggle';
27 const FIELD_TYPE_TEXT = 'text';
28 const FIELD_TYPE_NUMBER = 'number';
29 const FIELD_TYPE_COMBO = 'combo';
30 const FIELD_TYPE_PARAGRAPH = 'paragraph';
31 const FIELD_TYPE_ROLE = 'role';
32 const FIELD_TYPE_SELECT = 'select';
33
34 // =========================================================================
35 // Locking / Plan Helpers
36 // =========================================================================
37
38 /**
39 * Minimum plan label (display-ready, capitalised) for each extension slug.
40 * Used to build "Feature available starting with the X plan." messages.
41 */
42 private static $extension_min_plan_labels = array(
43 'strong-testimonials-country-selector' => 'Basic',
44 'strong-testimonials-assignment' => 'Plus',
45 'strong-testimonials-properties' => 'Plus',
46 'strong-testimonials-review-markup' => 'Plus',
47 'strong-testimonials-advanced-views' => 'Plus',
48 'strong-testimonials-captcha' => 'Plus',
49 'strong-testimonials-pro-templates' => 'Plus',
50 'strong-testimonials-importer' => 'Plus',
51 'strong-testimonials-infinite-scroll' => 'Business',
52 'strong-testimonials-emails' => 'Business',
53 'strong-testimonials-filters' => 'Business',
54 'strong-testimonials-role-management' => 'Business',
55 'strong-testimonials-mailchimp' => 'Business',
56 'strong-testimonials-custom-fields' => 'Business',
57 'strong-testimonials-multiple-forms' => 'Business',
58 'strong-testimonials-video' => 'Business',
59 );
60
61 /**
62 * Build the locked/badge/message triplet for an extension-gated subtab.
63 *
64 * Two distinct messages are returned:
65 * • Plan doesn't cover the extension (no Pro, no license, or wrong tier):
66 * "Feature available starting with the X plan."
67 * • Plan covers it but extension is not activated:
68 * The caller-supplied $activate_msg.
69 *
70 * @param string $slug Extension slug, e.g. 'strong-testimonials-role-management'.
71 * @param string $activate_msg Message to show when the plan covers the extension but it is inactive.
72 * @param object|null $ext Pro Extensions instance, or null when Pro is not installed.
73 * @return array{ locked: bool, badge: string, message: string }
74 */
75 private function build_lock_info( $slug, $activate_msg, $ext ) {
76 $plan_label = isset( self::$extension_min_plan_labels[ $slug ] )
77 ? self::$extension_min_plan_labels[ $slug ]
78 : 'Pro';
79
80 $is_active = $ext && $ext->extension_enabled( $slug );
81 $in_plan = $ext && $ext->extension_in_plan( $slug );
82
83 if ( $is_active ) {
84 return array(
85 'locked' => false,
86 'badge' => '',
87 'message' => '',
88 );
89 }
90
91 if ( $in_plan ) {
92 return array(
93 'locked' => true,
94 'badge' => 'Pro',
95 'message' => $activate_msg,
96 );
97 }
98
99 return array(
100 'locked' => true,
101 'badge' => $plan_label,
102 'message' => sprintf(
103 /* translators: %s: plan name, e.g. "Business" */
104 esc_html__( 'Feature available starting with the %s plan.', 'strong-testimonials' ),
105 $plan_label
106 ),
107 );
108 }
109
110 // =========================================================================
111 // Helper Methods — Option Value Retrieval
112 // =========================================================================
113
114 /**
115 * Retrieve a single key from a saved option array, with a fallback default.
116 *
117 * @param array $options Saved option array (result of get_option).
118 * @param string $key Key to look up.
119 * @param mixed $default Fallback when the key is absent.
120 *
121 * @return mixed
122 */
123 private function get_field_value( $options, $key, $default = null ) {
124 if ( isset( $options[ $key ] ) ) {
125 return $options[ $key ];
126 }
127 return $default;
128 }
129
130 // =========================================================================
131 // Helper Methods — Field Builders
132 // =========================================================================
133
134 /**
135 * Build a generic field definition array.
136 *
137 * @param string $type Field type constant.
138 * @param string $name Field name (matches the WP option key).
139 * @param array $args Additional field properties.
140 *
141 * @return array
142 */
143 private function build_field( $type, $name, $args = array() ) {
144 return array_merge(
145 array(
146 'type' => $type,
147 'name' => $name,
148 ),
149 $args
150 );
151 }
152
153 /**
154 * Build a toggle (boolean checkbox) field.
155 *
156 * @param string $name WP option key.
157 * @param string $label Field label shown in the UI.
158 * @param bool $default Current saved value (used as the React defaultValue).
159 * @param array $args Extra properties (description, conditions, …).
160 *
161 * @return array
162 */
163 private function build_toggle_field( $name, $label, $default, $args = array() ) {
164 return $this->build_field(
165 self::FIELD_TYPE_TOGGLE,
166 $name,
167 array_merge(
168 array(
169 'label' => $label,
170 'default' => $default,
171 'sanitization' => array( 'bool' ),
172 ),
173 $args
174 )
175 );
176 }
177
178 /**
179 * Build a text input field.
180 *
181 * @param string $name WP option key.
182 * @param string $label Field label.
183 * @param string $default Current saved value.
184 * @param array $args Extra properties.
185 *
186 * @return array
187 */
188 private function build_text_field( $name, $label, $default, $args = array() ) {
189 return $this->build_field(
190 self::FIELD_TYPE_TEXT,
191 $name,
192 array_merge(
193 array(
194 'label' => $label,
195 'default' => $default,
196 'sanitization' => array( 'text' ),
197 ),
198 $args
199 )
200 );
201 }
202
203 /**
204 * Build a number input field.
205 *
206 * @param string $name WP option key.
207 * @param string $label Field label.
208 * @param int $default Current saved value.
209 * @param array $args Extra properties (min, max, …).
210 *
211 * @return array
212 */
213 private function build_number_field( $name, $label, $default, $args = array() ) {
214 return $this->build_field(
215 self::FIELD_TYPE_NUMBER,
216 $name,
217 array_merge(
218 array(
219 'label' => $label,
220 'default' => $default,
221 'sanitization' => array( 'number' ),
222 ),
223 $args
224 )
225 );
226 }
227
228 /**
229 * Build a select field
230 *
231 * @param string $name Field name.
232 * @param string $label Field label.
233 * @param array $options Select options array.
234 * @param mixed $default Default value.
235 * @param array $args Additional field arguments.
236 *
237 * @return array Field definition array
238 *
239 * @since 2.11.0
240 */
241 private function build_select_field( $name, $label, $options, $default, $args = array() ) {
242 $sanitization = array();
243 if ( ! empty( $options ) && is_array( $options ) ) {
244 if ( isset( $options[0]['value'] ) ) {
245 // Options are in format array( array( 'value' => ..., 'label' => ... ) ).
246 $sanitization = array( 'enum' => array_column( $options, 'value' ) );
247 } else {
248 // Options are simple key-value pairs.
249 $sanitization = array( 'enum' => array_keys( $options ) );
250 }
251 }
252
253 return $this->build_field(
254 self::FIELD_TYPE_SELECT,
255 $name,
256 array_merge(
257 array(
258 'label' => $label,
259 'options' => $options,
260 'default' => $default,
261 'sanitization' => $sanitization,
262 ),
263 $args
264 )
265 );
266 }
267
268
269 /**
270 * Build a combo field — groups multiple related fields into a single row.
271 *
272 * @param array $fields Array of field definitions.
273 * @param array $args Extra properties.
274 *
275 * @return array
276 */
277 private function build_combo_field( $fields, $args = array() ) {
278 return $this->build_field(
279 self::FIELD_TYPE_COMBO,
280 '',
281 array_merge(
282 array( 'fields' => $fields ),
283 $args
284 )
285 );
286 }
287
288 /**
289 * Build a paragraph field — used for section headings or informational text.
290 *
291 * @param string $name Unique identifier (not an option key).
292 * @param string $label Heading text.
293 * @param string $description Body text / description.
294 * @param array $args Extra properties.
295 *
296 * @return array
297 */
298 private function build_paragraph_field( $name, $label, $description, $args = array() ) {
299 return $this->build_field(
300 self::FIELD_TYPE_PARAGRAPH,
301 $name,
302 array_merge(
303 array(
304 'label' => $label,
305 'description' => $description,
306 ),
307 $args
308 )
309 );
310 }
311
312 // =========================================================================
313 // Private Methods — Settings Tab Builders
314 // =========================================================================
315
316 /**
317 * Build the "Comments" subtab config for the General tab.
318 *
319 * @return array
320 */
321 private function get_general_comments() {
322 $options = get_option( self::OPTION_GENERAL, array() );
323 $support_comments = $this->get_field_value( $options, 'support_comments', false );
324
325 return array(
326 'option' => self::OPTION_GENERAL,
327 'fields' => array(
328 $this->build_toggle_field(
329 'support_comments',
330 esc_html__( 'Enable comments for testimonials', 'strong-testimonials' ),
331 (bool) $support_comments,
332 array(
333 'description' => esc_html__( 'This will only be visible in the single testimonial page.', 'strong-testimonials' ),
334 )
335 ),
336 ),
337 );
338 }
339
340 /**
341 * Build the "Embed" subtab config for the General tab.
342 *
343 * @return array
344 */
345 private function get_general_embed() {
346 $options = get_option( self::OPTION_GENERAL, array() );
347 $embed_width = $this->get_field_value( $options, 'embed_width', '' );
348
349 $fields = array(
350 $this->build_number_field(
351 'embed_width',
352 esc_html__( 'Embedded link width in pixels', 'strong-testimonials' ),
353 (int) $embed_width,
354 array(
355 'description' => esc_html__( 'For embedded links (YouTube, Twitter, etc.) set the frame width in pixels. Leave at 0 for default width (usually 100% for videos).', 'strong-testimonials' ),
356 'min' => 0,
357 )
358 ),
359 );
360
361 return array(
362 'option' => self::OPTION_GENERAL,
363 'fields' => $fields,
364 );
365 }
366
367 /**
368 * Build the "Link Control" subtab config for the General tab.
369 *
370 * @return array
371 */
372 private function get_general_link_control() {
373 $options = get_option( self::OPTION_GENERAL, array() );
374 $nofollow = $this->get_field_value( $options, 'nofollow', true );
375 $noopener = $this->get_field_value( $options, 'noopener', true );
376 $noreferrer = $this->get_field_value( $options, 'noreferrer', true );
377
378 return array(
379 'option' => self::OPTION_GENERAL,
380 'fields' => array(
381 $this->build_toggle_field(
382 'nofollow',
383 esc_html__( 'Add nofollow', 'strong-testimonials' ),
384 (bool) $nofollow,
385 array(
386 'description' => esc_html__( 'Add rel="nofollow" attribute to URL custom fields.', 'strong-testimonials' ),
387 )
388 ),
389 $this->build_toggle_field(
390 'noopener',
391 esc_html__( 'Add noopener', 'strong-testimonials' ),
392 (bool) $noopener,
393 array(
394 'description' => esc_html__( 'Add rel="noopener" attribute to URL custom fields.', 'strong-testimonials' ),
395 )
396 ),
397 $this->build_toggle_field(
398 'noreferrer',
399 esc_html__( 'Add noreferrer', 'strong-testimonials' ),
400 (bool) $noreferrer,
401 array(
402 'description' => esc_html__( 'Add rel="noreferrer" attribute to URL custom fields.', 'strong-testimonials' ),
403 )
404 ),
405 ),
406 );
407 }
408
409 /**
410 * Build the "Debug" subtab config for the General tab.
411 * Contains settings that control wp-admin behaviour.
412 *
413 * @return array
414 */
415 private function get_general_debug() {
416 $options = get_option( self::OPTION_ADVANCED, array() );
417
418 $debug_log = $this->get_field_value( $options, 'debug_log', false );
419
420 return array(
421 'option' => self::OPTION_ADVANCED,
422 'fields' => array(
423 $this->build_toggle_field(
424 'debug_log',
425 esc_html__( 'Enable debug log', 'strong-testimonials' ),
426 (bool) $debug_log,
427 array(
428 'description' => sprintf(
429 '%s <a href="%s">%s</a>',
430 esc_html__( "Creates debug logs for Strong Testimonial module and it's addons.", 'strong-testimonials' ),
431 esc_url( admin_url( 'edit.php?post_type=wpm-testimonial&page=strong-testimonials-logs' ) ),
432 esc_html__( 'View logs', 'strong-testimonials' )
433 ),
434 )
435 ),
436 ),
437 );
438 }
439
440 /**
441 * Build the Role Management subtab config.
442 *
443 * Iterates all non-admin WordPress roles and maps each one to a `role` field
444 * containing the testimonial-specific capabilities as child toggle fields.
445 * The default value for every toggle is read directly from the current WP role
446 * object via has_cap(), so the React form always reflects the live state.
447 *
448 * @return array
449 */
450 private function get_roles() {
451 global $wp_roles;
452
453 $capabilities = array(
454 'read_testimonial' => esc_html__( 'Read Own Testimonial', 'strong-testimonials' ),
455 'edit_testimonial' => esc_html__( 'Edit Own Testimonial', 'strong-testimonials' ),
456 'delete_testimonial' => esc_html__( 'Delete Own Testimonial', 'strong-testimonials' ),
457 'read_private_testimonials' => esc_html__( 'Read Private Testimonials', 'strong-testimonials' ),
458 'edit_testimonials' => esc_html__( 'Edit Any Testimonials', 'strong-testimonials' ),
459 'edit_other_testimonials' => esc_html__( 'Edit Other Testimonials', 'strong-testimonials' ),
460 'publish_testimonials' => esc_html__( 'Publish Any Testimonials', 'strong-testimonials' ),
461 'delete_testimonials' => esc_html__( 'Delete Any Testimonials', 'strong-testimonials' ),
462 'delete_others_testimonials' => esc_html__( 'Delete Other Testimonials', 'strong-testimonials' ),
463 'strong_testimonials_options' => esc_html__( 'Manage Settings', 'strong-testimonials' ),
464 );
465
466 $roles_array = array();
467
468 foreach ( $wp_roles->roles as $role_key => $wp_role ) {
469 if ( 'administrator' === $role_key ) {
470 continue;
471 }
472
473 $role = get_role( $role_key );
474
475 $role_field = array(
476 'type' => self::FIELD_TYPE_ROLE,
477 'name' => $role_key . '.enabled',
478 'label' => translate_user_role( $wp_role['name'] ),
479 'default' => $this->is_role_enabled( $role, $capabilities ),
480 'fields' => array(),
481 );
482
483 foreach ( $capabilities as $capability => $capability_name ) {
484 $role_field['fields'][] = $this->build_toggle_field(
485 $role_key . '.' . $capability,
486 $capability_name,
487 (bool) $role->has_cap( $capability )
488 );
489 }
490
491 // Upload Files is only relevant for lower-privilege roles.
492 if ( in_array( $role_key, array( 'subscriber', 'contributor' ), true ) ) {
493 $role_field['fields'][] = $this->build_toggle_field(
494 $role_key . '.upload_files',
495 esc_html__( 'Upload Files', 'strong-testimonials' ),
496 (bool) $role->has_cap( 'upload_files' )
497 );
498 }
499
500 $roles_array[] = $role_field;
501 }
502
503 return array(
504 'option' => self::OPTION_ROLES,
505 'grid' => true,
506 'fields' => $roles_array,
507 );
508 }
509
510 /**
511 * Check whether a role currently holds at least one testimonial capability.
512 * Used to set the master "Enable" toggle default in the Role field header.
513 *
514 * @param WP_Role $role WordPress role object.
515 * @param array $capabilities Capabilities map (capability => label).
516 *
517 * @return bool
518 */
519 private function is_role_enabled( $role, $capabilities ) {
520 foreach ( array_keys( $capabilities ) as $cap ) {
521 if ( $role->has_cap( $cap ) ) {
522 return true;
523 }
524 }
525 return false;
526 }
527
528 /**
529 * Build the "Admin" subtab config for the Mailchimp tab.
530 * Contains settings that control wp-admin behaviour.
531 *
532 * @return array
533 */
534 private function get_mailchimp_admin() {
535 $options = get_option( self::OPTION_MAILCHIMP, array() );
536
537 $api_key = $this->get_field_value( $options, 'mailchimp_api_key', '' );
538 $connected = false;
539 if ( class_exists( 'Strong_Testimonials_Pro\Extensions\Mailchimp\Mailchimp' ) ) {
540 $connected = Strong_Testimonials_Pro\Extensions\Mailchimp\Mailchimp::get_status();
541 }
542 $status_class = $connected ? 'positive' : 'negative';
543 $status_text = $connected
544 ? esc_html__( 'CONNECTED', 'strong-testimonials' )
545 : esc_html__( 'NOT CONNECTED', 'strong-testimonials' );
546 return array(
547 'option' => self::OPTION_MAILCHIMP,
548 'fields' => array(
549 $this->build_paragraph_field(
550 '',
551 esc_html__( 'Status', 'strong-testimonials' ),
552 wp_kses_post( '<span class="status ' . esc_attr( $status_class ) . '">' . $status_text . '</span>' ),
553 ),
554 $this->build_text_field(
555 'mailchimp_api_key',
556 esc_html__( 'API Key', 'strong-testimonials' ),
557 (string) $api_key,
558 array(
559 /* translators: %1$s and %2$s are opening and closing HTML anchor tags for the Mailchimp API key link */
560 'description' => sprintf( esc_html__( 'The API key for connecting with your Mailchimp account. %1$s Get your API key here %2$s.', 'strong-testimonials' ), '<a target="_blank" href="https://admin.mailchimp.com/account/api">', '</a>' ),
561 )
562 ),
563 ),
564 );
565 }
566
567
568 /**
569 * Build the "Admin" subtab config for the Mailchimp tab.
570 * Contains settings that control wp-admin behaviour.
571 *
572 * @return array
573 */
574 private function get_assignment_fields() {
575 $options = get_option( self::OPTION_ASSIGNMENT, array() );
576
577 $assignees = $this->get_field_value( $options, 'assignees', '' );
578 $selection = $this->get_field_value( $options, 'selection', '' );
579 $post_types = get_post_types( array( 'public' => true ), 'objects' );
580
581 $fields = array();
582 foreach ( $post_types as $post_type => $post_type_object ) {
583 $fields[] = $this->build_toggle_field(
584 'assignees.' . $post_type,
585 sprintf( '%s (%s)', $post_type_object->label, $post_type_object->name ),
586 isset( $options['assignees'][ $post_type ] ) ? $options['assignees'][ $post_type ] : false
587 );
588 }
589
590 return array(
591 'option' => self::OPTION_ASSIGNMENT,
592 'fields' => array(
593 $this->build_paragraph_field(
594 '',
595 esc_html__( 'Content Types', 'strong-testimonials' ),
596 esc_html__( 'Allow testimonials to be assigned to these content types:', 'strong-testimonials' ),
597 ),
598 $this->build_combo_field( $fields ),
599 $this->build_select_field(
600 'selection',
601 esc_html__( 'Selection', 'strong-testimonials' ),
602 array(
603 array(
604 'label' => esc_html__( 'Single selections only', 'strong-testimonials' ),
605 'value' => 'single',
606 ),
607 array(
608 'label' => esc_html__( 'Multiple selections allowed', 'strong-testimonials' ),
609 'value' => 'multiple',
610 ),
611 ),
612 $selection,
613 array( 'size' => 'small' )
614 ),
615 ),
616 );
617 }
618
619 /**
620 * Build a placeholder config for a locked subtab (provides something for the blur overlay to cover).
621 *
622 * @return array
623 */
624 private function get_locked_placeholder() {
625 return array(
626 'option' => '',
627 'fields' => array(
628 $this->build_paragraph_field(
629 'locked_placeholder',
630 '',
631 esc_html__( 'Upgrade your plan to unlock this feature.', 'strong-testimonials' )
632 ),
633 ),
634 );
635 }
636
637 /**
638 * Build the Properties subtabs config.
639 * Always returns the full 5-section config; locked when the extension is inactive.
640 *
641 * @param bool $is_active Whether the Properties extension is enabled.
642 * @return array Subtabs array keyed by subtab slug.
643 */
644 private function get_properties_subtabs( $lock_info ) {
645 // Hardcoded defaults — mirrors Options_Defaults in the Pro plugin.
646 $defaults = array(
647 'cpt' => array(
648 'labels' => array(
649 'name' => _x( 'Testimonials', 'post type general name', 'strong-testimonials' ),
650 'singular_name' => _x( 'Testimonial', 'post type singular name', 'strong-testimonials' ),
651 ),
652 'menu_icon' => 'dashicons-editor-quote',
653 'menu_position' => 20,
654 'show_in_admin_bar' => true,
655 'show_in_nav_menus' => true,
656 'can_export' => true,
657 'publicly_queryable' => false,
658 'exclude_from_search' => false,
659 'supports' => array( 'title', 'excerpt', 'editor', 'thumbnail', 'page-attributes', 'comments' ),
660 'rewrite' => array(
661 'slug' => _x( 'testimonial', 'slug', 'strong-testimonials' ),
662 'with_front' => true,
663 'feeds' => false,
664 'pages' => true,
665 ),
666 ),
667 'tax' => array(
668 'labels' => array(
669 'name' => __( 'Testimonial Categories', 'strong-testimonials' ),
670 'singular_name' => __( 'Testimonial Category', 'strong-testimonials' ),
671 ),
672 'publicly_queryable' => true,
673 'rewrite' => array(
674 'slug' => _x( 'testimonial-category', 'slug', 'strong-testimonials' ),
675 'with_front' => true,
676 'hierarchical' => false,
677 ),
678 ),
679 );
680
681 $options = get_option( self::OPTION_PROPERTIES, array() );
682 $locked = $lock_info['locked'];
683 $badge = $lock_info['badge'];
684 $message = $lock_info['message'];
685
686 $cpt = wp_parse_args(
687 isset( $options['cpt'] ) ? $options['cpt'] : array(),
688 $defaults['cpt']
689 );
690 $tax = wp_parse_args(
691 isset( $options['tax'] ) ? $options['tax'] : array(),
692 $defaults['tax']
693 );
694
695 $cpt_labels = wp_parse_args(
696 isset( $cpt['labels'] ) ? $cpt['labels'] : array(),
697 $defaults['cpt']['labels']
698 );
699 $tax_labels = wp_parse_args(
700 isset( $tax['labels'] ) ? $tax['labels'] : array(),
701 $defaults['tax']['labels']
702 );
703 $cpt_rewrite = wp_parse_args(
704 isset( $cpt['rewrite'] ) ? $cpt['rewrite'] : array(),
705 $defaults['cpt']['rewrite']
706 );
707 $cpt_query_var = wp_parse_args(
708 isset( $cpt['query_var'] ) ? $cpt['query_var'] : array(),
709 array(
710 'on' => false,
711 'using' => 'default',
712 'name' => 'testimonial',
713 )
714 );
715 $cpt_has_archive = wp_parse_args(
716 isset( $cpt['has_archive'] ) ? $cpt['has_archive'] : array(),
717 array(
718 'on' => false,
719 'using' => 'current',
720 'slug' => 'testimonial',
721 )
722 );
723 $tax_rewrite = wp_parse_args(
724 isset( $tax['rewrite'] ) ? $tax['rewrite'] : array(),
725 $defaults['tax']['rewrite']
726 );
727 $tax_query_var = wp_parse_args(
728 isset( $tax['query_var'] ) ? $tax['query_var'] : array(),
729 array(
730 'on' => false,
731 'using' => 'default',
732 )
733 );
734
735 // Scalar booleans.
736 $cpt_show_in_admin_bar = (bool) ( isset( $cpt['show_in_admin_bar'] ) ? $cpt['show_in_admin_bar'] : true );
737 $cpt_show_in_nav_menus = (bool) ( isset( $cpt['show_in_nav_menus'] ) ? $cpt['show_in_nav_menus'] : true );
738 $cpt_can_export = (bool) ( isset( $cpt['can_export'] ) ? $cpt['can_export'] : true );
739 $cpt_publicly_queryable = (bool) ( isset( $cpt['publicly_queryable'] ) ? $cpt['publicly_queryable'] : false );
740 $cpt_exclude_from_search = (bool) ( isset( $cpt['exclude_from_search'] ) ? $cpt['exclude_from_search'] : false );
741 $tax_publicly_queryable = (bool) ( isset( $tax['publicly_queryable'] ) ? $tax['publicly_queryable'] : true );
742
743 // Nested booleans.
744 $cpt_query_var_on = isset( $cpt_query_var['on'] ) && (bool) $cpt_query_var['on'];
745 $cpt_rewrite_on = isset( $cpt_rewrite['on'] ) && (bool) $cpt_rewrite['on'];
746 $cpt_rewrite_with_front = (bool) ( isset( $cpt_rewrite['with_front'] ) ? $cpt_rewrite['with_front'] : true );
747 $cpt_rewrite_feeds = (bool) ( isset( $cpt_rewrite['feeds'] ) ? $cpt_rewrite['feeds'] : false );
748 $cpt_rewrite_pages = (bool) ( isset( $cpt_rewrite['pages'] ) ? $cpt_rewrite['pages'] : true );
749 $cpt_has_archive_on = isset( $cpt_has_archive['on'] ) && (bool) $cpt_has_archive['on'];
750 $tax_query_var_on = isset( $tax_query_var['on'] ) && (bool) $tax_query_var['on'];
751 $tax_rewrite_on = isset( $tax_rewrite['on'] ) && (bool) $tax_rewrite['on'];
752 $tax_rewrite_with_front = (bool) ( isset( $tax_rewrite['with_front'] ) ? $tax_rewrite['with_front'] : true );
753 $tax_rewrite_hierarchical = (bool) ( isset( $tax_rewrite['hierarchical'] ) ? $tax_rewrite['hierarchical'] : false );
754 $cpt_supports = isset( $cpt['supports'] ) ? $cpt['supports'] : $defaults['cpt']['supports'];
755
756 // Shared dropdown option lists.
757 $menu_position_options = array(
758 array(
759 'value' => 5,
760 'label' => __( 'below Posts', 'strong-testimonials' ),
761 ),
762 array(
763 'value' => 10,
764 'label' => __( 'below Media', 'strong-testimonials' ),
765 ),
766 array(
767 'value' => 15,
768 'label' => __( 'below Links', 'strong-testimonials' ),
769 ),
770 array(
771 'value' => 20,
772 'label' => __( 'below Pages', 'strong-testimonials' ),
773 ),
774 array(
775 'value' => 25,
776 'label' => __( 'below Comments', 'strong-testimonials' ),
777 ),
778 array(
779 'value' => 60,
780 'label' => __( 'below first separator', 'strong-testimonials' ),
781 ),
782 array(
783 'value' => 65,
784 'label' => __( 'below Plugins', 'strong-testimonials' ),
785 ),
786 array(
787 'value' => 70,
788 'label' => __( 'below Users', 'strong-testimonials' ),
789 ),
790 array(
791 'value' => 75,
792 'label' => __( 'below Tools', 'strong-testimonials' ),
793 ),
794 array(
795 'value' => 80,
796 'label' => __( 'below Settings', 'strong-testimonials' ),
797 ),
798 array(
799 'value' => 100,
800 'label' => __( 'below second separator', 'strong-testimonials' ),
801 ),
802 );
803 $query_var_using_options = array(
804 array(
805 'value' => 'default',
806 'label' => __( 'using preset', 'strong-testimonials' ),
807 ),
808 array(
809 'value' => 'custom',
810 'label' => __( 'using custom', 'strong-testimonials' ),
811 ),
812 );
813 $archive_using_options = array(
814 array(
815 'value' => 'current',
816 'label' => __( 'using current slug', 'strong-testimonials' ),
817 ),
818 array(
819 'value' => 'custom',
820 'label' => __( 'using custom slug', 'strong-testimonials' ),
821 ),
822 );
823
824 $base = array(
825 'option' => self::OPTION_PROPERTIES,
826 'remove_button' => true,
827 );
828
829 // ----------------------------------------------------------------
830 // Admin Options
831 // ----------------------------------------------------------------
832 $admin_options = array_merge(
833 $base,
834 array(
835 'restore_defaults' => array(
836 'cpt' => array(
837 'menu_icon' => $defaults['cpt']['menu_icon'],
838 'menu_position' => $defaults['cpt']['menu_position'],
839 'show_in_admin_bar' => $defaults['cpt']['show_in_admin_bar'],
840 'show_in_nav_menus' => $defaults['cpt']['show_in_nav_menus'],
841 'can_export' => $defaults['cpt']['can_export'],
842 ),
843 ),
844 'fields' => array(
845 array(
846 'type' => 'dashicons',
847 'name' => 'cpt.menu_icon',
848 'label' => __( 'Menu icon', 'strong-testimonials' ),
849 'default' => isset( $cpt['menu_icon'] ) ? $cpt['menu_icon'] : 'dashicons-editor-quote',
850 ),
851 array(
852 'type' => 'select',
853 'name' => 'cpt.menu_position',
854 'label' => __( 'Menu position', 'strong-testimonials' ),
855 'options' => $menu_position_options,
856 'default' => isset( $cpt['menu_position'] ) ? (int) $cpt['menu_position'] : 20,
857 ),
858 array(
859 'type' => 'toggle',
860 'name' => 'cpt.show_in_admin_bar',
861 'label' => __( 'Admin bar', 'strong-testimonials' ),
862 'description' => __( 'Make testimonials available in the New menu in the admin bar', 'strong-testimonials' ),
863 'default' => $cpt_show_in_admin_bar,
864 ),
865 array(
866 'type' => 'toggle',
867 'name' => 'cpt.show_in_nav_menus',
868 'label' => __( 'Navigation menus', 'strong-testimonials' ),
869 'description' => __( 'Make testimonials available for selection in navigation menus', 'strong-testimonials' ),
870 'default' => $cpt_show_in_nav_menus,
871 ),
872 array(
873 'type' => 'toggle',
874 'name' => 'cpt.can_export',
875 'label' => __( 'Export', 'strong-testimonials' ),
876 'description' => __( 'Make testimonials available for export (on Tools menu)', 'strong-testimonials' ),
877 'default' => $cpt_can_export,
878 ),
879 ),
880 )
881 );
882
883 // ----------------------------------------------------------------
884 // Labels
885 // ----------------------------------------------------------------
886 $labels_config = array_merge(
887 $base,
888 array(
889 'restore_defaults' => array(
890 'cpt' => array(
891 'labels' => array(
892 'name' => $defaults['cpt']['labels']['name'],
893 'singular_name' => $defaults['cpt']['labels']['singular_name'],
894 ),
895 ),
896 'tax' => array(
897 'labels' => array(
898 'name' => $defaults['tax']['labels']['name'],
899 'singular_name' => $defaults['tax']['labels']['singular_name'],
900 ),
901 ),
902 ),
903 'fields' => array(
904 array(
905 'type' => 'text',
906 'name' => 'cpt.labels.name',
907 'label' => __( 'Plural name', 'strong-testimonials' ),
908 'default' => $cpt_labels['name'],
909 ),
910 array(
911 'type' => 'text',
912 'name' => 'cpt.labels.singular_name',
913 'label' => __( 'Singular name', 'strong-testimonials' ),
914 'default' => $cpt_labels['singular_name'],
915 ),
916 array(
917 'type' => 'text',
918 'name' => 'tax.labels.name',
919 'label' => __( 'Category plural name', 'strong-testimonials' ),
920 'default' => $tax_labels['name'],
921 ),
922 array(
923 'type' => 'text',
924 'name' => 'tax.labels.singular_name',
925 'label' => __( 'Category singular name', 'strong-testimonials' ),
926 'default' => $tax_labels['singular_name'],
927 ),
928 ),
929 )
930 );
931
932 // ----------------------------------------------------------------
933 // Posts
934 // ----------------------------------------------------------------
935 $posts_config = array_merge(
936 $base,
937 array(
938 'restore_defaults' => array(
939 'cpt' => array(
940 'publicly_queryable' => false,
941 'query_var' => array(
942 'on' => false,
943 'using' => 'default',
944 'name' => 'testimonial',
945 ),
946 'rewrite' => $defaults['cpt']['rewrite'],
947 'has_archive' => array(
948 'on' => false,
949 'using' => 'current',
950 'slug' => 'testimonial',
951 ),
952 'exclude_from_search' => $defaults['cpt']['exclude_from_search'],
953 ),
954 ),
955 'fields' => array(
956 array(
957 'type' => 'toggle',
958 'name' => 'cpt.publicly_queryable',
959 'label' => __( 'Viewable', 'strong-testimonials' ),
960 'description' => __( 'Publicly viewable by parameter or permalink', 'strong-testimonials' ),
961 'default' => $cpt_publicly_queryable,
962 ),
963 array(
964 'type' => 'toggle',
965 'name' => 'cpt.query_var.on',
966 'label' => __( 'Parameter', 'strong-testimonials' ),
967 'description' => __( 'Viewable by custom parameter. Will override default structure.', 'strong-testimonials' ),
968 'default' => $cpt_query_var_on,
969 ),
970 array(
971 'type' => 'select',
972 'name' => 'cpt.query_var.using',
973 'label' => __( 'Using', 'strong-testimonials' ),
974 'options' => $query_var_using_options,
975 'default' => isset( $cpt_query_var['using'] ) ? $cpt_query_var['using'] : 'default',
976 'conditions' => array(
977 array(
978 'field' => 'cpt.query_var.on',
979 'comparison' => '===',
980 'value' => true,
981 ),
982 ),
983 ),
984 array(
985 'type' => 'text',
986 'name' => 'cpt.query_var.name',
987 'label' => __( 'Parameter name', 'strong-testimonials' ),
988 'default' => isset( $cpt_query_var['name'] ) ? $cpt_query_var['name'] : 'testimonial',
989 'conditions' => array(
990 array(
991 'field' => 'cpt.query_var.on',
992 'comparison' => '===',
993 'value' => true,
994 ),
995 array(
996 'field' => 'cpt.query_var.using',
997 'comparison' => '===',
998 'value' => 'custom',
999 ),
1000 ),
1001 ),
1002 array(
1003 'type' => 'toggle',
1004 'name' => 'cpt.rewrite.on',
1005 'label' => __( 'Permalink', 'strong-testimonials' ),
1006 'description' => __( 'Viewable by permalink. Will override default structure.', 'strong-testimonials' ),
1007 'default' => $cpt_rewrite_on,
1008 ),
1009 array(
1010 'type' => 'text',
1011 'name' => 'cpt.rewrite.slug',
1012 'label' => __( 'Slug', 'strong-testimonials' ),
1013 'default' => isset( $cpt_rewrite['slug'] ) ? $cpt_rewrite['slug'] : 'testimonial',
1014 'conditions' => array(
1015 array(
1016 'field' => 'cpt.rewrite.on',
1017 'comparison' => '===',
1018 'value' => true,
1019 ),
1020 ),
1021 ),
1022 array(
1023 'type' => 'toggle',
1024 'name' => 'cpt.rewrite.with_front',
1025 'label' => __( 'Front base', 'strong-testimonials' ),
1026 'description' => __( 'Use the same front base as the general permalink setting', 'strong-testimonials' ),
1027 'default' => $cpt_rewrite_with_front,
1028 'conditions' => array(
1029 array(
1030 'field' => 'cpt.rewrite.on',
1031 'comparison' => '===',
1032 'value' => true,
1033 ),
1034 ),
1035 ),
1036 array(
1037 'type' => 'toggle',
1038 'name' => 'cpt.has_archive.on',
1039 'label' => __( 'Archive page', 'strong-testimonials' ),
1040 'description' => __( 'Enable archive page', 'strong-testimonials' ),
1041 'default' => $cpt_has_archive_on,
1042 'conditions' => array(
1043 array(
1044 'field' => 'cpt.rewrite.on',
1045 'comparison' => '===',
1046 'value' => true,
1047 ),
1048 ),
1049 ),
1050 array(
1051 'type' => 'select',
1052 'name' => 'cpt.has_archive.using',
1053 'label' => __( 'Archive using', 'strong-testimonials' ),
1054 'options' => $archive_using_options,
1055 'default' => isset( $cpt_has_archive['using'] ) ? $cpt_has_archive['using'] : 'current',
1056 'conditions' => array(
1057 array(
1058 'field' => 'cpt.has_archive.on',
1059 'comparison' => '===',
1060 'value' => true,
1061 ),
1062 ),
1063 ),
1064 array(
1065 'type' => 'text',
1066 'name' => 'cpt.has_archive.slug',
1067 'label' => __( 'Archive slug', 'strong-testimonials' ),
1068 'default' => isset( $cpt_has_archive['slug'] ) ? $cpt_has_archive['slug'] : 'testimonial',
1069 'conditions' => array(
1070 array(
1071 'field' => 'cpt.has_archive.using',
1072 'comparison' => '===',
1073 'value' => 'custom',
1074 ),
1075 ),
1076 ),
1077 array(
1078 'type' => 'toggle',
1079 'name' => 'cpt.rewrite.feeds',
1080 'label' => __( 'Feeds', 'strong-testimonials' ),
1081 'description' => __( 'Build a feed permalink structure', 'strong-testimonials' ),
1082 'default' => $cpt_rewrite_feeds,
1083 'conditions' => array(
1084 array(
1085 'field' => 'cpt.has_archive.on',
1086 'comparison' => '===',
1087 'value' => true,
1088 ),
1089 ),
1090 ),
1091 array(
1092 'type' => 'toggle',
1093 'name' => 'cpt.rewrite.pages',
1094 'label' => __( 'Pages', 'strong-testimonials' ),
1095 'description' => __( 'Allow pagination in the archive permalink structure', 'strong-testimonials' ),
1096 'default' => $cpt_rewrite_pages,
1097 'conditions' => array(
1098 array(
1099 'field' => 'cpt.has_archive.on',
1100 'comparison' => '===',
1101 'value' => true,
1102 ),
1103 ),
1104 ),
1105 array(
1106 'type' => 'toggle',
1107 'name' => 'cpt.exclude_from_search',
1108 'label' => __( 'Search', 'strong-testimonials' ),
1109 'description' => __( 'Exclude testimonials from front-end search results', 'strong-testimonials' ),
1110 'default' => $cpt_exclude_from_search,
1111 ),
1112 ),
1113 )
1114 );
1115
1116 // ----------------------------------------------------------------
1117 // Categories
1118 // ----------------------------------------------------------------
1119 $categories_config = array_merge(
1120 $base,
1121 array(
1122 'restore_defaults' => array(
1123 'tax' => array(
1124 'publicly_queryable' => $defaults['tax']['publicly_queryable'],
1125 'query_var' => array(
1126 'on' => false,
1127 'using' => 'default',
1128 ),
1129 'rewrite' => $defaults['tax']['rewrite'],
1130 ),
1131 ),
1132 'fields' => array(
1133 array(
1134 'type' => 'toggle',
1135 'name' => 'tax.publicly_queryable',
1136 'label' => __( 'Viewable', 'strong-testimonials' ),
1137 'description' => __( 'Publicly viewable by parameter or permalink', 'strong-testimonials' ),
1138 'default' => $tax_publicly_queryable,
1139 ),
1140 array(
1141 'type' => 'toggle',
1142 'name' => 'tax.query_var.on',
1143 'label' => __( 'Parameter', 'strong-testimonials' ),
1144 'description' => __( 'Viewable by custom parameter.', 'strong-testimonials' ),
1145 'default' => $tax_query_var_on,
1146 ),
1147 array(
1148 'type' => 'select',
1149 'name' => 'tax.query_var.using',
1150 'label' => __( 'Using', 'strong-testimonials' ),
1151 'options' => $query_var_using_options,
1152 'default' => isset( $tax_query_var['using'] ) ? $tax_query_var['using'] : 'default',
1153 'conditions' => array(
1154 array(
1155 'field' => 'tax.query_var.on',
1156 'comparison' => '===',
1157 'value' => true,
1158 ),
1159 ),
1160 ),
1161 array(
1162 'type' => 'toggle',
1163 'name' => 'tax.rewrite.on',
1164 'label' => __( 'Permalink', 'strong-testimonials' ),
1165 'description' => __( 'Viewable by permalink.', 'strong-testimonials' ),
1166 'default' => $tax_rewrite_on,
1167 ),
1168 array(
1169 'type' => 'text',
1170 'name' => 'tax.rewrite.slug',
1171 'label' => __( 'Slug', 'strong-testimonials' ),
1172 'default' => isset( $tax_rewrite['slug'] ) ? $tax_rewrite['slug'] : 'testimonial-category',
1173 'conditions' => array(
1174 array(
1175 'field' => 'tax.rewrite.on',
1176 'comparison' => '===',
1177 'value' => true,
1178 ),
1179 ),
1180 ),
1181 array(
1182 'type' => 'toggle',
1183 'name' => 'tax.rewrite.with_front',
1184 'label' => __( 'Front base', 'strong-testimonials' ),
1185 'default' => $tax_rewrite_with_front,
1186 'conditions' => array(
1187 array(
1188 'field' => 'tax.rewrite.on',
1189 'comparison' => '===',
1190 'value' => true,
1191 ),
1192 ),
1193 ),
1194 array(
1195 'type' => 'toggle',
1196 'name' => 'tax.rewrite.hierarchical',
1197 'label' => __( 'Hierarchical', 'strong-testimonials' ),
1198 'description' => __( 'Use hierarchical URL structure for subcategories', 'strong-testimonials' ),
1199 'default' => $tax_rewrite_hierarchical,
1200 'conditions' => array(
1201 array(
1202 'field' => 'tax.rewrite.on',
1203 'comparison' => '===',
1204 'value' => true,
1205 ),
1206 ),
1207 ),
1208 ),
1209 )
1210 );
1211
1212 // ----------------------------------------------------------------
1213 // Editor permissions
1214 // ----------------------------------------------------------------
1215 $editor_config = array_merge(
1216 $base,
1217 array(
1218 'restore_defaults' => array(
1219 'cpt' => array(
1220 'supports' => $defaults['cpt']['supports'],
1221 ),
1222 ),
1223 'fields' => array(
1224 array(
1225 'type' => 'checks',
1226 'name' => 'cpt.supports',
1227 'label' => __( 'Supported features', 'strong-testimonials' ),
1228 'default' => $cpt_supports,
1229 'options' => array(
1230 array(
1231 'value' => 'title',
1232 'label' => __( 'Title', 'strong-testimonials' ),
1233 'description' => __( 'The title field', 'strong-testimonials' ),
1234 ),
1235 array(
1236 'value' => 'editor',
1237 'label' => __( 'Content', 'strong-testimonials' ),
1238 'description' => __( 'The content field', 'strong-testimonials' ),
1239 ),
1240 array(
1241 'value' => 'excerpt',
1242 'label' => __( 'Excerpt', 'strong-testimonials' ),
1243 'description' => __( 'The excerpt field', 'strong-testimonials' ),
1244 ),
1245 array(
1246 'value' => 'thumbnail',
1247 'label' => __( 'Featured Image', 'strong-testimonials' ),
1248 'description' => __( 'The featured image', 'strong-testimonials' ),
1249 ),
1250 array(
1251 'value' => 'page-attributes',
1252 'label' => __( 'Page Attributes', 'strong-testimonials' ),
1253 'description' => __( 'The page attributes meta box', 'strong-testimonials' ),
1254 ),
1255 array(
1256 'value' => 'custom-fields',
1257 'label' => __( 'Custom Fields', 'strong-testimonials' ),
1258 'description' => __( 'The custom fields meta box', 'strong-testimonials' ),
1259 ),
1260 array(
1261 'value' => 'comments',
1262 'label' => __( 'Comments', 'strong-testimonials' ),
1263 'description' => __( 'The comments meta box', 'strong-testimonials' ),
1264 ),
1265 array(
1266 'value' => 'trackbacks',
1267 'label' => __( 'Trackbacks', 'strong-testimonials' ),
1268 'description' => __( 'The trackbacks meta box', 'strong-testimonials' ),
1269 ),
1270 array(
1271 'value' => 'revisions',
1272 'label' => __( 'Revisions', 'strong-testimonials' ),
1273 'description' => __( 'The revisions meta box', 'strong-testimonials' ),
1274 ),
1275 array(
1276 'value' => 'author',
1277 'label' => __( 'Author', 'strong-testimonials' ),
1278 'description' => __( 'The author meta box', 'strong-testimonials' ),
1279 ),
1280 array(
1281 'value' => 'post-formats',
1282 'label' => __( 'Post Formats', 'strong-testimonials' ),
1283 'description' => __( 'The post formats meta box', 'strong-testimonials' ),
1284 ),
1285 ),
1286 ),
1287 ),
1288 )
1289 );
1290
1291 return array(
1292 'properties_admin' => array(
1293 'label' => esc_html__( 'Admin Options', 'strong-testimonials' ),
1294 'locked' => $locked,
1295 'badge' => $badge,
1296 'message' => $message,
1297 'config' => $admin_options,
1298 ),
1299 'properties_labels' => array(
1300 'label' => esc_html__( 'Labels', 'strong-testimonials' ),
1301 'locked' => $locked,
1302 'badge' => $badge,
1303 'message' => $message,
1304 'config' => $labels_config,
1305 ),
1306 'properties_posts' => array(
1307 'label' => esc_html__( 'Posts', 'strong-testimonials' ),
1308 'locked' => $locked,
1309 'badge' => $badge,
1310 'message' => $message,
1311 'config' => $posts_config,
1312 ),
1313 'properties_cats' => array(
1314 'label' => esc_html__( 'Categories', 'strong-testimonials' ),
1315 'locked' => $locked,
1316 'badge' => $badge,
1317 'message' => $message,
1318 'config' => $categories_config,
1319 ),
1320 'properties_editor' => array(
1321 'label' => esc_html__( 'Editor Permissions', 'strong-testimonials' ),
1322 'locked' => $locked,
1323 'badge' => $badge,
1324 'message' => $message,
1325 'config' => $editor_config,
1326 ),
1327 );
1328 }
1329
1330 /**
1331 * Build select options from custom fields filtered by one or more input_type values.
1332 *
1333 * @param string|array $filter_types input_type value(s) to include. Empty string = all.
1334 * @param array $prepend Options to prepend before the field list.
1335 *
1336 * @return array Array of { value, label } option objects.
1337 */
1338 private function get_custom_field_options( $filter_types, $prepend = array() ) {
1339 $options = $prepend;
1340 $filter_types = $filter_types ? (array) $filter_types : array();
1341
1342 if ( function_exists( 'wpmtst_get_custom_fields' ) ) {
1343 $custom_fields = array_diff_key( wpmtst_get_custom_fields(), array( 'email' => '' ) );
1344 foreach ( $custom_fields as $key => $field ) {
1345 if ( empty( $filter_types ) || in_array( $field['input_type'], $filter_types, true ) ) {
1346 $options[] = array(
1347 'value' => $key,
1348 'label' => isset( $field['label'] ) ? $field['label'] : $key,
1349 );
1350 }
1351 }
1352 }
1353
1354 return $options;
1355 }
1356
1357 /**
1358 * Build the Review Markup subtabs config.
1359 * Always returns the full config; locked when the extension is inactive.
1360 *
1361 * @param bool $is_active Whether the Review Markup extension is enabled.
1362 * @return array Subtabs array keyed by subtab slug.
1363 */
1364 private function get_review_markup_subtabs( $lock_info ) {
1365 $options = get_option( self::OPTION_REVIEW_MARKUP, array() );
1366 $locked = $lock_info['locked'];
1367 $badge = $lock_info['badge'];
1368 $message = $lock_info['message'];
1369
1370 $none_option = array(
1371 'value' => '',
1372 'label' => esc_html__( '— None —', 'strong-testimonials' ),
1373 );
1374
1375 $content_options = array(
1376 array(
1377 'value' => 'content',
1378 'label' => esc_html__( 'Post Content', 'strong-testimonials' ),
1379 ),
1380 array(
1381 'value' => 'excerpt',
1382 'label' => esc_html__( 'Post Excerpt', 'strong-testimonials' ),
1383 ),
1384 );
1385 $text_options = $this->get_custom_field_options( 'text', array( $none_option ) );
1386 $url_text_options = $this->get_custom_field_options( array( 'text', 'url' ), array( $none_option ) );
1387 $url_options = $this->get_custom_field_options( 'url', array( $none_option ) );
1388 $rating_options = $this->get_custom_field_options( 'rating', array( $none_option ) );
1389
1390 $thing_type_options = array(
1391 array(
1392 'value' => 'Book',
1393 'label' => 'Book',
1394 ),
1395 array(
1396 'value' => 'Course',
1397 'label' => 'Course',
1398 ),
1399 array(
1400 'value' => 'CreativeWorkSeason',
1401 'label' => 'CreativeWorkSeason',
1402 ),
1403 array(
1404 'value' => 'CreativeWorkSeries',
1405 'label' => 'CreativeWorkSeries',
1406 ),
1407 array(
1408 'value' => 'Episode',
1409 'label' => 'Episode',
1410 ),
1411 array(
1412 'value' => 'Event',
1413 'label' => 'Event',
1414 ),
1415 array(
1416 'value' => 'Game',
1417 'label' => 'Game',
1418 ),
1419 array(
1420 'value' => 'LocalBusiness',
1421 'label' => 'LocalBusiness',
1422 ),
1423 array(
1424 'value' => 'MediaObject',
1425 'label' => 'MediaObject',
1426 ),
1427 array(
1428 'value' => 'Movie',
1429 'label' => 'Movie',
1430 ),
1431 array(
1432 'value' => 'MusicPlaylist',
1433 'label' => 'MusicPlaylist',
1434 ),
1435 array(
1436 'value' => 'MusicRecording',
1437 'label' => 'MusicRecording',
1438 ),
1439 array(
1440 'value' => 'Organization',
1441 'label' => 'Organization',
1442 ),
1443 array(
1444 'value' => 'Product',
1445 'label' => 'Product',
1446 ),
1447 array(
1448 'value' => 'SoftwareApplication',
1449 'label' => 'SoftwareApplication',
1450 ),
1451 );
1452
1453 $availability_options = array(
1454 array(
1455 'value' => 'InStock',
1456 'label' => esc_html__( 'In Stock', 'strong-testimonials' ),
1457 ),
1458 array(
1459 'value' => 'OutOfStock',
1460 'label' => esc_html__( 'Out of Stock', 'strong-testimonials' ),
1461 ),
1462 array(
1463 'value' => 'PreOrder',
1464 'label' => esc_html__( 'Pre-Order', 'strong-testimonials' ),
1465 ),
1466 array(
1467 'value' => 'Discontinued',
1468 'label' => esc_html__( 'Discontinued', 'strong-testimonials' ),
1469 ),
1470 array(
1471 'value' => 'InStoreOnly',
1472 'label' => esc_html__( 'In Store Only', 'strong-testimonials' ),
1473 ),
1474 array(
1475 'value' => 'LimitedAvailability',
1476 'label' => esc_html__( 'Limited Availability', 'strong-testimonials' ),
1477 ),
1478 array(
1479 'value' => 'OnlineOnly',
1480 'label' => esc_html__( 'Online Only', 'strong-testimonials' ),
1481 ),
1482 array(
1483 'value' => 'SoldOut',
1484 'label' => esc_html__( 'Sold Out', 'strong-testimonials' ),
1485 ),
1486 );
1487
1488 $offer_availability_options = array(
1489 array(
1490 'value' => 'None',
1491 'label' => esc_html__( '— None —', 'strong-testimonials' ),
1492 ),
1493 array(
1494 'value' => 'InStock',
1495 'label' => esc_html__( 'In Stock', 'strong-testimonials' ),
1496 ),
1497 array(
1498 'value' => 'SoldOut',
1499 'label' => esc_html__( 'Sold Out', 'strong-testimonials' ),
1500 ),
1501 array(
1502 'value' => 'PreOrder',
1503 'label' => esc_html__( 'Pre-Order', 'strong-testimonials' ),
1504 ),
1505 );
1506
1507 $performer_options = array(
1508 array(
1509 'value' => 'Person',
1510 'label' => esc_html__( 'Person', 'strong-testimonials' ),
1511 ),
1512 array(
1513 'value' => 'Organization',
1514 'label' => esc_html__( 'Organization', 'strong-testimonials' ),
1515 ),
1516 );
1517
1518 $g = isset( $options['thing_settings'] ) ? $options['thing_settings'] : array();
1519
1520 $ts = function ( $type, $key, $default = '' ) use ( $g ) {
1521 return isset( $g[ $type ][ $key ] ) ? $g[ $type ][ $key ] : $default;
1522 };
1523
1524 $single_product_options = array(
1525 array(
1526 'value' => '',
1527 'label' => esc_html__( 'do not add markup to the single testimonial', 'strong-testimonials' ),
1528 ),
1529 array(
1530 'value' => 'global',
1531 'label' => esc_html__( 'add markup using the Thing Name', 'strong-testimonials' ),
1532 ),
1533 array(
1534 'value' => 'category',
1535 'label' => esc_html__( 'add markup using the category name', 'strong-testimonials' ),
1536 ),
1537 );
1538
1539 $use_max_rating_options = array(
1540 array(
1541 'value' => 1,
1542 'label' => esc_html__( 'if no rating, use the maximum value (5/5) instead', 'strong-testimonials' ),
1543 ),
1544 array(
1545 'value' => 0,
1546 'label' => esc_html__( 'if no rating, add no rating markup', 'strong-testimonials' ),
1547 ),
1548 );
1549
1550 // Markup Fields subtab
1551 $fields_config = array(
1552 'option' => self::OPTION_REVIEW_MARKUP,
1553 'fields' => array(
1554 // ---- Markup Fields section ----
1555 array(
1556 'type' => 'paragraph',
1557 'name' => 'section_markup_fields',
1558 'label' => esc_html__( 'Markup Fields', 'strong-testimonials' ),
1559 ),
1560 array(
1561 'type' => 'select',
1562 'name' => 'content_field',
1563 'label' => esc_html__( 'Content Field', 'strong-testimonials' ),
1564 'description' => esc_html__( '(Required)', 'strong-testimonials' ),
1565 'options' => $content_options,
1566 'default' => isset( $options['content_field'] ) ? $options['content_field'] : 'content',
1567 ),
1568 array(
1569 'type' => 'select',
1570 'name' => 'author_field',
1571 'label' => esc_html__( 'Author Field', 'strong-testimonials' ),
1572 'description' => esc_html__( '(Recommended)', 'strong-testimonials' ),
1573 'options' => $text_options,
1574 'default' => isset( $options['author_field'] ) ? $options['author_field'] : '',
1575 ),
1576 array(
1577 'type' => 'select',
1578 'name' => 'company_field',
1579 'label' => esc_html__( 'Company Field', 'strong-testimonials' ),
1580 'options' => $url_text_options,
1581 'default' => isset( $options['company_field'] ) ? $options['company_field'] : '',
1582 ),
1583 array(
1584 'type' => 'select',
1585 'name' => 'company_url',
1586 'label' => esc_html__( 'Company URL Field', 'strong-testimonials' ),
1587 'options' => $url_options,
1588 'default' => isset( $options['company_url'] ) ? $options['company_url'] : '',
1589 ),
1590 array(
1591 'type' => 'select',
1592 'name' => 'rating_field',
1593 'label' => esc_html__( 'Rating Field', 'strong-testimonials' ),
1594 'description' => esc_html__( '(Recommended)', 'strong-testimonials' ),
1595 'options' => $rating_options,
1596 'default' => isset( $options['rating_field'] ) ? $options['rating_field'] : '',
1597 ),
1598 array(
1599 'type' => 'select',
1600 'name' => 'use_max_rating',
1601 'label' => esc_html__( 'Max Rating', 'strong-testimonials' ),
1602 'options' => $use_max_rating_options,
1603 'default' => isset( $options['use_max_rating'] ) ? (int) $options['use_max_rating'] : 1,
1604 ),
1605 array(
1606 'type' => 'toggle',
1607 'name' => 'include_ratings',
1608 'label' => esc_html__( 'Include Ratings', 'strong-testimonials' ),
1609 'description' => esc_html__( 'Include rating values in the markup output.', 'strong-testimonials' ),
1610 'default' => isset( $options['include_ratings'] ) ? (bool) $options['include_ratings'] : true,
1611 ),
1612 array(
1613 'type' => 'separator',
1614 'name' => 'sep_schema',
1615 ),
1616 // ---- Schema Settings section ----
1617 array(
1618 'type' => 'paragraph',
1619 'name' => 'section_schema_settings',
1620 'label' => esc_html__( 'Schema Settings', 'strong-testimonials' ),
1621 ),
1622 array(
1623 'type' => 'text',
1624 'name' => 'product_name',
1625 'label' => esc_html__( 'Thing Name', 'strong-testimonials' ),
1626 'description' => esc_html__( '(Recommended)', 'strong-testimonials' ),
1627 'default' => isset( $options['product_name'] ) ? $options['product_name'] : '',
1628 ),
1629 array(
1630 'type' => 'text',
1631 'name' => 'product_id',
1632 'label' => esc_html__( 'Thing ID', 'strong-testimonials' ),
1633 'default' => isset( $options['product_id'] ) ? $options['product_id'] : '',
1634 ),
1635 array(
1636 'type' => 'select',
1637 'name' => 'thing_type',
1638 'label' => esc_html__( 'Thing Type', 'strong-testimonials' ),
1639 'description' => esc_html__( '(Recommended)', 'strong-testimonials' ),
1640 'options' => $thing_type_options,
1641 'default' => isset( $options['thing_type'] ) ? $options['thing_type'] : 'Product',
1642 ),
1643 array(
1644 'type' => 'text',
1645 'name' => 'thing_description',
1646 'label' => esc_html__( 'Thing Description', 'strong-testimonials' ),
1647 'description' => esc_html__( 'The description of your product, service or business.', 'strong-testimonials' ),
1648 'default' => isset( $options['thing_description'] ) ? $options['thing_description'] : '',
1649 ),
1650 // Course settings
1651 array(
1652 'type' => 'select',
1653 'name' => 'thing_settings.course.provider',
1654 'label' => esc_html__( 'Course Provider Type', 'strong-testimonials' ),
1655 'options' => $performer_options,
1656 'default' => $ts( 'course', 'provider', 'Person' ),
1657 'conditions' => array(
1658 array(
1659 'field' => 'thing_type',
1660 'comparison' => '===',
1661 'value' => 'Course',
1662 ),
1663 ),
1664 ),
1665 array(
1666 'type' => 'text',
1667 'name' => 'thing_settings.course.provider_name',
1668 'label' => esc_html__( 'Course Provider Name', 'strong-testimonials' ),
1669 'default' => $ts( 'course', 'provider_name' ),
1670 'conditions' => array(
1671 array(
1672 'field' => 'thing_type',
1673 'comparison' => '===',
1674 'value' => 'Course',
1675 ),
1676 ),
1677 ),
1678 // Event settings
1679 array(
1680 'type' => 'text',
1681 'name' => 'thing_settings.event.venue_name',
1682 'label' => esc_html__( 'Venue Name', 'strong-testimonials' ),
1683 'default' => $ts( 'event', 'venue_name' ),
1684 'conditions' => array(
1685 array(
1686 'field' => 'thing_type',
1687 'comparison' => '===',
1688 'value' => 'Event',
1689 ),
1690 ),
1691 ),
1692 array(
1693 'type' => 'text',
1694 'name' => 'thing_settings.event.street_address',
1695 'label' => esc_html__( 'Street Address', 'strong-testimonials' ),
1696 'default' => $ts( 'event', 'street_address' ),
1697 'conditions' => array(
1698 array(
1699 'field' => 'thing_type',
1700 'comparison' => '===',
1701 'value' => 'Event',
1702 ),
1703 ),
1704 ),
1705 array(
1706 'type' => 'text',
1707 'name' => 'thing_settings.event.locality',
1708 'label' => esc_html__( 'City', 'strong-testimonials' ),
1709 'default' => $ts( 'event', 'locality' ),
1710 'conditions' => array(
1711 array(
1712 'field' => 'thing_type',
1713 'comparison' => '===',
1714 'value' => 'Event',
1715 ),
1716 ),
1717 ),
1718 array(
1719 'type' => 'text',
1720 'name' => 'thing_settings.event.region',
1721 'label' => esc_html__( 'State / Region', 'strong-testimonials' ),
1722 'default' => $ts( 'event', 'region' ),
1723 'conditions' => array(
1724 array(
1725 'field' => 'thing_type',
1726 'comparison' => '===',
1727 'value' => 'Event',
1728 ),
1729 ),
1730 ),
1731 array(
1732 'type' => 'text',
1733 'name' => 'thing_settings.event.postal_code',
1734 'label' => esc_html__( 'Postal Code', 'strong-testimonials' ),
1735 'default' => $ts( 'event', 'postal_code' ),
1736 'conditions' => array(
1737 array(
1738 'field' => 'thing_type',
1739 'comparison' => '===',
1740 'value' => 'Event',
1741 ),
1742 ),
1743 ),
1744 array(
1745 'type' => 'text',
1746 'name' => 'thing_settings.event.country',
1747 'label' => esc_html__( 'Country', 'strong-testimonials' ),
1748 'default' => $ts( 'event', 'country' ),
1749 'conditions' => array(
1750 array(
1751 'field' => 'thing_type',
1752 'comparison' => '===',
1753 'value' => 'Event',
1754 ),
1755 ),
1756 ),
1757 array(
1758 'type' => 'text',
1759 'name' => 'thing_settings.event.start_date',
1760 'label' => esc_html__( 'Start Date', 'strong-testimonials' ),
1761 'description' => esc_html__( 'Event date in format yyyy-mm-dd.', 'strong-testimonials' ),
1762 'default' => $ts( 'event', 'start_date' ),
1763 'conditions' => array(
1764 array(
1765 'field' => 'thing_type',
1766 'comparison' => '===',
1767 'value' => 'Event',
1768 ),
1769 ),
1770 ),
1771 array(
1772 'type' => 'text',
1773 'name' => 'thing_settings.event.end_date',
1774 'label' => esc_html__( 'End Date', 'strong-testimonials' ),
1775 'description' => esc_html__( 'Event date in format yyyy-mm-dd.', 'strong-testimonials' ),
1776 'default' => $ts( 'event', 'end_date' ),
1777 'conditions' => array(
1778 array(
1779 'field' => 'thing_type',
1780 'comparison' => '===',
1781 'value' => 'Event',
1782 ),
1783 ),
1784 ),
1785 array(
1786 'type' => 'text',
1787 'name' => 'thing_settings.event.image',
1788 'label' => esc_html__( 'Event Image URL', 'strong-testimonials' ),
1789 'description' => esc_html__( 'The URL of the event image.', 'strong-testimonials' ),
1790 'default' => $ts( 'event', 'image' ),
1791 'conditions' => array(
1792 array(
1793 'field' => 'thing_type',
1794 'comparison' => '===',
1795 'value' => 'Event',
1796 ),
1797 ),
1798 ),
1799 array(
1800 'type' => 'select',
1801 'name' => 'thing_settings.event.performer',
1802 'label' => esc_html__( 'Performer Type', 'strong-testimonials' ),
1803 'options' => $performer_options,
1804 'default' => $ts( 'event', 'performer', 'Person' ),
1805 'conditions' => array(
1806 array(
1807 'field' => 'thing_type',
1808 'comparison' => '===',
1809 'value' => 'Event',
1810 ),
1811 ),
1812 ),
1813 array(
1814 'type' => 'text',
1815 'name' => 'thing_settings.event.performer_name',
1816 'label' => esc_html__( 'Performer Name', 'strong-testimonials' ),
1817 'default' => $ts( 'event', 'performer_name' ),
1818 'conditions' => array(
1819 array(
1820 'field' => 'thing_type',
1821 'comparison' => '===',
1822 'value' => 'Event',
1823 ),
1824 ),
1825 ),
1826 array(
1827 'type' => 'text',
1828 'name' => 'thing_settings.event.ticket_url',
1829 'label' => esc_html__( 'Ticket URL', 'strong-testimonials' ),
1830 'description' => esc_html__( 'A URL where visitors can purchase tickets for the event.', 'strong-testimonials' ),
1831 'default' => $ts( 'event', 'ticket_url' ),
1832 'conditions' => array(
1833 array(
1834 'field' => 'thing_type',
1835 'comparison' => '===',
1836 'value' => 'Event',
1837 ),
1838 ),
1839 ),
1840 array(
1841 'type' => 'text',
1842 'name' => 'thing_settings.event.offer_price',
1843 'label' => esc_html__( 'Entry Price', 'strong-testimonials' ),
1844 'description' => esc_html__( 'Entry price of the event.', 'strong-testimonials' ),
1845 'default' => $ts( 'event', 'offer_price' ),
1846 'conditions' => array(
1847 array(
1848 'field' => 'thing_type',
1849 'comparison' => '===',
1850 'value' => 'Event',
1851 ),
1852 ),
1853 ),
1854 array(
1855 'type' => 'text',
1856 'name' => 'thing_settings.event.offer_currency',
1857 'label' => esc_html__( 'Currency', 'strong-testimonials' ),
1858 'description' => esc_html__( 'ISO 4217 Currency code. Example: EUR.', 'strong-testimonials' ),
1859 'default' => $ts( 'event', 'offer_currency' ),
1860 'conditions' => array(
1861 array(
1862 'field' => 'thing_type',
1863 'comparison' => '===',
1864 'value' => 'Event',
1865 ),
1866 ),
1867 ),
1868 array(
1869 'type' => 'select',
1870 'name' => 'thing_settings.event.offer_availability',
1871 'label' => esc_html__( 'Availability', 'strong-testimonials' ),
1872 'options' => $offer_availability_options,
1873 'default' => $ts( 'event', 'offer_availability', 'None' ),
1874 'conditions' => array(
1875 array(
1876 'field' => 'thing_type',
1877 'comparison' => '===',
1878 'value' => 'Event',
1879 ),
1880 ),
1881 ),
1882 array(
1883 'type' => 'text',
1884 'name' => 'thing_settings.event.offer_availability_starts',
1885 'label' => esc_html__( 'Availability Starts', 'strong-testimonials' ),
1886 'default' => $ts( 'event', 'offer_availability_starts' ),
1887 'conditions' => array(
1888 array(
1889 'field' => 'thing_type',
1890 'comparison' => '===',
1891 'value' => 'Event',
1892 ),
1893 ),
1894 ),
1895 // LocalBusiness settings
1896 array(
1897 'type' => 'text',
1898 'name' => 'thing_settings.localbusiness.address',
1899 'label' => esc_html__( 'Address', 'strong-testimonials' ),
1900 'description' => esc_html__( 'The postal address of your business.', 'strong-testimonials' ),
1901 'default' => $ts( 'localbusiness', 'address' ),
1902 'conditions' => array(
1903 array(
1904 'field' => 'thing_type',
1905 'comparison' => '===',
1906 'value' => 'LocalBusiness',
1907 ),
1908 ),
1909 ),
1910 array(
1911 'type' => 'text',
1912 'name' => 'thing_settings.localbusiness.image',
1913 'label' => esc_html__( 'Image URL', 'strong-testimonials' ),
1914 'description' => esc_html__( 'The URL of a local business image.', 'strong-testimonials' ),
1915 'default' => $ts( 'localbusiness', 'image' ),
1916 'conditions' => array(
1917 array(
1918 'field' => 'thing_type',
1919 'comparison' => '===',
1920 'value' => 'LocalBusiness',
1921 ),
1922 ),
1923 ),
1924 array(
1925 'type' => 'text',
1926 'name' => 'thing_settings.localbusiness.price_range',
1927 'label' => esc_html__( 'Price Range', 'strong-testimonials' ),
1928 'default' => $ts( 'localbusiness', 'price_range' ),
1929 'conditions' => array(
1930 array(
1931 'field' => 'thing_type',
1932 'comparison' => '===',
1933 'value' => 'LocalBusiness',
1934 ),
1935 ),
1936 ),
1937 array(
1938 'type' => 'text',
1939 'name' => 'thing_settings.localbusiness.telephone',
1940 'label' => esc_html__( 'Telephone', 'strong-testimonials' ),
1941 'default' => $ts( 'localbusiness', 'telephone' ),
1942 'conditions' => array(
1943 array(
1944 'field' => 'thing_type',
1945 'comparison' => '===',
1946 'value' => 'LocalBusiness',
1947 ),
1948 ),
1949 ),
1950 // Movie settings
1951 array(
1952 'type' => 'text',
1953 'name' => 'thing_settings.movie.image',
1954 'label' => esc_html__( 'Image', 'strong-testimonials' ),
1955 'description' => esc_html__( '(Required) The URL of a movie image.', 'strong-testimonials' ),
1956 'default' => $ts( 'movie', 'image' ),
1957 'conditions' => array(
1958 array(
1959 'field' => 'thing_type',
1960 'comparison' => '===',
1961 'value' => 'Movie',
1962 ),
1963 ),
1964 ),
1965 array(
1966 'type' => 'text',
1967 'name' => 'thing_settings.movie.date_created',
1968 'label' => esc_html__( 'Date Created', 'strong-testimonials' ),
1969 'description' => esc_html__( 'The created date of the movie.', 'strong-testimonials' ),
1970 'default' => $ts( 'movie', 'date_created' ),
1971 'conditions' => array(
1972 array(
1973 'field' => 'thing_type',
1974 'comparison' => '===',
1975 'value' => 'Movie',
1976 ),
1977 ),
1978 ),
1979 array(
1980 'type' => 'text',
1981 'name' => 'thing_settings.movie.director',
1982 'label' => esc_html__( 'Director', 'strong-testimonials' ),
1983 'description' => esc_html__( 'The director of the movie.', 'strong-testimonials' ),
1984 'default' => $ts( 'movie', 'director' ),
1985 'conditions' => array(
1986 array(
1987 'field' => 'thing_type',
1988 'comparison' => '===',
1989 'value' => 'Movie',
1990 ),
1991 ),
1992 ),
1993 // Product settings
1994 array(
1995 'type' => 'text',
1996 'name' => 'thing_settings.product.sku',
1997 'label' => esc_html__( 'Product SKU', 'strong-testimonials' ),
1998 'default' => $ts( 'product', 'sku' ),
1999 'conditions' => array(
2000 array(
2001 'field' => 'thing_type',
2002 'comparison' => '===',
2003 'value' => 'Product',
2004 ),
2005 ),
2006 ),
2007 array(
2008 'type' => 'text',
2009 'name' => 'thing_settings.product.brand',
2010 'label' => esc_html__( 'Product Brand', 'strong-testimonials' ),
2011 'default' => $ts( 'product', 'brand' ),
2012 'conditions' => array(
2013 array(
2014 'field' => 'thing_type',
2015 'comparison' => '===',
2016 'value' => 'Product',
2017 ),
2018 ),
2019 ),
2020 array(
2021 'type' => 'text',
2022 'name' => 'thing_settings.product.image',
2023 'label' => esc_html__( 'Product Image', 'strong-testimonials' ),
2024 'description' => esc_html__( '(Required)', 'strong-testimonials' ),
2025 'default' => $ts( 'product', 'image' ),
2026 'conditions' => array(
2027 array(
2028 'field' => 'thing_type',
2029 'comparison' => '===',
2030 'value' => 'Product',
2031 ),
2032 ),
2033 ),
2034 array(
2035 'type' => 'text',
2036 'name' => 'thing_settings.product.price',
2037 'label' => esc_html__( 'Product Price', 'strong-testimonials' ),
2038 'description' => esc_html__( '(Required)', 'strong-testimonials' ),
2039 'default' => $ts( 'product', 'price' ),
2040 'conditions' => array(
2041 array(
2042 'field' => 'thing_type',
2043 'comparison' => '===',
2044 'value' => 'Product',
2045 ),
2046 ),
2047 ),
2048 array(
2049 'type' => 'text',
2050 'name' => 'thing_settings.product.currency',
2051 'label' => esc_html__( 'Product Currency', 'strong-testimonials' ),
2052 'description' => esc_html__( 'ISO 4217 Currency code. Example: EUR.', 'strong-testimonials' ),
2053 'default' => $ts( 'product', 'currency' ),
2054 'conditions' => array(
2055 array(
2056 'field' => 'thing_type',
2057 'comparison' => '===',
2058 'value' => 'Product',
2059 ),
2060 ),
2061 ),
2062 array(
2063 'type' => 'text',
2064 'name' => 'thing_settings.product.url',
2065 'label' => esc_html__( 'Product URL', 'strong-testimonials' ),
2066 'default' => $ts( 'product', 'url' ),
2067 'conditions' => array(
2068 array(
2069 'field' => 'thing_type',
2070 'comparison' => '===',
2071 'value' => 'Product',
2072 ),
2073 ),
2074 ),
2075 array(
2076 'type' => 'text',
2077 'name' => 'thing_settings.product.price_valid_until',
2078 'label' => esc_html__( 'Price Valid Until', 'strong-testimonials' ),
2079 'description' => esc_html__( 'The date after which the price will no longer be available.', 'strong-testimonials' ),
2080 'default' => $ts( 'product', 'price_valid_until' ),
2081 'conditions' => array(
2082 array(
2083 'field' => 'thing_type',
2084 'comparison' => '===',
2085 'value' => 'Product',
2086 ),
2087 ),
2088 ),
2089 array(
2090 'type' => 'select',
2091 'name' => 'thing_settings.product.availability',
2092 'label' => esc_html__( 'Product Availability', 'strong-testimonials' ),
2093 'options' => $availability_options,
2094 'default' => $ts( 'product', 'availability', 'InStock' ),
2095 'conditions' => array(
2096 array(
2097 'field' => 'thing_type',
2098 'comparison' => '===',
2099 'value' => 'Product',
2100 ),
2101 ),
2102 ),
2103 // SoftwareApplication settings
2104 array(
2105 'type' => 'text',
2106 'name' => 'thing_settings.software_application.price',
2107 'label' => esc_html__( 'Price', 'strong-testimonials' ),
2108 'description' => esc_html__( '(Required) Setting Price to 0 will disable Offer markup.', 'strong-testimonials' ),
2109 'default' => $ts( 'software_application', 'price' ),
2110 'conditions' => array(
2111 array(
2112 'field' => 'thing_type',
2113 'comparison' => '===',
2114 'value' => 'SoftwareApplication',
2115 ),
2116 ),
2117 ),
2118 array(
2119 'type' => 'text',
2120 'name' => 'thing_settings.software_application.price_currency',
2121 'label' => esc_html__( 'Price Currency', 'strong-testimonials' ),
2122 'description' => esc_html__( 'ISO 4217 Currency code. Example: EUR.', 'strong-testimonials' ),
2123 'default' => $ts( 'software_application', 'price_currency' ),
2124 'conditions' => array(
2125 array(
2126 'field' => 'thing_type',
2127 'comparison' => '===',
2128 'value' => 'SoftwareApplication',
2129 ),
2130 ),
2131 ),
2132 array(
2133 'type' => 'text',
2134 'name' => 'thing_settings.software_application.os',
2135 'label' => esc_html__( 'Operating System', 'strong-testimonials' ),
2136 'default' => $ts( 'software_application', 'os' ),
2137 'conditions' => array(
2138 array(
2139 'field' => 'thing_type',
2140 'comparison' => '===',
2141 'value' => 'SoftwareApplication',
2142 ),
2143 ),
2144 ),
2145 array(
2146 'type' => 'text',
2147 'name' => 'thing_settings.software_application.category',
2148 'label' => esc_html__( 'Application Category', 'strong-testimonials' ),
2149 'default' => $ts( 'software_application', 'category' ),
2150 'conditions' => array(
2151 array(
2152 'field' => 'thing_type',
2153 'comparison' => '===',
2154 'value' => 'SoftwareApplication',
2155 ),
2156 ),
2157 ),
2158 array(
2159 'type' => 'separator',
2160 'name' => 'sep_page',
2161 ),
2162 // ---- Page Settings section ----
2163 array(
2164 'type' => 'paragraph',
2165 'name' => 'section_page_settings',
2166 'label' => esc_html__( 'Page Settings', 'strong-testimonials' ),
2167 ),
2168 array(
2169 'type' => 'select',
2170 'name' => 'single_product',
2171 'label' => esc_html__( 'Single Testimonial Page', 'strong-testimonials' ),
2172 'options' => $single_product_options,
2173 'default' => isset( $options['single_product'] ) ? $options['single_product'] : '',
2174 ),
2175 ),
2176 );
2177
2178 // Aggregate Rating subtab — informative, no save, custom React component
2179 $aggregate_config = array(
2180 'option' => '',
2181 'remove_button' => false,
2182 'fields' => array(
2183 array(
2184 'type' => 'aggregate_rating',
2185 'name' => 'aggregate_rating_display',
2186 ),
2187 ),
2188 );
2189
2190 return array(
2191 'review_markup_fields' => array(
2192 'label' => esc_html__( 'Markup Fields', 'strong-testimonials' ),
2193 'locked' => $locked,
2194 'badge' => $badge,
2195 'message' => $message,
2196 'config' => $fields_config,
2197 ),
2198 'review_markup_aggregate' => array(
2199 'label' => esc_html__( 'Aggregate Rating', 'strong-testimonials' ),
2200 'locked' => $locked,
2201 'badge' => $badge,
2202 'message' => $message,
2203 'config' => $aggregate_config,
2204 ),
2205 );
2206 }
2207
2208 /**
2209 * Return available fields and display types for the client section builder.
2210 *
2211 * @return array
2212 */
2213 public function get_client_section_options() {
2214 $allowed_record_types = array( 'custom', 'optional', 'builtin' );
2215
2216 $raw_custom = array_filter(
2217 wpmtst_get_custom_fields(),
2218 function ( $field ) use ( $allowed_record_types ) {
2219 if ( ! in_array( $field['record_type'], $allowed_record_types, true ) ) {
2220 return false;
2221 }
2222 if ( 'category' === strtok( $field['input_type'], '-' ) ) {
2223 return false;
2224 }
2225 if ( 'email' === $field['input_type'] ) {
2226 return false;
2227 }
2228 return true;
2229 }
2230 );
2231
2232 $custom_fields = array_values(
2233 array_map(
2234 function ( $f ) {
2235 return array(
2236 'name' => $f['name'],
2237 'label' => isset( $f['label'] ) ? $f['label'] : $f['name'],
2238 'input_type' => $f['input_type'],
2239 );
2240 },
2241 $raw_custom
2242 )
2243 );
2244
2245 $raw_builtin = wpmtst_get_builtin_fields();
2246 $builtin_fields = array_values(
2247 array_map(
2248 function ( $f ) {
2249 return array(
2250 'name' => $f['name'],
2251 'label' => isset( $f['label'] ) ? $f['label'] : $f['name'],
2252 'input_type' => $f['input_type'],
2253 );
2254 },
2255 $raw_builtin
2256 )
2257 );
2258
2259 $url_fields = array_values(
2260 array_filter(
2261 $custom_fields,
2262 function ( $f ) {
2263 return 'url' === $f['input_type'];
2264 }
2265 )
2266 );
2267
2268 $raw_types = apply_filters(
2269 'wpmtst_view_field_inputs_types',
2270 array(
2271 'text' => esc_html__( 'text', 'strong-testimonials' ),
2272 'link' => esc_html__( 'link with another field', 'strong-testimonials' ),
2273 'link2' => esc_html__( 'link (must be URL type)', 'strong-testimonials' ),
2274 'date' => esc_html__( 'date', 'strong-testimonials' ),
2275 'category' => esc_html__( 'category', 'strong-testimonials' ),
2276 'rating' => esc_html__( 'rating', 'strong-testimonials' ),
2277 'platform' => esc_html__( 'platform', 'strong-testimonials' ),
2278 'shortcode' => esc_html__( 'shortcode', 'strong-testimonials' ),
2279 'checkbox' => esc_html__( 'checkbox', 'strong-testimonials' ),
2280 )
2281 );
2282
2283 $types = array();
2284 foreach ( $raw_types as $value => $label ) {
2285 $types[] = array(
2286 'value' => $value,
2287 'label' => $label,
2288 );
2289 }
2290
2291 return array(
2292 'custom_fields' => $custom_fields,
2293 'builtin_fields' => $builtin_fields,
2294 'url_fields' => $url_fields,
2295 'types' => $types,
2296 );
2297 }
2298
2299 /**
2300 * Return aggregate rating data for the REST API.
2301 *
2302 * @return array
2303 */
2304 public function get_aggregate_rating() {
2305 $args = array(
2306 'posts_per_page' => -1,
2307 'post_type' => 'wpm-testimonial',
2308 'post_status' => 'publish',
2309 'suppress_filters' => true,
2310 );
2311 $testimonial_count = count( get_posts( $args ) );
2312
2313 $aggregate = get_option( 'wpmtst_aggregate_rating', array() );
2314 $timestamp = get_option( 'wpmtst_aggregate_recalculated', null );
2315 $can_recalculate = isset( $GLOBALS['strong_review_markup'] )
2316 && isset( $GLOBALS['strong_review_markup']->counter );
2317
2318 return array(
2319 'testimonial_count' => $testimonial_count,
2320 'review_count' => isset( $aggregate['review_count'] ) ? $aggregate['review_count'] : null,
2321 'rating_count' => isset( $aggregate['rating_count'] ) ? $aggregate['rating_count'] : null,
2322 'rating_total' => isset( $aggregate['rating_total'] ) ? $aggregate['rating_total'] : null,
2323 'rating_value' => isset( $aggregate['rating_value'] ) ? $aggregate['rating_value'] : null,
2324 'last_updated' => $timestamp
2325 ? date_i18n( get_option( 'date_format' ) . ' @ ' . get_option( 'time_format' ), $timestamp )
2326 : null,
2327 'last_updated_diff' => $timestamp ? human_time_diff( $timestamp, current_time( 'U' ) ) : null,
2328 'can_recalculate' => $can_recalculate,
2329 );
2330 }
2331
2332 /**
2333 * Trigger aggregate recalculation directly via the Pro counter and return fresh data.
2334 *
2335 * @return array
2336 */
2337 public function recalculate_aggregate() {
2338 if ( isset( $GLOBALS['strong_review_markup'] ) && isset( $GLOBALS['strong_review_markup']->counter ) ) {
2339 $GLOBALS['strong_review_markup']->counter->update_aggregates();
2340 }
2341 return $this->get_aggregate_rating();
2342 }
2343
2344 // =========================================================================
2345 // Public API Methods
2346 // =========================================================================
2347
2348 /**
2349 * Return the full tabs configuration for the React settings page.
2350 *
2351 * Each tab has:
2352 * - label : Display name shown in the navigation bar
2353 * - slug : URL-friendly identifier (used as ?tab= query param)
2354 * - subtabs : Associative array of accordion panels, each with label/locked/badge/config
2355 *
2356 * Each subtab config has:
2357 * - option : WordPress option name that will be updated on save
2358 * - fields : Array of field definitions consumed by React SettingsForm
2359 *
2360 * @return array
2361 */
2362 public function get_tabs() {
2363 $ext = null;
2364 if ( class_exists( 'Strong_Testimonials_Pro\Extensions\Extensions' ) ) {
2365 $ext = Strong_Testimonials_Pro\Extensions\Extensions::get_instance();
2366 }
2367
2368 $has_valid_license = $ext && $ext->has_valid_plan();
2369
2370 $roles_lock = $this->build_lock_info(
2371 'strong-testimonials-role-management',
2372 esc_html__( 'Activate the Role Management extension in Strong Testimonials Pro to manage role capabilities.', 'strong-testimonials' ),
2373 $ext
2374 );
2375 $mailchimp_lock = $this->build_lock_info(
2376 'strong-testimonials-mailchimp',
2377 esc_html__( 'Activate the Mailchimp extension in Strong Testimonials Pro to manage Mailchimp lists.', 'strong-testimonials' ),
2378 $ext
2379 );
2380 $assignment_lock = $this->build_lock_info(
2381 'strong-testimonials-assignment',
2382 esc_html__( 'Activate the Assignment extension in Strong Testimonials Pro to manage post assignments.', 'strong-testimonials' ),
2383 $ext
2384 );
2385 $properties_lock = $this->build_lock_info(
2386 'strong-testimonials-properties',
2387 esc_html__( 'Activate the Properties extension in Strong Testimonials Pro to manage post type properties.', 'strong-testimonials' ),
2388 $ext
2389 );
2390 $review_markup_lock = $this->build_lock_info(
2391 'strong-testimonials-review-markup',
2392 esc_html__( 'Activate the Review Markup extension in Strong Testimonials Pro to manage review markup settings.', 'strong-testimonials' ),
2393 $ext
2394 );
2395 $captcha_lock = $this->build_lock_info(
2396 'strong-testimonials-captcha',
2397 esc_html__( 'Activate the Captcha extension in Strong Testimonials Pro to manage spam control settings.', 'strong-testimonials' ),
2398 $ext
2399 );
2400
2401 $tabs = array(
2402 array(
2403 'label' => esc_html__( 'General', 'strong-testimonials' ),
2404 'slug' => 'general',
2405 'subtabs' => array(
2406 'comments' => array(
2407 'label' => esc_html__( 'Comments', 'strong-testimonials' ),
2408 'locked' => false,
2409 'badge' => '',
2410 'config' => apply_filters( 'wpmtst_react_general_comments_config', $this->get_general_comments() ),
2411 ),
2412 'embed' => array(
2413 'label' => esc_html__( 'Embed', 'strong-testimonials' ),
2414 'locked' => false,
2415 'badge' => '',
2416 'config' => apply_filters( 'wpmtst_react_general_embed_config', $this->get_general_embed() ),
2417 ),
2418 'link-control' => array(
2419 'label' => esc_html__( 'Link Control', 'strong-testimonials' ),
2420 'locked' => false,
2421 'badge' => '',
2422 'config' => apply_filters( 'wpmtst_react_general_link_control_config', $this->get_general_link_control() ),
2423 ),
2424 'debug' => array(
2425 'label' => esc_html__( 'Debug', 'strong-testimonials' ),
2426 'locked' => false,
2427 'badge' => '',
2428 'config' => apply_filters( 'wpmtst_react_general_debugt_config', $this->get_general_debug() ),
2429 ),
2430 ),
2431 ),
2432 array(
2433 'label' => esc_html__( 'Role Management', 'strong-testimonials' ),
2434 'slug' => 'role-management',
2435 'subtabs' => apply_filters(
2436 'wpmtst_react_role_management_subtabs',
2437 array(
2438 'role-management' => array(
2439 'label' => esc_html__( 'Role Management', 'strong-testimonials' ),
2440 'locked' => $roles_lock['locked'],
2441 'badge' => $roles_lock['badge'],
2442 'message' => $roles_lock['message'],
2443 'config' => $this->get_roles(),
2444 ),
2445 )
2446 ),
2447 ),
2448 array(
2449 'label' => esc_html__( 'Mailchimp', 'strong-testimonials' ),
2450 'slug' => 'mailchimp',
2451 'subtabs' => array(
2452 'mailchimp' => array(
2453 'label' => esc_html__( 'Mailchimp', 'strong-testimonials' ),
2454 'locked' => $mailchimp_lock['locked'],
2455 'badge' => $mailchimp_lock['badge'],
2456 'message' => $mailchimp_lock['message'],
2457 'config' => apply_filters( 'wpmtst_react_mailchimp_admin_config', $this->get_mailchimp_admin() ),
2458 ),
2459 ),
2460 ),
2461 array(
2462 'label' => esc_html__( 'Assignment', 'strong-testimonials' ),
2463 'slug' => 'assignment',
2464 'subtabs' => apply_filters(
2465 'wpmtst_react_assignment_subtabs',
2466 array(
2467 'assignment' => array(
2468 'label' => esc_html__( 'Assignment', 'strong-testimonials' ),
2469 'locked' => $assignment_lock['locked'],
2470 'badge' => $assignment_lock['badge'],
2471 'message' => $assignment_lock['message'],
2472 'config' => apply_filters( 'wpmtst_react_assignment_admin_config', $this->get_assignment_fields() ),
2473 ),
2474 )
2475 ),
2476 ),
2477 array(
2478 'label' => esc_html__( 'Advanced Controls', 'strong-testimonials' ),
2479 'slug' => 'properties',
2480 'subtabs' => $this->get_properties_subtabs( $properties_lock ),
2481 ),
2482 array(
2483 'label' => esc_html__( 'Review Markup', 'strong-testimonials' ),
2484 'slug' => 'review-markup',
2485 'subtabs' => $this->get_review_markup_subtabs( $review_markup_lock ),
2486 ),
2487 array(
2488 'label' => esc_html__( 'Spam Control', 'strong-testimonials' ),
2489 'slug' => 'spam-control',
2490 'subtabs' => array(
2491 'honeypot' => array(
2492 'label' => esc_html__( 'Honeypot', 'strong-testimonials' ),
2493 'locked' => $captcha_lock['locked'],
2494 'badge' => $captcha_lock['badge'],
2495 'message' => $captcha_lock['message'],
2496 'config' => $this->get_spam_control_honeypot(),
2497 ),
2498 'turnstile' => array(
2499 'label' => esc_html__( 'Turnstile', 'strong-testimonials' ),
2500 'locked' => $captcha_lock['locked'],
2501 'badge' => $captcha_lock['badge'],
2502 'message' => $captcha_lock['message'],
2503 'config' => $this->get_spam_control_turnstile(),
2504 ),
2505 'captcha' => array(
2506 'label' => esc_html__( 'Captcha', 'strong-testimonials' ),
2507 'locked' => $captcha_lock['locked'],
2508 'badge' => $captcha_lock['badge'],
2509 'message' => $captcha_lock['message'],
2510 'config' => $this->get_spam_control_captcha(),
2511 ),
2512 'ip-restriction' => array(
2513 'label' => esc_html__( 'IP Restriction', 'strong-testimonials' ),
2514 'locked' => $captcha_lock['locked'],
2515 'badge' => $captcha_lock['badge'],
2516 'message' => $captcha_lock['message'],
2517 'config' => $this->get_spam_control_ip(),
2518 ),
2519 ),
2520 ),
2521 );
2522
2523 return apply_filters( 'wpmtst_react_settings_tabs', $tabs );
2524 }
2525
2526 /**
2527 * Return the current saved settings values (used by the GET /general-settings endpoint).
2528 *
2529 * @return array
2530 */
2531 public function get_settings() {
2532 $settings = array(
2533 self::OPTION_GENERAL => get_option( self::OPTION_GENERAL, array() ),
2534 self::OPTION_ADVANCED => get_option( self::OPTION_ADVANCED, array() ),
2535 self::OPTION_PROPERTIES => get_option( self::OPTION_PROPERTIES, array() ),
2536 self::OPTION_REVIEW_MARKUP => get_option( self::OPTION_REVIEW_MARKUP, array() ),
2537 );
2538
2539 return apply_filters( 'wpmtst_react_settings_values', $settings );
2540 }
2541
2542 // =========================================================================
2543 // Spam Control Subtab Configs
2544 // =========================================================================
2545
2546 private function get_spam_control_honeypot() {
2547 $options = get_option( 'wpmtst_form_options', array() );
2548 return array(
2549 'option' => 'wpmtst_form_options',
2550 'fields' => array(
2551 $this->build_toggle_field(
2552 'honeypot_before',
2553 esc_html__( 'Honeypot Before', 'strong-testimonials' ),
2554 ! empty( $options['honeypot_before'] ),
2555 array(
2556 'description' => esc_html__( 'Adds an invisible empty field. Spambots fill every field — empty = human, not empty = spambot.', 'strong-testimonials' ),
2557 )
2558 ),
2559 $this->build_toggle_field(
2560 'honeypot_after',
2561 esc_html__( 'Honeypot After', 'strong-testimonials' ),
2562 ! empty( $options['honeypot_after'] ),
2563 array(
2564 'description' => esc_html__( 'Adds a hidden field on submit via JavaScript. Spambots cannot run JS so the field is never added — missing field = spambot.', 'strong-testimonials' ),
2565 )
2566 ),
2567 ),
2568 );
2569 }
2570
2571 private function get_spam_control_turnstile() {
2572 $options = get_option( 'wpmtst_form_options', array() );
2573 $cond_on = array(
2574 array(
2575 'field' => 'enable_turnstile',
2576 'comparison' => '===',
2577 'value' => true,
2578 ),
2579 );
2580 return array(
2581 'option' => 'wpmtst_form_options',
2582 'fields' => array(
2583 $this->build_toggle_field(
2584 'enable_turnstile',
2585 esc_html__( 'Enable Cloudflare Turnstile', 'strong-testimonials' ),
2586 ! empty( $options['enable_turnstile'] )
2587 ),
2588 $this->build_text_field(
2589 'turnstile_site_key',
2590 esc_html__( 'Site Key', 'strong-testimonials' ),
2591 $options['turnstile_site_key'] ?? '',
2592 array( 'conditions' => $cond_on )
2593 ),
2594 $this->build_text_field(
2595 'turnstile_secret_key',
2596 esc_html__( 'Secret Key', 'strong-testimonials' ),
2597 $options['turnstile_secret_key'] ?? '',
2598 array( 'conditions' => $cond_on )
2599 ),
2600 $this->build_text_field(
2601 'turnstile_label',
2602 esc_html__( 'Label', 'strong-testimonials' ),
2603 $options['turnstile_label'] ?? _x( 'Captcha', 'Default label for Captcha field on submission form.', 'strong-testimonials' ),
2604 array( 'conditions' => $cond_on )
2605 ),
2606 ),
2607 );
2608 }
2609
2610 private function get_spam_control_captcha() {
2611 $options = get_option( 'wpmtst_form_options', array() );
2612 $cond_on = array(
2613 array(
2614 'field' => 'enable_recaptcha',
2615 'comparison' => '===',
2616 'value' => true,
2617 ),
2618 );
2619 return array(
2620 'option' => 'wpmtst_form_options',
2621 'fields' => array(
2622 $this->build_toggle_field(
2623 'enable_recaptcha',
2624 esc_html__( 'Enable Google reCAPTCHA', 'strong-testimonials' ),
2625 ! empty( $options['enable_recaptcha'] )
2626 ),
2627 $this->build_paragraph_field(
2628 'recaptcha_api_info',
2629 '',
2630 esc_html__( 'Register your website with Google to get required API keys and enter them below', 'strong-testimonials' )
2631 . '. <a target="_blank" href="https://www.google.com/recaptcha/admin#list">'
2632 . esc_html__( 'Get the API Keys', 'strong-testimonials' )
2633 . '</a>',
2634 array( 'conditions' => $cond_on )
2635 ),
2636 array(
2637 'type' => self::FIELD_TYPE_SELECT,
2638 'name' => 'recaptcha_version',
2639 'label' => esc_html__( 'Version', 'strong-testimonials' ),
2640 'default' => $options['recaptcha_version'] ?? 'v2',
2641 'options' => array(
2642 array(
2643 'value' => 'v2',
2644 'label' => 'reCAPTCHA v2',
2645 ),
2646 array(
2647 'value' => 'invisible',
2648 'label' => esc_html__( 'Invisible reCAPTCHA badge', 'strong-testimonials' ),
2649 ),
2650 array(
2651 'value' => 'v3',
2652 'label' => 'reCAPTCHA v3',
2653 ),
2654 ),
2655 'conditions' => $cond_on,
2656 ),
2657 $this->build_text_field(
2658 'recaptcha_site_key',
2659 esc_html__( 'Site Key', 'strong-testimonials' ),
2660 $options['recaptcha_site_key'] ?? '',
2661 array( 'conditions' => $cond_on )
2662 ),
2663 $this->build_text_field(
2664 'recaptcha_secret_key',
2665 esc_html__( 'Secret Key', 'strong-testimonials' ),
2666 $options['recaptcha_secret_key'] ?? '',
2667 array( 'conditions' => $cond_on )
2668 ),
2669 $this->build_text_field(
2670 'recaptcha_label',
2671 esc_html__( 'Label', 'strong-testimonials' ),
2672 $options['recaptcha_label'] ?? _x( 'Captcha', 'Default label for Captcha field on submission form.', 'strong-testimonials' ),
2673 array( 'conditions' => $cond_on )
2674 ),
2675 $this->build_number_field(
2676 'recaptcha_score',
2677 esc_html__( 'Minimum Score (v3 only)', 'strong-testimonials' ),
2678 isset( $options['recaptcha_score'] ) ? (float) $options['recaptcha_score'] : 0.5,
2679 array(
2680 'description' => esc_html__( 'Score from 0.0 to 1.0. Users scoring below this threshold are blocked.', 'strong-testimonials' ),
2681 'min' => 0,
2682 'max' => 1,
2683 'step' => 0.1,
2684 'conditions' => array(
2685 array(
2686 'field' => 'enable_recaptcha',
2687 'comparison' => '===',
2688 'value' => true,
2689 ),
2690 array(
2691 'field' => 'recaptcha_version',
2692 'comparison' => '===',
2693 'value' => 'v3',
2694 ),
2695 ),
2696 )
2697 ),
2698 ),
2699 );
2700 }
2701
2702 private function get_spam_control_ip() {
2703 $options = get_option( 'wpmtst_form_options', array() );
2704 $raw = $options['restrict_ip'] ?? array();
2705 $default = is_array( $raw )
2706 ? implode( "\n", array_filter( $raw ) )
2707 : (string) $raw;
2708 return array(
2709 'option' => 'wpmtst_form_options',
2710 'fields' => array(
2711 array(
2712 'type' => 'textarea',
2713 'name' => 'restrict_ip',
2714 'label' => esc_html__( 'Blocked IP Addresses', 'strong-testimonials' ),
2715 'default' => $default,
2716 'description' => esc_html__( 'Enter each IP address on a new line. Submissions from blocked IPs will be rejected.', 'strong-testimonials' ),
2717 ),
2718 ),
2719 );
2720 }
2721
2722 /**
2723 * Return the sanitization schema consumed by the REST API save handler.
2724 *
2725 * Structure: array( 'option_name' => array( 'field_name' => array( 'sanitizer' ) ) )
2726 *
2727 * @return array
2728 */
2729 public function settings_sanitization() {
2730 $schema = array(
2731 self::OPTION_GENERAL => array(
2732 'support_custom_fields' => array( 'bool' ),
2733 'disable_rewrite' => array( 'bool' ),
2734 'single_testimonial_slug' => array( 'text' ),
2735 'touch_enabled' => array( 'bool' ),
2736 'scrolltop' => array( 'bool' ),
2737 'scrolltop_offset' => array( 'number' ),
2738 'remove_whitespace' => array( 'bool' ),
2739 'support_comments' => array( 'bool' ),
2740 'embed_width' => array( 'number' ),
2741 'nofollow' => array( 'bool' ),
2742 'noopener' => array( 'bool' ),
2743 'noreferrer' => array( 'bool' ),
2744 'lazyload' => array( 'bool' ),
2745 'no_lazyload_plugin' => array( 'bool' ),
2746 'disable_upsells' => array( 'bool' ),
2747 ),
2748 self::OPTION_ADVANCED => array(
2749 'debug_log' => array( 'bool' ),
2750 ),
2751 self::OPTION_MAILCHIMP => array(
2752 'mailchimp_api_key' => array( 'text' ),
2753 ),
2754 self::OPTION_ASSIGNMENT => array(
2755 'assignees' => array( 'text' ),
2756 'selection' => array( 'text' ),
2757 ),
2758 );
2759
2760 $schema[ self::OPTION_PROPERTIES ] = array(
2761 'cpt' => array(
2762 'labels' => array(
2763 'name' => array( 'text' ),
2764 'singular_name' => array( 'text' ),
2765 ),
2766 'menu_icon' => array( 'text' ),
2767 'menu_position' => array( 'number' ),
2768 'show_in_admin_bar' => array( 'bool' ),
2769 'show_in_nav_menus' => array( 'bool' ),
2770 'can_export' => array( 'bool' ),
2771 'publicly_queryable' => array( 'bool' ),
2772 'query_var' => array(
2773 'on' => array( 'bool' ),
2774 'using' => array( 'enum' => array( 'default', 'custom' ) ),
2775 'name' => array( 'text' ),
2776 ),
2777 'rewrite' => array(
2778 'on' => array( 'bool' ),
2779 'slug' => array( 'text' ),
2780 'with_front' => array( 'bool' ),
2781 'feeds' => array( 'bool' ),
2782 'pages' => array( 'bool' ),
2783 ),
2784 'has_archive' => array(
2785 'on' => array( 'bool' ),
2786 'using' => array( 'enum' => array( 'current', 'custom' ) ),
2787 'slug' => array( 'text' ),
2788 ),
2789 'exclude_from_search' => array( 'bool' ),
2790 // supports passes through unsanitized (closed set of known strings).
2791 ),
2792 'tax' => array(
2793 'labels' => array(
2794 'name' => array( 'text' ),
2795 'singular_name' => array( 'text' ),
2796 ),
2797 'publicly_queryable' => array( 'bool' ),
2798 'query_var' => array(
2799 'on' => array( 'bool' ),
2800 'using' => array( 'enum' => array( 'default', 'custom' ) ),
2801 ),
2802 'rewrite' => array(
2803 'on' => array( 'bool' ),
2804 'slug' => array( 'text' ),
2805 'with_front' => array( 'bool' ),
2806 'hierarchical' => array( 'bool' ),
2807 ),
2808 ),
2809 );
2810
2811 $thing_settings_schema = array(
2812 'course' => array(
2813 'provider' => array( 'text' ),
2814 'provider_name' => array( 'text' ),
2815 ),
2816 'event' => array(
2817 'street_address' => array( 'text' ),
2818 'locality' => array( 'text' ),
2819 'region' => array( 'text' ),
2820 'postal_code' => array( 'text' ),
2821 'country' => array( 'text' ),
2822 'venue_name' => array( 'text' ),
2823 'start_date' => array( 'text' ),
2824 'end_date' => array( 'text' ),
2825 'image' => array( 'text' ),
2826 'performer' => array( 'text' ),
2827 'performer_name' => array( 'text' ),
2828 'ticket_url' => array( 'text' ),
2829 'offer_price' => array( 'text' ),
2830 'offer_currency' => array( 'text' ),
2831 'offer_availability' => array( 'text' ),
2832 'offer_availability_starts' => array( 'text' ),
2833 ),
2834 'localbusiness' => array(
2835 'address' => array( 'text' ),
2836 'image' => array( 'text' ),
2837 'price_range' => array( 'text' ),
2838 'telephone' => array( 'text' ),
2839 ),
2840 'movie' => array(
2841 'image' => array( 'text' ),
2842 'date_created' => array( 'text' ),
2843 'director' => array( 'text' ),
2844 ),
2845 'product' => array(
2846 'sku' => array( 'text' ),
2847 'brand' => array( 'text' ),
2848 'image' => array( 'text' ),
2849 'price' => array( 'text' ),
2850 'url' => array( 'text' ),
2851 'currency' => array( 'text' ),
2852 'price_valid_until' => array( 'text' ),
2853 'availability' => array( 'text' ),
2854 ),
2855 'software_application' => array(
2856 'price' => array( 'text' ),
2857 'price_currency' => array( 'text' ),
2858 'os' => array( 'text' ),
2859 'category' => array( 'text' ),
2860 ),
2861 );
2862
2863 $schema[ self::OPTION_REVIEW_MARKUP ] = array(
2864 'content_field' => array( 'text' ),
2865 'author_field' => array( 'text' ),
2866 'company_field' => array( 'text' ),
2867 'company_url' => array( 'text' ),
2868 'rating_field' => array( 'text' ),
2869 'use_max_rating' => array( 'number' ),
2870 'include_ratings' => array( 'bool' ),
2871 'single_product' => array( 'text' ),
2872 'product_name' => array( 'text' ),
2873 'product_id' => array( 'text' ),
2874 'thing_type' => array( 'text' ),
2875 'thing_description' => array( 'text' ),
2876 'thing_settings' => $thing_settings_schema,
2877 );
2878
2879 // Role Management: build schema dynamically from WP roles when the extension is active.
2880 if ( class_exists( 'Strong_Testimonials_Pro\Extensions\Role_Management\Role_Management' ) ) {
2881 global $wp_roles;
2882
2883 $capabilities_list = array(
2884 'read_testimonial',
2885 'edit_testimonial',
2886 'delete_testimonial',
2887 'read_private_testimonials',
2888 'edit_testimonials',
2889 'edit_other_testimonials',
2890 'publish_testimonials',
2891 'delete_testimonials',
2892 'delete_others_testimonials',
2893 'strong_testimonials_options',
2894 );
2895
2896 $roles_schema = array();
2897
2898 foreach ( $wp_roles->roles as $role_key => $wp_role ) {
2899 if ( 'administrator' === $role_key ) {
2900 continue;
2901 }
2902
2903 $roles_schema[ $role_key ] = array( 'enabled' => array( 'bool' ) );
2904
2905 foreach ( $capabilities_list as $cap ) {
2906 $roles_schema[ $role_key ][ $cap ] = array( 'bool' );
2907 }
2908
2909 if ( in_array( $role_key, array( 'subscriber', 'contributor' ), true ) ) {
2910 $roles_schema[ $role_key ]['upload_files'] = array( 'bool' );
2911 }
2912 }
2913
2914 $schema[ self::OPTION_ROLES ] = $roles_schema;
2915 }
2916
2917 $schema['wpmtst_form_options'] = array(
2918 'honeypot_before' => array( 'bool' ),
2919 'honeypot_after' => array( 'bool' ),
2920 'enable_turnstile' => array( 'bool' ),
2921 'turnstile_site_key' => array( 'text' ),
2922 'turnstile_secret_key' => array( 'text' ),
2923 'turnstile_label' => array( 'text' ),
2924 'enable_recaptcha' => array( 'bool' ),
2925 'recaptcha_version' => array( 'enum' => array( 'v2', 'invisible', 'v3' ) ),
2926 'recaptcha_site_key' => array( 'text' ),
2927 'recaptcha_secret_key' => array( 'text' ),
2928 'recaptcha_label' => array( 'text' ),
2929 'recaptcha_score' => array( 'float' ),
2930 'restrict_ip' => array( 'ip_list' ),
2931 );
2932
2933 return apply_filters( 'wpmtst_react_settings_sanitization', $schema );
2934 }
2935 }
2936