PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.12
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.12
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 / helpers / FrmEmailSummaryHelper.php

FrmEmailSummaryHelper.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.12, at classes/helpers/FrmEmailSummaryHelper.php

561 lines 14.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * In-plugin summary emails helper
4 *
5 * @since 6.7
6 * @package Formidable
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 die( 'You are not allowed to call this page directly.' );
11 }
12
13 /**
14 * Class FrmEmailSummaryHelper
15 */
16 class FrmEmailSummaryHelper {
17
18 const MONTHLY = 'monthly';
19
20 const YEARLY = 'yearly';
21
22 /**
23 * Number of days to send the next monthly email.
24 */
25 const MONTHLY_PERIOD = 30;
26
27 /**
28 * Number of days to send the next yearly email.
29 */
30 const YEARLY_PERIOD = 365;
31
32 /**
33 * Number of days before renewal date to send yearly email.
34 */
35 const BEFORE_RENEWAL_PERIOD = 45;
36
37 /**
38 * Number of days before sending the first summary email after upgrade plugin.
39 */
40 const DELAY_AFTER_UPGRADE = 15;
41
42 /**
43 * Summary emails option name.
44 *
45 * @var string
46 */
47 public static $option_name = 'frm_summary_emails_options';
48
49 /**
50 * Checks if summary emails are enabled.
51 *
52 * @return bool
53 */
54 public static function is_enabled() {
55 $frm_settings = FrmAppHelper::get_settings();
56 return ! empty( $frm_settings->summary_emails ) && ! empty( $frm_settings->summary_emails_recipients );
57 }
58
59 /**
60 * Gets summary emails options.
61 *
62 * @return array
63 */
64 private static function get_options() {
65 $options = get_option( self::$option_name );
66 if ( ! $options ) {
67 $default_options = array(
68 // Do not send email within 15 days after updating.
69 'last_' . self::MONTHLY => self::get_date_from_today( '-' . self::DELAY_AFTER_UPGRADE . ' days' ),
70 'last_' . self::YEARLY => '',
71 'renewal_date' => '',
72 );
73
74 self::save_options( $default_options );
75 return $default_options;
76 }
77
78 return $options;
79 }
80
81 /**
82 * Saves summary emails options.
83 *
84 * @param array $options Options data.
85 */
86 private static function save_options( $options ) {
87 update_option( self::$option_name, $options );
88 }
89
90 /**
91 * Checks if should send summary emails.
92 *
93 * @return array|false Return array of emails should be sent, or `false` if not send any emails.
94 */
95 public static function should_send_emails() {
96 if ( ! self::is_enabled() ) {
97 return false;
98 }
99
100 $emails = array();
101 $current_date = self::get_date_from_today();
102
103 // Check for monthly or yearly email.
104 $last_monthly = self::get_last_sent_date( 'monthly' );
105 $last_yearly = self::get_last_sent_date( 'yearly' );
106 $last_stats = max( $last_monthly, $last_yearly );
107
108 // Do not send any email if it isn't enough 30 days from the last stats email.
109 if ( $last_stats && self::MONTHLY_PERIOD > self::get_date_diff( $current_date, $last_stats ) ) {
110 return $emails;
111 }
112
113 if ( $last_yearly ) {
114 // If this isn't the first yearly email, send the new one after 1 year.
115 if ( $last_yearly && self::YEARLY_PERIOD <= self::get_date_diff( $current_date, $last_yearly ) ) {
116 $emails[] = self::YEARLY;
117 return $emails;
118 }
119 } else {
120 // If no yearly email has been sent, send it if it's less than 45 days until the renewal date.
121 $renewal_date = self::get_renewal_date();
122 if ( $renewal_date && self::BEFORE_RENEWAL_PERIOD >= self::get_date_diff( $current_date, $renewal_date ) ) {
123 $emails[] = self::YEARLY;
124 return $emails;
125 }
126 }
127
128 // If it isn't time for yearly email, it's time for monthly email.
129 $emails[] = self::MONTHLY;
130
131 return $emails;
132 }
133
134 /**
135 * Sends monthly email.
136 */
137 public static function send_monthly() {
138 $monthly_email = new FrmEmailMonthly();
139
140 if ( $monthly_email->send() ) {
141 self::set_last_sent_date( self::MONTHLY );
142 }
143 }
144
145 /**
146 * Sends yearly email.
147 */
148 public static function send_yearly() {
149 $yearly_email = new FrmEmailYearly();
150
151 if ( $yearly_email->send() ) {
152 self::set_last_sent_date( self::YEARLY );
153 }
154 }
155
156 /**
157 * Gets the renewal date and save to options. If it doesn't exist, get the created date of the lowest ID form then plus 1 year.
158 *
159 * @return string
160 */
161 private static function get_renewal_date() {
162 $options = self::get_options();
163
164 // Get cached value from options.
165 if ( ! empty( $options['renewal_date'] ) ) {
166 return $options['renewal_date'];
167 }
168
169 // Return the actual renewal date if it exists.
170 $license_info = FrmAddonsController::get_primary_license_info();
171 if ( ! empty( $license_info['expires'] ) ) {
172 $renewal_date = gmdate( 'Y-m-d', $license_info['expires'] );
173
174 $options['renewal_date'] = $renewal_date;
175 self::save_options( $options );
176 return $renewal_date;
177 }
178
179 // If renewal date doesn't exist, get from the first form creation date.
180 $first_form_date = self::get_earliest_form_created_date();
181 if ( $first_form_date ) {
182 $renewal_date = gmdate( 'Y-m-d', strtotime( $first_form_date . '+' . self::YEARLY_PERIOD . ' days' ) );
183
184 // If the first form is more than 1 year in the past, set renewal date to the next 45 days.
185 if ( $renewal_date < self::get_date_from_today() ) {
186 $renewal_date = self::get_date_from_today( '+' . self::BEFORE_RENEWAL_PERIOD . ' days' );
187 }
188
189 $options['renewal_date'] = $renewal_date;
190 self::save_options( $options );
191 return $renewal_date;
192 }
193
194 return false;
195 }
196
197 /**
198 * Gets date object.
199 *
200 * @param DateTime|string $date Date string or object.
201 * @return DateTime|false
202 */
203 private static function get_date_obj( $date ) {
204 if ( $date instanceof DateTime ) {
205 return $date;
206 }
207
208 return date_create( $date );
209 }
210
211 /**
212 * Gets the days different between 2 dates.
213 *
214 * @param DateTime|string $date1 Date 1.
215 * @param DateTime|string $date2 Date 2.
216 * @return false|int
217 */
218 private static function get_date_diff( $date1, $date2 ) {
219 $date1 = self::get_date_obj( $date1 );
220 if ( ! $date1 ) {
221 return false;
222 }
223
224 $date2 = self::get_date_obj( $date2 );
225 if ( ! $date2 ) {
226 return false;
227 }
228
229 return date_diff( $date1, $date2 )->days;
230 }
231
232 /**
233 * Gets sent date of the last monthly or yearly email.
234 *
235 * @param string $type Accepts `monthly`, `yearly`.
236 * @return false|string
237 */
238 public static function get_last_sent_date( $type ) {
239 $options = self::get_options();
240 if ( empty( $options[ 'last_' . $type ] ) ) {
241 return false;
242 }
243
244 return $options[ 'last_' . $type ];
245 }
246
247 /**
248 * Sets the last sent date of an email type.
249 *
250 * @param string $type Email type.
251 * @param mixed $value Set custom value. If this is null, set the current date.
252 */
253 public static function set_last_sent_date( $type, $value = null ) {
254 $options = self::get_options();
255
256 $options[ 'last_' . $type ] = null === $value ? self::get_date_from_today() : '';
257 self::save_options( $options );
258 }
259
260 /**
261 * Gets the created date of earliest form.
262 *
263 * @return string
264 */
265 private static function get_earliest_form_created_date() {
266 return FrmDb::get_var(
267 'frm_forms',
268 array(),
269 'created_at',
270 array( 'order_by' => 'id ASC' )
271 );
272 }
273
274 /**
275 * Gets payments data.
276 *
277 * @param string $from_date From date.
278 * @param string $to_date To date.
279 * @return array Contains `count` and `total`.
280 */
281 public static function get_payments_data( $from_date, $to_date ) {
282 $payment = new FrmTransLitePayment();
283 return $payment->get_payments_stats( $from_date, $to_date );
284 }
285
286 /**
287 * Gets entries count in a date range.
288 *
289 * @param string $from_date From date.
290 * @param string $to_date To date.
291 * @return int
292 */
293 public static function get_entries_count( $from_date, $to_date ) {
294 return FrmDb::get_count(
295 'frm_items',
296 array(
297 // The `=` is added after `>` in the query.
298 'created_at >' => $from_date,
299 'created_at <' => $to_date . ' 23:59:59',
300 'is_draft' => 0,
301 // Do not count repeater entries.
302 'parent_item_id' => 0,
303 )
304 );
305 }
306
307 /**
308 * Gets top forms in a date range.
309 *
310 * @param string $from_date From date.
311 * @param string $to_date To date.
312 * @param int $limit Limit the result. Default is 5.
313 * @return array Contains `form_id`, `form_name`, and `items_count`.
314 */
315 public static function get_top_forms( $from_date, $to_date, $limit = 5 ) {
316 global $wpdb;
317
318 $result = $wpdb->get_results(
319 $wpdb->prepare(
320 "SELECT fr.id AS form_id, fr.name AS form_name, COUNT(*) as items_count
321 FROM {$wpdb->prefix}frm_items AS it INNER JOIN {$wpdb->prefix}frm_forms AS fr ON it.form_id = fr.id
322 WHERE it.created_at BETWEEN %s AND %s AND it.is_draft = 0 AND parent_form_id = 0
323 GROUP BY form_id ORDER BY items_count DESC LIMIT %d",
324 $from_date,
325 $to_date . ' 23:59:59',
326 intval( $limit )
327 )
328 );
329
330 // Remove slashes from form name.
331 foreach ( $result as &$value ) {
332 $value->form_name = wp_unslash( $value->form_name );
333 }
334
335 return $result;
336 }
337
338 /**
339 * Shows the comparison HTML in the email.
340 *
341 * @param float $diff Percentage of difference.
342 */
343 public static function show_comparison( $diff ) {
344 if ( ! $diff ) {
345 return;
346 }
347
348 if ( $diff > 0 ) {
349 $arrow = '&uarr;';
350 $color = '#12b76a';
351 } else {
352 $arrow = '&darr;';
353 $color = '#f04438';
354 }
355
356 $displayed_value = round( $diff * 100 );
357 if ( ! $displayed_value ) {
358 // Do not show 0 value.
359 $displayed_value = $diff > 0 ? 1 : -1;
360 }
361
362 printf(
363 '<span style="color: %1$s; font-size: 0.75em; font-weight: 700;">
364 %2$s<span style="display: inline-block; line-height: 1.33;">%3$s</span>
365 </span>',
366 esc_attr( $color ),
367 esc_html( $arrow ),
368 intval( $displayed_value ) . '%'
369 );
370 }
371
372 /**
373 * Gets section CSS in the email.
374 *
375 * @param string $border_pos Border position. Default is `top`. Set to empty if no border.
376 * @return string
377 */
378 public static function get_section_style( $border_pos = 'top' ) {
379 if ( $border_pos ) {
380 $border = 'border-' . $border_pos . ': 1px solid #eaecf0;';
381 } else {
382 $border = '';
383 }
384 return 'padding: 3em 4.375em;' . $border;
385 }
386
387 /**
388 * Gets h2 CSS in the email.
389 *
390 * @return string
391 */
392 public static function get_heading2_style() {
393 return 'font-size: 1.125em; line-height: 1.33em; margin: 0 0 1.33em;';
394 }
395
396 /**
397 * Gets CSS for button.
398 *
399 * @return string
400 */
401 public static function get_button_style( $display_block = false ) {
402 return 'display: ' . ( $display_block ? 'block' : 'inline-block' ) . '; font-size: 0.875em; line-height: 2.4; border-radius: 1.2em; border: 1px solid #d0d5dd; font-weight: 600; text-align: center; margin-top: 2.6em; color: #1d2939; text-decoration: none; padding-left: 1em; padding-right: 1em;';
403 }
404
405 /**
406 * Shows the section heading with icon.
407 *
408 * @param string $icon Icon file name, without file path and extension. Use .png image.
409 * @param string $text Heading text.
410 */
411 public static function section_heading_with_icon( $icon, $text ) {
412 ?>
413 <h2 style="<?php echo esc_attr( self::get_heading2_style() ); ?>">
414 <img style="vertical-align: bottom; height: 24px; width: auto;" src="<?php echo esc_url( FrmAppHelper::plugin_url() . '/images/' . $icon . '.png' ); ?>" alt="<?php echo esc_attr( $icon ); ?>" />
415 <span style="display: inline-block; vertical-align: text-bottom;"><?php echo esc_html( $text ); ?></span>
416 </h2>
417 <?php
418 }
419
420 /**
421 * Gets Formidable URL with tracking params.
422 *
423 * @param string $url The URL.
424 * @param array|string $args Custom tracking args if is array, or `utm_content` if is string.
425 * @return string
426 */
427 public static function get_frm_url( $url, $args = array() ) {
428 if ( is_array( $args ) ) {
429 $args = wp_parse_args(
430 $args,
431 array(
432 'medium' => 'summary-email',
433 'content' => 'link',
434 )
435 );
436 } else {
437 $args = array(
438 'medium' => 'summary-email',
439 'content' => $args,
440 );
441 }
442
443 return FrmAppHelper::admin_upgrade_link( $args, $url );
444 }
445
446 /**
447 * Gets the latest inbox message.
448 *
449 * @return array|false
450 */
451 public static function get_latest_inbox_message() {
452 $inbox = new FrmInbox();
453 $messages = $inbox->get_messages( 'filter' );
454 if ( ! $messages || ! is_array( $messages ) ) {
455 return false;
456 }
457
458 $messages = array_reverse( $messages );
459 foreach ( $messages as $message ) {
460 if ( 'news' !== $message['type'] ) {
461 continue;
462 }
463
464 return $message;
465 }
466
467 return false;
468 }
469
470 /**
471 * Gets out of date plugin names.
472 *
473 * @return array
474 */
475 public static function get_out_of_date_plugins() {
476 $update_data = FrmAddonsController::check_update( '' );
477 if ( ! $update_data || ! is_object( $update_data ) || empty( $update_data->response ) ) {
478 return array();
479 }
480
481 $plugins = array();
482 foreach ( $update_data->response as $plugin_data ) {
483 $plugins[] = $plugin_data->display_name;
484 }
485
486 return $plugins;
487 }
488
489 /**
490 * Processes inbox CTA button before showing in email.
491 *
492 * @param string $button_html Button HTML. This usually contains 1 button and 1 dismiss button.
493 * @return string
494 */
495 public static function process_inbox_cta_button( $button_html ) {
496 // Remove dismiss button.
497 $button_html = preg_replace( '/<a[^>]*class="[^"]*\bfrm_inbox_dismiss\b[^"]*"[^>]*>[^<]*<\/a>/', '', $button_html );
498
499 // Replace link utm.
500 $button_html = str_replace( 'utm_medium=inbox', 'utm_medium=summary-email', $button_html );
501
502 if ( strpos( $button_html, 'style="' ) ) {
503 // Maybe this button contains inline style.
504 return $button_html;
505 }
506
507 // Add inline CSS for specific button types.
508 if ( strpos( $button_html, 'frm-button-primary' ) ) {
509 $button_html = str_replace( '<a', '<a style="' . self::get_button_style() . '"', $button_html );
510 }
511
512 return $button_html;
513 }
514
515 /**
516 * Gets the localized date with the date diff from today.
517 *
518 * @param string $date_diff Date diff string. By default, this is empty, the result will be the current date.
519 * @return string
520 */
521 public static function get_date_from_today( $date_diff = '' ) {
522 if ( ! $date_diff ) {
523 return FrmAppHelper::get_localized_date( 'Y-m-d', gmdate( 'Y-m-d H:i:s' ) );
524 }
525 return FrmAppHelper::get_localized_date( 'Y-m-d', gmdate( 'Y-m-d H:i:s', strtotime( $date_diff ) ) );
526 }
527
528 /**
529 * Maybe remove recipients from setting from API.
530 *
531 * @since 6.8
532 *
533 * @param string $recipients Recipients.
534 * @return void
535 */
536 public static function maybe_remove_recipients_from_api( &$recipients ) {
537 $api = new FrmFormApi();
538 $addons = $api->get_api_info();
539 if ( empty( $addons['no_emails'] ) ) {
540 return;
541 }
542
543 $skip_emails = is_string( $addons['no_emails'] ) ? explode( ',', $addons['no_emails'] ) : (array) $addons['no_emails'];
544
545 $recipients = array_map( 'trim', explode( ',', $recipients ) );
546 $recipients = array_diff( $recipients, $skip_emails );
547 $recipients = implode( ',', $recipients );
548 }
549
550 /**
551 * Echos string in plain text email.
552 *
553 * @since 6.8
554 *
555 * @param string $string string.
556 */
557 public static function plain_text_echo( $string ) {
558 echo wp_strip_all_tags( $string ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
559 }
560 }
561