is_past( $this->settings['start'] ) ) { return false; } // Let's check if the campaign will ever end if ( null !== ( $end = $this->get_global_end_date() ) && ! $this->is_future( $end ) ) { return false; } //check if end cookie is set for this campaign if ( isset( $_COOKIE[ $this->cookie_end_name() ] ) ) { return false; } // check if any conversion event meet the criteria and end the campaign if it does so if ( ! empty( $this->conversion_events ) ) { foreach ( $this->conversion_events as $event ) { $trigger = $this->check_triggers( $event['trigger_options'] ); if ( ! $trigger ) { return false; } } } // Check if it's a period where the campaign should repeat $check_repeat = 'check_repeat_' . $this->settings['rolling_type']; if ( ! method_exists( $this, $check_repeat ) ) { return false; } return $this->$check_repeat(); } /** * returns the end date calculated for the campaign (the "global" end date - when the campaign is actually ending) * if no end date or number of occurrences have been set, returns null * * the return date / time is constructed by adding time intervals to the start date, if the end date is not fixed * * @return array|null array with 2 keys: * date - a date formatted with date('j F Y') * time - time in the format H:i (no seconds) */ public function get_global_end_date() { if ( empty( $this->settings['end'] ) ) { /* this campaign will never end */ return null; } if ( is_array( $this->settings['end'] ) ) { /* end is already a fixed date */ $absolute_end_date = $this->settings['end']; } else { $absolute_end_date = $this->set_after_occurrence_date( $this->settings['rolling_type'], $this->settings['start'], $this->settings['end'] * $this->settings['repeat'] ); } return $absolute_end_date; } /** * get the start and end date / time for the currently detected interval * * @return array with keys: date, time ( j F Y and H:i ) */ public function get_current_daily_interval() { // let's make our variable names shorter so the code is more readable $start = $this->settings['start']; $repeat = $this->settings['repeat']; $duration = $this->settings['duration']; // check for the difference in days between the start of the campaign and today // in order to find the start of the roll interval we're in $diff = floor( ( $this->now() - $this->date( $start ) ) / ( 60 * 60 * 24 ) ); $int_number = floor( $diff / $repeat ); // set the start date of this roll interval (for daily this will also be the start of the campaign on each roll) $temp = $this->set_after_occurrence_date( $this->settings['rolling_type'], $start, $int_number * $repeat ); $interval = array( 'start' => array( 'date' => $temp['date'], 'time' => $start['time'], ), ); $interval['end'] = tve_ult_add_to_date( $interval['start'], 0, $duration ); return $interval; } /** * get all the applicable weekly intervals that can apply based on the current date * * @return array of intervals ( each interval with start and end date / time, formatted in j F Y and H:i ) */ public function get_current_weekly_intervals() { // let's make our variable names shorter so the code is more readable $start = $this->settings['start']; $repeat = (int) $this->settings['repeat']; // get the first day of the week before the start date so we can do the week difference $start_date = date( 'w', strtotime( $start['date'] ) ); //set the fake start date of the campaign $week_start['date'] = date( 'j F Y', strtotime( $start['date'] . '-' . $start_date . ' days' ) ); $week_start['time'] = $start['time']; //get the number of whole campaigns that rolled until today (weeks + repeat every x weeks) $diff = floor( ( $this->now() - $this->date( $week_start ) ) / ( 60 * 60 * 24 * 7 ) ); $int_number = floor( $diff / $repeat ); // set the start date of this roll interval $temp = $this->set_after_occurrence_date( $this->settings['rolling_type'], $week_start, $int_number * $repeat ); $int_start['date'] = $temp['date']; $int_start['time'] = $this->settings['start']['time']; //construct an array of all weekdays where the campaign should show $display_intervals = $this->get_display_days( $int_start ); // check if the repeat is 1, and it's not the first week // if we're in our first week this could cause erroneous displays of the campaign if ( $repeat === 1 && $int_start !== $week_start ) { // check if any end interval is in the next week foreach ( $display_intervals as $day_id => $interval ) { $partial_start = date( 'w', strtotime( $interval['start']['date'] ) ); $partial_end = date( 'w', strtotime( $interval['end']['date'] ) ); // if the start day is smaller and end week are not the same then the end // is in the next month and should be shown at the beginning of // this month too because we're already in the second month if ( $partial_start >= $partial_end ) { // create the new array with the correct interval $display_intervals['new']['start']['date'] = date( 'j F Y', strtotime( 'last sunday', strtotime( $interval['start']['date'] ) ) ); $display_intervals['new']['start']['time'] = '00:00'; $display_intervals['new']['end']['date'] = date( 'j F Y', strtotime( $interval['end']['date'] . ' -1 week' ) ); $display_intervals['new']['end']['time'] = $interval['end']['time']; } } } return $display_intervals; } /** * get all the applicable monthly intervals that can apply based on the current date * * @return array of intervals ( each interval with start and end date / time, formatted in j F Y and H:i ) */ public function get_current_monthly_intervals() { $start = $this->settings['start']; $repeat = $this->settings['repeat']; // get the first day of the month before the start date so we can do the month difference $start_date = date( 'j', strtotime( $start['date'] ) ); //set the fake start date of the campaign $month_start['date'] = date( '1 F Y', strtotime( $start['date'] ) ); $month_start['time'] = $start['time']; //get the number of whole campaigns that rolled until today (moths + repeat every x months) $start_date = $this->date( $month_start ); $now = $this->now(); $i = 0; while ( ( $start_date = strtotime( '+1 MONTH', $start_date ) ) <= $now ) { $i ++; } $int_number = floor( $i / $repeat ); // set the start date of this roll interval $temp = $this->set_after_occurrence_date( $this->settings['rolling_type'], $month_start, $int_number * $repeat ); $int_start['date'] = $temp['date']; $int_start['time'] = $start['time']; //construct an array of all weekdays where the campaign should show $display_intervals = $this->get_display_days( $int_start ); // check if the repeat is 1, and it's not the first month // if we're in our first month this could cause erroneous displlays of the campaign if ( $repeat === 1 && $int_start !== $month_start ) { // check if any end interval is in the next month foreach ( $display_intervals as $day_id => $interval ) { $partial_start = date( 'F', strtotime( $interval['start']['date'] ) ); $partial_end = date( 'F', strtotime( $interval['end']['date'] ) ); // if the start month and end month are not the same then the end // is in the next month and should be shown at the beginning of // this month too because we're already in the second month if ( $partial_start !== $partial_end ) { // create the new array with the correct interval $display_intervals['new']['start']['date'] = date( '1 F Y', strtotime( $interval['start']['date'] ) ); $display_intervals['new']['start']['time'] = '00:00'; $display_intervals['new']['end']['date'] = date( 'j F Y', strtotime( $interval['end']['date'] . ' -1 Month' ) ); $display_intervals['new']['end']['time'] = $interval['end']['time']; } } } return $display_intervals; } /** * get the start and end date / time for the currently detected interval * * @return array with keys: date, time ( j F Y and H:i ) */ public function get_current_yearly_interval() { $start = $this->settings['start']; $repeat = $this->settings['repeat']; $duration = $this->settings['duration']; // check for the difference in years between the start of the campaign and today // in order to find the start of the roll interval we're in $diff = floor( ( $this->now() - $this->date( $start ) ) / ( 60 * 60 * 24 * 365 ) ); $int_number = floor( $diff / $repeat ); // set the start date of this roll interval (for daily this will also be the start of the campaign on each roll) $temp = $this->set_after_occurrence_date( $this->settings['rolling_type'], $start, $int_number * $repeat ); $int_start['date'] = $temp['date']; $int_start['time'] = $start['time']; // define the date where the campaign should stop showing in this rol; $int_end = tve_ult_add_to_date( $int_start, $duration ); return array( 'start' => $int_start, 'end' => $int_end, ); } /** * Check if the daily campaign should show * * @return bool */ public function check_repeat_daily() { $interval = $this->get_current_daily_interval(); // check if we're in the interval if we are we'll show the campaign return $this->is_future( $interval['end'] ) && $this->is_past( $interval['start'] ); } /** * Check if the weekly campaign should show * * @return bool */ public function check_repeat_weekly() { $display_intervals = $this->get_current_weekly_intervals(); // check if today is in any of the display intervals foreach ( $display_intervals as $interval ) { if ( $this->is_future( $interval['end'] ) && $this->is_past( $interval['start'] ) ) { return true; } } return false; } /** * Check if the monthly campaign should show * * @return bool */ public function check_repeat_monthly() { $display_intervals = $this->get_current_monthly_intervals(); // check if today is in any of the display intervals foreach ( $display_intervals as $interval ) { if ( $this->is_future( $interval['end'] ) && $this->is_past( $interval['start'] ) ) { return true; } } return false; } /** * Check if the yearly campaign should show * * @return bool */ public function check_repeat_yearly() { $interval = $this->get_current_yearly_interval(); // check if we're in the interval if we are we'll show the campaign return $this->is_future( $interval['end'] ) && $this->is_past( $interval['start'] ); } /** * Set the end date accordingly taking in consideration the rolling type * * @param string $rolling campaign type * @param array $date with 2 indexes [date,time] * @param int $occurrence in days|weeks|months|years * * @return mixed */ public function set_after_occurrence_date( $rolling, $date, $occurrence ) { switch ( $rolling ) { //append number of days case TVE_Ult_Const::CAMPAIGN_ROLLING_TYPE_DAILY: $date['date'] = date( 'j F Y', strtotime( $date['date'] . ' ' . $date['time'] . ' + ' . $occurrence . ' days' ) ); break; //append number of weeks case TVE_Ult_Const::CAMPAIGN_ROLLING_TYPE_WEEKLY: $date['date'] = date( 'j F Y', strtotime( $date['date'] . ' ' . $date['time'] . ' + ' . $occurrence . ' weeks' ) ); break; //append number of months case TVE_Ult_Const::CAMPAIGN_ROLLING_TYPE_MONTHLY: $date['date'] = date( 'j F Y', strtotime( $date['date'] . ' ' . $date['time'] . ' + ' . $occurrence . ' months' ) ); break; //append number of years case TVE_Ult_Const::CAMPAIGN_ROLLING_TYPE_YEARLY: $date['date'] = date( 'j F Y', strtotime( $date['date'] . ' ' . $date['time'] . ' + ' . $occurrence . ' years' ) ); break; } return $date; } /** * Get the days on which the campaign should show * * @return array */ public function get_display_days( $start_date ) { $dates = array(); foreach ( $this->settings['repeatOn'] as $day ) { $dates[ $day ]['start'] = tve_ult_add_to_date( $start_date, $day ); $dates[ $day ]['end'] = tve_ult_add_to_date( $start_date, $day + $this->settings['duration'] ); } return $dates; } /** * Returns the number of hours remained until the campaign/interval ends * * @return int */ public function hours_until_end() { $method = "get_{$this->settings['rolling_type']}_end_date"; $now = date( 'Y-m-d H:i', $this->now() ); if ( method_exists( $this, $method ) ) { $end = call_user_func( array( $this, $method ) ); } else { $end = date( 'Y-m-d H:00:00', $this->now() ); } $diff = tve_ult_date_diff( $now, $end ); return ( $diff['days'] * 24 ) + $diff['hours']; } /** * Adds duration hours to start date and returns the string date * * @return bool|string */ public function get_daily_end_date() { $start_date = date( 'Y-m-d' ); $start_hour = $this->settings['start']['time']; $start = tve_ult_pre_format_date( $start_date, $start_hour ); $start_at = strtotime( $start ); if ( $start_at > $this->now( false ) ) { // this means the campaign's end hour is after the current time - meaning that the current interval has actually started "yesterday" $start_at -= DAY_IN_SECONDS; $start = date( 'Y-m-d H:i:s', $start_at ); } return date( 'Y-m-d H:i:s', strtotime( $start . ' +' . $this->settings['duration'] . ' hours' ) ); } /** * Adds duration days to start date and returns the string date * * @return string */ public function get_yearly_end_date() { // check for the difference in years between the start of the campaign and today // in order to find the start of the roll interval we're in $diff = floor( ( $this->now() - $this->date( $this->settings['start'] ) ) / ( 60 * 60 * 24 * 365 ) ); $int_number = floor( $diff / $this->settings['repeat'] ); // set the start date of this roll interval (for daily this will also be the start of the campaign on each roll) $temp = $this->set_after_occurrence_date( $this->settings['rolling_type'], $this->settings['start'], $int_number * $this->settings['repeat'] ); $int_start['date'] = $temp['date']; $int_start['time'] = $this->settings['start']['time']; $start = tve_ult_pre_format_date( $int_start['date'], $int_start['time'] ); $interval_end = date( 'Y-m-d H:i:s', strtotime( $start . ' +' . $this->settings['duration'] . ' days' ) ); if ( is_array( $this->settings['end'] ) ) { $end_date = tve_ult_pre_format_date( $this->settings['end']['date'], $this->settings['end']['time'] ); } return isset( $end_date ) && $end_date < $interval_end ? ( $end_date . ':00' ) : $interval_end; } /** * Calculates the end date of the selected interval * If the current date is not within any interval * NOW() is returned. This will make the last event occur * * @return string */ public function get_weekly_end_date() { $intervals = $this->get_current_weekly_intervals(); $now = $this->now(); /** * Foreach possible display intervals - find the one that applies to the current time * If now is between the interval that means * we have to calculate how many hours we have to until * the current interval ends. * Based on that we select the current event. */ foreach ( $intervals as $possible_option ) { $interval_start = $this->date( $possible_option['start'] ); $interval_end = $this->date( $possible_option['end'] ); if ( $interval_start <= $now && $now < $interval_end ) { $interval = array( 'start' => date( 'Y-m-d H:i', $interval_start ), 'end' => date( 'Y-m-d H:i:00', $interval_end ), ); } } if ( ! isset( $interval ) ) { /** * this will make the last event to be selected because the events * are ordered by duration asc and last event has smallest duration * */ return date( 'Y-m-d H:00', $this->now() ); } if ( is_array( $this->settings['end'] ) ) { $end_date = tve_ult_pre_format_date( $this->settings['end']['date'], $this->settings['end']['time'] ); } return isset( $end_date ) && $end_date < $interval['end'] ? ( $end_date . ':00' ) : $interval['end']; } /** * Calculates the end date of the selected interval * If the current date is not within any interval * NOW() is returned. This will make the last event occur * * @return string */ public function get_monthly_end_date() { $campaign_start = date( 'Y-m-01 ' . $this->settings['start']['time'] . ':00', $this->now() ); foreach ( $this->settings['repeatOn'] as $day ) { $interval_start = date( 'Y-m-d H:i', strtotime( $campaign_start . ' +' . ( $day ) . ' days' ) ); $interval_end = date( 'Y-m-d H:i:00', strtotime( $interval_start . ' +' . (int) $this->settings['duration'] . ' days' ) ); if ( strtotime( $interval_start ) <= $this->now() && $this->now() < strtotime( $interval_end ) ) { $interval = array( 'start' => $interval_start, 'end' => $interval_end, ); break; } } if ( ! isset( $interval ) ) { /** * this will make the last event to be selected because the events * are ordered by duration asc and last event has smallest duration */ return date( 'Y-m-d H:00:00', $this->now() ); } if ( is_array( $this->settings['end'] ) ) { $end_date = tve_ult_pre_format_date( $this->settings['end']['date'], $this->settings['end']['time'] ); } return isset( $end_date ) && $end_date < $interval['end'] ? ( $end_date . ':00' ) : $interval['end']; } /** * get the end date of the current campaign instance (interval) * * should return a date in the format: Y-m-d H:i:s * * @return string */ public function get_end_date() { $method = "get_{$this->settings['rolling_type']}_end_date"; if ( empty( $this->settings['rolling_type'] ) || ! method_exists( $this, $method ) ) { return ''; } return call_user_func( array( $this, $method ) ); } /** * @inheritdoc */ public function get_duration() { $duration = 0; switch ( $this->settings['rolling_type'] ) { case TVE_Ult_Const::CAMPAIGN_ROLLING_TYPE_DAILY: $duration = $this->settings['duration']; break; case TVE_Ult_Const::CAMPAIGN_ROLLING_TYPE_WEEKLY: case TVE_Ult_Const::CAMPAIGN_ROLLING_TYPE_MONTHLY: case TVE_Ult_Const::CAMPAIGN_ROLLING_TYPE_YEARLY: $duration = $this->settings['duration'] * 24; break; } return $duration; } /** * checks performed for a daily campaign - pre access page * * @return bool */ protected function should_redirect_pre_access_daily() { $interval = $this->get_current_daily_interval(); /* if the currently detected interval has ended, this means that there is at least one more interval (global settings are checked in a previous step) */ return $this->is_past( $interval['end'] ); } /** * checks performed for a weekly campaign - pre-access * * @return bool */ protected function should_redirect_pre_access_weekly() { /** * pre-access redirection is achieved when the campaign did not end and it currently does not apply */ if ( $this->should_redirect_expired() || $this->applies() ) { return false; } return true; } /** * checks performed for a monthly campaign * * @return bool */ protected function should_redirect_pre_access_monthly() { /** * use the expired and applies functionalities for this */ if ( $this->should_redirect_expired() || $this->applies() ) { return false; } return true; } /** * checks for pre-access redirects for yearly campaigns * * @return bool */ protected function should_redirect_pre_access_yearly() { /** * use the expires and applies functionalities */ if ( $this->should_redirect_expired_yearly() || $this->applies() ) { return false; } return true; } /** * checks if a weekly campaign is completely ended at this point * * the global end date is not sufficient, because it actually represents the actual and date of the last week in which this campaign occurs */ public function should_redirect_expired_weekly() { $end_date = $this->get_global_end_date(); $end_timestamp = $this->date( $end_date ); /* if end date is farther in the future than one cycle from now, this campaign is not ended */ if ( $end_timestamp > $this->now() + ( $this->settings['repeat'] * 7 * DAY_IN_SECONDS ) ) { return false; } /* if the campaign ends a less than one cycle from now and if all intervals have ended, the campaign is finished */ foreach ( $this->get_current_weekly_intervals() as $interval ) { if ( $this->is_future( $interval['end'] ) ) { return false; } } return true; } /** * checks performed for a monthly campaign expired page redirection * * @return bool */ public function should_redirect_expired_monthly() { $end_date = $this->get_global_end_date(); if ( $this->is_past( $end_date ) ) { return true; } $end_timestamp = strtotime( tve_ult_pre_format_date( $end_date['date'], $end_date['time'] ) . " -{$this->settings['repeat']} months" ); /* if end date is farther in the future than one cycle from now, this campaign is not ended */ if ( $end_timestamp > $this->now() ) { return false; } /* if we have at least one interval that has not ended yet between now and the end date, the campaign is not expired */ foreach ( $this->get_current_monthly_intervals() as $interval ) { if ( $this->is_future( $interval['end'] ) && $this->date( $interval['end'] ) < $this->date( $end_date ) ) { return false; } } return true; } /** * checks performed for redirects to the expired page of a yearly campaign * * @return bool */ protected function should_redirect_expired_yearly() { $end_date = $this->get_global_end_date(); $end_timestamp = strtotime( tve_ult_pre_format_date( $end_date['date'], $end_date['time'] ) . " -{$this->settings['repeat']} years" ); /* if end date is farther in the future than one cycle from now, this campaign is not ended */ if ( $end_timestamp > $this->now() ) { return false; } /* if we have at least one interval that has not ended yet between now and the end date, the campaign is not expired */ $interval = $this->get_current_yearly_interval(); if ( $this->is_future( $interval['end'] ) ) { return false; } return true; } /** * the redirection to the pre-access page should happen: * * a) when the campaign has not yet started * b) when a cycle is finished and another cycle is yet to begin * * @return bool */ public function should_redirect_pre_access() { /* first, some global checks */ if ( $this->is_future( $this->settings['start'] ) ) { return true; } if ( isset( $_COOKIE[ $this->cookie_end_name() ] ) ) { return false; } /* if the campaign has ended, return false */ if ( null !== ( $end_date = $this->get_global_end_date() ) && ! $this->is_future( $end_date ) ) { return false; } $method = 'should_redirect_pre_access_' . $this->settings['rolling_type']; if ( ! method_exists( $this, $method ) ) { return false; } return $this->{$method}(); } /** * checks performed for a daily campaign - expired page * * the redirection to the expired page should only happen after the campaign has ended * * @return bool */ public function should_redirect_expired() { /* first, some global checks */ if ( $this->is_future( $this->settings['start'] ) ) { return false; } if ( isset( $_COOKIE[ $this->cookie_end_name() ] ) ) { return true; } /* * short-circuit if the campaign never ends */ if ( empty( $this->settings['end'] ) ) { return false; } /* if the campaign has ended, return false */ if ( null !== ( $end_date = $this->get_global_end_date() ) && $this->is_past( $end_date ) ) { return true; } $method = 'should_redirect_expired_' . $this->settings['rolling_type']; if ( method_exists( $this, $method ) ) { return $this->{$method}(); } return false; } }