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