PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / trunk
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More vtrunk
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
← All changes | classes/models/FrmSalesApi.php +67 -133 6.25.1 → trunk View file →
@@ -29,45 +29,40 @@
29 29 */
30 30 private static $best_sale;
31 31
32 32 /**
33 - * @since 6.25.1
33 + * Flag to prevent the $this->set_sales() call in the constructor.
34 34 *
35 - * @var string|null
36 - */
37 - private static $cross_sell_text;
38 -
39 - /**
40 - * @since 6.25.1
35 + * @since 6.34
41 36 *
42 - * @var string|null
37 + * @var bool
43 38 */
44 - private static $cross_sell_link;
39 + private static $prevent_new_sales_request = false;
45 40
46 41 public function __construct() {
47 42 $this->set_cache_key();
48 43
49 - if ( false === self::$sales ) {
44 + if ( ! self::$prevent_new_sales_request && false === self::$sales ) {
50 45 $this->set_sales();
51 46 }
52 47 }
53 48
54 49 /**
55 - * @since 6.17
50 + * @since 6.34
56 51 *
57 52 * @return void
58 53 */
59 - protected function set_cache_key() {
60 - $this->cache_key = 'frm_sales_cache';
54 + public static function prevent_new_sales_requests() {
55 + self::$prevent_new_sales_request = true;
61 56 }
62 57
63 58 /**
64 - * @since 6.17
59 + * @since 6.34
65 60 *
66 - * @return string
61 + * @return void
67 62 */
68 - protected function api_url() {
69 - return 'https://plapi.formidableforms.com/sales/';
63 + public static function allow_new_sales_requests() {
64 + self::$prevent_new_sales_request = false;
70 65 }
71 66
72 67 /**
73 68 * @since 6.17
@@ -73,106 +68,55 @@
73 68 * @since 6.17
74 69 *
75 70 * @return void
76 71 */
77 - private function set_sales() {
78 - self::$sales = array();
79 -
80 - $api = $this->get_api_info();
81 - if ( empty( $api ) ) {
82 - return;
83 - }
84 -
85 - foreach ( $api as $sale ) {
86 - $this->add_sale( $sale );
87 -
88 - if ( is_array( $sale ) && isset( $sale['cross_sell_text'] ) ) {
89 - $this->set_cross_sell( $sale );
90 - }
91 - }
72 + protected function set_cache_key() {
73 + $this->cache_key = 'frm_sales_cache';
92 74 }
93 75
94 76 /**
95 - * Check for a special array in the sales API array.
96 - * Normally cross_sell_text and cross_sell_link are not set.
97 - * But one array, that isn't actually a sale, contains cross sell data.
98 - * This should be near the end of the array.
77 + * @since 6.17
99 78 *
100 - * @since 6.25.1
101 - *
102 - * @param array $data
103 - * @return void
79 + * @return string
104 80 */
105 - private function set_cross_sell( $data ) {
106 - if ( ! self::cross_sell_is_valid( $data ) ) {
107 - return;
108 - }
109 -
110 - $cross_sell_text = $data['cross_sell_text'];
111 - $cross_sell_links = $data['cross_sell_link'];
112 - $index = self::determine_cross_sell_index( $cross_sell_text );
113 -
114 - self::$cross_sell_text = sanitize_text_field( $cross_sell_text[ $index ] );
115 - self::$cross_sell_link = esc_url_raw( $cross_sell_links[ $index ] );
81 + protected function api_url() {
82 + return 'https://plapi.formidableforms.com/sales/';
116 83 }
117 84
118 85 /**
119 - * Check that both cross_sell_text and cross_sell_link are set and are arrays of the same size.
86 + * If the last check was a rate limit, we'll need to check again sooner.
87 + * Other APIs use the FrmFormApi function which uses a 5 minute timeout. But for Sales, we can use 1 hour.
120 88 *
121 - * @since 6.25.1
89 + * @since 6.32
122 90 *
123 - * @param array $data
124 - * @return bool
91 + * @param array $addons
92 + *
93 + * @return string
125 94 */
126 - private function cross_sell_is_valid( $data ) {
127 - if ( empty( $data['cross_sell_text'] ) || empty( $data['cross_sell_link'] ) ) {
128 - return false;
95 + protected function get_cache_timeout( $addons ) {
96 + if ( isset( $addons['response_code'] ) && 429 === $addons['response_code'] ) {
97 + return '+1 hour';
129 98 }
130 -
131 - if ( ! is_array( $data['cross_sell_text'] ) || ! is_array( $data['cross_sell_link'] ) ) {
132 - return false;
133 - }
134 -
135 - return count( $data['cross_sell_link'] ) === count( $data['cross_sell_text'] );
99 + return $this->cache_timeout;
136 100 }
137 101
138 102 /**
139 - * Determine which cross sell text to use.
140 - * These are shown in order for 30 days before moving on to the next one.
103 + * @since 6.17
141 104 *
142 - * @since 6.25.1
143 - *
144 - * @param array $cross_sell_text
145 - * @return int
105 + * @return void
146 106 */
147 - private static function determine_cross_sell_index( $cross_sell_text ) {
148 - $option_name = 'frm_cross_sell_settings';
149 - $cross_sell_settings = get_option( $option_name );
107 + private function set_sales() {
108 + self::$sales = array();
150 109
151 - if ( ! is_array( $cross_sell_settings ) ) {
152 - $cross_sell_settings = array(
153 - reset( $cross_sell_text ) => time(),
154 - );
155 - update_option( $option_name, $cross_sell_settings );
156 - return 0;
110 + $api = $this->get_api_info();
111 +
112 + if ( ! $api ) {
113 + return;
157 114 }
158 115
159 - foreach ( $cross_sell_text as $index => $current_text ) {
160 - if ( ! isset( $cross_sell_settings[ $current_text ] ) ) {
161 - $cross_sell_settings[ $current_text ] = time();
162 - update_option( $option_name, $cross_sell_settings );
163 - return $index;
164 - }
165 -
166 - $time_elapsed = time() - $cross_sell_settings[ $current_text ];
167 - if ( $time_elapsed < DAY_IN_SECONDS * 30 ) {
168 - return $index;
169 - }
116 + foreach ( $api as $sale ) {
117 + $this->add_sale( $sale );
170 118 }
171 -
172 - // If all options are expired, reset the option.
173 - delete_option( $option_name );
174 - return 0;
175 119 }
176 120
177 121 /**
178 122 * @param array|string $sale
@@ -186,10 +130,10 @@
186 130 return;
187 131 }
188 132
189 133 if ( ! is_array( $sale ) || ! isset( $sale['key'] ) ) {
190 - // if the API response is invalid, $sale may not be an array.
191 - // if there are no sales from the API, it is returning a "No Entries Found" item with no key, so check for a key as well.
134 + // If the API response is invalid, $sale may not be an array.
135 + // If there are no sales from the API, it is returning a "No Entries Found" item with no key, so check for a key as well.
192 136 return;
193 137 }
194 138
195 139 if ( ! $this->sale_is_active( $sale ) ) {
@@ -200,8 +144,9 @@
200 144 }
201 145
202 146 /**
203 147 * @param array $sale
148 + *
204 149 * @return array
205 150 */
206 151 private function fill_sale( $sale ) {
207 152 $defaults = array(
@@ -244,8 +189,9 @@
244 189 *
245 190 * @since 6.17
246 191 *
247 192 * @param array $sale
193 + *
248 194 * @return bool
249 195 */
250 196 private function sale_is_active( $sale ) {
251 197 $starts = $sale['starts'];
@@ -269,8 +215,9 @@
269 215 return self::$best_sale;
270 216 }
271 217
272 218 $best_sale = false;
219 +
273 220 foreach ( self::$sales as $sale ) {
274 221 if ( ! FrmApiHelper::is_for_user( $sale ) ) {
275 222 continue;
276 223 }
@@ -293,8 +240,9 @@
293 240 *
294 241 * @since 6.17
295 242 *
296 243 * @param string $key
244 + *
297 245 * @return false|string False if no sale is active.
298 246 */
299 247 public static function get_best_sale_value( $key ) {
300 248 if ( ! isset( self::$instance ) ) {
@@ -302,9 +250,19 @@
302 250 }
303 251
304 252 $sale = self::$instance->get_best_sale();
305 253
306 - return is_array( $sale ) && ! empty( $sale[ $key ] ) ? $sale[ $key ] : false;
254 + if ( ! is_array( $sale ) || empty( $sale[ $key ] ) ) {
255 + return false;
256 + }
257 +
258 + $sale_value = $sale[ $key ];
259 +
260 + if ( str_ends_with( $key, '_link' ) && ! str_starts_with( $sale_value, 'https://formidableforms.com' ) ) {
261 + return false;
262 + }
263 +
264 + return $sale_value;
307 265 }
308 266
309 267 /**
310 268 * @since 6.17
@@ -309,8 +267,9 @@
309 267 /**
310 268 * @since 6.17
311 269 *
312 270 * @param array $sale
271 + *
313 272 * @return bool True if the sale is a match for the applicable group (if one is defined).
314 273 */
315 274 private function matches_ab_group( $sale ) {
316 275 if ( ! is_numeric( $sale['test_group'] ) ) {
@@ -328,13 +287,15 @@
328 287 * @return int 1 or 0.
329 288 */
330 289 private function get_ab_group_for_current_site() {
331 290 $option = get_option( 'frm_sale_ab_group' );
291 +
332 292 if ( ! is_numeric( $option ) ) {
333 293 // Generate either 0 or 1.
334 294 $option = mt_rand( 0, 1 );
335 295 update_option( 'frm_sale_ab_group', $option, false );
336 296 }
297 +
337 298 return (int) $option;
338 299 }
339 300
340 301 /**
@@ -353,8 +314,9 @@
353 314 self::$instance = new FrmSalesApi();
354 315 }
355 316
356 317 $sale = self::$instance->get_best_sale();
318 +
357 319 if ( ! $sale || ! is_array( $sale ) ) {
358 320 return false;
359 321 }
360 322
@@ -402,11 +364,13 @@
402 364 $cta_attrs = array(
403 365 'href' => '#',
404 366 'style' => '',
405 367 );
368 +
406 369 if ( false !== $banner_cta_text_color ) {
407 370 $cta_attrs['style'] .= 'color: ' . esc_attr( $banner_cta_text_color ) . ';';
408 371 }
372 +
409 373 if ( false !== $banner_cta_bg_color ) {
410 374 $cta_attrs['style'] .= 'background-color: ' . esc_attr( $banner_cta_bg_color ) . ';';
411 375 }
412 376
@@ -421,8 +385,9 @@
421 385 $content_attrs['style'] = 'color: ' . esc_attr( $banner_text_color ) . ';';
422 386 $dismiss_attrs['style'] = 'color: ' . esc_attr( $banner_text_color ) . ';';
423 387 }
424 388
389 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
425 390 ?>
426 391 <div <?php FrmAppHelper::array_to_html_params( $banner_attrs, true ); ?>>
427 392 <div>
428 393 <img src="<?php echo esc_url( FrmAppHelper::plugin_url() . '/images/sales/' . $banner_icon . '.svg' ); ?>" alt="<?php echo esc_attr( $banner_title ); ?>" />
@@ -439,11 +404,12 @@
439 404 <a <?php FrmAppHelper::array_to_html_params( $cta_attrs, true ); ?>>
440 405 <?php echo esc_html( $banner_cta_text ); ?>
441 406 </a>
442 407 </div>
443 - <a <?php FrmAppHelper::array_to_html_params( $dismiss_attrs, true ); ?>><?php FrmAppHelper::icon_by_class( 'frm_icon_font frm_close_icon' ); ?></a>
408 + <a <?php FrmAppHelper::array_to_html_params( $dismiss_attrs, true ); ?>><?php FrmAppHelper::icon_by_class( 'frmfont frm_close_icon' ); ?></a>
444 409 </div>
445 410 <?php
411 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
446 412
447 413 return true;
448 414 }
449 415
@@ -460,13 +426,15 @@
460 426 self::$instance = new FrmSalesApi();
461 427 }
462 428
463 429 $sale = self::$instance->get_best_sale();
430 +
464 431 if ( ! $sale || ! is_array( $sale ) ) {
465 432 wp_send_json_error();
466 433 }
467 434
468 435 $dismissed_sales = get_user_option( 'frm_dismissed_sales', get_current_user_id() );
436 +
469 437 if ( ! is_array( $dismissed_sales ) ) {
470 438 $dismissed_sales = array();
471 439 }
472 440
@@ -477,46 +445,12 @@
477 445 }
478 446
479 447 /**
480 448 * @param string $key
449 + *
481 450 * @return bool
482 451 */
483 452 private static function is_banner_dismissed( $key ) {
484 453 $dismissed_sales = get_user_option( 'frm_dismissed_sales', get_current_user_id() );
485 454 return is_array( $dismissed_sales ) && in_array( $key, $dismissed_sales, true );
486 - }
487 -
488 - public static function menu() {
489 - if ( false === self::$sales ) {
490 - new self();
491 - }
492 -
493 - if ( ! self::$cross_sell_text || ! self::$cross_sell_link ) {
494 - return;
495 - }
496 -
497 - add_submenu_page(
498 - 'formidable',
499 - esc_html( self::$cross_sell_text ) . ' | Formidable',
500 - esc_html( self::$cross_sell_text ),
501 - 'activate_plugins',
502 - 'frm-sales-api-cross-sell',
503 - function () {
504 - // There is no page. The redirect logic is handled below, before this callback is triggered.
505 - }
506 - );
507 -
508 - add_action(
509 - 'admin_init',
510 - function () {
511 - if ( ! current_user_can( 'activate_plugins' ) ) {
512 - return;
513 - }
514 -
515 - if ( 'frm-sales-api-cross-sell' === FrmAppHelper::simple_get( 'page' ) && ! empty( self::$cross_sell_link ) ) {
516 - wp_redirect( self::$cross_sell_link );
517 - exit;
518 - }
519 - }
520 - );
521 455 }
522 456 }