PluginProbe
Timed Content / 2.99
Timed Content v2.99
2.99 trunk 1.0 1.1 1.2 2.0 2.1 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.10 2.11 2.12 2.15 2.2 2.3 2.3.1 2.4 2.5 2.5.1 2.50 2.51 2.52 All 67 releases
timed-content / lib / class-timedcontentplugin.php

class-timedcontentplugin.php in Timed Content 2.99, at lib/class-timedcontentplugin.php

2,537 lines 86.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class TimedContentPlugin {
4 var $rule_freq_array;
5 var $rule_days_array;
6 var $rule_ordinal_array;
7 var $rule_ordinal_days_array;
8 var $rule_occurrence_custom_fields;
9 var $rule_pattern_custom_fields;
10 var $rule_recurrence_custom_fields;
11 var $rule_exceptions_custom_fields;
12 var $meridiem;
13 var $show_period;
14 var $show_period_labels;
15 var $show_leading_zero;
16 var $jquery_ui_datetime_datepicker_i18n;
17 var $jquery_ui_datetime_timepicker_i18n;
18 var $current_timezone;
19
20 /**
21 * Constructor
22 */
23 function __construct() {
24 add_filter( 'timed_content_filter', 'convert_smilies' );
25 add_filter( 'timed_content_filter', 'convert_chars' );
26 add_filter( 'timed_content_filter', 'prepend_attachment' );
27 add_filter( 'timed_content_filter', 'do_shortcode' );
28 add_filter( 'manage_' . TIMED_CONTENT_RULE_TYPE . '_posts_columns', array( $this, 'add_desc_column_head' ) );
29 add_filter( 'pre_get_posts', array( $this, 'timed_content_pre_get_posts' ) );
30 add_filter( 'post_updated_messages', array( $this, 'timed_content_rule_updated_messages' ), 1 );
31 add_filter( 'dashboard_glance_items', array( $this, 'add_rules_count' ) );
32
33 add_action( 'init', array( $this, 'init' ), 2 );
34 add_action( 'wp_head', array( $this, 'add_header_code' ), 1 );
35 add_action( 'manage_' . TIMED_CONTENT_RULE_TYPE . '_posts_custom_column', array( $this, 'add_desc_column_content' ), 10, 2 );
36 add_action( 'admin_enqueue_scripts', array( $this, 'add_admin_header_code' ), 1 );
37 add_action( 'admin_init', array( $this, 'set_tinymce_plugin_vars' ), 1 );
38 add_action( 'admin_init', array( $this, 'init_tinymce_plugin' ), 2 );
39 add_action( 'wp_ajax_timedContentPluginGetTinyMCEDialog', array( $this, 'timed_content_plugin_get_tinymce_dialog' ), 1 );
40 add_action( 'wp_ajax_timedContentPluginGetRulePeriodsAjax', array( $this, 'timed_content_plugin_get_rule_periods_ajax' ), 1 );
41 add_action( 'wp_ajax_timedContentPluginGetScheduleDescriptionAjax', array( $this, 'timed_content_plugin_get_schedule_description_ajax' ), 1 );
42 add_action( 'admin_head', array( $this, 'add_post_type_icons' ), 1 );
43 add_shortcode( TIMED_CONTENT_SHORTCODE_CLIENT, array( $this, 'client_show_html' ) );
44 add_shortcode( TIMED_CONTENT_SHORTCODE_SERVER, array( $this, 'server_show_html' ) );
45 add_shortcode( TIMED_CONTENT_SHORTCODE_RULE, array( $this, 'rules_show_html' ) );
46
47 $this->set_format_timezone( wp_timezone_string() );
48 }
49
50 /**
51 * Initialise plugin
52 */
53 function init() {
54 global $wp_locale;
55
56 load_plugin_textdomain( 'timed-content', false, '/timed-content/lang/' );
57
58 $this->rule_freq_array = array(
59 0 => __( 'hourly', 'timed-content' ),
60 1 => __( 'daily', 'timed-content' ),
61 2 => __( 'weekly', 'timed-content' ),
62 3 => __( 'monthly', 'timed-content' ),
63 4 => __( 'yearly', 'timed-content' ),
64 );
65
66 $this->rule_days_array = array(
67 0 => __( 'Sunday', 'timed-content' ),
68 1 => __( 'Monday', 'timed-content' ),
69 2 => __( 'Tuesday', 'timed-content' ),
70 3 => __( 'Wednesday', 'timed-content' ),
71 4 => __( 'Thursday', 'timed-content' ),
72 5 => __( 'Friday', 'timed-content' ),
73 6 => __( 'Saturday', 'timed-content' ),
74 );
75
76 $this->rule_ordinal_array = array(
77 0 => __( 'first', 'timed-content' ),
78 1 => __( 'second', 'timed-content' ),
79 2 => __( 'third', 'timed-content' ),
80 3 => __( 'fourth', 'timed-content' ),
81 4 => __( 'last', 'timed-content' ),
82 5 => __( 'fifth', 'timed-content' ),
83 );
84
85 $this->rule_ordinal_days_array = array(
86 0 => __( 'Sunday', 'timed-content' ),
87 1 => __( 'Monday', 'timed-content' ),
88 2 => __( 'Tuesday', 'timed-content' ),
89 3 => __( 'Wednesday', 'timed-content' ),
90 4 => __( 'Thursday', 'timed-content' ),
91 5 => __( 'Friday', 'timed-content' ),
92 6 => __( 'Saturday', 'timed-content' ),
93 7 => __( 'day', 'timed-content' ),
94 );
95
96 $this->jquery_ui_datetime_datepicker_i18n = array(
97 'closeText' => _x( 'Done', 'jQuery UI Datepicker Close label', 'timed-content' ), // Display text for close link
98 'prevText' => _x( 'Prev', 'jQuery UI Datepicker Previous label', 'timed-content' ), // Display text for previous month link
99 'nextText' => _x( 'Next', 'jQuery UI Datepicker Next label', 'timed-content' ), // Display text for next month link
100 'currentText' => _x( 'Today', 'jQuery UI Datepicker Today label', 'timed-content' ), // Display text for current month link
101 'weekHeader' => _x( 'Wk', 'jQuery UI Datepicker Week label', 'timed-content' ), // Column header for week of the year
102 // Replace the text indices for the following arrays with 0-based arrays
103 'monthNames' => $this->strip_array_indices( $wp_locale->month ), // Names of months for drop-down and formatting
104 'monthNamesShort' => $this->strip_array_indices( $wp_locale->month_abbrev ), // For formatting
105 'dayNames' => $this->strip_array_indices( $wp_locale->weekday ), // For formatting
106 'dayNamesShort' => $this->strip_array_indices( $wp_locale->weekday_abbrev ), // For formatting
107 'dayNamesMin' => $this->strip_array_indices( $wp_locale->weekday_initial ), // Column headings for days starting at Sunday
108 'dateFormat' => 'yy-mm-dd',
109 'firstDay' => get_option( 'start_of_week' ),
110 'isRTL' => $wp_locale->is_rtl(),
111 'showMonthAfterYear' => false, // True if the year select precedes month, false for month then year
112 'yearSuffix' => '', // Additional text to append to the year in the month headers
113 );
114
115 $tf = get_option( 'time_format' );
116 if ( false !== strpos( $tf, 'A' ) ) {
117 $this->meridiem = array( $wp_locale->meridiem['AM'], $wp_locale->meridiem['PM'] );
118 $this->show_period = true;
119 $this->show_period_labels = true;
120 $this->show_leading_zero = false;
121 } elseif ( false !== strpos( $tf, 'a' ) ) {
122 $this->meridiem = array( $wp_locale->meridiem['am'], $wp_locale->meridiem['pm'] );
123 $this->show_period = true;
124 $this->show_period_labels = true;
125 $this->show_leading_zero = false;
126 } else {
127 $this->meridiem = array( '', '' );
128 $this->show_period = false;
129 $this->show_period_labels = false;
130 $this->show_leading_zero = true;
131 }
132
133 $this->jquery_ui_datetime_timepicker_i18n = array(
134 'hourText' => _x( 'Hour', "jQuery UI Timepicker 'Hour' label", 'timed-content' ),
135 'minuteText' => _x( 'Minute', "jQuery UI Timepicker 'Minute' label", 'timed-content' ),
136 'timeSeparator' => _x( ':', 'jQuery UI Datepicker: Character used to separate hours and minutes in translated language', 'timed-content' ),
137 'closeButtonText' => _x( 'Done', "jQuery UI Timepicker 'Done' label", 'timed-content' ),
138 'nowButtonText' => _x( 'Now', "jQuery UI Timepicker 'Now' label", 'timed-content' ),
139 'deselectButtonText' => _x( 'Deselect', "jQuery UI Timepicker 'Deselect' label", 'timed-content' ),
140 'amPmText' => array( '', '' ),
141 'showPeriod' => false,
142 'showPeriodLabels' => false,
143 'showLeadingZero' => false,
144 'timeFormat' => 'G:i',
145 );
146
147 $this->timed_content_rule_type_init();
148 $this->setup_custom_fields();
149 }
150
151 /**
152 * Creates the Timed Content Rule post type and registers it with WordPress
153 */
154 function timed_content_rule_type_init() {
155 $labels = array(
156 'name' => _x( 'Timed Content rules', 'post type general name', 'timed-content' ),
157 'singular_name' => _x( 'Timed Content rule', 'post type singular name', 'timed-content' ),
158 'add_new' => _x(
159 'Add new',
160 'Menu item/button label on Timed Content Rules admin page',
161 'timed-content'
162 ),
163 'add_new_item' => __( 'Add new Timed Content rule', 'timed-content' ),
164 'edit_item' => __( 'Edit Timed Content rule', 'timed-content' ),
165 'new_item' => __( 'New Timed Content rule', 'timed-content' ),
166 'view_item' => __( 'View Timed Content rule', 'timed-content' ),
167 'search_items' => __( 'Search Timed Content rules', 'timed-content' ),
168 'not_found' => __( 'No Timed Content rules found', 'timed-content' ),
169 'not_found_in_trash' => __( 'No Timed Content rules found in trash', 'timed-content' ),
170 'parent_item_colon' => '',
171 'menu_name' => _x( 'Timed Content rules', 'post type general name', 'timed-content' ),
172 );
173 $args = array(
174 'labels' => $labels,
175 'description' => __(
176 'Create regular schedules to show or hide selected content in a page or post.',
177 'timed-content'
178 ),
179 'public' => false,
180 'publicly_queryable' => false,
181 'exclude_from_search' => false,
182 'show_ui' => true,
183 'show_in_menu' => true,
184 'show_in_nav_menus' => true,
185 'show_in_admin_bar' => true,
186 'query_var' => false,
187 'rewrite' => false,
188 'capability_type' => 'post',
189 'has_archive' => false,
190 'hierarchical' => false,
191 'menu_position' => 5,
192 'supports' => array( 'title' ),
193 );
194 register_post_type( TIMED_CONTENT_RULE_TYPE, $args );
195 }
196
197
198 /**
199 * Filter to change sort order to title
200 */
201 function timed_content_pre_get_posts( $query ) {
202 if ( $query->is_admin ) {
203 if ( $query->get( 'post_type' ) === TIMED_CONTENT_RULE_TYPE ) {
204 $query->set( 'orderby', 'title' );
205 $query->set( 'order', 'ASC' );
206 }
207 }
208
209 return $query;
210 }
211
212 /**
213 * Filter to customize CRUD messages for Timed Content Rules
214 */
215 function timed_content_rule_updated_messages( $messages ) {
216 global $post;
217
218 if ( ! empty( $post ) ) {
219 /* translators: date and time format to activate rule. http://ca2.php.net/manual/en/function.date.php*/
220 $post_date = date_i18n( __( 'M j, Y @ G:i', 'timed-content' ), strtotime( $post->post_date ) );
221 } else {
222 $post_date = '';
223 }
224
225 $messages[ TIMED_CONTENT_RULE_TYPE ] = array(
226 0 => '', // Unused. Messages start at index 1.
227 1 => __( 'Timed Content Rule updated.', 'timed-content' ),
228 2 => __( 'Custom field updated.', 'timed-content' ),
229 3 => __( 'Custom field deleted.', 'timed-content' ),
230 4 => __( 'Timed Content Rule updated.', 'timed-content' ),
231 5 => isset( $_GET['revision'] ) ? sprintf(
232 /* translators: %s: date and time of the revision */
233 __(
234 'Timed Content Rule restored to revision from %s',
235 'timed-content'
236 ),
237 wp_post_revision_title( (int) $_GET['revision'], false )
238 ) : false,
239 6 => __( 'Timed Content Rule published.', 'timed-content' ),
240 7 => __( 'Timed Content Rule saved.', 'timed-content' ),
241 8 => __( 'Timed Content Rule submitted.', 'timed-content' ),
242 9 => sprintf(
243 /* translators: %s: date and time to activate rule. */
244 __( 'Timed Content Rule scheduled for: %s.', 'timed-content' ),
245 '<strong>' . $post_date . '</strong>'
246 ),
247 10 => __( 'Timed Content Rule draft updated.', 'timed-content' ),
248 );
249
250 return $messages;
251 }
252
253 /**
254 * Convert localized date/time string to English
255 */
256 function date_time_to_english( $date, $time = '' ) {
257 $months = array(
258 'January',
259 'February',
260 'March',
261 'April',
262 'May',
263 'June',
264 'July',
265 'August',
266 'September',
267 'October',
268 'November',
269 'December',
270 );
271 $months_i18n = array(
272 __( 'January', 'timed-content' ),
273 __( 'February', 'timed-content' ),
274 __( 'March', 'timed-content' ),
275 __( 'April', 'timed-content' ),
276 __( 'May', 'timed-content' ),
277 __( 'June', 'timed-content' ),
278 __( 'July', 'timed-content' ),
279 __( 'August', 'timed-content' ),
280 __( 'September', 'timed-content' ),
281 __( 'October', 'timed-content' ),
282 __( 'November', 'timed-content' ),
283 __( 'December', 'timed-content' ),
284 );
285 $english_date = str_replace( $months_i18n, $months, $date );
286
287 return $english_date . ' ' . $time;
288 }
289
290 /**
291 * Advances a date by a set number of days
292 */
293 function get_next_day( $current, $interval_multiplier ) {
294 return strtotime( $interval_multiplier . ' day', $current );
295 }
296
297 /**
298 * Advances a date/time by a set number of hours
299 */
300 function get_next_hour( $current, $interval_multiplier ) {
301 return strtotime( $interval_multiplier . ' hour', $current );
302 }
303
304 /**
305 * Advances a date/time by a set number of weeks. If given an array of days of the week, this function will
306 * advance the date/time to the next day in that array in the jumped-to week. Use this function if you're
307 * repeating an action on specific days of the week (i.e., on Weekdays, Tuesdays and Thursdays, etc.).
308 */
309 function get_next_week( $current, $interval_multiplier, $days = array() ) {
310 // If $days is empty, advance $interval_multiplier weeks from $current and return the timestamp
311 if ( empty( $days ) ) {
312 return strtotime( $interval_multiplier . ' week', $current );
313 }
314
315 // Otherwise, set up an array combining the days of the week to repeat on and the current day
316 // (keys and values of the array will be the same, and the array is sorted)
317 $current_day_of_week_index = $this->format_timestamp( 'w', $current );
318 $days = array_merge( array( $current_day_of_week_index ), $days );
319 $days = array_unique( $days );
320 $days = array_values( $days );
321 sort( $days );
322 $days_of_week = array_combine( $days, $days );
323
324 // If the current day is the last one of the days of the week to repeat on, jump ahead to
325 // the next week to be repeating on and get the earliest day in the array
326 if ( max( $days_of_week ) === $current_day_of_week_index ) {
327 $pattern = ( ( 7 - $current_day_of_week_index ) + ( 7 * ( $interval_multiplier - 1 ) ) + ( min( array_keys( $days_of_week ) ) ) ) . ' day';
328 } else {
329 // Otherwise, cycle through the array until we find the next day to repeat on
330 $next_day_of_week_index = $current_day_of_week_index;
331 do {
332 } while ( ! isset( $days_of_week[ ++$next_day_of_week_index ] ) );
333 $pattern = ( $next_day_of_week_index - $current_day_of_week_index ) . ' day';
334 }
335
336 return strtotime( $pattern, $current );
337 }
338
339 /**
340 * Advances a date by a set number of months. When the date of the first active period lies
341 * on the 29th, 30th, or 31st of the month, this function will return a date on the last day
342 * of the month for those months not containing those days.
343 */
344 function get_next_month( $current, $start, $interval_multiplier ) {
345 $dateCurrent = new DateTime();
346 $dateCurrent->setTimestamp( $current );
347 $dateCurrent->setTimezone($this->get_format_timezone());
348
349 $dateStart = new DateTime();
350 $dateStart->setTimestamp( $start );
351 $dateStart->setTimezone($this->get_format_timezone());
352
353 $day = $dateCurrent->format( 'j' );
354 $month = $dateCurrent->format( 'n' );
355 $year = $dateCurrent->format( 'Y' );
356
357 // It can happen, that previous months limited the day of the month to a lower value (28, 29, 30)
358 // so we make sure, that we use the original day if possible
359 $dayStart = $dateStart->format( 'j' );
360 if ($dayStart > $day) {
361 $day = $dayStart;
362 }
363
364 $month++;
365 if ( $month > 12 ) {
366 $month = 1;
367 $year++;
368 }
369
370 if ( $day > 28 ) {
371 $dateCurrent->setDate($year, $month, 1);
372 $day = $dateCurrent->format('t');
373 if ($day > $dayStart) {
374 $day = $dayStart;
375 }
376 }
377
378 $dateCurrent->setDate($year, $month, $day);
379
380 return $dateCurrent->getTimestamp();
381 }
382
383 /**
384 * Advances a date to the 'n'th weekday of the next month (eg., first Wednesday, third Monday, last Friday, etc.).
385 *
386 * Note: If $ordinal is set to '4' and $day is set to '7', it will return the last day of the month.
387 */
388 function get_nth_weekday_of_month( $current, $ordinal, $day ) {
389 // First, get the month/year we need to work with
390 $the_month = $this->format_timestamp( 'm', $current );
391 $the_year = $this->format_timestamp( 'Y', $current );
392 $last_day_this_month = $this->format_timestamp( 't', $current );
393
394 // Get the time for the $current timestamp
395 $the_hour = $this->format_timestamp( 'H', $current );
396 $the_minute = $this->format_timestamp( 'i', $current );
397
398 // The target day
399 $the_day = '';
400
401 if ( 7 === $day ) { // If $day is "day of the month", get the day of month based on the ordinal
402 switch ( $ordinal ) {
403 case 0:
404 $the_day = '1';
405 break; // First day of the month //
406 case 1:
407 $the_day = '2';
408 break; // Second day of the month //
409 case 2:
410 $the_day = '3';
411 break; // Third day of the month //
412 case 3:
413 $the_day = '4';
414 break; // Fourth day of the month //
415 case 4:
416 case 5:
417 $the_day = $last_day_this_month;
418 break; // Last day of the month //
419 default:
420 $the_day = '1';
421 break;
422 }
423 } else { // If $day is one of the days of the week...
424 $day_range = array();
425 switch ( $ordinal ) { // ...get a 7-day range based on the ordinal...
426 case 0:
427 $day_range = range( 1, 7 );
428 break; // First 7 days of the month //
429 case 1:
430 $day_range = range( 8, 14 );
431 break; // Second 7 days of the month //
432 case 2:
433 $day_range = range( 15, 21 );
434 break; // Third 7 days of the month //
435 case 3:
436 $day_range = range( 22, 28 );
437 break; // Fourth 7 days of the month //
438 case 4:
439 case 5:
440 $day_range = range( $last_day_this_month - 6, $last_day_this_month );
441 break; // Last 7 days of the month //
442 default:
443 $day_range = range( 1, 7 );
444 break;
445 }
446 foreach ( $day_range as $a_day ) { // ...and find the matching weekday in that range.
447 $date_test = date_create_from_format('Y-m-d H:i', sprintf( '%04d-%02d-%02d 00:00', $the_year, $the_month, $a_day ), $this->get_format_timezone() );
448 if ( $date_test->format( 'w') === $day ) {
449 $the_day = $a_day;
450 break;
451 }
452 }
453
454 // If this should be the fifth day of the month, it may happen
455 // that the fourth and last day are identical - then you gave
456 // go on to the next month
457 if ('5' === $ordinal) {
458 $day_range = range( 22, 28 );
459 foreach ( $day_range as $a_day ) { // ...and find the matching weekday in that range.
460 $date_test = date_create_from_format('Y-m-d H:i', sprintf( '%04d-%02d-%02d 00:00', $the_year, $the_month, $a_day ), $this->get_format_timezone() );
461 if ( $date_test->format( 'w') === $day ) {
462 $the_day_previous = $a_day;
463 break;
464 }
465 }
466
467 if ($the_day_previous === $the_day) {
468 $the_day = 1;
469 $the_month++;
470 if ($the_month > 12) {
471 $the_month = 1;
472 $the_year++;
473 }
474 $date = date_create_from_format( 'Y-m-d H:i', sprintf( '%04d-%02d-%02d %02d:%02d', $the_year, $the_month, $the_day, $the_hour, $the_minute ), $this->get_format_timezone() );
475 $current_next_month = $date->getTimestamp();
476
477 return $this->get_nth_weekday_of_month( $current_next_month, $ordinal, $day );
478 }
479 }
480 }
481
482 $date = date_create_from_format( 'Y-m-d H:i', sprintf( '%04d-%02d-%02d %02d:%02d', $the_year, $the_month, $the_day, $the_hour, $the_minute ), $this->get_format_timezone() );
483 return $date->getTimestamp();
484 }
485
486 /**
487 * Advances a date by a set number of years
488 */
489 function get_next_year( $current, $interval_multiplier ) {
490 return strtotime( $interval_multiplier . ' year', $current );
491 }
492
493 /**
494 * Validates the various Timed Content Rule parameters and returns a series of error messages.
495 */
496 function validate( $args ) {
497 $errors = array();
498
499 $instance_start = DateTime::createFromFormat(
500 'Y-m-d H:i',
501 $args['instance_start']['date'] . ' ' . $args['instance_start']['time']
502 );
503 if ( false !== $instance_start ) {
504 $instance_start = $instance_start->getTimestamp();
505 }
506 $instance_end = DateTime::createFromFormat(
507 'Y-m-d H:i',
508 $args['instance_end']['date'] . ' ' . $args['instance_end']['time']
509 );
510 if ( false !== $instance_end ) {
511 $instance_end = $instance_end->getTimestamp();
512 }
513 $end_date = DateTime::createFromFormat( 'Y-m-d', $args['end_date'] );
514 if ( false !== $end_date ) {
515 $end_date = $end_date->getTimestamp();
516 }
517
518 if ( empty( $args['instance_start']['date'] ) ) {
519 $errors[] = __( 'Starting date must not be empty.', 'timed-content' );
520 }
521 if ( empty( $args['instance_start']['time'] ) ) {
522 $errors[] = __( 'Starting time must not be empty.', 'timed-content' );
523 }
524 if ( empty( $args['instance_end']['date'] ) ) {
525 $errors[] = __( 'Ending date must not be empty.', 'timed-content' );
526 }
527 if ( empty( $args['instance_end']['time'] ) ) {
528 $errors[] = __( 'Ending time must not be empty.', 'timed-content' );
529 }
530 if ( empty( $args['interval_multiplier'] ) ) {
531 $errors[] = __( 'Interval must not be empty.', 'timed-content' );
532 } elseif ( ! is_numeric( $args['interval_multiplier'] ) ) {
533 $errors[] = __( 'Number of recurrences must be a number.', 'timed-content' );
534 }
535 if ( 'recurrence_duration_num_repeat' === $args['recurr_type'] ) {
536 if ( empty( $args['num_repeat'] ) ) {
537 $errors[] = __( 'Number of repetitions must not be empty.', 'timed-content' );
538 } elseif ( ! is_numeric( $args['num_repeat'] ) ) {
539 $errors[] = __( 'Number of repetitions must be a number.', 'timed-content' );
540 }
541 } elseif ( 'recurrence_duration_end_date' === $args['recurr_type'] ) {
542 if ( empty( $args['end_date'] ) ) {
543 $errors[] = __( 'End date must not be empty.', 'timed-content' );
544 }
545 if ( $instance_end > $end_date ) {
546 $errors[] = __( 'Recurrence end date must be after ending date/time.', 'timed-content' );
547 }
548 }
549 if ( empty( $args['instance_start'] ) ) {
550 $errors[] = __( 'Starting date/time must be valid.', 'timed-content' );
551 }
552 if ( empty( $args['instance_end'] ) ) {
553 $errors[] = __( 'Ending date/time must be valid.', 'timed-content' );
554 }
555 if ( $instance_start > $instance_end ) {
556 $errors[] = __( 'Starting date/time must be before ending date/time.', 'timed-content' );
557 }
558
559 return $errors;
560 }
561
562 /**
563 * Helper to determine if the current rule loop is already finished
564 */
565 function loop_test( $recurr_type, $current, $last_occurrence_start, $period_count, $num_repeat ) {
566 if ( 'recurrence_duration_end_date' === $recurr_type ) {
567 return $current <= $last_occurrence_start;
568 }
569
570 return $period_count < $num_repeat;
571 }
572
573 /**
574 * Calculates the active periods for a Timed Content Rule
575 *
576 * Note: if $args['human_readable'] is set to true, then the result will be in human readable form
577 */
578 function get_rule_periods( $args ) {
579 global $post;
580
581 $active_periods = array();
582 $period_count = 0;
583
584 $human_readable = (bool) $args['human_readable'];
585 $freq = (int) $args['freq'];
586 $timezone = $args['timezone'];
587 $recurr_type = $args['recurr_type'];
588 $num_repeat = intval( $args['num_repeat'] );
589 $end_date = $args['end_date'];
590 $days_of_week = $args['days_of_week'];
591 $interval_multiplier = $args['interval_multiplier'];
592 $instance_start_date = $args['instance_start']['date'];
593 $instance_start_time = $args['instance_start']['time'];
594 $instance_end_date = $args['instance_end']['date'];
595 $instance_end_time = $args['instance_end']['time'];
596 $monthly_pattern = $args['monthly_pattern'];
597 $monthly_pattern_ord = $args['monthly_pattern_ord'];
598 $monthly_pattern_day = $args['monthly_pattern_day'];
599 $exceptions_dates = $args['exceptions_dates'];
600
601 $this->set_format_timezone( $timezone );
602 $right_now_t = time();
603
604 // use debug parameter if current user is allowed to edit the post
605 if ( isset( $_GET['tctest'] ) && ! empty( $post ) && current_user_can( 'edit_post', $post->post_id ) ) {
606 $dt = DateTime::createFromFormat( 'Y-m-d H:i:s', sanitize_text_field( $_GET['tctest'] ), $this->current_timezone );
607 if ( false !== $dt ) {
608 $right_now_t = $dt->getTimestamp();
609 }
610 }
611
612 // Beginning of first occurrence
613 $instance_start = strtotime( $this->date_time_to_english( $instance_start_date, $instance_start_time ) . ' ' . $timezone );
614 $start_has_dst = $this->format_timestamp( 'I', $instance_start );
615
616 // End of first occurrence
617 $instance_end = strtotime( $this->date_time_to_english( $instance_end_date, $instance_end_time ) . ' ' . $timezone );
618
619 $current = $instance_start;
620 $end_current = $instance_end;
621
622 if ( 'recurrence_duration_num_repeat' === $recurr_type ) {
623 $last_occurrence_start = strtotime( TIMED_CONTENT_TIME_END );
624 } else {
625 $last_occurrence_start = strtotime(
626 $this->date_time_to_english(
627 $end_date,
628 $instance_start_time
629 ) . ' ' . $timezone
630 );
631 }
632
633 switch ( $freq ) {
634 case TIMED_CONTENT_FREQ_HOURLY:
635 $day_limit = 1;
636 break;
637 case TIMED_CONTENT_FREQ_DAILY:
638 $day_limit = 7;
639 break;
640 case TIMED_CONTENT_FREQ_WEEKLY:
641 $day_limit = 21;
642 break;
643 case TIMED_CONTENT_FREQ_MONTHLY:
644 $day_limit = 80;
645 break;
646 default: // TIMED_CONTENT_FREQ_YEARLY:
647 $day_limit = 380;
648 break;
649 }
650 $future_repeats = 0;
651 while (
652 $this->loop_test( $recurr_type, $current, $last_occurrence_start, $period_count, $num_repeat ) &&
653 ( $current < $right_now_t || $future_repeats < 20 )
654 ) {
655 $exception_period = false;
656 $current_date = $this->format_timestamp( 'Y-m-d', $current );
657 if ( is_array( $exceptions_dates ) ) {
658 foreach ( $exceptions_dates as $exceptions_date ) {
659 if ( is_numeric( $exceptions_date ) ) {
660 $exceptions_date = $this->format_timestamp( 'Y-m-d', $exceptions_date );
661 }
662 if ( $current_date === $exceptions_date ) {
663 $exception_period = true;
664 break;
665 }
666 }
667 }
668
669 if ( ! $exception_period && $current > $right_now_t - $day_limit * 86400 ) {
670 // Adjust current date offset if start DST differs from current DST and we don't have a monthly pattern
671 $current_adjusted = $current;
672 if (TIMED_CONTENT_FREQ_MONTHLY !== $freq) {
673 $current_has_dst = $this->format_timestamp('I', $current);
674 if ('1' === $start_has_dst && '0' === $current_has_dst) {
675 $current_adjusted += 3600;
676 }
677 if ('0' === $start_has_dst && '1' === $current_has_dst) {
678 $current_adjusted -= 3600;
679 }
680 }
681
682 if ( $current > $right_now_t ) {
683 $future_repeats++;
684 }
685 $end_current = $current_adjusted + ( $instance_end - $instance_start );
686 if ( true === $human_readable ) {
687 $active_periods[ $period_count ]['start'] = $this->format_timestamp( TIMED_CONTENT_DATE_FORMAT_OUTPUT, $current_adjusted );
688 $active_periods[ $period_count ]['end'] = $this->format_timestamp( TIMED_CONTENT_DATE_FORMAT_OUTPUT, $end_current );
689 $active_periods[ $period_count ]['timezone'] = $timezone;
690 if ( $right_now_t < $current ) {
691 $active_periods[ $period_count ]['status'] = 'upcoming';
692 $active_periods[ $period_count ]['time'] = sprintf(
693 /* translators: %s: time difference */
694 _x(
695 '%s from now.',
696 'Human readable time difference',
697 'timed-content'
698 ),
699 human_time_diff( $current, $right_now_t )
700 );
701 } elseif ( ( $current <= $right_now_t ) && ( $right_now_t <= $end_current ) ) {
702 $active_periods[ $period_count ]['status'] = 'active';
703 $active_periods[ $period_count ]['time'] = __( 'Right now!', 'timed-content' );
704 } else {
705 $active_periods[ $period_count ]['status'] = 'expired';
706 $active_periods[ $period_count ]['time'] = sprintf(
707 /* translators: %s: time difference */
708 _x(
709 '%s ago.',
710 'Human readable time difference',
711 'timed-content'
712 ),
713 human_time_diff( $end_current, $right_now_t )
714 );
715 }
716 } else {
717 $active_periods[ $period_count ]['start'] = $current_adjusted;
718 $active_periods[ $period_count ]['end'] = $end_current;
719 $active_periods[ $period_count ]['timezone'] = $timezone;
720 }
721
722 $period_count++;
723 }
724
725 switch ( $freq ) {
726 case TIMED_CONTENT_FREQ_HOURLY:
727 $current = $this->get_next_hour( $current, $interval_multiplier );
728 break;
729 case TIMED_CONTENT_FREQ_DAILY:
730 $current = $this->get_next_day( $current, $interval_multiplier );
731 break;
732 case TIMED_CONTENT_FREQ_WEEKLY:
733 $current = $this->get_next_week( $current, $interval_multiplier, $days_of_week );
734 break;
735 case TIMED_CONTENT_FREQ_MONTHLY:
736 $current = $this->get_next_month( $current, $instance_start, $interval_multiplier );
737 $temp_current = $current;
738 if ( 'yes' === $monthly_pattern ) {
739 $current = $this->get_nth_weekday_of_month(
740 $current,
741 $monthly_pattern_ord,
742 $monthly_pattern_day
743 );
744 } else {
745 $current = $temp_current;
746 }
747 break;
748 default: // TIMED_CONTENT_FREQ_YEARLY:
749 $current = $this->get_next_year( $current, $interval_multiplier );
750 break;
751 }
752 }
753
754 return $active_periods;
755 }
756
757 /**
758 * Get stored rule periods by ID
759 */
760 function get_rule_periods_by_id( $id, $human_readable = false ) {
761 if ( TIMED_CONTENT_RULE_TYPE !== get_post_type( $id ) ) {
762 return array();
763 }
764
765 $prefix = TIMED_CONTENT_RULE_POSTMETA_PREFIX;
766 $args = array();
767
768 $args['human_readable'] = (bool) $human_readable;
769 $args['freq'] = get_post_meta( $id, $prefix . 'frequency', true );
770 $args['timezone'] = get_post_meta( $id, $prefix . 'timezone', true );
771 $args['recurr_type'] = get_post_meta( $id, $prefix . 'recurrence_duration', true );
772 $args['num_repeat'] = get_post_meta( $id, $prefix . 'recurrence_duration_num_repeat', true );
773 $args['end_date'] = get_post_meta( $id, $prefix . 'recurrence_duration_end_date', true );
774 $args['days_of_week'] = get_post_meta( $id, $prefix . 'weekly_days_of_week_to_repeat', true );
775 switch ( intval( $args['freq'] ) ) {
776 case 0:
777 $args['interval_multiplier'] = get_post_meta( $id, $prefix . 'hourly_num_of_hours', true );
778 break;
779 case 1:
780 $args['interval_multiplier'] = get_post_meta( $id, $prefix . 'daily_num_of_days', true );
781 break;
782 case 2:
783 $args['interval_multiplier'] = get_post_meta( $id, $prefix . 'weekly_num_of_weeks', true );
784 break;
785 case 3:
786 $args['interval_multiplier'] = get_post_meta( $id, $prefix . 'monthly_num_of_months', true );
787 break;
788 case 4:
789 $args['interval_multiplier'] = get_post_meta( $id, $prefix . 'yearly_num_of_years', true );
790 break;
791 default:
792 $args['interval_multiplier'] = 1;
793 }
794 $args['instance_start'] = get_post_meta( $id, $prefix . 'instance_start', true );
795 $args['instance_end'] = get_post_meta( $id, $prefix . 'instance_end', true );
796 $args['monthly_pattern'] = get_post_meta( $id, $prefix . 'monthly_nth_weekday_of_month', true );
797 $args['monthly_pattern_ord'] = get_post_meta( $id, $prefix . 'monthly_nth_weekday_of_month_nth', true );
798 $args['monthly_pattern_day'] = get_post_meta( $id, $prefix . 'monthly_nth_weekday_of_month_weekday', true );
799
800 $exceptions_dates = get_post_meta( $id, $prefix . 'exceptions_dates' );
801 if ( false !== $exceptions_dates && isset( $exceptions_dates[0] ) && is_array( $exceptions_dates[0] ) ) {
802 $args['exceptions_dates'] = $exceptions_dates[0];
803 } else {
804 $args['exceptions_dates'] = false;
805 }
806
807 $args = $this->convert_date_time_parameters_to_iso( $args );
808
809 return $this->get_rule_periods( $args );
810 }
811
812 /**
813 * Helper to get a sanitized post parameter
814 */
815 function get_post_param( $name ) {
816 $full_name = TIMED_CONTENT_RULE_POSTMETA_PREFIX . $name;
817 if ( ! isset( $_POST[ $full_name ] ) ) {
818 return '';
819 }
820
821 return sanitize_text_field( $_POST[ $full_name ] );
822 }
823
824 /**
825 * Helper to get a sanitized post array parameter
826 */
827 function get_post_array_param( $name ) {
828 $full_name = TIMED_CONTENT_RULE_POSTMETA_PREFIX . $name;
829
830 if ( ! isset( $_POST[ $full_name ] ) || ! is_array( $_POST[ $full_name ] ) ) {
831 return array();
832 }
833
834 $array = array();
835 foreach ( $_POST[ $full_name ] as $key => $value ) {
836 $array[ sanitize_text_field( $key ) ] = sanitize_text_field( $value );
837 }
838
839 return $array;
840 }
841
842 /**
843 * Get rule periods based on the contents of the form fields of the Add Timed Content Rule and
844 * Edit Timed Content Rule screens. Output is sent as JSON.
845 */
846 function timed_content_plugin_get_rule_periods_ajax() {
847 if ( current_user_can( 'edit_posts' ) || current_user_can( 'edit_pages' ) ) {
848 $prefix = TIMED_CONTENT_RULE_POSTMETA_PREFIX;
849 $args = array();
850
851 $args['human_readable'] = $this->get_post_param( 'human_readable' );
852 $args['freq'] = $this->get_post_param( 'frequency' );
853 $args['timezone'] = $this->get_post_param( 'timezone' );
854 $args['recurr_type'] = $this->get_post_param( 'recurrence_duration' );
855 $args['num_repeat'] = $this->get_post_param( 'recurrence_duration_num_repeat' );
856 $args['end_date'] = $this->get_post_param( 'recurrence_duration_end_date' );
857 $args['days_of_week'] = $this->get_post_array_param( 'weekly_days_of_week_to_repeat' );
858 $args['interval_multiplier'] = $this->get_post_param( 'interval_multiplier' );
859 $args['instance_start'] = $this->get_post_array_param( 'instance_start' );
860 $args['instance_end'] = $this->get_post_array_param( 'instance_end' );
861 $args['monthly_pattern'] = $this->get_post_param( 'monthly_nth_weekday_of_month' );
862 $args['monthly_pattern_ord'] = $this->get_post_param( 'monthly_nth_weekday_of_month_nth' );
863 $args['monthly_pattern_day'] = $this->get_post_param( 'monthly_nth_weekday_of_month_weekday' );
864 $args['exceptions_dates'] = $this->get_post_array_param( 'exceptions_dates' );
865
866 $errors = $this->validate( $args );
867 if ( count( $errors ) === 0 ) {
868 header( 'Content-Type: application/json' );
869 echo json_encode( $this->get_rule_periods( $args ) );
870 }
871 }
872 die();
873 }
874
875 /**
876 * Returns a human-readable description of a Timed Content Rule
877 */
878 function get_schedule_description( $args ) {
879 $interval_multiplier = 1;
880 $desc = '';
881
882 $errors = $this->validate( $args );
883 if ( $errors ) {
884 $message = sprintf(
885 '<div class="tcr-warning">' .
886 '<p class="heading">%s</p>' .
887 '<p>%s</p>' .
888 '<ul><li>%s</li></ul>' .
889 '<p>%s</p>',
890 __( 'Warning!', 'timed-content' ),
891 __( 'Some problems have been detected. Although you can still publish this rule, it may not work the way you expect.', 'timed-content' ),
892 implode( '</li><li>', $errors ),
893 __( 'Check that all of the conditions for this rule are correct, and use <b>Show projected dates/times</b> to ensure your rule is working properly.', 'timed-content' )
894 );
895
896 return $message;
897 }
898
899 if ( $args['action'] ) {
900 $action = __( 'Show the content', 'timed-content' );
901 } else {
902 $action = __( 'Hide the content', 'timed-content' );
903 }
904 $freq = (int)$args['freq'];
905 $timezone = $args['timezone'];
906 $recurr_type = $args['recurr_type'];
907 $num_repeat = intval( $args['num_repeat'] );
908 $end_date = $args['end_date'];
909 $days_of_week = $args['days_of_week'];
910 $interval_multiplier = $args['interval_multiplier'];
911 $instance_start_date = $args['instance_start']['date'];
912 $instance_start_time = $args['instance_start']['time'];
913 $instance_end_date = $args['instance_end']['date'];
914 $instance_end_time = $args['instance_end']['time'];
915 $monthly_pattern = $args['monthly_pattern'];
916 $monthly_pattern_ord = $args['monthly_pattern_ord'];
917 $monthly_pattern_day = $args['monthly_pattern_day'];
918 $exceptions_dates = $args['exceptions_dates'];
919
920 $desc = sprintf(
921 /* translators: %1$s: action, %2$s: start date, %3$s: start time, %4$s: end date, %5$s: end time */
922 _x(
923 '%1$s on %2$s @ %3$s until %4$s @ %5$s.',
924 'Perform action (%1$s) from date/time of first active period (%2$s @ %3$s) until date/time of last active period (%4$s @ %5$s).',
925 'timed-content'
926 ),
927 $action,
928 $instance_start_date,
929 $instance_start_time,
930 $instance_end_date,
931 $instance_end_time
932 );
933
934 if ( TIMED_CONTENT_FREQ_HOURLY === $freq ) {
935 $desc .= '<br />' . sprintf(
936 /* translators: %d numerical hours value */
937 _n(
938 'Repeat this action every %d hour.',
939 'Repeat this action every %d hours.',
940 $interval_multiplier,
941 'timed-content'
942 ),
943 $interval_multiplier
944 );
945 } elseif ( TIMED_CONTENT_FREQ_DAILY === $freq ) {
946 $desc .= '<br />' . sprintf(
947 /* translators: %d numerical days value */
948 _n(
949 'Repeat this action every %d day.',
950 'Repeat this action every %d days.',
951 $interval_multiplier,
952 'timed-content'
953 ),
954 $interval_multiplier
955 );
956 } elseif ( TIMED_CONTENT_FREQ_WEEKLY === $freq ) {
957 if ( ( $days_of_week ) && ( is_array( $days_of_week ) ) ) {
958 $days = array();
959 $days_list = '';
960 foreach ( $days_of_week as $v ) {
961 $days[] = $this->rule_days_array[ $v ];
962 }
963 switch ( count( $days ) ) {
964 case 1:
965 $days_list = $days[0];
966 break;
967 case 2:
968 $days_list = sprintf(
969 /* translators: %1$s, %2$s: weekday */
970 _x( '%1$s and %2$s', 'List of two weekdays', 'timed-content' ),
971 $days[0],
972 $days[1]
973 );
974 break;
975 case 3:
976 $days_list = sprintf(
977 /* translators: %1$s, %2$s, %3$s: weekday */
978 _x(
979 '%1$s, %2$s and %3$s',
980 'List of three weekdays',
981 'timed-content'
982 ),
983 $days[0],
984 $days[1],
985 $days[2]
986 );
987 break;
988 case 4:
989 $days_list = sprintf(
990 /* translators: %1$s, %2$s, %3$s, %4$s: weekday */
991 _x(
992 '%1$s, %2$s, %3$s and %4$s',
993 'List of four weekdays',
994 'timed-content'
995 ),
996 $days[0],
997 $days[1],
998 $days[2],
999 $days[3]
1000 );
1001 break;
1002 case 5:
1003 $days_list = sprintf(
1004 /* translators: %1$s, %2$s, %3$s, %4$s, %5$s: weekday */
1005 _x(
1006 '%1$s, %2$s, %3$s, %4$s and %5$s',
1007 'List of five weekdays',
1008 'timed-content'
1009 ),
1010 $days[0],
1011 $days[1],
1012 $days[2],
1013 $days[3],
1014 $days[4]
1015 );
1016 break;
1017 case 6:
1018 $days_list = sprintf(
1019 /* translators: %1$s, %2$s, %3$s, %4$s, %5$s, %6$s: weekday */
1020 _x(
1021 '%1$s, %2$s, %3$s, %4$s, %5$s and %6$s',
1022 'List of six weekdays',
1023 'timed-content'
1024 ),
1025 $days[0],
1026 $days[1],
1027 $days[2],
1028 $days[3],
1029 $days[4],
1030 $days[5]
1031 );
1032 break;
1033 case 7:
1034 $days_list = sprintf(
1035 /* translators: %1$s, %2$s, %3$s, %4$s, %5$s, %6$s, %7$s: weekday */
1036 _x(
1037 '%1$s, %2$s, %3$s, %4$s, %5$s, %6$s and %7$s',
1038 'List of all weekdays',
1039 'timed-content'
1040 ),
1041 $days[0],
1042 $days[1],
1043 $days[2],
1044 $days[3],
1045 $days[4],
1046 $days[5],
1047 $days[6]
1048 );
1049 break;
1050 }
1051 if ( 1 === $interval_multiplier ) {
1052 $desc .= '<br />' . sprintf(
1053 /* translators: %s: weekday */
1054 _x(
1055 'Repeat this action every week on %s.',
1056 'List the weekdays to repeat the rule when frequency is every week. %s is the list of weekdays.',
1057 'timed-content'
1058 ),
1059 $days_list
1060 );
1061 } else {
1062 $desc .= '<br />' . sprintf(
1063 /* translators: %1$d: number of weeks, %2$s: weekday */
1064 _x(
1065 'Repeat this action every %1$d weeks on %2$s.',
1066 'List the weekdays to repeat the rule when frequency is every %1$d weeks. %2$s is the list of weekdays.',
1067 'timed-content'
1068 ),
1069 $interval_multiplier,
1070 $days_list
1071 );
1072 }
1073 } else {
1074 $desc .= '<br />' . sprintf(
1075 /* translators: %d: number of weeks */
1076 _n(
1077 'Repeat this action every %d week.',
1078 'Repeat this action every %d weeks.',
1079 $interval_multiplier,
1080 'timed-content'
1081 ),
1082 $interval_multiplier
1083 );
1084 }
1085 } elseif ( TIMED_CONTENT_FREQ_MONTHLY === $freq ) {
1086 if ( 'yes' === $monthly_pattern ) {
1087 if ( 1 === $interval_multiplier ) {
1088 $desc .= '<br />' . sprintf(
1089 /* translators: %1$s: recurrence, %2$s: weekday */
1090 _x(
1091 'Repeat this action every month on the %1$s %2$s of the month.',
1092 "Example: 'Repeat this action every month on the second Friday of the month.'",
1093 'timed-content'
1094 ),
1095 $this->rule_ordinal_array[ $monthly_pattern_ord ],
1096 $this->rule_ordinal_days_array[ $monthly_pattern_day ]
1097 );
1098 } else {
1099 $desc .= '<br />' . sprintf(
1100 /* translators: %1$d: month count, %2$s: recurrence, %3$s: weekday */
1101 _x(
1102 'Repeat this action every %1$d months on the %2$s %3$s of the month.',
1103 "Example: 'Repeat this action every 2 months on the second Friday of the month.'",
1104 'timed-content'
1105 ),
1106 $interval_multiplier,
1107 $this->rule_ordinal_array[ $monthly_pattern_ord ],
1108 $this->rule_ordinal_days_array[ $monthly_pattern_day ]
1109 );
1110 }
1111 } else {
1112 $desc .= '<br />' . sprintf(
1113 /* translators: %d: month count */
1114 _n(
1115 'Repeat this action every %d month.',
1116 'Repeat this action every %d months.',
1117 $interval_multiplier,
1118 'timed-content'
1119 ),
1120 $interval_multiplier
1121 );
1122 }
1123 } elseif ( TIMED_CONTENT_FREQ_YEARLY === $freq ) {
1124 $desc .= '<br />' . sprintf(
1125 /* translators: %d: year count */
1126 _n(
1127 'Repeat this action every %d year.',
1128 'Repeat this action every %d years.',
1129 $interval_multiplier,
1130 'timed-content'
1131 ),
1132 $interval_multiplier
1133 );
1134 }
1135
1136 if ( 'recurrence_duration_num_repeat' === $recurr_type ) {
1137 $desc .= '<br />' . sprintf(
1138 /* translators: %d: number of recurrences */
1139 _n(
1140 'This rule will be active for %d recurrence.',
1141 'This rule will be active for %d recurrences.',
1142 $num_repeat,
1143 'timed-content'
1144 ),
1145 $num_repeat
1146 );
1147 } elseif ( 'recurrence_duration_end_date' === $recurr_type ) {
1148 $desc .= '<br />' . sprintf( /* translators: %s: end date */ __( 'This rule will be active until %s.', 'timed-content' ), $end_date );
1149 }
1150
1151 if ( ( $exceptions_dates ) && ( is_array( $exceptions_dates ) ) ) {
1152 sort( $exceptions_dates, SORT_NUMERIC );
1153 $exceptions_dates = array_unique( $exceptions_dates );
1154 if ( 0 === $exceptions_dates[0] ) {
1155 array_shift( $exceptions_dates );
1156 }
1157 if ( ! empty( $exceptions_dates ) ) {
1158 $desc .= '<br />' . sprintf(
1159 /* translators: %s: list of dates */
1160 __(
1161 'This rule will be inactive on the following dates: %s.',
1162 'timed-content'
1163 ),
1164 join( ', ', $exceptions_dates )
1165 );
1166 }
1167 }
1168
1169 $desc .= '<br />' . sprintf(
1170 /* translators: %s: timezone */
1171 __( 'All times are in the %s timezone.', 'timed-content' ),
1172 $timezone
1173 );
1174
1175 return $desc;
1176 }
1177
1178 /**
1179 * Get the description of a rule
1180 */
1181 function get_schedule_description_by_id( $id ) {
1182 $defaults = array();
1183
1184 foreach ( $this->rule_occurrence_custom_fields as $field ) {
1185 $defaults[ $field['name'] ] = $field['default'];
1186 }
1187 foreach ( $this->rule_pattern_custom_fields as $field ) {
1188 $defaults[ $field['name'] ] = $field['default'];
1189 }
1190 foreach ( $this->rule_recurrence_custom_fields as $field ) {
1191 $defaults[ $field['name'] ] = $field['default'];
1192 }
1193 foreach ( $this->rule_exceptions_custom_fields as $field ) {
1194 $defaults[ $field['name'] ] = $field['default'];
1195 }
1196
1197 $prefix = TIMED_CONTENT_RULE_POSTMETA_PREFIX;
1198 $args = array();
1199
1200 $args['action'] = ( false === get_post_meta(
1201 $id,
1202 $prefix . 'action',
1203 true
1204 ) ? $defaults['action'] : get_post_meta( $id, $prefix . 'action', true ) );
1205 $args['freq'] = ( false === get_post_meta(
1206 $id,
1207 $prefix . 'frequency',
1208 true
1209 ) ? $defaults['frequency'] : get_post_meta( $id, $prefix . 'frequency', true ) );
1210 $args['timezone'] = ( false === get_post_meta(
1211 $id,
1212 $prefix . 'timezone',
1213 true
1214 ) ? $defaults['timezone'] : get_post_meta( $id, $prefix . 'timezone', true ) );
1215 $args['recurr_type'] = ( false === get_post_meta(
1216 $id,
1217 $prefix . 'recurrence_duration',
1218 true
1219 ) ? $defaults['recurrence_duration'] : get_post_meta(
1220 $id,
1221 $prefix . 'recurrence_duration',
1222 true
1223 ) );
1224 $args['num_repeat'] = ( false === get_post_meta(
1225 $id,
1226 $prefix . 'recurrence_duration_num_repeat',
1227 true
1228 ) ? $defaults['recurrence_duration_num_repeat'] : get_post_meta(
1229 $id,
1230 $prefix . 'recurrence_duration_num_repeat',
1231 true
1232 ) );
1233 $args['end_date'] = ( false === get_post_meta(
1234 $id,
1235 $prefix . 'recurrence_duration_end_date',
1236 true
1237 ) ? $defaults['recurrence_duration_end_date'] : get_post_meta(
1238 $id,
1239 $prefix . 'recurrence_duration_end_date',
1240 true
1241 ) );
1242 $args['days_of_week'] = ( false === get_post_meta(
1243 $id,
1244 $prefix . 'weekly_days_of_week_to_repeat',
1245 true
1246 ) ? $defaults['weekly_days_of_week_to_repeat'] : get_post_meta(
1247 $id,
1248 $prefix . 'weekly_days_of_week_to_repeat',
1249 true
1250 ) );
1251 switch ( $args['freq'] ) {
1252 case '0':
1253 $args['interval_multiplier'] = ( false === get_post_meta(
1254 $id,
1255 $prefix . 'hourly_num_of_hours',
1256 true
1257 ) ? $defaults['hourly_num_of_hours'] : get_post_meta(
1258 $id,
1259 $prefix . 'hourly_num_of_hours',
1260 true
1261 ) );
1262 break;
1263 case '1':
1264 $args['interval_multiplier'] = ( false === get_post_meta(
1265 $id,
1266 $prefix . 'daily_num_of_days',
1267 true
1268 ) ? $defaults['daily_num_of_days'] : get_post_meta(
1269 $id,
1270 $prefix . 'daily_num_of_days',
1271 true
1272 ) );
1273 break;
1274 case '2':
1275 $args['interval_multiplier'] = ( false === get_post_meta(
1276 $id,
1277 $prefix . 'weekly_num_of_weeks',
1278 true
1279 ) ? $defaults['weekly_num_of_weeks'] : get_post_meta(
1280 $id,
1281 $prefix . 'weekly_num_of_weeks',
1282 true
1283 ) );
1284 break;
1285 case '3':
1286 $args['interval_multiplier'] = ( false === get_post_meta(
1287 $id,
1288 $prefix . 'monthly_num_of_months',
1289 true
1290 ) ? $defaults['monthly_num_of_months'] : get_post_meta(
1291 $id,
1292 $prefix . 'monthly_num_of_months',
1293 true
1294 ) );
1295 break;
1296 case '4':
1297 $args['interval_multiplier'] = ( false === get_post_meta(
1298 $id,
1299 $prefix . 'yearly_num_of_years',
1300 true
1301 ) ? $defaults['yearly_num_of_years'] : get_post_meta(
1302 $id,
1303 $prefix . 'yearly_num_of_years',
1304 true
1305 ) );
1306 break;
1307 }
1308 $args['instance_start'] = ( false === get_post_meta(
1309 $id,
1310 $prefix . 'instance_start',
1311 true
1312 ) ? $defaults['instance_start'] : get_post_meta( $id, $prefix . 'instance_start', true ) );
1313 if ( ! is_array( $args['instance_start'] ) ) {
1314 $args['instance_start'] = array(
1315 'date' => '',
1316 'time' => '',
1317 );
1318 }
1319 $args['instance_end'] = ( false === get_post_meta(
1320 $id,
1321 $prefix . 'instance_end',
1322 true
1323 ) ? $defaults['instance_end'] : get_post_meta( $id, $prefix . 'instance_end', true ) );
1324 if ( ! is_array( $args['instance_end'] ) ) {
1325 $args['instance_end'] = array(
1326 'date' => '',
1327 'time' => '',
1328 );
1329 }
1330 $args['monthly_pattern'] = ( false === get_post_meta(
1331 $id,
1332 $prefix . 'monthly_nth_weekday_of_month',
1333 true
1334 ) ? $defaults['monthly_nth_weekday_of_month'] : get_post_meta(
1335 $id,
1336 $prefix . 'monthly_nth_weekday_of_month',
1337 true
1338 ) );
1339 $args['monthly_pattern_ord'] = ( false === get_post_meta(
1340 $id,
1341 $prefix . 'monthly_nth_weekday_of_month_nth',
1342 true
1343 ) ? $defaults['monthly_nth_weekday_of_month_nth'] : get_post_meta(
1344 $id,
1345 $prefix . 'monthly_nth_weekday_of_month_nth',
1346 true
1347 ) );
1348 $args['monthly_pattern_day'] = ( false === get_post_meta(
1349 $id,
1350 $prefix . 'monthly_nth_weekday_of_month_weekday',
1351 true
1352 ) ? $defaults['monthly_nth_weekday_of_month_weekday'] : get_post_meta(
1353 $id,
1354 $prefix . 'monthly_nth_weekday_of_month_weekday',
1355 true
1356 ) );
1357 $exceptions_dates = get_post_meta( $id, $prefix . 'exceptions_dates' );
1358 if ( false !== $exceptions_dates && isset( $exceptions_dates[0] ) && is_array( $exceptions_dates[0] ) ) {
1359 $args['exceptions_dates'] = $exceptions_dates[0];
1360 } else {
1361 $args['exceptions_dates'] = $defaults['exceptions_dates'];
1362 }
1363
1364 $args = $this->convert_date_time_parameters_to_iso( $args );
1365
1366 return $this->get_schedule_description( $args );
1367 }
1368
1369 /**
1370 * Get the descroption based on the contents of the form fields of the Add Timed Content Rule and
1371 * Edit Timed Content Rule screens. Output is sent to output as plain text.
1372 */
1373 function timed_content_plugin_get_schedule_description_ajax() {
1374 if ( current_user_can( 'edit_posts' ) || current_user_can( 'edit_pages' ) ) {
1375 $prefix = TIMED_CONTENT_RULE_POSTMETA_PREFIX;
1376 $args = array();
1377
1378 $args['action'] = $this->get_post_param( $prefix . 'action' );
1379 $args['freq'] = $this->get_post_param( 'frequency' );
1380 $args['timezone'] = $this->get_post_param( 'timezone' );
1381 $args['recurr_type'] = $this->get_post_param( 'recurrence_duration' );
1382 $args['num_repeat'] = $this->get_post_param( 'recurrence_duration_num_repeat' );
1383 $args['end_date'] = $this->get_post_param( 'recurrence_duration_end_date' );
1384 $args['days_of_week'] = $this->get_post_array_param( 'weekly_days_of_week_to_repeat' );
1385 $args['interval_multiplier'] = $this->get_post_param( 'interval_multiplier' );
1386 $args['instance_start'] = $this->get_post_array_param( 'instance_start' );
1387 $args['instance_end'] = $this->get_post_array_param( 'instance_end' );
1388 $args['monthly_pattern'] = $this->get_post_param( 'monthly_nth_weekday_of_month' );
1389 $args['monthly_pattern_ord'] = $this->get_post_param( 'monthly_nth_weekday_of_month_nth' );
1390 $args['monthly_pattern_day'] = $this->get_post_param( 'monthly_nth_weekday_of_month_weekday' );
1391 $args['exceptions_dates'] = $this->get_post_array_param( 'exceptions_dates' );
1392
1393 // response output
1394 header( 'Content-Type: text/plain' );
1395 echo wp_kses_post( $this->get_schedule_description( $args ) );
1396 }
1397 die();
1398 }
1399
1400 /**
1401 * Processes the [timed-content-client] shortcode.
1402 */
1403 function client_show_html( $atts, $content = null ) {
1404 $show_attr = '';
1405 $hide_attr = '';
1406 $atts_parsed = shortcode_atts(
1407 array(
1408 'show' => '0:00:000',
1409 'hide' => '0:00:000',
1410 'display' => 'div',
1411 ),
1412 $atts
1413 );
1414
1415 $show_min = 0;
1416 $show_sec = 0;
1417 $show_fade = 0;
1418 $hide_min = 0;
1419 $hide_sec = 0;
1420 $hide_fade = 0;
1421
1422 $show_values = explode( ':', $atts_parsed['show'] );
1423 if ( false !== $show_values ) {
1424 if ( isset( $show_values[0] ) ) {
1425 $show_min = intval( $show_values[0] );
1426 }
1427 if ( isset( $show_values[1] ) ) {
1428 $show_sec = intval( $show_values[1] );
1429 }
1430 if ( isset( $show_values[2] ) ) {
1431 $show_fade = intval( $show_values[2] );
1432 }
1433 }
1434 $hide_values = explode( ':', $atts_parsed['hide'] );
1435 if ( false !== $hide_values ) {
1436 if ( isset( $hide_values[0] ) ) {
1437 $hide_min = intval( $hide_values[0] );
1438 }
1439 if ( isset( $hide_values[1] ) ) {
1440 $hide_sec = intval( $hide_values[1] );
1441 }
1442 if ( isset( $hide_values[2] ) ) {
1443 $hide_fade = intval( $hide_values[2] );
1444 }
1445 }
1446
1447 if ( ( $show_min + $show_sec ) > 0 ) {
1448 $show_attr = sprintf( '_show_%d_%d_%d', $show_min, $show_sec, $show_fade );
1449 }
1450 if ( ( $hide_min + $hide_sec ) > 0 ) {
1451 $hide_attr = sprintf( '_hide_%d_%d_%d', $hide_min, $hide_sec, $hide_fade );
1452 }
1453
1454 $the_class = TIMED_CONTENT_SHORTCODE_CLIENT . $show_attr . $hide_attr;
1455 $the_tag = ( 'div' === $atts_parsed['display'] ? 'div' : 'span' );
1456
1457 $the_filter = 'timed_content_filter';
1458 $the_filter = apply_filters( 'timed_content_filter_override', $the_filter );
1459
1460 return '<'
1461 . $the_tag
1462 . " class='"
1463 . $the_class
1464 . "'"
1465 . ( ( '' !== $show_attr ) ? " style='display: none;'" : '' ) . '>'
1466 . str_replace( ']]>', ']]&gt;', apply_filters( $the_filter, $content ) )
1467 . '</' . $the_tag . '>';
1468 }
1469
1470 /**
1471 * Processes the [timed-content-server] shortcode.
1472 */
1473 function server_show_html( $atts, $content = null ) {
1474 global $post;
1475
1476 $atts_parsed = shortcode_atts(
1477 array(
1478 'show' => 0,
1479 'hide' => 0,
1480 'debug' => 'false',
1481 ),
1482 $atts
1483 );
1484 $show = $atts_parsed['show'];
1485 $hide = $atts_parsed['hide'];
1486 $debug = $atts_parsed['debug'];
1487
1488 // Get time and timezone object for "show" time
1489 $pos = strrpos( $show, ' ' );
1490 if ( false !== $pos ) {
1491 $show_time = substr( $show, 0, $pos );
1492 $show_tzname = substr( $show, $pos + 1 );
1493 } else {
1494 $show_time = $show;
1495 $show_tzname = wp_timezone_string();
1496 }
1497 try {
1498 $show_tz = new DateTimeZone( $show_tzname );
1499 } catch ( Exception $e ) {
1500 $show_tz = new DateTimeZone( 'UTC' );
1501 }
1502
1503 // Create time and timezone object for "hide" time
1504 $pos = strrpos( $hide, ' ' );
1505 if ( false !== $pos ) {
1506 $hide_time = substr( $hide, 0, $pos );
1507 $hide_tzname = substr( $hide, $pos + 1 );
1508 } else {
1509 $hide_time = $hide;
1510 $hide_tzname = wp_timezone_string();
1511 }
1512 try {
1513 $hide_tz = new DateTimeZone( $hide_tzname );
1514 } catch ( Exception $e ) {
1515 $hide_tz = new DateTimeZone( 'UTC' );
1516 }
1517
1518 // Try to parse date as ISO first
1519 $show_dt = DateTime::createFromFormat( 'Y-m-d G:i', $show_time, $show_tz );
1520 // Fallback to American format
1521 if ( false === $show_dt ) {
1522 $show_dt = DateTime::createFromFormat( 'm/d/Y G:i', $show_time, $show_tz );
1523 }
1524
1525 if ( false !== $show_dt ) {
1526 $show_t = $show_dt->getTimeStamp();
1527 } else {
1528 // If nothing else worked so far, try strtotime()
1529 // as it was before version 2.50
1530 $show_t = strtotime( $show );
1531 if ( false === $show_t ) {
1532 $show_t = 0;
1533 }
1534 $show_dt = new DateTime();
1535 $show_dt->setTimeStamp( $show_t );
1536 $show_dt->setTimezone( $show_tz );
1537 }
1538
1539 // Try to parse date as ISO first
1540 $hide_dt = DateTime::createFromFormat( 'Y-m-d G:i', $hide_time, $hide_tz );
1541 if ( false === $hide_dt ) {
1542 $hide_dt = DateTime::createFromFormat( 'm/d/Y G:i', $hide_time, $hide_tz );
1543 }
1544 if ( false !== $hide_dt ) {
1545 $hide_t = $hide_dt->getTimeStamp();
1546 } else {
1547 // If nothing else worked so far, try strtotime()
1548 // as it was before version 2.50
1549 $hide_t = strtotime( $hide );
1550 if ( false === $hide_t ) {
1551 $hide_t = 0;
1552 }
1553 $hide_dt = new DateTime();
1554 $hide_dt->setTimeStamp( $hide_t );
1555 $hide_dt->setTimezone( $hide_tz );
1556 }
1557
1558 $right_now_t = time();
1559 $debug_message = '';
1560
1561 // use debug parameter if current user is allowed to edit the post
1562 if ( isset( $_GET['tctest'] ) && ! empty( $post ) && current_user_can( 'edit_post', $post->post_id ) ) {
1563 $dt = DateTime::createFromFormat( 'Y-m-d H:i:s', sanitize_text_field( $_GET['tctest'] ) );
1564 if ( false !== $dt ) {
1565 $right_now_t = $dt->getTimestamp();
1566 }
1567 }
1568
1569 $the_filter = 'timed_content_filter';
1570 $the_filter = apply_filters( 'timed_content_filter_override', $the_filter );
1571
1572 $show_content = false;
1573 if ( ( $show_t <= $right_now_t ) && ( $right_now_t <= $hide_t || 0 === $hide_t ) ) {
1574 $show_content = true;
1575 }
1576
1577 if ( ( ( 'true' === $debug ) || ( ( ! $show_content ) && ( 'when_hidden' === $debug ) ) ) && ( ! empty( $post ) && current_user_can( 'edit_post', $post->post_id ) ) ) {
1578
1579 $right_now = $this->format_timestamp( TIMED_CONTENT_DATE_FORMAT_OUTPUT, $right_now_t );
1580
1581 if ( $show_t > $right_now_t ) {
1582 $show_diff_str = sprintf(
1583 /* translators: %s: time difference */
1584 _x( '%s from now.', 'Human readable time difference', 'timed-content' ),
1585 human_time_diff( $show_t, $right_now_t )
1586 );
1587 } else {
1588 $show_diff_str = sprintf(
1589 /* translators: %s: time difference */
1590 _x( '%s ago.', 'Human readable time difference', 'timed-content' ),
1591 human_time_diff( $show_t, $right_now_t )
1592 );
1593 }
1594 if ( $hide_t > $right_now_t ) {
1595 $hide_diff_str = sprintf(
1596 /* translators: %s: time difference */
1597 _x( '%s from now.', 'Human readable time difference', 'timed-content' ),
1598 human_time_diff( $hide_t, $right_now_t )
1599 );
1600 } else {
1601 $hide_diff_str = sprintf(
1602 /* translators: %s: time difference */
1603 _x( '%s ago.', 'Human readable time difference', 'timed-content' ),
1604 human_time_diff( $hide_t, $right_now_t )
1605 );
1606 }
1607
1608 $debug_message = "<div class=\"tcr-warning\">\n";
1609 $debug_message .= '<p class="heading">' . _x( 'Notice', 'Noun', 'timed-content' ) . "</p>\n";
1610 $debug_message .= '<p>' . sprintf(
1611 /* translators: %1$s: shortcode, %2$s attribute in shortcode */
1612 __(
1613 'Debugging has been turned on for a %1$s shortcode on this post/page. Only website users who are currently logged in and can edit this post/page will see this. To turn off this message, remove the %2$s attribute from the shortcode.',
1614 'timed-content'
1615 ),
1616 '<code>[timed-content-server]</code>',
1617 '<code>debug</code>'
1618 ) . "</p>\n";
1619
1620 if ( 0 === $show_t ) {
1621 $debug_message .= '<p>' . sprintf(
1622 /* translators: %s: attribute name */
1623 __( 'The %s attribute is not set or invalid.', 'timed-content' ),
1624 '<code>show</code>'
1625 ) . "</p>\n";
1626 } else {
1627 $debug_message .= '<p>' . sprintf(
1628 /* translators: %s: attribute name */
1629 __( 'The %s attribute is currently set to', 'timed-content' ),
1630 '<code>show</code>'
1631 ) . ': ' . $show . ",<br />\n "
1632 . __(
1633 'The Timed Content plugin thinks the intended date/time is',
1634 'timed-content'
1635 ) . ': ' . $show_dt->format( TIMED_CONTENT_DATE_FORMAT_OUTPUT )
1636 . ' (' . $show_diff_str . ")</p>\n";
1637 }
1638
1639 if ( 0 === $hide ) {
1640 $debug_message .= '<p>' . sprintf(
1641 /* translators: %s: attribute name */
1642 __( 'The %s attribute is not set or invalid.', 'timed-content' ),
1643 '<code>hide</code>'
1644 ) . "</p>\n";
1645 } else {
1646 $debug_message .= '<p>' . sprintf(
1647 /* translators: %s: attribute name */
1648 __( 'The %s attribute is currently set to', 'timed-content' ),
1649 '<code>hide</code>'
1650 ) . ': ' . $hide . ",<br />\n"
1651 . __(
1652 'The Timed Content plugin thinks the intended date/time is',
1653 'timed-content'
1654 ) . ': ' . $hide_dt->format( TIMED_CONTENT_DATE_FORMAT_OUTPUT )
1655 . ' (' . $hide_diff_str . ").</p>\n";
1656 }
1657
1658 $debug_message .= '<p>' . __(
1659 'Current date:',
1660 'timed-content'
1661 ) . '&nbsp;' . $right_now . "</p>\n";
1662 $debug_message .= '<p>' . __( 'Content filter:', 'timed-content' ) . '&nbsp;' . $the_filter . "</p>\n";
1663 $debug_message .= '<p>' . _x( 'Content:', 'Noun', 'timed-content' ) . '</p><p>' . $content . "</p>\n";
1664
1665 if ( true === $show_content ) {
1666 $debug_message .= '<p>' . __( 'The plugin will show the content.', 'timed-content' ) . '</p>';
1667 } else {
1668 $debug_message .= '<p>' . __( 'The plugin will hide the content.', 'timed-content' ) . '</p>';
1669 }
1670
1671 $debug_message .= "</div>\n";
1672 }
1673
1674 if ( true === $show_content ) {
1675 if ( ! empty( $post ) ) {
1676 do_action( 'timed_content_server_show', $post->ID, $show, $hide, $content );
1677 } else {
1678 do_action( 'timed_content_server_show', null, $show, $hide, $content );
1679 }
1680
1681 return $debug_message . str_replace( ']]>', ']]&gt;', apply_filters( $the_filter, $content ) ) . "\n";
1682 } else {
1683 if ( ! empty( $post ) ) {
1684 do_action( 'timed_content_server_hide', $post->ID, $show, $hide, $content );
1685 } else {
1686 do_action( 'timed_content_server_hide', null, $show, $hide, $content );
1687 }
1688
1689 return $debug_message . "\n";
1690 }
1691
1692 }
1693
1694 /**
1695 * Processes the [timed-content-rule] shortcode.
1696 */
1697 function rules_show_html( $atts, $content = null ) {
1698 global $post;
1699
1700 $atts_parsed = shortcode_atts( array( 'id' => 0 ), $atts );
1701 $id = $atts_parsed['id'];
1702
1703 if ( ! is_numeric( $id ) ) {
1704 $page = get_page_by_title( $id, OBJECT, TIMED_CONTENT_RULE_TYPE );
1705 if ( null === $page ) {
1706 return '';
1707 }
1708 $id = $page->ID;
1709 }
1710 if ( TIMED_CONTENT_RULE_TYPE !== get_post_type( $id ) ) {
1711 return '';
1712 }
1713
1714 $prefix = TIMED_CONTENT_RULE_POSTMETA_PREFIX;
1715 $right_now_t = time();
1716 $rule_is_active = false;
1717
1718 // use debug parameter if current user is allowed to edit the post
1719 if ( isset( $_GET['tctest'] ) && current_user_can( 'edit_post', $post->post_id ) ) {
1720 $dt = DateTime::createFromFormat( 'Y-m-d H:i:s', sanitize_text_field( $_GET['tctest'] ) );
1721 if ( false !== $dt ) {
1722 $right_now_t = $dt->getTimestamp();
1723 }
1724 }
1725
1726 $active_periods = $this->get_rule_periods_by_id( $id, false );
1727 $action_is_show = (bool) get_post_meta( $id, $prefix . 'action', true );
1728
1729 foreach ( $active_periods as $period ) {
1730 if ( ( $period['start'] <= $right_now_t ) && ( $right_now_t <= $period['end'] ) ) {
1731 $rule_is_active = true;
1732 break;
1733 }
1734 }
1735
1736 $the_filter = 'timed_content_filter';
1737 $the_filter = apply_filters( 'timed_content_filter_override', $the_filter );
1738
1739 if ( ( true === $rule_is_active && true === $action_is_show ) || ( false === $rule_is_active && false === $action_is_show ) ) {
1740 if ( ! empty( $post ) ) {
1741 do_action( 'timed_content_rule_show', $post->ID, $id, $content );
1742 } else {
1743 do_action( 'timed_content_rule_show', null, $id, $content );
1744 }
1745
1746 return str_replace( ']]>', ']]&gt;', apply_filters( $the_filter, $content ) );
1747 } else {
1748 if ( ! empty( $post ) ) {
1749 do_action( 'timed_content_rule_hide', $post->ID, $id, $content );
1750 } else {
1751 do_action( 'timed_content_rule_hide', null, $id, $content );
1752 }
1753
1754 return '';
1755 }
1756 }
1757
1758 /**
1759 * Enqueues the JavaScript code necessary for the functionality of the [timed-content-client] shortcode.
1760 */
1761 function add_header_code() {
1762 if ( ! is_admin() ) {
1763 wp_enqueue_style( 'timed-content-css', TIMED_CONTENT_CSS, false, TIMED_CONTENT_VERSION );
1764 wp_enqueue_script(
1765 'timed-content_js',
1766 TIMED_CONTENT_PLUGIN_URL . '/js/timed-content.js',
1767 array( 'jquery' ),
1768 TIMED_CONTENT_VERSION
1769 );
1770 }
1771 }
1772
1773 /**
1774 * Enqueues the CSS code necessary for custom icons for the Timed Content Rules management screens
1775 * and the TinyMCE editor.
1776 */
1777 function add_post_type_icons() {
1778 wp_enqueue_style( 'timed-content-dashicons', TIMED_CONTENT_CSS_DASHICONS, false, TIMED_CONTENT_VERSION );
1779 ?>
1780 <style type="text/css" media="screen">
1781 #adminmenu #menu-posts-<?php echo TIMED_CONTENT_RULE_TYPE; ?>.menu-icon-post div.wp-menu-image:before {
1782 font-family: 'timed-content-dashicons' !important;
1783 content: '\e601';
1784 }
1785
1786 #dashboard_right_now a.<?php echo TIMED_CONTENT_RULE_TYPE; ?>-count:before {
1787 font-family: 'timed-content-dashicons' !important;
1788 content: '\e601' !important;
1789 }
1790
1791 .mce-i-timed_content:before {
1792 font: 400 24px/1 'timed-content-dashicons' !important;
1793 padding: 0;
1794 vertical-align: top;
1795 margin-left: -2px;
1796 padding-right: 2px;
1797 content: '\e601';
1798 }
1799 </style>
1800 <?php
1801 }
1802
1803 /**
1804 * Enqueues the JavaScript code necessary for the functionality of the Timed Content Rules management screens.
1805 */
1806 function add_admin_header_code() {
1807 if ( ( isset( $_GET['post_type'] ) && TIMED_CONTENT_RULE_TYPE === $_GET['post_type'] )
1808 || ( isset( $post_type ) && TIMED_CONTENT_RULE_TYPE === $post_type )
1809 || ( isset( $_GET['post'] ) && TIMED_CONTENT_RULE_TYPE === get_post_type( sanitize_text_field( $_GET['post'] ) ) ) ) {
1810 wp_enqueue_style( 'thickbox' );
1811 wp_enqueue_style( 'timed-content-css', TIMED_CONTENT_CSS, false, TIMED_CONTENT_VERSION );
1812 // Enqueue the JavaScript file that manages the meta box UI
1813 wp_enqueue_script(
1814 'timed-content-admin_js',
1815 TIMED_CONTENT_PLUGIN_URL . '/js/timed-content-admin.js',
1816 array( 'jquery' ),
1817 TIMED_CONTENT_VERSION
1818 );
1819 // Enqueue the JavaScript file that makes AJAX requests
1820 wp_enqueue_script(
1821 'timed-content-ajax_js',
1822 TIMED_CONTENT_PLUGIN_URL . '/js/timed-content-ajax.js',
1823 array( 'jquery', 'thickbox' ),
1824 TIMED_CONTENT_VERSION
1825 );
1826
1827 // Set up local variables used in the Admin JavaScript file
1828 wp_localize_script(
1829 'timed-content-admin_js',
1830 'timedContentRuleAdmin',
1831 array(
1832 'no_exceptions_label' => __( '- No exceptions set -', 'timed-content' ),
1833 )
1834 );
1835
1836 // Set up local variables used in the AJAX JavaScript file
1837 wp_localize_script(
1838 'timed-content-ajax_js',
1839 'timedContentRuleAjax',
1840 array(
1841 'ajaxurl' => admin_url( 'admin-ajax.php' ),
1842 'start_label' => _x(
1843 'Start',
1844 'Scheduled Dates/Times dialog - Beginning of active period table header',
1845 'timed-content'
1846 ),
1847 'end_label' => _x(
1848 'End',
1849 'Scheduled Dates/Times dialog - End of active period table header',
1850 'timed-content'
1851 ),
1852 'dialog_label' => _x(
1853 'Scheduled dates/times',
1854 'Scheduled Dates/Times dialog - dialog header',
1855 'timed-content'
1856 ),
1857 'button_loading_label' => __( 'Calculating dates/times', 'timed-content' ),
1858 'button_finished_label' => __( 'Show projected dates/times', 'timed-content' ),
1859 'dialog_width' => 800,
1860 'dialog_height' => 500,
1861 'error' => __( 'Error', 'timed-content' ),
1862 'error_desc' => __(
1863 'Something unexpected has happened along the way. The specific details are below:',
1864 'timed-content'
1865 ),
1866 )
1867 );
1868 }
1869 }
1870
1871 /**
1872 * Initializes the TinyMCE plugin bundled with this WordPress plugin
1873 */
1874 function init_tinymce_plugin() {
1875 if ( ( ! current_user_can( 'edit_posts' ) ) && ( ! current_user_can( 'edit_pages' ) ) ) {
1876 return;
1877 }
1878
1879 // Add only in Rich Editor mode
1880 if ( get_user_option( 'rich_editing' ) === 'true' ) {
1881 add_filter( 'mce_external_plugins', array( &$this, 'add_timed_content_tinymce_plugin' ) );
1882 add_filter( 'mce_buttons', array( &$this, 'register_tinymce_button' ) );
1883 }
1884 }
1885
1886 /**
1887 * Sets up variables to use in the TinyMCE plugin's plugin.js.
1888 */
1889 function set_tinymce_plugin_vars() {
1890 global $wp_version;
1891 if ( ( ! current_user_can( 'edit_posts' ) ) && ( ! current_user_can( 'edit_pages' ) ) ) {
1892 return;
1893 }
1894
1895 // Add only in Rich Editor mode
1896 if ( get_user_option( 'rich_editing' ) === 'true' ) {
1897 if ( version_compare( $wp_version, '3.8', '<' ) ) {
1898 $image = '/clock.gif';
1899 } else {
1900 $image = '';
1901 }
1902 wp_localize_script(
1903 'editor',
1904 'timedContentAdminTinyMCEOptions',
1905 array(
1906 'version' => TIMED_CONTENT_VERSION,
1907 'desc' => __( 'Add Timed Content shortcodes', 'timed-content' ),
1908 'image' => $image,
1909 )
1910 );
1911 }
1912 }
1913
1914 /**
1915 * Sets up the button for the associated TinyMCE plugin for use in the editor menubar.
1916 */
1917 function register_tinymce_button( $buttons ) {
1918 array_push( $buttons, '|', 'timed_content' );
1919
1920 return $buttons;
1921 }
1922
1923 /**
1924 * Loads the associated TinyMCE plugin into TinyMCE's plugin array
1925 */
1926 function add_timed_content_tinymce_plugin( $plugin_array ) {
1927 $plugin_array['timed_content'] = TIMED_CONTENT_PLUGIN_URL . '/tinymce_plugin/plugin.js';
1928
1929 return $plugin_array;
1930 }
1931
1932 /**
1933 * Generates JavaScript array of objects describing Timed Content rules. Used in the dialog box created by
1934 * timedContentPlugin::timedContentPluginGetTinyMCEDialog().
1935 */
1936 function get_rules_js() {
1937 $the_js = "var rules = [\n";
1938 $args = array(
1939 'post_type' => TIMED_CONTENT_RULE_TYPE,
1940 'posts_per_page' => -1,
1941 'post_status' => 'publish',
1942 );
1943 $the_rules = get_posts( $args );
1944 foreach ( $the_rules as $rule ) {
1945 $desc = $this->get_schedule_description_by_id( $rule->ID );
1946 $desc = str_replace( '<br />', ' ', $desc );
1947 // Only add a rule if there's no errors or warnings
1948 if ( false === strpos( $desc, 'tcr-warning' ) ) {
1949 $the_js .= " { 'ID': " . $rule->ID . ", 'title': '" . esc_js(
1950 ( ( strlen( $rule->post_title ) > 0 ) ? $rule->post_title : _x(
1951 '(no title)',
1952 'No Timed Content Rule title',
1953 'timed-content'
1954 ) )
1955 ) . "', 'desc': '" . esc_js( $desc ) . "' },\n";
1956 }
1957 }
1958 if ( empty( $the_rules ) ) {
1959 $the_js .= " { 'ID': -999, 'title': ' ---- ', 'desc': '" . __(
1960 'No Timed Content Rules found',
1961 'timed-content'
1962 ) . "' }\n";
1963 }
1964
1965 $the_js .= "];\n";
1966
1967 return $the_js;
1968 }
1969
1970 /**
1971 * Display a dialog box for this plugin's associated TinyMCE plugin. Called from TinyMCE via AJAX.
1972 */
1973 function timed_content_plugin_get_tinymce_dialog() {
1974 wp_enqueue_style( TIMED_CONTENT_SLUG . '-jquery-ui-css', TIMED_CONTENT_JQUERY_UI_CSS );
1975 wp_enqueue_script( 'jquery-ui-datepicker' );
1976 wp_register_style(
1977 TIMED_CONTENT_SLUG . '-jquery-ui-timepicker-css',
1978 TIMED_CONTENT_JQUERY_UI_TIMEPICKER_CSS
1979 );
1980 wp_enqueue_style( TIMED_CONTENT_SLUG . '-jquery-ui-timepicker-css' );
1981 wp_register_script(
1982 TIMED_CONTENT_SLUG . '-jquery-ui-timepicker-js',
1983 TIMED_CONTENT_JQUERY_UI_TIMEPICKER_JS,
1984 array( 'jquery', 'jquery-ui-datepicker' ),
1985 TIMED_CONTENT_VERSION
1986 );
1987 wp_enqueue_script( TIMED_CONTENT_SLUG . '-jquery-ui-timepicker-js' );
1988 if ( ! ( wp_script_is( TIMED_CONTENT_SLUG . '-jquery-ui-datetime-i18n-js', 'registered' ) ) ) {
1989 wp_register_script(
1990 TIMED_CONTENT_SLUG . '-jquery-ui-datetime-i18n-js',
1991 TIMED_CONTENT_PLUGIN_URL . '/js/timed-content-datetime-i18n.js',
1992 array( 'jquery', 'jquery-ui-datepicker', TIMED_CONTENT_SLUG . '-jquery-ui-timepicker-js' ),
1993 TIMED_CONTENT_VERSION
1994 );
1995 wp_enqueue_script( TIMED_CONTENT_SLUG . '-jquery-ui-datetime-i18n-js' );
1996 wp_localize_script(
1997 TIMED_CONTENT_SLUG . '-jquery-ui-datetime-i18n-js',
1998 'TimedContentJQDatepickerI18n',
1999 $this->jquery_ui_datetime_datepicker_i18n
2000 );
2001 wp_localize_script(
2002 TIMED_CONTENT_SLUG . '-jquery-ui-datetime-i18n-js',
2003 'TimedContentJQTimepickerI18n',
2004 $this->jquery_ui_datetime_timepicker_i18n
2005 );
2006 }
2007
2008 wp_register_script(
2009 'TIMED_CONTENT_SLUG' . '-tinymce-popup-js',
2010 includes_url() . '/js/tinymce/tiny_mce_popup.js',
2011 null,
2012 TIMED_CONTENT_VERSION
2013 );
2014 wp_enqueue_script( 'TIMED_CONTENT_SLUG' . '-tinymce-popup-js' );
2015 wp_register_script(
2016 'TIMED_CONTENT_SLUG' . '-tinymce-mctabs-js',
2017 includes_url() . '/js/tinymce/utils/mctabs.js',
2018 null,
2019 TIMED_CONTENT_VERSION
2020 );
2021 wp_enqueue_script( 'TIMED_CONTENT_SLUG' . '-tinymce-mctabs-js' );
2022
2023 ob_start();
2024 require __DIR__ . '/../tinymce_plugin/dialog.php';
2025 $content = ob_get_contents();
2026 ob_end_clean();
2027 echo $content;
2028 die();
2029 }
2030
2031 /**
2032 * Add custom columns to the Timed Content Rules overview page
2033 */
2034 function add_desc_column_head( $defaults ) {
2035 unset( $defaults['date'] );
2036 $defaults['description'] = __( 'Description', 'timed-content' );
2037 $defaults['shortcode'] = __( 'Shortcode', 'timed-content' );
2038
2039 return $defaults;
2040 }
2041
2042 /**
2043 * Display content associated with custom columns on the Timed Content rules overview page
2044 */
2045 function add_desc_column_content( $column_name, $post_id ) {
2046 if ( 'shortcode' === $column_name ) {
2047 echo sprintf(
2048 '<code>[%s id="%s"]...[/%s]</code>',
2049 TIMED_CONTENT_SHORTCODE_RULE,
2050 $post_id,
2051 TIMED_CONTENT_SHORTCODE_RULE
2052 );
2053 }
2054 if ( 'description' === $column_name ) {
2055 $desc = $this->get_schedule_description_by_id( $post_id );
2056 if ( $desc ) {
2057 echo sprintf( '<em>%s</em>', $desc );
2058 }
2059 }
2060 }
2061
2062 /**
2063 * Display a count of Timed Content rules in the Dashboard's Right Now widget
2064 */
2065 function add_rules_count() {
2066 $items = [];
2067 if ( ! post_type_exists( TIMED_CONTENT_RULE_TYPE ) ) {
2068 return;
2069 }
2070
2071 $num_posts = wp_count_posts( TIMED_CONTENT_RULE_TYPE );
2072 $num = number_format_i18n( $num_posts->publish );
2073 $text = _n(
2074 'Timed Content rule',
2075 'Timed Content rules',
2076 intval( $num_posts->publish ),
2077 'timed-content'
2078 );
2079 if ( current_user_can( 'edit_posts' ) || current_user_can( 'edit_pages' ) ) {
2080 $items[] = '<a class="'.TIMED_CONTENT_RULE_TYPE.'-count" href="edit.php?post_type=' . TIMED_CONTENT_RULE_TYPE . '">'
2081 . $num
2082 . ' '
2083 . $text
2084 . '</a>';
2085 }
2086
2087 if ( $num_posts->pending > 0 ) {
2088 $num = number_format_i18n( $num_posts->pending );
2089 $text = _n(
2090 'Timed Content rule pending',
2091 'Timed Content rules pending',
2092 intval( $num_posts->pending ),
2093 'timed-content'
2094 );
2095 if ( current_user_can( 'edit_posts' ) || current_user_can( 'edit_pages' ) ) {
2096 $items[] = '<a class="'.TIMED_CONTENT_RULE_TYPE.'-count" href="edit.php?post_status=pending&post_type=' . TIMED_CONTENT_RULE_TYPE . '">'
2097 . $num
2098 . ' '
2099 . $text
2100 . '</a>';
2101 }
2102 }
2103
2104 return $items;
2105 }
2106
2107 /**
2108 * Setup custom fields for Timed Content rules
2109 */
2110 function setup_custom_fields() {
2111 global $post;
2112
2113 $now_ts = time();
2114 $now_plus1h_dt = new DateTime();
2115 $now_plus2h_dt = new DateTime();
2116 $now_plus1y_dt = new DateTime();
2117 $now_plus1h_dt->setTimeStamp( $now_ts );
2118 $now_plus2h_dt->setTimeStamp( $now_ts );
2119 $now_plus1y_dt->setTimeStamp( $now_ts );
2120 $now_plus1h_dt->add( new DateInterval( 'PT1H' ) );
2121 $now_plus2h_dt->add( new DateInterval( 'PT2H' ) );
2122 $now_plus1y_dt->add( new DateInterval( 'P1Y' ) );
2123
2124 $post_id = ( isset( $_GET['post'] ) && ( TIMED_CONTENT_RULE_TYPE === get_post_type( $_GET['post'] ) ) ? intval( $_GET['post'] ) : intval( 0 ) );
2125 $exceptions_dates = get_post_meta( $post_id, TIMED_CONTENT_RULE_POSTMETA_PREFIX . 'exceptions_dates' );
2126 if ( false !== $exceptions_dates && isset( $exceptions_dates[0] ) && is_array( $exceptions_dates[0] ) ) {
2127 $timed_content_rules_exceptions_dates = $exceptions_dates[0];
2128 sort( $timed_content_rules_exceptions_dates, SORT_NUMERIC );
2129 $timed_content_rules_exceptions_dates = array_unique( $timed_content_rules_exceptions_dates );
2130
2131 // If the exceptions are stored as timestamps, convert them to ISO first
2132 $num = 0;
2133 while ( $num < count( $timed_content_rules_exceptions_dates ) ) {
2134 if ( is_numeric( $timed_content_rules_exceptions_dates[ $num ] ) ) {
2135 $timed_content_rules_exceptions_dates[ $num ] = $this->format_timestamp( 'Y-m-d', $timed_content_rules_exceptions_dates[ $num ] );
2136 }
2137 $num++;
2138 }
2139
2140 $timed_content_rules_exceptions_dates_array = array_combine( $timed_content_rules_exceptions_dates, $timed_content_rules_exceptions_dates );
2141 } else {
2142 $timed_content_rules_exceptions_dates_array = array( '0' => __( '- No exceptions set -', 'timed-content' ) );
2143 }
2144
2145 $this->rule_occurrence_custom_fields = array(
2146 array(
2147 'name' => 'action',
2148 'display' => 'block',
2149 'title' => __( 'Action', 'timed-content' ),
2150 'description' => __( 'Sets the action to be performed when the rule is active.', 'timed-content' ),
2151 'type' => 'radio',
2152 'values' => array(
2153 '1' => __( 'Show the content', 'timed-content' ),
2154 '0' => __( 'Hide the content', 'timed-content' ),
2155 ),
2156 'default' => '1',
2157 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2158 'capability' => 'edit_posts',
2159 ),
2160 array(
2161 'name' => 'instance_start',
2162 'display' => 'block',
2163 'title' => __( 'Starting date/time', 'timed-content' ),
2164 'description' => __( 'Sets the date and time for the beginning of the first active period for this rule.', 'timed-content' ),
2165 'type' => 'datetime',
2166 'default' => array(
2167 'date' => $this->format_timestamp( 'Y-m-d', $now_plus1h_dt->getTimeStamp() ),
2168 'time' => $this->format_timestamp( 'H:i', $now_plus1h_dt->getTimeStamp() ),
2169 ),
2170 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2171 'capability' => 'edit_posts',
2172 ),
2173 array(
2174 'name' => 'instance_end',
2175 'display' => 'block',
2176 'title' => __( 'Ending date/time', 'timed-content' ),
2177 'description' => __( 'Sets the date and time for the end of the first active period for this rule.', 'timed-content' ),
2178 'type' => 'datetime',
2179 'default' => array(
2180 'date' => $this->format_timestamp( 'Y-m-d', $now_plus2h_dt->getTimeStamp() ),
2181 'time' => $this->format_timestamp( 'H:i', $now_plus2h_dt->getTimeStamp() ),
2182 ),
2183 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2184 'capability' => 'edit_posts',
2185 ),
2186 array(
2187 'name' => 'timezone',
2188 'display' => 'block',
2189 'title' => __( 'Timezone', 'timed-content' ),
2190 'description' => __( 'Select the timezone you wish to use for this rule.', 'timed-content' ),
2191 'type' => 'timezone-list',
2192 'default' => get_option( 'timezone_string' ),
2193 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2194 'capability' => 'edit_posts',
2195 ),
2196 );
2197
2198 $this->rule_pattern_custom_fields = array(
2199 array(
2200 'name' => 'frequency',
2201 'display' => 'block',
2202 'title' => __( 'Frequency', 'timed-content' ),
2203 'description' => __( 'Sets the frequency at which the action should be repeated.', 'timed-content' ),
2204 'type' => 'list',
2205 'default' => '1',
2206 'values' => $this->rule_freq_array,
2207 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2208 'capability' => 'edit_posts',
2209 ),
2210 array(
2211 'name' => 'hourly_num_of_hours',
2212 'display' => 'none',
2213 'title' => __( 'Interval of recurrences', 'timed-content' ),
2214 'description' => __( 'Repeat this action every X hours.', 'timed-content' ),
2215 'type' => 'number',
2216 'default' => '1',
2217 'min' => '1',
2218 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2219 'capability' => 'edit_posts',
2220 ),
2221 array(
2222 'name' => 'daily_num_of_days',
2223 'display' => 'none',
2224 'title' => __( 'Interval of recurrences', 'timed-content' ),
2225 'description' => __( 'Repeat this action every X days.', 'timed-content' ),
2226 'type' => 'number',
2227 'default' => '1',
2228 'min' => '1',
2229 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2230 'capability' => 'edit_posts',
2231 ),
2232 array(
2233 'name' => 'weekly_num_of_weeks',
2234 'display' => 'none',
2235 'title' => __( 'Interval of recurrences', 'timed-content' ),
2236 'description' => __( 'Repeat this action every X weeks.', 'timed-content' ),
2237 'type' => 'number',
2238 'default' => '1',
2239 'min' => '1',
2240 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2241 'capability' => 'edit_posts',
2242 ),
2243 array(
2244 'name' => 'weekly_days_of_week_to_repeat',
2245 'display' => 'none',
2246 'title' => __( 'Repeat on the following days', 'timed-content' ),
2247 'description' => __( 'Repeat this action on these days of the week <strong>instead</strong> of the day of week the starting date/time falls on.', 'timed-content' ),
2248 'type' => 'checkbox-list',
2249 'default' => array(),
2250 'values' => $this->rule_days_array,
2251 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2252 'capability' => 'edit_posts',
2253 ),
2254 array(
2255 'name' => 'monthly_num_of_months',
2256 'display' => 'none',
2257 'title' => __( 'Interval of recurrences', 'timed-content' ),
2258 'description' => __( 'Repeat this action every X months.', 'timed-content' ),
2259 'type' => 'number',
2260 'default' => '1',
2261 'min' => '1',
2262 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2263 'capability' => 'edit_posts',
2264 ),
2265 array(
2266 'name' => 'monthly_nth_weekday_of_month',
2267 'display' => 'none',
2268 'title' => __( 'Repeat on a specific weekday of the month', 'timed-content' ),
2269 'description' => __( 'Repeat this action on a specific weekday of the month (for example, "every third Tuesday"). Check this box to select a pattern below.', 'timed-content' ),
2270 'type' => 'checkbox',
2271 'default' => 'no',
2272 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2273 'capability' => 'edit_posts',
2274 ),
2275 array(
2276 'name' => 'monthly_nth_weekday_of_month_nth',
2277 'display' => 'none',
2278 'title' => __( 'Weekday ordinal', 'timed-content' ),
2279 'description' => __( 'Select a value for week of the month (for example "first", "second", etc.). Please note, that "fifth" will skip months, where the day is not repeated five times!', 'timed-content' ),
2280 'type' => 'list',
2281 'default' => 0,
2282 'values' => $this->rule_ordinal_array,
2283 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2284 'capability' => 'edit_posts',
2285 ),
2286 array(
2287 'name' => 'monthly_nth_weekday_of_month_weekday',
2288 'display' => 'none',
2289 'title' => __( 'Day of the week', 'timed-content' ),
2290 'description' => __( 'Select the day of week.', 'timed-content' ),
2291 'type' => 'list',
2292 'default' => 0,
2293 'values' => $this->rule_ordinal_days_array,
2294 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2295 'capability' => 'edit_posts',
2296 ),
2297 array(
2298 'name' => 'yearly_num_of_years',
2299 'display' => 'none',
2300 'title' => __( 'Interval of recurrences', 'timed-content' ),
2301 'description' => __( 'Repeat this action every X years.', 'timed-content' ),
2302 'type' => 'number',
2303 'default' => '1',
2304 'min' => '1',
2305 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2306 'capability' => 'edit_posts',
2307 ),
2308 );
2309
2310 $this->rule_recurrence_custom_fields = array(
2311 array(
2312 'name' => 'recurrence_duration',
2313 'display' => 'block',
2314 'title' => __( 'How often to repeat this action', 'timed-content' ),
2315 'description' => '',
2316 'type' => 'radio',
2317 'values' => array(
2318 'recurrence_duration_end_date' => __( 'Keep repeating until a given date', 'timed-content' ),
2319 'recurrence_duration_num_repeat' => __( 'Repeat a set number of times', 'timed-content' ),
2320 ),
2321 'default' => 'recurrence_duration_end_date',
2322 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2323 'capability' => 'edit_posts',
2324 ),
2325 array(
2326 'name' => 'recurrence_duration_end_date',
2327 'display' => 'none',
2328 'title' => __( 'End Date', 'timed-content' ),
2329 'description' => __( 'Using the settings above, repeat this action until this date.', 'timed-content' ),
2330 'type' => 'date',
2331 'default' => $this->format_timestamp( 'Y-m-d', $now_plus1y_dt->getTimeStamp() ),
2332 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2333 'capability' => 'edit_posts',
2334 ),
2335 array(
2336 'name' => 'recurrence_duration_num_repeat',
2337 'display' => 'none',
2338 'title' => __( 'Number of repetitions', 'timed-content' ),
2339 'description' => __( 'Using the settings above, repeat this action this many times.', 'timed-content' ),
2340 'type' => 'number',
2341 'default' => '1',
2342 'min' => '1',
2343 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2344 'capability' => 'edit_posts',
2345 ),
2346 );
2347
2348 $this->rule_exceptions_custom_fields = array(
2349 array(
2350 'name' => 'exceptions_dates_picker',
2351 'display' => 'block',
2352 'title' => __( 'Add exception date:', 'timed-content' ),
2353 'description' => __( 'Select a date to add to the exception dates list.', 'timed-content' ),
2354 'type' => 'date',
2355 'default' => '',
2356 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2357 'capability' => 'edit_posts',
2358 ),
2359 array(
2360 'name' => 'exceptions_dates',
2361 'display' => 'block',
2362 'title' => __( 'Exception dates list', 'timed-content' ),
2363 'description' => __( 'Dates that this Timed Content rule will not be active. Double-click on a date to remove it from the list.', 'timed-content' ),
2364 'type' => 'menu',
2365 'values' => $timed_content_rules_exceptions_dates_array,
2366 'size' => '10',
2367 'default' => array(),
2368 'scope' => array( TIMED_CONTENT_RULE_TYPE ),
2369 'capability' => 'edit_posts',
2370 ),
2371 );
2372
2373 $rule_description = '';
2374 if ( isset( $_GET['post'] ) ) {
2375 $post_id = intval( $_GET['post'] );
2376 if ( get_post_type( $post_id ) === TIMED_CONTENT_RULE_TYPE ) {
2377 $rule_description = $this->get_schedule_description_by_id( $post_id );
2378 }
2379 }
2380
2381 $scf = new CustomFieldsInterface(
2382 'timed_content_rule_schedule',
2383 __( 'Rule description/schedule', 'timed-content' ),
2384 '',
2385 TIMED_CONTENT_RULE_POSTMETA_PREFIX,
2386 array( TIMED_CONTENT_RULE_TYPE ),
2387 array(),
2388 $this->jquery_ui_datetime_datepicker_i18n,
2389 $this->jquery_ui_datetime_timepicker_i18n,
2390 $rule_description
2391 );
2392 $ocf = new CustomFieldsInterface(
2393 'timed_content_rule_initial_event',
2394 __( 'Action/Initial Event', 'timed-content' ),
2395 __( 'Set the action to be taken and when it should first run.', 'timed-content' ),
2396 TIMED_CONTENT_RULE_POSTMETA_PREFIX,
2397 array( TIMED_CONTENT_RULE_TYPE ),
2398 $this->rule_occurrence_custom_fields,
2399 $this->jquery_ui_datetime_datepicker_i18n,
2400 $this->jquery_ui_datetime_timepicker_i18n
2401 );
2402 $pcf = new CustomFieldsInterface(
2403 'timed_content_rule_recurrence',
2404 __( 'Repeating Pattern', 'timed-content' ),
2405 __( 'Set how often the action should repeat.', 'timed-content' ),
2406 TIMED_CONTENT_RULE_POSTMETA_PREFIX,
2407 array( TIMED_CONTENT_RULE_TYPE ),
2408 $this->rule_pattern_custom_fields,
2409 $this->jquery_ui_datetime_datepicker_i18n,
2410 $this->jquery_ui_datetime_timepicker_i18n
2411 );
2412 $rcf = new CustomFieldsInterface(
2413 'timed_content_rule_stop_condition',
2414 __( 'Stopping Condition', 'timed-content' ),
2415 __( 'Set how long or how many times the action should occur.', 'timed-content' ),
2416 TIMED_CONTENT_RULE_POSTMETA_PREFIX,
2417 array( TIMED_CONTENT_RULE_TYPE ),
2418 $this->rule_recurrence_custom_fields,
2419 $this->jquery_ui_datetime_datepicker_i18n,
2420 $this->jquery_ui_datetime_timepicker_i18n
2421 );
2422 $ecf = new CustomFieldsInterface(
2423 'timed_content_rule_exceptions',
2424 __( 'Exceptions', 'timed-content' ),
2425 __( 'Set up any exceptions to this Timed Content Rule.', 'timed-content' ),
2426 TIMED_CONTENT_RULE_POSTMETA_PREFIX,
2427 array( TIMED_CONTENT_RULE_TYPE ),
2428 $this->rule_exceptions_custom_fields,
2429 $this->jquery_ui_datetime_datepicker_i18n,
2430 $this->jquery_ui_datetime_timepicker_i18n
2431 );
2432 }
2433
2434 /**
2435 * Strips indices from an array
2436 */
2437 function strip_array_indices( $array_to_strip ) {
2438 foreach ( $array_to_strip as $array_item ) {
2439 $new_array[] = $array_item;
2440 }
2441
2442 return $new_array;
2443 }
2444
2445 /**
2446 * Convert dates and times to ISO format if needed
2447 */
2448 function convert_date_time_parameters_to_iso( $args ) {
2449 $date_parsed = date_create_from_format( 'Y-m-d', $args['instance_start']['date'] );
2450 if ( false === $date_parsed ) {
2451 $date_source = strtotime( $this->date_time_to_english( $args['instance_start']['date'] ) );
2452 $args['instance_start']['date'] = $this->format_timestamp( 'Y-m-d', $date_source );
2453 }
2454
2455 $date_parsed = date_create_from_format( 'Y-m-d', $args['instance_end']['date'] );
2456 if ( false === $date_parsed ) {
2457 $date_source = strtotime( $this->date_time_to_english( $args['instance_end']['date'] ) );
2458 $args['instance_end']['date'] = $this->format_timestamp( 'Y-m-d', $date_source );
2459 }
2460
2461 $args['instance_start']['time'] = $this->convert_time_to_iso( $args['instance_start']['time'] );
2462
2463 $args['instance_end']['time'] = $this->convert_time_to_iso( $args['instance_end']['time'] );
2464
2465 $date_parsed = date_create_from_format( 'Y-m-d', $args['end_date'] );
2466 if ( false === $date_parsed ) {
2467 $date_source = strtotime( $this->date_time_to_english( $args['end_date'] ) );
2468 $args['end_date'] = $this->format_timestamp( 'Y-m-d', $date_source );
2469 }
2470
2471 if ( is_array( $args['exceptions_dates'] ) ) {
2472 foreach ( $args['exceptions_dates'] as $key => $value ) {
2473 $date_parsed = date_create_from_format( 'Y-m-d', $value );
2474 if ( false === $date_parsed ) {
2475 $date_source = strtotime( $this->date_time_to_english( $args['end_date'] ) );
2476 $args['exceptions_dates'][ $key ] = $this->format_timestamp( 'Y-m-d', $date_source );
2477 }
2478 }
2479 }
2480
2481 return $args;
2482 }
2483
2484 /**
2485 * Convert time to ISO format if needed
2486 */
2487 function convert_time_to_iso( $time ) {
2488 if ( strpos( $time, 'AM' ) !== false ) {
2489 $time_base = trim( substr( $time, 0, strlen( $time ) - 2 ) );
2490 $time_dt = date_create_from_format( 'G:i', $time_base );
2491 if ( false !== $time_dt ) {
2492 $time = $this->format_timestamp( 'H:i', $time_dt->getTimestamp() );
2493 }
2494 } elseif ( strpos( $time, 'PM' ) !== false ) {
2495 $time_base = trim( substr( $time, 0, strlen( $time ) - 2 ) );
2496 $time_dt = date_create_from_format( 'G:i', $time_base );
2497 if ( false !== $time_dt ) {
2498 $time = $this->format_timestamp( 'H:i', $time_dt->getTimestamp() + 43200 );
2499 }
2500 }
2501
2502 return $time;
2503 }
2504
2505 /**
2506 * Set the timezone to be used for format_date()
2507 */
2508 function set_format_timezone( $timezone ) {
2509 $this->current_timezone = new DateTimeZone( $timezone );
2510 if ( false === $this->current_timezone ) {
2511 $this->current_timezone = new DateTimeZone( 'UTC' );
2512 }
2513 }
2514
2515 /**
2516 * Get the timezone to be used for format_date()
2517 */
2518 function get_format_timezone() {
2519 return $this->current_timezone;
2520 }
2521
2522 /**
2523 * Format a given timestamp with the specified timezone
2524 */
2525 function format_timestamp( $format, $timestamp ) {
2526 try {
2527 $dt = new DateTime();
2528 $dt->setTimestamp( $timestamp );
2529 $dt->setTimezone( $this->current_timezone );
2530 } catch ( Exception $e ) {
2531 return '';
2532 }
2533
2534 return $dt->format( $format );
2535 }
2536 }
2537