PluginProbe
HurryTimer – An Scarcity and Urgency Countdown Timer for WordPress & WooCommerce / 2.12.0
HurryTimer – An Scarcity and Urgency Countdown Timer for WordPress & WooCommerce v2.12.0
2.15.0 trunk 1.0.0 1.0.1 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.1.2 2.1.3 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 All 62 releases
hurrytimer / includes / Campaign.php

Campaign.php in HurryTimer – An Scarcity and Urgency Countdown Timer for WordPress & WooCommerce 2.12.0, at includes/Campaign.php

1,997 lines 47.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Hurrytimer;
4
5 use DateInterval;
6 use DatePeriod;
7 use DateTime;
8 use Hurrytimer\Dependencies\Carbon\Carbon;
9 use Hurrytimer\Dependencies\Carbon\CarbonPeriod;
10 use Hurrytimer\Utils\Helpers;
11
12 class Campaign
13 {
14
15 /**
16 * Campaign custom post ID
17 *
18 * @var int
19 */
20 private $id;
21
22 /**
23 * Campaign mode.
24 *
25 * @see C.php
26 *
27 * @var int
28 */
29 public $mode = C::MODE_REGULAR;
30
31 /**
32 * Evergreen duration array.
33 *
34 * @see getDuration()
35 *
36 * @var array
37 */
38 public $duration;
39
40 /**
41 * Recurrence duration
42 *
43 * @var int
44 */
45 public $recurringDuration;
46 public $recurringPauseDuration = ['days' => 0, 'hours' => 0, 'minutes' => 0, 'seconds' => 0];
47
48 public $recurringDurationOption = 'none';
49
50 /**
51 * Recurrence start date/time
52 *
53 * @var string
54 */
55 public $recurringStartTime;
56
57 /**
58 * Recurrence end option
59 *
60 * @var int
61 */
62 public $recurringEnd = C::RECURRING_END_NEVER;
63
64 /**
65 * Recurrence frequency
66 *
67 * @see C
68 * @var string
69 */
70 public $recurringFrequency = C::RECURRING_DAILY;
71
72 /**
73 * Recurrence interval
74 *
75 * @var int
76 */
77 public $recurringInterval = 1;
78
79 /**
80 * Recurrence count
81 *
82 * @var int
83 */
84 public $recurringCount = 2;
85
86 /**
87 * Recurrence end date/time
88 *
89 * @var
90 */
91 public $recurringUntil;
92
93 /**
94 * Recurrence allowed days
95 *
96 * @var array
97 */
98 public $recurringDays = [0, 1, 2, 3, 4, 5, 6];
99
100 /**
101 * Restart option after expiration.
102 *
103 * @see \Hurrytimer\CampaignRestart
104 *
105 * @var int
106 */
107 public $restart;
108 public $restartDuration = ['days' => 0, 'hours' => 0, 'minutes' => 0, 'seconds' => 0];
109
110 /**
111 * Headline text.
112 *
113 * @var string
114 */
115 public $headline = "Hurry Up!";
116
117 /**
118 * Headline color.
119 *
120 * @var string
121 */
122 public $headlineColor = "#000";
123
124 /**
125 * Headline size.
126 *
127 * @var int
128 */
129 public $headlineSize = 30;
130
131 /**
132 * Headline position.
133 *
134 * @var int
135 */
136 public $headlinePosition = C::HEADLINE_POSITION_ABOVE_TIMER;
137
138 /**
139 * Headline visibility.
140 *
141 * @var boolean
142 */
143 public $headlineVisibility = C::YES;
144
145 public $headlineSpacing = 5;
146
147 /**
148 * Control labels visibility.
149 *
150 * @var string
151 */
152 public $labelVisibility = C::YES;
153
154 /**
155 * Show/hide days block.
156 *
157 * @var string
158 */
159 public $daysVisibility = C::YES;
160
161 public $monthsVisibility = C::NO;
162
163 /**
164 * Show/hide hours block.
165 *
166 * @var string
167 */
168 public $hoursVisibility = C::YES;
169
170 /**
171 * Show/hide minutes block.
172 *
173 * @var string
174 */
175 public $minutesVisibility = C::YES;
176
177 /**
178 * Show/hide seconds block.
179 *
180 * @var string
181 */
182 public $secondsVisibility = C::YES;
183
184 /**
185 * Regular campaign end date/time.
186 *
187 * @var string
188 */
189 public $endDatetime;
190
191 /**
192 * Labels texts.
193 *
194 * @var array
195 */
196 public $labels
197 = [
198 'months' => 'months',
199 'days' => 'days',
200 'hours' => 'hrs',
201 'minutes' => 'mins',
202 'seconds' => 'secs',
203 ];
204
205 /**
206 * Digit color.
207 *
208 * @var string
209 */
210 public $digitColor = "#000";
211
212 /**
213 * Digit size.
214 *
215 * @var int
216 */
217 public $digitSize = 35;
218
219 /**
220 * Redirect action url.
221 *
222 * @var string
223 */
224 public $redirectUrl;
225
226 /**
227 * Label size.
228 *
229 * @var int
230 */
231 public $labelSize = 12;
232
233 /**
234 * Label color.
235 *
236 * @var string
237 */
238 public $labelColor = "#000";
239
240 /**
241 * End action.
242 *
243 * @var array
244 */
245 public $actions = [];
246
247 /**
248 * WooCommerce compaign position in product page.
249 *
250 * @var int
251 */
252 public $wcPosition = C::WC_POSITION_ABOVE_TITLE;
253
254 /**
255 * Enable/disable woocommerce integration.
256 *
257 * @var string
258 */
259 public $wcEnable = C::NO;
260
261 /**
262 * WooCommerce products selection.
263 *
264 * @var array
265 */
266 public $wcProductsSelection;
267
268 /**
269 * WooCommerce products selection type.
270 *
271 * @var int
272 */
273 public $wcProductsSelectionType;
274
275 /**
276 * WooCommerce conditions.
277 * @var array $wcConditions
278 */
279 public $wcConditions;
280
281 /**
282 * Timer block border color.
283 *
284 * @var string
285 */
286 public $blockBorderColor = "";
287
288 /**
289 * Timer Block border width.
290 *
291 * @var int
292 */
293
294 public $blockBorderWidth = 0;
295
296 /**
297 * Timer block radius.
298 *
299 * @var int
300 */
301 public $blockBorderRadius = 0;
302
303 /**
304 * Block size.
305 *
306 * @var int
307 */
308
309 public $blockSize = 'auto';
310
311 /**
312 * Block background color.
313 *
314 * @var string
315 */
316 public $blockBgColor = '';
317
318 /**
319 * Block spacing.
320 *
321 * @var int
322 */
323 public $blockSpacing = 5;
324
325 /**
326 * Block padding.
327 *
328 * @var int
329 */
330 public $blockPadding = 0;
331
332 /**
333 * Block spearator visibility.
334 *
335 * @var boolean
336 */
337 public $blockSeparatorVisibility = C::YES;
338
339 /**
340 * Label case
341 *
342 * @var string
343 */
344 public $labelCase = C::TRANSFORM_UPPERCASE;
345
346 /**
347 * Custom CSS
348 *
349 * @var string
350 */
351 public $customCss = '';
352
353 /**
354 * Block elements display
355 * Values: block, inline
356 *
357 * @var string
358 */
359 public $blockDisplay = 'block';
360
361 /**
362 * Enable sticky bar.
363 */
364 public $enableSticky = C::NO;
365
366 /**
367 * Sticky bar background color.
368 *
369 * @var string
370 */
371 public $stickyBarBgColor = '#eee';
372
373 /**
374 * Sticky bar close button color.
375 *
376 * @var string
377 */
378 public $stickyBarCloseBtnColor = '#fff';
379
380 /**
381 * Sticky bar position.
382 * Values: top, bottom
383 *
384 * @var string
385 */
386 public $stickyBarPosition = 'top';
387
388 /**
389 * Sticky bar padding.
390 *
391 * @var integer
392 */
393 public $stickyBarPadding = 5;
394
395 /**
396 * Sticky bar display pages.
397 *
398 * @var array
399 */
400 public $stickyBarPages = [];
401 public $stickyIncludeUrls = [];
402 public $stickyExcludeUrls = [];
403
404 /**
405 * Sticky exclude pages.
406 *
407 * @var array
408 */
409 public $stickyExcludePages = [];
410
411 /**
412 * Where to display the sticky bar option
413 *
414 * @var string
415 */
416 public $stickyBarDisplayOn = 'all_pages';
417
418 /**
419 * Show sticky bar close button?
420 *
421 * @var string
422 */
423 public $stickyBarDismissible = C::YES;
424
425
426 /**
427 * If dismissed, re-open sticky bar after x days.
428 * @var int
429 */
430 public $stickyBarDismissTimeout = 7;
431
432 /**
433 * CTA settings.
434 *
435 * @var array
436 */
437 public $callToAction
438 = [
439 'enabled' => C::NO,
440 'new_tab' => C::NO,
441 'url' => '',
442 'text' => 'Learn More',
443 'text_size' => 15,
444 'text_color' => '#fff',
445 'bg_color' => '#000',
446 'y_padding' => 10,
447 'x_padding' => 15,
448 'border_radius' => 3,
449 'spacing' => 5,
450
451 ];
452
453 /**
454 * Campaign elements display
455 * values: block,inline
456 *
457 * @var string
458 */
459 public $campaignDisplay = 'block';
460
461 /**
462 * Campaign aligments
463 * values: center,left,right
464 *
465 * @var string
466 */
467 public $campaignAlign = 'center';
468
469 /**
470 * Campaign spacing.
471 *
472 * @var integer
473 */
474 public $campaignSpacing = 10;
475
476 /**
477 * Campaign horizontal padding.
478 *
479 * @var integer
480 */
481 public $campaignXPadding = 10;
482
483 /**
484 * Campaign vertical padding.
485 *
486 * @var integer
487 */
488 public $campaignYPadding = 10;
489
490 /**
491 * Force evergreen timer to reset on reload.
492 */
493 public $reloadReset = false;
494
495
496 /**
497 *
498 */
499
500 public $detectionMethods = [
501 C::DETECTION_METHOD_COOKIE,
502 C::DETECTION_METHOD_IP,
503 ];
504
505 public $timezone;
506
507
508 public $recurringMonthlyDayType = C::RECURRING_MONTHLY_DAY_OF_MONTH;
509
510
511 public $recurringUnselectedDaysAction = 'skip';
512
513 public function __construct($id)
514 {
515 $this->recurringStartTime = Carbon::now(hurryt_tz($this->getTimezone()))->format('Y-m-d h:i A');
516 $this->id = $id;
517 }
518
519 /**
520 * Returns compaign post iD.
521 *
522 * @return int
523 */
524 public function get_id()
525 {
526 return $this->id;
527 }
528
529
530 /**
531 * Returns true if the current sticky bar can be displayed on the given page.
532 *
533 * @param $pageId
534 *
535 * @return bool
536 * @throws \Exception
537 */
538 public function show_sticky_on_page($pageId)
539 {
540 if ($this->enableSticky === C::NO) {
541 return true;
542 }
543
544 switch ($this->getStickyBarDisplayOn()):
545
546 case 'wc_products_pages':
547 if (!empty($pageId) && function_exists('is_product') && is_product()) {
548
549 $wc_campaign = new WCCampaign();
550 if (($wc_campaign->has_campaign($this, $pageId))
551 && $wc_campaign->met_conditions($this, $pageId)
552 ) {
553 return apply_filters('hurryt_show_sticky_bar', true, $this->get_id());
554 }
555 }
556
557 return apply_filters('hurryt_show_sticky_bar', false, $this->get_id());
558
559 case 'specific_pages':
560 $pageIds = $this->getStickyBarPages();
561
562 if (!empty($pageId) && !empty($pageIds) && in_array($pageId, $pageIds)) {
563 return apply_filters('hurryt_show_sticky_bar', true, $this->get_id());
564 }
565
566 foreach ($this->stickyIncludeUrls as $url) {
567 $current_url = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
568
569 if (Helpers::are_urls_match($url, $current_url)) {
570 return apply_filters('hurryt_show_sticky_bar', true, $this->get_id());
571 break;
572 }
573 }
574 return apply_filters('hurryt_show_sticky_bar', false, $this->get_id());
575
576 case 'exclude_pages':
577 $pageIds = $this->getStickyExcludePages();
578 if (!empty($pageId) && !empty($pageIds) && in_array($pageId, $pageIds)) {
579 return apply_filters('hurryt_show_sticky_bar', false, $this->get_id());
580 }
581
582 foreach ($this->stickyExcludeUrls as $url) {
583 $current_url = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
584 if (Helpers::are_urls_match($current_url, $url)) {
585 return apply_filters('hurryt_show_sticky_bar', false, $this->get_id());
586 break;
587 }
588 }
589
590 return apply_filters('hurryt_show_sticky_bar', true, $this->get_id());
591 case 'all_pages':
592 return apply_filters('hurryt_show_sticky_bar', true, $this->get_id());
593 default:
594 return apply_filters('hurryt_show_sticky_bar', false, $this->get_id());
595 endswitch;
596 }
597
598 /**
599 * Returns sticky bar pages.
600 *
601 * @return array
602 */
603 public function getStickyBarPages()
604 {
605 $pagesIds = $this->get_prop('sticky_bar_pages');
606
607 return array_filter(array_map('intval', $pagesIds));
608 }
609
610 public function getStickyExcludePages()
611 {
612 $page_ids = $this->get_prop('sticky_exclude_pages');
613
614 return array_filter(array_map('intval', $page_ids));
615 }
616
617 /**
618 * Set sticky bar pages.
619 *
620 * @param $value
621 */
622 public function setStickyBarPages($value)
623 {
624 $this->set_prop('sticky_bar_pages', $value);
625 }
626
627 public function setStickyExcludePages($value)
628 {
629 $this->set_prop('sticky_exclude_pages', $value);
630 }
631
632 public function setStickyBarDismissTimeout($value)
633 {
634 $this->set_prop('sticky_bar_dismiss_timeout', intval($value));
635 }
636
637 public function getStickyBarDismissTimeout()
638 {
639 return intval($this->get_prop('sticky_bar_dismiss_timeout'));
640 }
641
642 public function getStickyBarDisplayOn()
643 {
644
645 // backward compat.
646 $all_pages = $this->get_prop('sticky_bar_show_on_all_pages', false);
647
648 if ($all_pages === C::YES) {
649 $this->setStickyBarDisplayOn('all_pages');
650 }
651
652 $this->delete_prop('sticky_bar_show_on_all_pages');
653
654 $value = $this->get_prop('_hurryt_sticky_bar_display_on', false);
655
656 return !empty($value) ? $value : $this->stickyBarDisplayOn;
657 }
658
659 public function setStickyBarDisplayOn($value)
660 {
661 $this->set_prop('_hurryt_sticky_bar_display_on', $value);
662 }
663
664 /**
665 * Get raw setting.
666 *
667 * @param string $name
668 * @param boolean
669 *
670 * @return mixed
671 */
672 public function get_prop($name, $useDefault = true)
673 {
674
675 $value = get_post_meta($this->id, $name, true);
676 if (!empty($value)) {
677 return $value;
678 }
679 if ($useDefault) {
680 $name = str_replace('_hurryt_', '', $name);
681 return $this->{Helpers::snakeToCamelCase($name)};
682 }
683
684 return $value;
685 }
686
687 public function set_prop($name, $value)
688 {
689 $value = !empty($value) ? $value : '';
690 $_name = str_replace('_hurryt_', '', $name);
691 $getterMethod = Helpers::snakeToCamelCase($_name);
692 if (empty($value) && method_exists($this, $getterMethod)) {
693 $value = $this->{$getterMethod}();
694 }
695
696 update_post_meta($this->id, $name, $value);
697 }
698
699 /**
700 * Bulk save for compaign settings.
701 *
702 * @param $data
703 */
704 public function storeSettings($data)
705 {
706 if (!hurrytimer_allow_unfiltered_html()) {
707 $data = wp_kses_post_deep($data);
708 }
709
710 foreach ($data as $prop => $value) {
711 $method = Helpers::snakeToCamelCase("set_{$prop}");
712 if (method_exists($this, $method)) {
713 $this->$method($value ?: $this->$prop);
714 } elseif (property_exists(__CLASS__, Helpers::snakeToCamelCase($prop))) {
715 $this->set_prop($prop, $value);
716 }
717 }
718 if (!isset($data['wc_conditions'])) {
719 $this->setWcConditions([]);
720 }
721 if (!isset($data['sticky_bar_pages'])) {
722 $this->setStickyBarPages([]);
723 }
724
725 CSS_Builder::get_instance()->generate_css();
726 }
727
728 //removeIf(pro)
729
730 /**
731 * @param int $mode
732 * ::PROP
733 */
734 public function setMode($mode)
735 {
736 // if ($mode == C::MODE_RECURRING) {
737 // return;
738 // }
739 $this->set_prop('mode', $mode);
740 }
741 //endRemoveIf(pro)
742
743 /**
744 * TODO: Settings should load automatically upon instance creation.
745 */
746 public function loadSettings()
747 {
748
749 $reflection = new \ReflectionObject($this);
750 // TODO: Refactor prop loading to `$this->get_{prop}()`
751 foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $prop) {
752 $name = $prop->getName();
753 $method = 'get' . ucfirst($name);
754 if (method_exists($this, $method)) {
755 $this->{$name} = $this->$method();
756 } else {
757 $this->{$name} = $this->get_prop(Helpers::camelToSnakeCase($name));
758 }
759 }
760
761 }
762
763 public function getHeadline()
764 {
765 $content = $this->get_prop('_hurryt_headline', false);
766
767 // User has set a custom headline
768 if( !empty( $content ) ){ return $content; }
769
770 // Backward compat.
771 if (empty($content) && version_compare(get_option('hurrytimer_version'), '2.3.0', '<=')) {
772 return get_the_title($this->id);
773 }
774
775 // Show default headline.
776 return $this->headline;
777 }
778
779 public function setHeadline($value)
780 {
781 $this->set_prop('_hurryt_headline', $value);
782 }
783
784 public function is_published()
785 {
786 return get_post_status($this->id) === "publish";
787 }
788
789 /**
790 * Check if WooCommerce is enabled for this campaign.
791 *
792 * @return bool
793 */
794 public function is_wc_enabled()
795 {
796 return $this->getWcEnable() === C::YES;
797 }
798
799 /**
800 * Check current countdown timer mode
801 * ::PROP
802 *
803 * @return bool
804 */
805 public function is_evergreen()
806 {
807 return $this->get_prop('mode') == C::MODE_EVERGREEN;
808 }
809
810 public function is_one_time()
811 {
812 return $this->get_prop('mode') == C::MODE_REGULAR;
813 }
814
815 public function is_recurring()
816 {
817 return $this->get_prop('mode') == C::MODE_RECURRING;
818 }
819
820 public function getTimezone()
821 {
822 return '';
823
824 }
825 public function setTimezone($timezone)
826 {
827 $this->set_prop('timezone', $timezone);
828 }
829 /**
830 * Save compaign endtime for regular mode.
831 *
832 * @param $date
833 */
834 public function setEndDatetime($date)
835 {
836 $this->set_prop('end_datetime', $date ?: $this->defaultEndDatetime());
837 }
838
839 /**
840 * Returns default end datetime for regular mode.
841 * ::PROP
842 *
843 * @return false|string
844 */
845 private function defaultEndDatetime()
846 {
847 return date('Y-m-d h:i A', strtotime('+1 week'));
848 }
849
850 /**
851 * Save digit color.
852 * ::PROP
853 *
854 * @param $color
855 */
856 public function setDigitColor($color)
857 {
858 $this->set_prop('digit_color', $color);
859 }
860
861 /**
862 * Returns headline visibility.
863 * ::PROP
864 *
865 * @return mixed
866 */
867
868 public function getHeadlineVisibility()
869 {
870 $meta = 'headline_visibility';
871
872 $value = $this->get_prop($meta, false);
873 if ($value === C::NO || $value === C::YES) {
874 return $value;
875 }
876
877 $legacyMeta = 'display_headline';
878 $legacyValue = filter_var($this->get_prop_legacy($legacyMeta), FILTER_VALIDATE_BOOLEAN);
879 $legacyValue = $legacyValue ? C::YES : C::NO;
880
881 $this->set_prop($meta, $legacyValue);
882
883 $this->delete_prop($legacyMeta);
884
885 return $this->get_prop($meta);
886 }
887
888 /**
889 * Returns digit color.
890 * Backward compat with older versions.
891 * ::PROP
892 *
893 * @return mixed
894 */
895 public function getDigitColor()
896 {
897 $value = $this->get_prop('digit_color', false);
898 if ($value) {
899 return $value;
900 }
901
902 $legacy = $this->get_prop_legacy('text_color');
903 if (!$legacy) {
904 return $this->digitColor;
905 }
906
907 $this->setDigitColor($legacy);
908 if (!$this->get_prop('label_color', false)) {
909 $this->setLabelColor($legacy);
910 }
911
912 $this->delete_prop('text_color');
913
914 return $this->get_prop('digit_color');
915 }
916
917 /**
918 * ::PROP
919 *
920 * @param $color
921 */
922 public function setLabelColor($color)
923 {
924 $this->set_prop('label_color', $color);
925 }
926
927 /**
928 * Get label color
929 * ::PROP
930 *
931 * @return mixed
932 */
933 public function getLabelColor()
934 {
935 $value = $this->get_prop('label_color', false);
936 if ($value) {
937 return $value;
938 }
939
940 $legacy = $this->get_prop_legacy('text_color');
941 if (!$legacy) {
942 return $this->labelColor;
943 }
944
945 $this->setLabelColor($legacy);
946 if (!$this->get_prop('digit_color', false)) {
947 $this->setDigitColor($legacy);
948 }
949
950 $this->delete_prop('text_color');
951
952 return $this->get_prop('label_color');
953 }
954
955 /**
956 *
957 * Return timer digit size.
958 * Backward comapt. with older versions.
959 * ::PROP
960 *
961 * @return int
962 * @since 1.2.4
963 */
964 public function getDigitSize()
965 {
966 $meta = 'digit_size';
967 $value = $this->get_prop($meta, false);
968 if (!empty($value)) {
969 return $value;
970 }
971 $legacy = $this->get_prop_legacy('text_size');
972
973 if (empty($legacy)) {
974 return $this->digitSize;
975 }
976
977 $this->setDigitSize($legacy);
978
979 $this->delete_prop('text_size');
980
981 return $this->get_prop('digit_size');
982 }
983
984 /**
985 * Save timer digit size.
986 * ::PROP
987 *
988 * @param $size
989 */
990 public function setDigitSize($size)
991 {
992 $this->set_prop('digit_size', $size);
993 }
994
995 /**
996 * Save timer label size.
997 * ::PROP
998 *
999 * @param $size
1000 */
1001 public function setLabelSize($size)
1002 {
1003 $this->set_prop('label_size', $size);
1004 }
1005
1006
1007 /**
1008 * ::PROP
1009 *
1010 * @param $size
1011 */
1012 public function setHeadlineSize($size)
1013 {
1014 $this->set_prop('headline_size', $size);
1015 }
1016
1017 /**
1018 * Returns evergreen duration.
1019 * ::PROP
1020 *
1021 * @return array
1022 */
1023 public function getDuration()
1024 {
1025 $duration = (array)$this->get_prop('duration');
1026 return array_pad(array_map('intval', $duration), 4, 0);
1027 }
1028
1029
1030
1031 public function getRestartDuration($asSeconds = false)
1032 {
1033 $duration = (array)$this->get_prop('hurryt_restart_duration', false);
1034
1035 $duration = wp_parse_args($duration, $this->restartDuration);
1036 foreach ($duration as $unit => $value) {
1037 $duration[$unit] = (int)$value;
1038 }
1039
1040 if ($asSeconds) {
1041 $duration = $duration['days'] * DAY_IN_SECONDS +
1042 $duration['hours'] * HOUR_IN_SECONDS +
1043 $duration['minutes'] * MINUTE_IN_SECONDS +
1044 $duration['seconds'];
1045 }
1046 return $duration;
1047 }
1048
1049
1050 public function getRecurringPauseDuration($asSeconds = false)
1051 {
1052 $duration = (array)$this->get_prop('_hurryt_recurring_pause_duration', false);
1053
1054 $duration = wp_parse_args($duration, $this->recurringPauseDuration);
1055 foreach ($duration as $unit => $value) {
1056 $duration[$unit] = (int)$value;
1057 }
1058
1059 if ($asSeconds) {
1060 $duration = $duration['days'] * DAY_IN_SECONDS +
1061 $duration['hours'] * HOUR_IN_SECONDS +
1062 $duration['minutes'] * MINUTE_IN_SECONDS +
1063 $duration['seconds'];
1064 }
1065 return $duration;
1066 }
1067
1068 public function setRestartDuration($duration_array)
1069 {
1070 $this->set_prop('hurryt_restart_duration', $duration_array);
1071 }
1072 /**
1073 * ::PROP
1074 *
1075 * @param $value
1076 */
1077 public function setRecurringDuration($value)
1078 {
1079 $this->set_prop('_hurryt_recurring_duration', $value);
1080 }
1081 public function setRecurringPauseDuration($value)
1082 {
1083 $this->set_prop('_hurryt_recurring_pause_duration', $value);
1084 }
1085
1086 /**
1087 * Returns evergreen duration.
1088 *
1089 * @return array
1090 */
1091 public function getRecurringDuration()
1092 {
1093
1094 $duration = (array)$this->get_prop('_hurryt_recurring_duration', false);
1095 return array_pad(array_map('intval', $duration), 4, 0);
1096 }
1097
1098 /**
1099 * Returns actions array.
1100 *
1101 * @return array
1102 */
1103 public function getActions()
1104 {
1105 $legacy = $this->get_prop_legacy('end_action');
1106 if ($legacy) {
1107 $redirectUrl = $this->get_prop_legacy('redirect_url');
1108 $actions = [
1109 [
1110 'id' => intval($legacy),
1111 'redirectUrl' => $redirectUrl,
1112 ],
1113 ];
1114 $this->set_prop('actions', $actions);
1115 $this->delete_prop('end_action');
1116 $this->delete_prop('redirect_url');
1117
1118 return $this->mergeActions($actions);
1119 }
1120
1121 return $this->mergeActions($this->get_prop('actions'));
1122 }
1123
1124 private function mergeActions($actions)
1125 {
1126 $defaults = [
1127 [
1128 'id' => C::ACTION_NONE,
1129 'redirectUrl' => '',
1130 'message' => '',
1131 'coupon' => '',
1132 'wcStockStatus' => '',
1133 ],
1134 ];
1135
1136 if (empty($actions) || !is_array($actions)) {
1137 return $defaults;
1138 }
1139
1140 return array_map(function ($action) use ($defaults) {
1141 // Ensure we have an array
1142 $action = (array)$action;
1143
1144 // Parse with defaults to ensure all keys exist
1145 $action = wp_parse_args($action, $defaults[0]);
1146
1147 // Cast ID to integer
1148 $action['id'] = (int)$action['id'];
1149
1150 // Clean up message HTML if it exists
1151 if (!empty($action['message'])) {
1152 $action['message'] = preg_replace_callback(
1153 '#<style(\s=?[\S]*)?>([\s\S]*)?<\/style>#',
1154 function ($matches) {
1155 return isset($matches[1], $matches[2])
1156 ? '<style' . $matches[1] . '>' . preg_replace('/\<br(\s*)?\/?\>/i', '', $matches[2]) . '</style>'
1157 : '';
1158 },
1159 $action['message']
1160 );
1161 }
1162
1163 return $action;
1164 }, $actions);
1165 }
1166
1167
1168 public function get_action($needle)
1169 {
1170 $result = array_filter($this->actions, function ($action) use ($needle) {
1171 return $needle === (int)$action['id'];
1172 });
1173
1174 if (empty($result)) {
1175 return false;
1176 }
1177
1178 return array_shift($result);
1179 }
1180
1181 /**
1182 * Returns evergreen duration in seconds.
1183 *
1184 * @param array $duration
1185 *
1186 * @return int
1187 */
1188 public function durationInSeconds($duration = [])
1189 {
1190 list($d, $h, $m, $s) = empty($duration) ? $this->getDuration() : $duration;
1191
1192 return
1193 $d * DAY_IN_SECONDS +
1194 $h * HOUR_IN_SECONDS +
1195 $m * MINUTE_IN_SECONDS +
1196 $s;
1197 }
1198
1199 /**
1200 * Returns end datetime.
1201 *
1202 * @return string
1203 */
1204 public function getEndDatetime()
1205 {
1206 return $this->get_prop('end_datetime') ?: $this->defaultEndDatetime();
1207 }
1208
1209 /**
1210 * Return true if the recurrence is expired.
1211 *
1212 * @return bool
1213 */
1214 public function is_recurring_expired()
1215 {
1216 $endDate = $this->getRecurrenceEndDate();
1217
1218 if (!($endDate instanceof Carbon)) {
1219 return false;
1220 }
1221
1222 $now = Carbon::now(hurryt_tz($this->timezone));
1223
1224 return $now->greaterThan($endDate);
1225 }
1226
1227 /**
1228 * Calculate next recurrence.
1229 *
1230 * @return mixed
1231 * @throws \Exception
1232 */
1233 function timeToNextRecurrence()
1234 {
1235 $this->loadSettings();
1236
1237 if ($this->isMonthlyRecurring()) {
1238 $current = $this->getRecurrenceEndDate();
1239
1240 $next = $this->getRecurrenceEndDate(true);
1241
1242 $duration = $this->durationInSeconds($this->getRecurringDuration());
1243 $use_custom = $this->recurringDurationOption === 'custom' && $duration > 0;
1244
1245 if ($use_custom) {
1246 $time_remaining = $next->diffInSeconds($current);
1247 } else {
1248 $time_remaining = $this->getRecurringPauseDuration(true);
1249 }
1250 return $time_remaining;
1251 }
1252
1253 list($duration, $pause, $period) = $this->getRecurringDurationInSeconds();
1254
1255 $t = $pause ?: 0;
1256
1257 $waitBeforeRestart = $this->getRecurringPauseDuration(true);
1258
1259 $t += $waitBeforeRestart;
1260
1261 if (!$this->has_skipped_days()) {
1262 return $t;
1263 }
1264
1265 if ($this->isWeeklyRecurring()) {
1266 return $t;
1267 }
1268
1269 if ($this->recurringUnselectedDaysAction === 'hide') {
1270 return $t;
1271 }
1272
1273 return $waitBeforeRestart;
1274 }
1275
1276 /**
1277 * Return true if the current campaign can recur today.
1278 *
1279 * @return bool
1280 */
1281 public function can_recur_today()
1282 {
1283 $this->loadSettings();
1284
1285 $day = absint(Carbon::now(hurryt_tz($this->timezone))->format('w'));
1286
1287 $recur = in_array($day, array_map('absint', $this->recurringDays));
1288
1289 return apply_filters('hurryt_recur_today', $recur, $this->get_id());
1290 }
1291
1292 public function isMonthlyRecurring()
1293 {
1294 return $this->recurringFrequency === C::RECURRING_MONTHLY;
1295 }
1296
1297 public function isDayOfWeekRecurring()
1298 {
1299
1300 return $this->recurringMonthlyDayType === C::RECURRING_MONTHLY_DAY_OF_WEEK;
1301 }
1302
1303 public function isDayOfMonthRecurring()
1304 {
1305 return $this->recurringMonthlyDayType === C::RECURRING_MONTHLY_DAY_OF_MONTH;
1306 }
1307
1308 public function shouldEndRecurringByDate()
1309 {
1310 return absint($this->recurringEnd) === C::RECURRING_END_TIME;
1311 }
1312 public function shouldEndRecurringByRecurrences()
1313 {
1314 return (int)$this->recurringEnd === C::RECURRING_END_OCCURRENCES;
1315 }
1316
1317 public function shouldRecurForever()
1318 {
1319 return (int)$this->recurringEnd === C::RECURRING_END_NEVER;
1320 }
1321
1322 public function getRecurringRecurrences()
1323 {
1324 $num = (int)$this->recurringCount;
1325 return ++$num;
1326 }
1327
1328
1329 public function getRecurringPeriodEndDate()
1330 {
1331 list($d, $h, $m, $s) = $this->getRecurringDuration();
1332 $now = Carbon::now(hurryt_tz($this->timezone));
1333 $startDate = Carbon::parse($this->recurringStartTime, hurryt_tz($this->timezone));
1334 $interval = (int)$this->recurringInterval;
1335 $endDate = $now->copy();
1336
1337 switch ($this->recurringFrequency) {
1338 case C::RECURRING_DAILY:
1339 $endDate->minute = $startDate->minute;
1340 $endDate->addDays(max($interval, $d))->addHours($interval)->addMinutes($m)->addSeconds($s);
1341 break;
1342 case C::RECURRING_WEEKLY:
1343 $endDate->minute = $startDate->minute;
1344 $endDate->addWeeks($interval)->addHours($h)->addMinutes($m)->addSeconds($s);
1345 break;
1346 case C::RECURRING_HOURLY:
1347 $endDate->minute = $startDate->minute;
1348 $endDate->addHours(max($interval, $h))->addMinutes($m)->addSeconds($s);
1349 break;
1350 case C::RECURRING_MINUTELY:
1351 $minutes = max($interval, $m);
1352 $endDate->addMinutes($minutes)->addSeconds($s);
1353 break;
1354 }
1355
1356 return $endDate;
1357 }
1358
1359 public function getRecurringFrequencyInSeconds()
1360 {
1361 $freq = 0;
1362 switch ($this->recurringFrequency) {
1363 case C::RECURRING_WEEKLY:
1364 $freq = WEEK_IN_SECONDS;
1365 break;
1366 case C::RECURRING_DAILY:
1367 $freq = DAY_IN_SECONDS;
1368 break;
1369 case C::RECURRING_HOURLY:
1370 $freq = HOUR_IN_SECONDS;
1371 break;
1372 case C::RECURRING_MINUTELY:
1373 $freq = MINUTE_IN_SECONDS;
1374 break;
1375 }
1376
1377 return $freq * (int)$this->recurringInterval;
1378 }
1379 /**
1380 * @return array [duration, pause, period] in seconds
1381 */
1382 public function getRecurringDurationInSeconds()
1383 {
1384 $duration = $this->durationInSeconds($this->getRecurringDuration());
1385 $period = $this->getRecurringFrequencyInSeconds();
1386 $pause = $period - $duration;
1387 return [$duration, max(0, $pause), $period];
1388 }
1389
1390 /**
1391 * Returns the recurrence end date based on the current date/time.
1392 *
1393 * @param bool $next Return the next recurrence end date.
1394 *
1395 * @return Carbon
1396 */
1397
1398 public function getRecurrenceEndDate($next = false)
1399 {
1400
1401 $cached_key = $next ? 'recur_end_date__next' : 'recur_end_date';
1402 $cached = wp_cache_get($cached_key, 'hurrytimer');
1403 if ($cached instanceof Carbon) {
1404 return $cached;
1405 }
1406
1407 try {
1408
1409 $this->loadSettings();
1410 $now = Carbon::now(hurryt_tz($this->timezone));
1411 $startDate = Carbon::parse($this->recurringStartTime, hurryt_tz($this->timezone));
1412 $interval = (int)$this->recurringInterval;
1413
1414
1415 // Handle monthly recurring
1416 if ($this->isMonthlyRecurring()) {
1417 $start_date_modified = $startDate->copy();
1418
1419 if ($startDate->day >= 28) {
1420 $start_date_modified->startOfMonth();
1421 }
1422
1423 $endDate = $now->copy()->addMonths($interval);
1424
1425 if ($next) {
1426 $endDate->addMonths($interval);
1427 }
1428
1429 if ($this->shouldEndRecurringByDate()) {
1430 $endDate = Carbon::parse($this->recurringUntil, hurryt_tz($this->timezone));
1431 }
1432
1433 if ($this->isDayOfWeekRecurring()) {
1434 $endDate = $endDate->addMonths($interval);
1435 }
1436
1437 $period = CarbonPeriod::create($start_date_modified, 'P' . $interval . 'M', $endDate);
1438
1439 if ($this->shouldEndRecurringByRecurrences()) {
1440 $period->setRecurrences($this->getRecurringRecurrences());
1441 }
1442
1443 if ($period->count() === 0) {
1444 return $period->getEndDate();
1445 }
1446
1447 $dates = iterator_to_array($period);
1448
1449 $dates = array_map(function ($date) use ($startDate) {
1450 $is_overflow = $startDate->day >= $date->daysInMonth;
1451 if ($this->isDayOfMonthRecurring() && $is_overflow) {
1452 $date->endOfMonth();
1453 }
1454 if ($this->isDayOfWeekRecurring()) {
1455 $date->modifyWeekOfMonth($startDate);
1456 }
1457 $date->setTimeFrom($startDate);
1458
1459 return $date;
1460 }, $dates);
1461
1462
1463 $out_date = end($dates) ?: $period->getEndDate();
1464
1465 reset($dates);
1466
1467 foreach ($dates as $i => $date) {
1468 $duration = $this->durationInSeconds($this->getRecurringDuration());
1469 $custom = $this->recurringDurationOption === 'custom' && $duration > 0;
1470
1471 if ($custom && !$next) {
1472 $custom_end_date = $date->copy()->addSeconds($duration);
1473
1474 if ($custom_end_date->greaterThan($now)) {
1475 $out_date = $custom_end_date;
1476
1477 break;
1478 }
1479
1480 $next_date = isset($dates[$i + 1]) ? $dates[$i + 1] : $period->getEndDate();
1481
1482 if ($now->lessThan($next_date)) {
1483 $out_date = $custom_end_date;
1484 break;
1485 }
1486
1487 continue;
1488 }
1489
1490 if ($date->greaterThan($now)) {
1491 $pause_duration = $this->getRecurringPauseDuration(true);
1492
1493 if ($next && !$custom) {
1494 $out_date = isset($dates[$i + 1]) ? $dates[$i + 1] : $out_date;
1495
1496 if ($pause_duration > 0) {
1497 $out_date = $date;
1498 }
1499 } else {
1500 $out_date = $date;
1501 $prev_end_date = isset($dates[$i - 1]) ? $dates[$i - 1] : null;
1502 if ($prev_end_date && $prev_end_date->copy()->diffInSeconds($now) < $pause_duration) {
1503 $out_date = $prev_end_date;
1504 }
1505 }
1506
1507 break;
1508 }
1509 }
1510 if ($this->shouldEndRecurringByDate() && $out_date->lessThan($now)) {
1511 // TODO: Use the last recurrence instead of `getEndDate()`
1512 $out_date = $period->getEndDate();
1513 }
1514
1515 wp_cache_set($cached_key, $out_date, 'hurrytimer');
1516
1517 return $out_date;
1518 } // Monthly recurring
1519
1520
1521 // Prevent minutely recurring from affecting performance
1522 // by updating the start date year and month to current ones.
1523 if ($this->isMinutelyRecurring()) {
1524 $startDate->year = $now->year;
1525 $startDate->month = $now->month;
1526 $startDate->day = $now->day;
1527 }
1528
1529
1530 list($duration, $pause) = $this->getRecurringDurationInSeconds();
1531
1532 $period = CarbonPeriod::start($startDate, true);
1533
1534 $period->seconds($duration + $pause);
1535
1536 if ($this->shouldEndRecurringByRecurrences()) {
1537 $period->setRecurrences($this->getRecurringRecurrences());
1538 } else {
1539 $endDate = $this->getRecurringPeriodEndDate();
1540 $dateperiod_interval = new DateInterval('PT' .($duration + $pause) . 'S');
1541 $period = new DatePeriod($startDate->toDateTime(), $dateperiod_interval, $endDate->addDays(7 * $interval)->toDateTime());
1542 // $period->until($endDate->addDays(7 * $interval));
1543
1544 }
1545
1546 $date = null;
1547
1548 $i = 0;
1549
1550 $pause_duration = $this->getRecurringPauseDuration(true);
1551
1552 foreach ($period as $start) {
1553
1554 $start = Carbon::instance($start);
1555
1556 $end = $start->copy()->addSeconds($this->getRecurringFrequencyInSeconds() - 1);
1557
1558 if ($now->isBetween($start, $end->copy()->addSeconds($pause_duration))) {
1559 $date = $start->copy()->addSeconds($duration - 1);
1560 if (!in_array($date->format('w'), $this->recurringDays)) {
1561 continue;
1562 }
1563 break;
1564 }
1565
1566 if ($now->isBefore($end->copy()->addSeconds($pause_duration))) {
1567 $date = $start;
1568 if (!in_array($date->format('w'), $this->recurringDays)) {
1569 continue;
1570 }
1571 break;
1572 }
1573 }
1574
1575 if (empty($date)) {
1576 return $period->getEndDate();
1577 }
1578
1579 $stop_date = Carbon::parse($this->recurringUntil, hurryt_tz($this->timezone));
1580
1581 if ($this->shouldEndRecurringByDate() && $stop_date->lessThan($date)) {
1582 return $stop_date;
1583 }
1584
1585 return $date;
1586 } catch (\Exception $e) {
1587 return false;
1588 }
1589 }
1590
1591
1592 function should_hide_today($date, $now)
1593 {
1594 return ($date->format('w') === $now->format('w')) && ($this->isWeeklyRecurring() || $this->recurringUnselectedDaysAction === 'hide');
1595 }
1596 function has_skipped_days()
1597 {
1598 $recur_days = array_map('intval', $this->recurringDays);
1599 return count($recur_days) < 7;
1600 }
1601 function can_recur_on($date)
1602 {
1603 $recur_days = array_map('intval', $this->recurringDays);
1604
1605 if (in_array($date->format('w'), $recur_days)) {
1606 return true;
1607 }
1608
1609 if (($date->isStartOfDay() && in_array($this->get_previous_day($date->format('w')), $recur_days))) {
1610 return true;
1611 }
1612
1613 return false;
1614 }
1615 private function get_previous_day($current_day)
1616 {
1617 $current_day === 0 ? 6 : ($current_day - 1);
1618 }
1619
1620 /**
1621 * Returns compaign publish datetime.
1622 *
1623 * @return mixed
1624 */
1625 public function getStartTimestamp()
1626 {
1627 return get_the_date($this->id);
1628 }
1629
1630 /**
1631 * Returns position in WooCommerce product page.
1632 * ::PROP
1633 *
1634 * @return mixed
1635 */
1636 public function getWcPosition()
1637 {
1638 $legacy = $this->get_prop_legacy('position');
1639
1640 if (!$legacy) {
1641 return $this->get_prop('wc_position');
1642 }
1643 $this->set_prop('wc_position', $legacy);
1644 $this->delete_prop('position');
1645
1646 return $legacy;
1647 }
1648
1649 private function get_prop_legacy($name)
1650 {
1651 return get_post_meta($this->id, $name, true);
1652 }
1653
1654 /**
1655 * Returns true if WooCommerce integration is enabled.
1656 * ::PROP
1657 *
1658 * @return mixed
1659 */
1660 public function getWcEnable()
1661 {
1662 $value = $this->get_prop('wc_enable', false);
1663
1664 if ($value === C::YES || $value === C::NO) {
1665 return $value;
1666 }
1667
1668 $legacy = filter_var($value, FILTER_VALIDATE_BOOLEAN);
1669 if ($legacy) {
1670 $this->set_prop('wc_enable', C::YES);
1671 } else {
1672 $this->set_prop('wc_enable', C::NO);
1673 }
1674
1675 return $this->get_prop('wc_enable');
1676 }
1677
1678 /**
1679 * Returns true if label should be displayed.
1680 *
1681 * @return mixed
1682 */
1683 public function getLabelVisibility()
1684 {
1685 $meta = 'label_visibility';
1686 $value = $this->get_prop($meta, false);
1687 if ($value === C::NO || $value === C::YES) {
1688 return $value;
1689 }
1690
1691 $legacy = filter_var(
1692 $this->get_prop_legacy('display_labels'),
1693 FILTER_VALIDATE_BOOLEAN
1694 );
1695 $legacy = $legacy ? C::YES : C::NO;
1696
1697 $this->set_prop($meta, $legacy);
1698
1699 $this->delete_prop('display_labels');
1700
1701 return $this->get_prop($meta);
1702 }
1703
1704 /**
1705 *
1706 * Returns restart type.
1707 *
1708 * @return int
1709 */
1710 public function getRestart()
1711 {
1712 return $this->get_prop('restart') ?: C::RESTART_NONE;
1713 }
1714
1715 // removeIf(pro)
1716 public function setRestart($value)
1717 {
1718 if ($value == C::RESTART_AFTER_DURATION) {
1719 return;
1720 }
1721 $this->set_prop('restart', $value);
1722 }
1723 // endRemoveIf(pro)
1724 /**
1725 * Returns WooCommerce products selection type.
1726 *
1727 * @return string
1728 */
1729 public function getWcProductsSelectionType()
1730 {
1731 $legacy = $this->get_prop_legacy('products_type');
1732 if (!$legacy) {
1733 return $this->get_prop('wc_products_selection_type');
1734 }
1735 $this->set_prop('wc_products_selection_type', $legacy);
1736 $this->delete_prop('products_type');
1737
1738 return $legacy;
1739 }
1740
1741 /**
1742 * Delete setting item.
1743 *
1744 * @param $name
1745 */
1746 public function delete_prop($name)
1747 {
1748 delete_post_meta($this->id, $name);
1749 }
1750
1751 /**
1752 * Returns products selection IDs.
1753 * ::PROP
1754 *
1755 * @return array
1756 */
1757 public function getWcProductsSelection()
1758 {
1759 $result = $this->get_prop_legacy('products');
1760 if (!$result) {
1761 $result = $this->get_prop('wc_products_selection');
1762 } else {
1763 $this->set_prop('wc_products_selection', $result);
1764 $this->delete_prop('products');
1765 }
1766
1767 return is_array($result) ? $result : [];
1768 }
1769
1770 /**
1771 * Store custom labels.
1772 * ::PROP
1773 *
1774 * @param $labels
1775 */
1776 public function setLabels($labels)
1777 {
1778 $labels = wp_parse_args($labels, $this->labels);
1779 update_post_meta($this->id, 'labels', $labels);
1780 }
1781
1782 public function getLabels()
1783 {
1784 return wp_parse_args($this->get_prop('labels'), $this->labels);
1785 }
1786
1787 /**
1788 * Returns true if compaign can be published.
1789 *
1790 * @return bool
1791 */
1792 public function is_active()
1793 {
1794 return get_post_status($this->id) === "publish"
1795 || get_post_status($this->id) === "future";
1796 }
1797
1798 /**
1799 * Returns trus if the compaign is scheduled.
1800 *
1801 * @return bool
1802 */
1803 public function is_scheduled()
1804 {
1805 $scheduled = get_post_status($this->get_id()) === "future";
1806 if ($scheduled) {
1807 return true;
1808 }
1809
1810 if ($this->is_recurring()) {
1811 $start_date = Carbon::parse($this->recurringStartTime, hurryt_tz($this->timezone));
1812 $now = Carbon::now(hurryt_tz($this->timezone));
1813 $now->second = $start_date->second;
1814 if ($now->lessThan($start_date)) {
1815
1816 return true;
1817 }
1818 }
1819
1820 return $scheduled;
1821 }
1822
1823 /**
1824 * Returns true if fixed campaign datetime is expired.
1825 *
1826 * @param string|null $date
1827 *
1828 * @return bool
1829 */
1830 public function is_one_time_expired($date = null)
1831 {
1832 $endDate = $date === null ? date_create($this->endDatetime) : $date;
1833 $today = date_create(current_time("mysql"));
1834
1835 return $endDate < $today;
1836 }
1837
1838 public function is_sticky_dismissed()
1839 {
1840 return isset($_COOKIE[Cookie_Detection::COOKIE_PREFIX . $this->get_id() . '_dismissed'])
1841 && $this->stickyBarDismissible === C::YES
1842 && $this->enableSticky === C::YES;
1843 }
1844
1845 /**
1846 * Returns compaign template wrapper.
1847 *
1848 * @param string
1849 *
1850 * @return string
1851 */
1852 public function wrap_template($content, $options = [])
1853 {
1854 $campaignBuilder = new CampaignBuilder($this);
1855
1856 return apply_filters('hurryt_campaign_content', $campaignBuilder->build($content, $options), $this->get_id());
1857 }
1858
1859 /*
1860 * Returns campaign template content.
1861 */
1862 public function build_template()
1863 {
1864 $campaignBuilder = new CampaignBuilder($this);
1865
1866 return $campaignBuilder->template();
1867 }
1868
1869 /**
1870 * ::PROP
1871 *
1872 * @param $value
1873 */
1874 public function setWcConditions($value)
1875 {
1876 $this->set_prop('_hurryt_wc_conditions', !empty($value) ? $value : []);
1877 }
1878
1879 /**
1880 * ::PROP
1881 *
1882 * @return mixed
1883 */
1884 public function getWcConditions()
1885 {
1886 return $this->get_prop('_hurryt_wc_conditions', false);
1887 }
1888
1889
1890 public function setRecurringUnselectedDaysAction($value)
1891 {
1892 $this->set_prop('_hurryt_recurring_unselected_days_action', $value);
1893 }
1894
1895 public function getRecurringUnselectedDaysAction()
1896 {
1897 return $this->get_prop('_hurryt_recurring_unselected_days_action');
1898 }
1899
1900 public function is_running()
1901 {
1902 // TODO: add support for weekly campaigns.
1903 if (
1904 $this->is_recurring()
1905 && !$this->can_recur_today()
1906 && ($this->recurringUnselectedDaysAction === 'hide' || $this->isWeeklyRecurring())
1907 ) {
1908 return false;
1909 }
1910
1911 $not_running = !$this->is_active() || $this->is_scheduled()
1912 || ($this->is_recurring() && empty($this->getRecurrenceEndDate()));
1913
1914
1915 return !$not_running;
1916 }
1917
1918 /**
1919 * Check if recurring or onetime campaign is expired.
1920 *
1921 * @return bool
1922 */
1923 public function is_expired()
1924 {
1925 return ($this->is_one_time() && $this->is_one_time_expired())
1926 || ($this->is_recurring() && $this->is_recurring_expired());
1927 }
1928
1929 public function get_mode_slug()
1930 {
1931 switch ($this->mode) {
1932 case C::MODE_EVERGREEN:
1933 return 'evergreen';
1934 case C::MODE_RECURRING:
1935 return 'recurring';
1936 case C::MODE_REGULAR:
1937 return 'one_time';
1938 }
1939 }
1940
1941 public function getReloadReset()
1942 {
1943 return filter_var($this->get_prop('reload_reset'), FILTER_VALIDATE_BOOLEAN);
1944 }
1945
1946 public function getDetectionMethods()
1947 {
1948 return array_map('intval', $this->get_prop('_hurryt_detection_methods'));
1949 }
1950
1951 public function setDetectionMethods($methods)
1952 {
1953 // removeIf(pro)
1954 if (is_array($methods)) {
1955 $methods = array_filter($methods, function ($m) {
1956 return (int)$m !== C::DETECTION_METHOD_USER_SESSION;
1957 });
1958 }
1959 // endRemoveIf(pro)
1960 $this->set_prop('_hurryt_detection_methods', $methods);
1961 }
1962
1963
1964
1965 /**
1966 * @return bool
1967 */
1968 private function isDailyRecurring()
1969 {
1970 return $this->recurringFrequency === C::RECURRING_DAILY;
1971 }
1972
1973 /**
1974 * @return bool
1975 */
1976 private function isWeeklyRecurring()
1977 {
1978 return $this->recurringFrequency === C::RECURRING_WEEKLY;
1979 }
1980
1981 /**
1982 * @return bool
1983 */
1984 private function isHourlyRecurring()
1985 {
1986 return $this->recurringFrequency === C::RECURRING_HOURLY;
1987 }
1988
1989 /**
1990 * @return bool
1991 */
1992 private function isMinutelyRecurring()
1993 {
1994 return $this->recurringFrequency === C::RECURRING_MINUTELY;
1995 }
1996 }
1997