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 / rest-api / class-strong-testimonials-extensions-base.php
strong-testimonials / admin / rest-api Last commit date
class-strong-testimonials-extensions-base.php 1 week ago class-strong-testimonials-rest-api-base.php 2 weeks ago class-strong-testimonials-settings-sanitizer.php 1 month ago
class-strong-testimonials-extensions-base.php
622 lines
1 <?php
2
3 /**
4 * Extensions class for Strong Testimonials plugin.
5 */
6 class Strong_Testimonials_Extensions_Base {
7 /**
8 * Class instance.
9 *
10 * @var Extensions
11 */
12 private static $instance;
13 /**
14 * Extensions array
15 *
16 * @var array
17 */
18 public $extensions = array();
19 /**
20 * Active extensions option
21 *
22 * @var string
23 */
24 private $active_extensions = 'strong_testimonials_pro_active_extensions';
25 /**
26 * Current plan option
27 *
28 * @var string
29 */
30 private $current_plan = 'strong_testimonials_current_plan';
31 /**
32 * Active extensions cache
33 *
34 * @var array
35 */
36 private static $active_extensions_cache = array();
37 /**
38 * Plan map
39 *
40 * @var array
41 */
42 private $plan_map = array();
43 /**
44 * Initialize the extensions.
45 */
46 public function __construct() {
47 $this->create_plan_map();
48 add_action( 'init', array( $this, 'add_default_extensions' ) );
49 }
50
51 /**
52 * Create plan map
53 */
54 private function create_plan_map() {
55 $free = array();
56
57 $basic = array(
58 'divider-basic',
59 'strong-testimonials-country-selector',
60 );
61
62 $plus = array_merge(
63 $basic,
64 array(
65 'divider-plus',
66 'strong-testimonials-assignment',
67 'strong-testimonials-properties',
68 'strong-testimonials-review-markup',
69 'strong-testimonials-advanced-views',
70 'strong-testimonials-captcha',
71 'strong-testimonials-pro-templates',
72 'strong-testimonials-importer',
73 )
74 );
75
76 $business = array_merge(
77 $plus,
78 array(
79 'divider-business',
80 'strong-testimonials-infinite-scroll',
81 'strong-testimonials-emails',
82 'strong-testimonials-filters',
83 'strong-testimonials-role-management',
84 'strong-testimonials-mailchimp',
85 'strong-testimonials-custom-fields',
86 'strong-testimonials-multiple-forms',
87 'strong-testimonials-video',
88 )
89 );
90
91 $this->plan_map = array(
92 'free' => $free,
93 'basic' => $basic,
94 'plus' => $plus,
95 'business' => $business,
96 );
97 }
98
99 /**
100 * Get the instance of the class.
101 */
102 public static function get_instance() {
103 if ( ! isset( self::$instance ) || ! ( self::$instance instanceof Strong_Testimonials_Extensions_Base ) ) {
104 self::$instance = new Strong_Testimonials_Extensions_Base();
105 }
106
107 return self::$instance;
108 }
109
110 /**
111 * Add default extensions
112 */
113 public function add_default_extensions() {
114 $this->extensions = array(
115 'strong-testimonials-country-selector' => array(
116 'available' => false,
117 'enabled' => false,
118 'name' => __( 'Country Selector', 'strong-testimonials' ),
119 'slug' => 'strong-testimonials-country-selector',
120 'description' => __( 'Allow customers to select their country when submitting testimonials.', 'strong-testimonials' ),
121 ),
122 'strong-testimonials-assignment' => array(
123 'available' => false,
124 'enabled' => false,
125 'name' => __( 'Assignment', 'strong-testimonials' ),
126 'slug' => 'strong-testimonials-assignment',
127 'description' => __( 'Assign testimonials to custom post types.', 'strong-testimonials' ),
128 ),
129 'strong-testimonials-properties' => array(
130 'available' => false,
131 'enabled' => false,
132 'name' => __( 'Advanced Controls', 'strong-testimonials' ),
133 'slug' => 'strong-testimonials-properties',
134 'description' => __( 'Change properties of the testimonial post type: labels, permalink structure, admin options and post editor features.', 'strong-testimonials' ),
135 ),
136 'strong-testimonials-review-markup' => array(
137 'available' => false,
138 'enabled' => false,
139 'name' => __( 'Review Markup', 'strong-testimonials' ),
140 'slug' => 'strong-testimonials-review-markup',
141 'description' => __( 'SEO-friendly Testimonials. Take full advantage of your testimonials with our Schema.org Markup extension.', 'strong-testimonials' ),
142 ),
143 'strong-testimonials-advanced-views' => array(
144 'available' => false,
145 'enabled' => false,
146 'name' => __( 'Advanced Views', 'strong-testimonials' ),
147 'slug' => 'strong-testimonials-advanced-views',
148 'description' => __( 'Customize your testimonials beyond star ratings, reorder fields and more.', 'strong-testimonials' ),
149 ),
150 'strong-testimonials-captcha' => array(
151 'available' => false,
152 'enabled' => false,
153 'name' => __( 'Captcha', 'strong-testimonials' ),
154 'slug' => 'strong-testimonials-captcha',
155 'description' => __( 'Protect your testimonial submission forms against spam and other types of automated abuse.', 'strong-testimonials' ),
156 ),
157 'strong-testimonials-pro-templates' => array(
158 'available' => false,
159 'enabled' => false,
160 'name' => __( 'Pro Templates', 'strong-testimonials' ),
161 'slug' => 'strong-testimonials-pro-templates',
162 'description' => __( 'Create beautiful testimonial designs with a number of predesigned and easy-to-use premium templates that you can use on your website.', 'strong-testimonials' ),
163 ),
164 'strong-testimonials-importer' => array(
165 'available' => false,
166 'enabled' => false,
167 'name' => __( 'External Platform Importer', 'strong-testimonials' ),
168 'slug' => 'strong-testimonials-importer',
169 'description' => __( 'Import testimonials/reviews from other platforms like: Facebook, Google, Yelp, Zomato, Woocommerce.', 'strong-testimonials' ),
170 ),
171 'strong-testimonials-infinite-scroll' => array(
172 'available' => false,
173 'enabled' => false,
174 'name' => __( 'Infinite Scroll', 'strong-testimonials' ),
175 'slug' => 'strong-testimonials-infinite-scroll',
176 'description' => __( 'Load more testimonials on scroll. Using this extension you can control the number of testimonials that are visible on a pages first load. As the user starts scrolling down the page, more testimonials are brought into view using a continuous loading animation.', 'strong-testimonials' ),
177 ),
178 'strong-testimonials-emails' => array(
179 'available' => false,
180 'enabled' => false,
181 'name' => __( 'Enhanced Emails', 'strong-testimonials' ),
182 'slug' => 'strong-testimonials-emails',
183 'description' => __( 'Use the Enhanced Emails extension to: send a thank you email to your client once their testimonial\'s approved. Increase brand loyalty by showing you really care about your clients. Keep your clients engaged and increase your chances of selling more.', 'strong-testimonials' ),
184 ),
185 'strong-testimonials-filters' => array(
186 'available' => false,
187 'enabled' => false,
188 'name' => __( 'Filters', 'strong-testimonials' ),
189 'slug' => 'strong-testimonials-filters',
190 'description' => __( 'An extension that allows you to create filters for your testimonials. Filters can be created from categories. Import your testimonials from 3rd party sources and group them by platform, review, or whatever else you could think of.', 'strong-testimonials' ),
191 ),
192 'strong-testimonials-role-management' => array(
193 'available' => false,
194 'enabled' => false,
195 'name' => __( 'Role Management', 'strong-testimonials' ),
196 'slug' => 'strong-testimonials-role-management',
197 'description' => __( 'Granular control over who can add, edit, delete or manage testimonials. Expands on WordPress\'s current user role system.', 'strong-testimonials' ),
198 ),
199 'strong-testimonials-mailchimp' => array(
200 'available' => false,
201 'enabled' => false,
202 'name' => __( 'Mailchimp', 'strong-testimonials' ),
203 'slug' => 'strong-testimonials-mailchimp',
204 'description' => __( 'With this extension you can automatically subscribe your users to a MailChimp email list. Follow up with a targeted message or a coupon to thank them for leaving a good review. Unlock even more marketing & automation potential.', 'strong-testimonials' ),
205 ),
206 'strong-testimonials-custom-fields' => array(
207 'available' => false,
208 'enabled' => false,
209 'name' => __( 'Custom Fields', 'strong-testimonials' ),
210 'slug' => 'strong-testimonials-custom-fields',
211 'description' => __( 'Enhance your testimonials and submission forms with the Custom Fields extension to both collect and display additional information.', 'strong-testimonials' ),
212 ),
213 'strong-testimonials-multiple-forms' => array(
214 'available' => false,
215 'enabled' => false,
216 'name' => __( 'Multiple Forms', 'strong-testimonials' ),
217 'slug' => 'strong-testimonials-multiple-forms',
218 'description' => __( 'Easily collect testimonials from customers by creating and customizing multiple forms at once.', 'strong-testimonials' ),
219 ),
220 'strong-testimonials-video' => array(
221 'available' => false,
222 'enabled' => false,
223 'name' => __( 'Video', 'strong-testimonials' ),
224 'slug' => 'strong-testimonials-video',
225 'description' => __( 'Allow your customers to record and submit video testimonials directly from their browser using their camera and microphone.', 'strong-testimonials' ),
226 ),
227 );
228 }
229
230 /**
231 * Get extensions
232 *
233 * @return array
234 */
235 public function get_extensions() {
236 $active_extensions = get_option( $this->active_extensions, array() );
237 $current_plan = 'free';
238
239 $this->update_extension_status( $active_extensions, $current_plan );
240 $this->sort_extensions_by_business_order();
241
242 $divider_slugs = $this->find_divider_positions( $current_plan );
243 $ordered_extensions = $this->build_ordered_extensions();
244 $this->extensions = $this->insert_dividers( $ordered_extensions, $divider_slugs );
245
246 return $this->extensions;
247 }
248
249 /**
250 * Update enabled and available status for each extension
251 *
252 * @param array $active_extensions Active extension slugs.
253 * @param string $current_plan Current plan name.
254 */
255 private function update_extension_status( $active_extensions, $current_plan ) {
256 foreach ( $this->extensions as $extension => $data ) {
257 $this->extensions[ $extension ]['enabled'] = in_array( $extension, $active_extensions, true );
258 $this->extensions[ $extension ]['available'] = false;
259
260 if ( $current_plan && isset( $this->plan_map[ $current_plan ] ) ) {
261 if ( in_array( $extension, $this->plan_map[ $current_plan ], true ) ) {
262 $this->extensions[ $extension ]['available'] = true;
263 }
264 }
265 }
266 }
267
268 /**
269 * Sort extensions by business plan order
270 */
271 private function sort_extensions_by_business_order() {
272 $business_order = isset( $this->plan_map['business'] ) ? array_values( $this->plan_map['business'] ) : array();
273 $index_map = array_flip( $business_order );
274
275 uasort(
276 $this->extensions,
277 function ( $a, $b ) use ( $index_map ) {
278 $a_in = isset( $index_map[ $a['slug'] ] );
279 $b_in = isset( $index_map[ $b['slug'] ] );
280
281 if ( $a_in && $b_in ) {
282 return $index_map[ $a['slug'] ] - $index_map[ $b['slug'] ];
283 }
284 if ( $a_in ) {
285 return -1;
286 }
287 if ( $b_in ) {
288 return 1;
289 }
290
291 // Fallback: based on availability
292 if ( $a['available'] !== $b['available'] ) {
293 return intval( $b['available'] ) - intval( $a['available'] );
294 }
295
296 return strcmp( $a['slug'], $b['slug'] );
297 }
298 );
299 }
300
301 /**
302 * Get current plan
303 *
304 * @return string Current plan name.
305 */
306 public function get_current_plan() {
307 $plan = get_option( $this->current_plan, 'free' );
308 return isset( $this->plan_map[ $plan ] ) ? $plan : 'free';
309 }
310
311 /**
312 * Get plan upgrade hierarchy
313 *
314 * @return array Plan upgrade orders.
315 */
316 private function get_plan_upgrade_orders() {
317 return array(
318 'free' => array( 'basic', 'plus', 'business' ),
319 'basic' => array( 'plus', 'business' ),
320 'plus' => array( 'business' ),
321 'business' => array(),
322 );
323 }
324
325 /**
326 * Find positions where dividers should be placed
327 *
328 * @param string $current_plan Current plan name.
329 * @return array Divider slugs mapped to divider data (with divider_key and plan).
330 */
331 private function find_divider_positions( $current_plan ) {
332 $plan_orders = $this->get_plan_upgrade_orders();
333 $current_order = isset( $this->plan_map[ $current_plan ] ) ? array_values( $this->plan_map[ $current_plan ] ) : array();
334 $divider_slugs = array();
335
336 if ( empty( $plan_orders[ $current_plan ] ) ) {
337 return $divider_slugs;
338 }
339
340 foreach ( $plan_orders[ $current_plan ] as $next_plan ) {
341 if ( empty( $this->plan_map[ $next_plan ] ) ) {
342 continue;
343 }
344
345 $next_plan_order = array_values( $this->plan_map[ $next_plan ] );
346 $exclude_order = $this->get_exclude_order_for_plan( $current_plan, $next_plan, $current_order );
347
348 $first_unique_slug = $this->find_first_unique_extension( $next_plan_order, $exclude_order );
349 if ( $first_unique_slug ) {
350 $divider_slugs[ $first_unique_slug ] = array(
351 'divider_key' => 'divider-before-' . $first_unique_slug,
352 'plan' => $next_plan,
353 );
354 }
355 }
356
357 return $divider_slugs;
358 }
359
360 /**
361 * Get the order array to exclude when checking for unique extensions
362 *
363 * @param string $current_plan Current plan name.
364 * @param string $next_plan Next plan name.
365 * @param array $current_order Current plan order.
366 * @return array Order array to exclude.
367 */
368 private function get_exclude_order_for_plan( $current_plan, $next_plan, $current_order ) {
369 if ( 'business' === $next_plan ) {
370 if ( 'basic' === $current_plan ) {
371 return isset( $this->plan_map['plus'] ) ? array_values( $this->plan_map['plus'] ) : $current_order;
372 } elseif ( 'free' === $current_plan ) {
373 return isset( $this->plan_map['plus'] ) ? array_values( $this->plan_map['plus'] ) : $current_order;
374 }
375 }
376
377 if ( 'plus' === $next_plan && 'free' === $current_plan ) {
378 return isset( $this->plan_map['basic'] ) ? array_values( $this->plan_map['basic'] ) : $current_order;
379 }
380
381 return $current_order;
382 }
383
384 /**
385 * Find the first extension slug that's unique to the next plan
386 *
387 * @param array $next_plan_order Extension order from next plan.
388 * @param array $exclude_order Extensions to exclude.
389 * @return string|null First unique extension slug or null.
390 */
391 private function find_first_unique_extension( $next_plan_order, $exclude_order ) {
392 foreach ( $next_plan_order as $next_slug ) {
393 if ( strpos( $next_slug, 'divider-' ) === 0 ) {
394 continue;
395 }
396
397 if ( isset( $this->extensions[ $next_slug ] ) && ! in_array( $next_slug, $exclude_order, true ) ) {
398 return $next_slug;
399 }
400 }
401
402 return null;
403 }
404
405 /**
406 * Build ordered extensions array based on business plan order
407 *
408 * @return array Ordered extensions.
409 */
410 private function build_ordered_extensions() {
411 $business_order = isset( $this->plan_map['business'] ) ? array_values( $this->plan_map['business'] ) : array();
412 $ordered_extensions = array();
413
414 foreach ( $business_order as $slug ) {
415 if ( $this->is_divider_string( $slug ) ) {
416 continue;
417 }
418
419 if ( isset( $this->extensions[ $slug ] ) ) {
420 $ordered_extensions[ $slug ] = $this->extensions[ $slug ];
421 }
422 }
423
424 foreach ( $this->extensions as $slug => $data ) {
425 if ( ! isset( $ordered_extensions[ $slug ] ) ) {
426 $ordered_extensions[ $slug ] = $data;
427 }
428 }
429
430 return $ordered_extensions;
431 }
432
433 /**
434 * Check if a slug is a divider string
435 *
436 * @param string $slug Slug to check.
437 * @return bool True if divider string.
438 */
439 private function is_divider_string( $slug ) {
440 return strpos( $slug, 'divider-' ) === 0;
441 }
442
443 /**
444 * Insert dividers into the extensions array
445 *
446 * @param array $ordered_extensions Ordered extensions array.
447 * @param array $divider_slugs Divider slugs mapped to divider data (with divider_key and plan).
448 * @return array Extensions with dividers inserted.
449 */
450 private function insert_dividers( $ordered_extensions, $divider_slugs ) {
451 $extensions_with_divider = array();
452
453 foreach ( $ordered_extensions as $slug => $ext ) {
454 if ( isset( $divider_slugs[ $slug ] ) ) {
455 $divider_data = $divider_slugs[ $slug ];
456 $extensions_with_divider[ $divider_data['divider_key'] ] = array(
457 'is_divider' => true,
458 'slug' => $divider_data['divider_key'],
459 'plan' => $divider_data['plan'],
460 'url' => \WPMTST_STORE_UPGRADE_URL . '?utm_source=strong-testimonials-pro&utm_medium=extensions&utm_campaign=upgrade-to-' . $divider_data['plan'],
461 );
462 }
463 $extensions_with_divider[ $slug ] = $ext;
464 }
465
466 return $extensions_with_divider;
467 }
468
469 /**
470 * Calculate the numeric tier for a given plan.
471 *
472 * @param array $plan_hierarchy Plan to tier mapping.
473 * @param string $plan Current plan.
474 *
475 * @return int
476 */
477 private function get_plan_tier( $plan_hierarchy, $plan ) {
478 return $plan_hierarchy[ $plan ] ?? 0;
479 }
480
481 /**
482 * Apply badges for fields belonging to a tab with multiple plan mappings.
483 *
484 * @param array $fields Fields configuration (by reference).
485 * @param array $plans_to_fields Required plans mapped to field ids.
486 * @param array $plan_hierarchy Plan to tier mapping.
487 * @param int $current_tier Current plan tier.
488 */
489 private function apply_field_badges( array &$fields, array $plans_to_fields, array $plan_hierarchy, $current_tier ) {
490 foreach ( $plans_to_fields as $required_plan => $field_ids ) {
491 $required_tier = $this->get_plan_tier( $plan_hierarchy, $required_plan );
492
493 foreach ( $fields as &$field ) {
494 if ( empty( $field['id'] ) || ! in_array( $field['id'], (array) $field_ids, true ) ) {
495 continue;
496 }
497
498 if ( $current_tier >= $required_tier ) {
499 $field['badge'] = null;
500 $field['locked'] = false;
501 continue;
502 }
503
504 $field['badge'] = $required_plan;
505 $field['locked'] = true;
506 }
507 unset( $field );
508 }
509 }
510
511 /**
512 * Apply badge for a tab that maps to a single required plan.
513 *
514 * @param array $subtab Tab configuration.
515 * @param string $required_plan Plan required for unlocking.
516 * @param array $plan_hierarchy Plan to tier mapping.
517 * @param int $current_tier Current plan tier.
518 *
519 * @return array
520 */
521 private function apply_tab_badge( array $subtab, $required_plan, array $plan_hierarchy, $current_tier ) {
522 $required_tier = $this->get_plan_tier( $plan_hierarchy, $required_plan );
523
524 if ( $current_tier >= $required_tier ) {
525 $subtab['badge'] = null;
526 $subtab['locked'] = false;
527
528 return $subtab;
529 }
530
531 $subtab['badge'] = $required_plan;
532 $subtab['locked'] = true;
533
534 return $subtab;
535 }
536
537 /**
538 * Check if an addon is upgradable
539 *
540 * @param string $addon Addon slug.
541 * @return bool True if upgradable, false otherwise.
542 */
543 public function is_upgradable_addon( $addon = null ) {
544 if ( ! $addon ) {
545 return false;
546 }
547
548 $current_plan = get_option( $this->current_plan, 'free' );
549 if ( ! isset( $this->plan_map[ $current_plan ] ) ) {
550 $current_plan = 'free';
551 }
552
553 if ( 'strong-testimonials-pro' === $addon ) {
554 return false;
555 }
556
557 // Legacy standalone extension plugins (old PRO system) should not show upsells.
558 $plugin_file = $addon . '/' . $addon . '.php';
559 $active_plugins = (array) get_option( 'active_plugins', array() );
560 if ( in_array( $plugin_file, $active_plugins, true ) ) {
561 return false;
562 }
563 if ( is_multisite() ) {
564 $network_plugins = array_keys( (array) get_site_option( 'active_sitewide_plugins', array() ) );
565 if ( in_array( $plugin_file, $network_plugins, true ) ) {
566 return false;
567 }
568 }
569
570 if ( ! defined( 'WPMTST_PRO_VERSION' ) ) {
571 return true;
572 }
573
574 $owned_extensions = $this->plan_map[ $current_plan ] ?? array();
575
576 return ! in_array( $addon, $owned_extensions, true );
577 }
578 /**
579 * Get active extensions
580 *
581 * @return array Active extensions.
582 */
583 public function get_active_extensions() {
584 if ( empty( self::$active_extensions_cache ) ) {
585 self::$active_extensions_cache = get_option( $this->active_extensions, array() );
586 }
587 return self::$active_extensions_cache;
588 }
589
590 /**
591 * Check if an extension is enabled
592 *
593 * @param string $extension Extension slug.
594 * @return bool True if enabled, false otherwise.
595 */
596 public function extension_enabled( $extension ) {
597 $active_extensions = $this->get_active_extensions();
598 return in_array( $extension, $active_extensions, true );
599 }
600 }
601
602 /**
603 * Compatibility shim for old extensions that check for Strong_Testimonials_WPChill_Upsells.
604 */
605 if ( ! class_exists( 'Strong_Testimonials_WPChill_Upsells' ) ) {
606 class Strong_Testimonials_WPChill_Upsells {
607
608 private static $instance;
609
610 public static function get_instance( $args = array() ) {
611 if ( ! isset( self::$instance ) ) {
612 self::$instance = new self();
613 }
614 return self::$instance;
615 }
616
617 public function is_upgradable_addon( $addon ) {
618 return Strong_Testimonials_Extensions_Base::get_instance()->is_upgradable_addon( $addon );
619 }
620 }
621 }
622