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

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