PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.22
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.22
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
formidable / classes / models / FrmSalesApi.php

FrmSalesApi.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.22, at classes/models/FrmSalesApi.php

387 lines 9.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 /**
7 * @since 6.17
8 */
9 class FrmSalesApi extends FrmFormApi {
10
11 /**
12 * @var FrmSalesApi|null
13 */
14 private static $instance;
15
16 protected $cache_key;
17
18 /**
19 * All sales from API data.
20 *
21 * @var array|false
22 */
23 private static $sales = false;
24
25 /**
26 * Best sale from API data.
27 *
28 * @var array|false|null
29 */
30 private static $best_sale;
31
32 public function __construct() {
33 $this->set_cache_key();
34
35 if ( false === self::$sales ) {
36 $this->set_sales();
37 }
38 }
39
40 /**
41 * @since 6.17
42 *
43 * @return void
44 */
45 protected function set_cache_key() {
46 $this->cache_key = 'frm_sales_cache';
47 }
48
49 /**
50 * @since 6.17
51 *
52 * @return string
53 */
54 protected function api_url() {
55 return 'https://plapi.formidableforms.com/sales/';
56 }
57
58 /**
59 * @since 6.17
60 *
61 * @return void
62 */
63 private function set_sales() {
64 self::$sales = array();
65
66 $api = $this->get_api_info();
67 if ( empty( $api ) ) {
68 return;
69 }
70
71 foreach ( $api as $sale ) {
72 $this->add_sale( $sale );
73 }
74 }
75
76 /**
77 * @param array|string $sale
78 *
79 * @return void
80 */
81 private function add_sale( $sale ) {
82 if ( ! is_array( self::$sales ) ) {
83 // This gets set in the constructor.
84 // This check is just here for Psalm analysis.
85 return;
86 }
87
88 if ( ! is_array( $sale ) || ! isset( $sale['key'] ) ) {
89 // if the API response is invalid, $sale may not be an array.
90 // 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.
91 return;
92 }
93
94 if ( ! $this->sale_is_active( $sale ) ) {
95 return;
96 }
97
98 self::$sales[ $sale['key'] ] = $this->fill_sale( $sale );
99 }
100
101 /**
102 * @param array $sale
103 * @return array
104 */
105 private function fill_sale( $sale ) {
106 $defaults = array(
107 'key' => '',
108 'starts' => '',
109 'expires' => '',
110 // Use 'free', 'personal', 'business', 'elite', 'grandfathered'.
111 'who' => 'all',
112 'discount_percent' => 0,
113 'test_group' => '',
114 'lite_banner_cta_link' => '',
115 'lite_banner_cta_text' => '',
116 'menu_cta_link' => '',
117 'menu_cta_text' => '',
118 'dashboard_license_cta_link' => '',
119 'dashboard_license_cta_text' => '',
120 'global_settings_license_cta_link' => '',
121 'global_settings_license_cta_text' => '',
122 'global_settings_unlock_more_cta_link' => '',
123 'global_settings_unlock_more_cta_text' => '',
124 'global_settings_upgrade_cta_link' => '',
125 'builder_sidebar_cta_link' => '',
126 'builder_sidebar_cta_text' => '',
127 'banner_title' => '',
128 'banner_body' => '',
129 'banner_icon' => '',
130 'banner_text_color' => '',
131 'banner_bg_color' => '',
132 'banner_cta_link' => '',
133 'banner_cta_text' => '',
134 'banner_cta_text_color' => '',
135 'banner_cta_bg_color' => '',
136 );
137
138 return array_merge( $defaults, $sale );
139 }
140
141 /**
142 * Check if a sale is within the active period.
143 *
144 * @since 6.17
145 *
146 * @param array $sale
147 * @return bool
148 */
149 private function sale_is_active( $sale ) {
150 $starts = $sale['starts'];
151 $expires = $sale['expires'] + DAY_IN_SECONDS;
152 $date = new DateTime( 'now', new DateTimeZone( 'America/New_York' ) );
153 $today = $date->getTimestamp();
154 return $today >= $starts && $today <= $expires;
155 }
156
157 /**
158 * @since 6.17
159 *
160 * @return array|false
161 */
162 public function get_best_sale() {
163 if ( ! self::$sales ) {
164 return false;
165 }
166
167 if ( isset( self::$best_sale ) ) {
168 return self::$best_sale;
169 }
170
171 $best_sale = false;
172 foreach ( self::$sales as $sale ) {
173 if ( ! FrmApiHelper::is_for_user( $sale ) ) {
174 continue;
175 }
176
177 if ( ! $this->matches_ab_group( $sale ) ) {
178 continue;
179 }
180
181 if ( ! $best_sale || $sale['discount_percent'] > $best_sale['discount_percent'] ) {
182 $best_sale = $sale;
183 }
184 }
185
186 self::$best_sale = $best_sale;
187 return self::$best_sale;
188 }
189
190 /**
191 * Get text for best sale if applicable.
192 *
193 * @since 6.17
194 *
195 * @param string $key
196 * @return false|string False if no sale is active.
197 */
198 public static function get_best_sale_value( $key ) {
199 if ( ! isset( self::$instance ) ) {
200 self::$instance = new FrmSalesApi();
201 }
202
203 $sale = self::$instance->get_best_sale();
204
205 return is_array( $sale ) && ! empty( $sale[ $key ] ) ? $sale[ $key ] : false;
206 }
207
208 /**
209 * @since 6.17
210 *
211 * @param array $sale
212 * @return bool True if the sale is a match for the applicable group (if one is defined).
213 */
214 private function matches_ab_group( $sale ) {
215 if ( ! is_numeric( $sale['test_group'] ) ) {
216 // No test group, so return true.
217 return true;
218 }
219
220 $ab_group = $this->get_ab_group_for_current_site();
221 return $ab_group === $sale['test_group'];
222 }
223
224 /**
225 * @since 6.17
226 *
227 * @return int 1 or 0.
228 */
229 private function get_ab_group_for_current_site() {
230 $option = get_option( 'frm_sale_ab_group' );
231 if ( ! is_numeric( $option ) ) {
232 // Generate either 0 or 1.
233 $option = mt_rand( 0, 1 );
234 update_option( 'frm_sale_ab_group', $option, false );
235 }
236 return (int) $option;
237 }
238
239 /**
240 * Maybe show banner for the best sale.
241 *
242 * @since 6.21
243 *
244 * @return bool
245 */
246 public static function maybe_show_banner() {
247 if ( ! current_user_can( 'frm_change_settings' ) ) {
248 return false;
249 }
250
251 if ( ! isset( self::$instance ) ) {
252 self::$instance = new FrmSalesApi();
253 }
254
255 $sale = self::$instance->get_best_sale();
256 if ( ! $sale || ! is_array( $sale ) ) {
257 return false;
258 }
259
260 $banner_title = ! empty( $sale['banner_title'] ) ? $sale['banner_title'] : false;
261 $banner_body = ! empty( $sale['banner_body'] ) ? $sale['banner_body'] : false;
262
263 if ( false === $banner_title || false === $banner_body ) {
264 return false;
265 }
266
267 if ( self::is_banner_dismissed( $sale['key'] ) ) {
268 return false;
269 }
270
271 $banner_icon = ! empty( $sale['banner_icon'] ) ? $sale['banner_icon'] : 'generic';
272 $banner_bg_color = ! empty( $sale['banner_bg_color'] ) ? $sale['banner_bg_color'] : false;
273 $banner_text_color = ! empty( $sale['banner_text_color'] ) ? $sale['banner_text_color'] : false;
274 $banner_cta_link = ! empty( $sale['banner_cta_link'] ) ? $sale['banner_cta_link'] : false;
275
276 // translators: %s is the discount percentage.
277 $banner_cta_text = ! empty( $sale['banner_cta_text'] ) ? $sale['banner_cta_text'] : sprintf( __( 'GET %s OFF NOW', 'formidable' ), $sale['discount_percent'] . '%' );
278 $banner_cta_text_color = ! empty( $sale['banner_cta_text_color'] ) ? $sale['banner_cta_text_color'] : false;
279 $banner_cta_bg_color = ! empty( $sale['banner_cta_bg_color'] ) ? $sale['banner_cta_bg_color'] : false;
280
281 if ( false === $banner_cta_link ) {
282 $banner_cta_link = FrmAppHelper::admin_upgrade_link(
283 array(
284 'medium' => 'sales-api-banner',
285 'content' => $sale['key'],
286 )
287 );
288 }
289
290 $banner_attrs = array(
291 'id' => 'frm_sale_banner',
292 'data-url' => $banner_cta_link,
293 );
294
295 if ( false === $banner_bg_color || 'gradient' === $banner_bg_color ) {
296 $banner_attrs['class'] = 'frm-gradient';
297 } else {
298 $banner_attrs['style'] = 'background-color: ' . esc_attr( $banner_bg_color ) . ';';
299 }
300
301 $cta_attrs = array(
302 'href' => '#',
303 'style' => '',
304 );
305 if ( false !== $banner_cta_text_color ) {
306 $cta_attrs['style'] .= 'color: ' . esc_attr( $banner_cta_text_color ) . ';';
307 }
308 if ( false !== $banner_cta_bg_color ) {
309 $cta_attrs['style'] .= 'background-color: ' . esc_attr( $banner_cta_bg_color ) . ';';
310 }
311
312 $dismiss_attrs = array(
313 'href' => '#',
314 'class' => 'dismiss',
315 );
316
317 $content_attrs = array();
318
319 if ( false !== $banner_text_color ) {
320 $content_attrs['style'] = 'color: ' . esc_attr( $banner_text_color ) . ';';
321 $dismiss_attrs['style'] = 'color: ' . esc_attr( $banner_text_color ) . ';';
322 }
323
324 ?>
325 <div <?php FrmAppHelper::array_to_html_params( $banner_attrs, true ); ?>>
326 <div>
327 <img src="<?php echo esc_url( FrmAppHelper::plugin_url() . '/images/sales/' . $banner_icon . '.svg' ); ?>" alt="<?php echo esc_attr( $banner_title ); ?>" />
328 </div>
329 <div <?php FrmAppHelper::array_to_html_params( $content_attrs, true ); ?>>
330 <div>
331 <?php echo esc_html( $banner_title ); ?>
332 </div>
333 <div>
334 <?php echo esc_html( $banner_body ); ?>
335 </div>
336 </div>
337 <div>
338 <a <?php FrmAppHelper::array_to_html_params( $cta_attrs, true ); ?>>
339 <?php echo esc_html( $banner_cta_text ); ?>
340 </a>
341 </div>
342 <a <?php FrmAppHelper::array_to_html_params( $dismiss_attrs, true ); ?>><?php FrmAppHelper::icon_by_class( 'frm_icon_font frm_close_icon' ); ?></a>
343 </div>
344 <?php
345
346 return true;
347 }
348
349 /**
350 * Dismiss a banner via AJAX hook.
351 *
352 * @since 6.21
353 */
354 public static function dismiss_banner() {
355 FrmAppHelper::permission_check( 'frm_view_forms' );
356 check_ajax_referer( 'frm_ajax', 'nonce' );
357
358 if ( ! isset( self::$instance ) ) {
359 self::$instance = new FrmSalesApi();
360 }
361
362 $sale = self::$instance->get_best_sale();
363 if ( ! $sale || ! is_array( $sale ) ) {
364 wp_send_json_error();
365 }
366
367 $dismissed_sales = get_user_option( 'frm_dismissed_sales', get_current_user_id() );
368 if ( ! is_array( $dismissed_sales ) ) {
369 $dismissed_sales = array();
370 }
371
372 $dismissed_sales[] = $sale['key'];
373 update_user_option( get_current_user_id(), 'frm_dismissed_sales', $dismissed_sales );
374
375 wp_send_json_success();
376 }
377
378 /**
379 * @param string $key
380 * @return bool
381 */
382 private static function is_banner_dismissed( $key ) {
383 $dismissed_sales = get_user_option( 'frm_dismissed_sales', get_current_user_id() );
384 return is_array( $dismissed_sales ) && in_array( $key, $dismissed_sales, true );
385 }
386 }
387