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

timed-content.php in Timed Content 2.70, at timed-content.php

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