PluginProbe
HurryTimer – An Scarcity and Urgency Countdown Timer for WordPress & WooCommerce / 2.7.2
HurryTimer – An Scarcity and Urgency Countdown Timer for WordPress & WooCommerce v2.7.2
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.7.2, at includes/Campaign.php

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