Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
calendar_event_recurring.inc
1 <?php
17 require_once dirname(__FILE__).'/../../calendar_event/calendar_event.inc';
18 require_once dirname(__FILE__).'/../../lib/calendar_common.inc';
19 require_once SQ_FUDGE_PATH.'/datetime_field/datetime_field.inc';
20 require_once SQ_FUDGE_PATH.'/general/general.inc';
21 require_once SQ_FUDGE_PATH.'/general/datetime.inc';
22 
35 {
36  var $parent_links = Array();
37 
38 
45  function Calendar_Event_Recurring($assetid=0)
46  {
47  $this->Calendar_Event($assetid);
48 
49  }//end constructor
50 
51 
60  function load($assetid)
61  {
62  parent::load($assetid);
63 
64  $this->parent_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($assetid, SQ_SC_LINK_ALL, 'calendar_event_multi_date', FALSE, 'minor');
65 
66  }//end load()
67 
68 
89  function _preCreateCheck(&$link)
90  {
91  $freq = $this->attr('frequency');
92  if (empty($freq)) {
93  trigger_localised_error('CAL0047', E_USER_WARNING);
94  return FALSE;
95  }
96  return parent::_preCreateCheck($link);
97 
98  }//end _preCreateCheck()
99 
100 
109  function _secondsToday($timestamp)
110  {
111  return $timestamp - strtotime(gmdate('Y-m-d 00:00:00', $timestamp).' GMT');
112 
113  }//end _secondsToday()
114 
115 
137  function getFirstOccurrenceAfter($after=NULL, $start=NULL, $freq_type=NULL, $interval=NULL, $stop=NULL)
138  {
139  if (is_null($after)) $after = date('Y-m-d');
140 
141  // if we are calling statically make sure that we have all our arguments
142  if ((!(isset($this) && is_a($this, 'Calendar_Event_Recurring'))) && (func_num_args() < 5)) {
143  trigger_localised_error('CAL0030', E_USER_ERROR);
144  }
145 
146  if (is_null($start)) {
147  // called on an object
148  $freq_type = substr($this->attr('frequency'), 0, 3);
149  $interval = substr($this->attr('frequency'), 3);
150  if (!$interval) $interval = 1;
151  $start = substr($this->attr('start_date'),0,10);
152  $stop = $this->attr('stop_date');
153  // null stop date attribute?
154  if ($stop == '---------- --:--:--') $stop = NULL;
155  }
156 
157  $seconds_per_day = 60 * 60 * 24;
158  if ($after <= $start) return $start;
159  $result = '';
160 
161  while ($result == '') {
162  list($res_year,$res_month,$res_day) = sscanf($after, '%04d-%02d-%02d');
163  list($after_year,$after_month,$after_day) = sscanf($after, '%04d-%02d-%02d');
164  list($start_year,$start_month,$start_day) = sscanf($start, '%04d-%02d-%02d');
165  $month_diff = (12 * ($after_year - $start_year)) + $after_month - $start_month;
166 
167  switch ($freq_type) {
168  case 'DED':
169  $periods_so_far = ceil(days_between_isos($after, $start) / $interval);
170  $result = add_days_to_iso($start, $interval * $periods_so_far);
171  break;
172 
173  case 'DWD':
174  $periods_so_far = ceil(days_between_isos($after, $start) / $interval);
175  $result = add_days_to_iso($start, $interval * $periods_so_far);
176  while (in_array(date('D', iso8601_ts($result)), Array('Sat', 'Sun'))) {
177  $result = add_days_to_iso($result, 1);
178  }
179  break;
180 
181  case 'DWE':
182  $periods_so_far = ceil(days_between_isos($after, $start) / $interval);
183  $result = add_days_to_iso($start, $interval * $periods_so_far);
184  while (!in_array(date('D', iso8601_ts($result)), Array('Sat', 'Sun'))) {
185  $result = add_days_to_iso($result, 1);
186  }
187  break;
188 
189  case 'WEW':
190  $periods_so_far = ceil(days_between_isos($after, $start) / ($interval*7));
191  $result = add_days_to_iso($start, $interval * 7 * $periods_so_far);
192  break;
193 
194  // Every 'x' months on the 'y'th day of the month
195  case 'MFN':
196  if (($month_diff % $interval) != 0) {
197  increment_month($res_month, $res_year, ($interval - ($month_diff % $interval)));
198  } else if ($res_day > $start_day) {
199  increment_month($res_month, $res_year, $interval);
200  }
201  while ($start_day > days_in_month($res_month, $res_year)) {
202  increment_month($res_month, $res_year, $interval);
203  }
204  $result = sprintf('%04d-%02d-%02d',$res_year,$res_month,$start_day);
205  break;
206 
207  // Every 'x' months on the 'y'th weekday of the month (eg. 2nd Thursday)
208  case 'MFW':
209  $target_week_of_month = (int)(($start_day - 1) / 7);
210  $target_day_of_week = date('w', iso8601_ts($start));
211  $res_day = ($target_week_of_month * 7) + 1;
212 
213  if (($month_diff % $interval) != 0) {
214  // go to the next valid month
215  increment_month($res_month, $res_year, $interval - ($month_diff % $interval));
216  } else if ((int)($after_day / 7) > $target_week_of_month) {
217  // we're in a valid month, but too late in it, so go to next valid month
218  increment_month($res_month, $res_year, $interval);
219  } else if ((int)($after_day / 7) == $target_week_of_month) {
220  $res_day = max($res_day, $after_day);
221  }
222 
223  $month_length = days_in_month($res_month, $res_year);
224 
225  $result = sprintf('%04d-%02d-%02d',$res_year,$res_month,$res_day);
226  while (date('w', iso8601_ts($result)) != $target_day_of_week) {
227  $res_day += (7 + $target_day_of_week - date('w', iso8601_ts($result)) - 1) % 7 + 1;
228 
229  $result = sprintf('%04d-%02d-%02d',$res_year,$res_month,$res_day);
230  if (($res_day > $month_length) || ($res_day > (($target_week_of_month+1) * 7))) {
231 
232  // run out of days, so we'll have to start again in the next valid month
233  increment_month($res_month, $res_year, $interval);
234  $month_length = days_in_month($res_month, $res_year);
235  $res_day = ($target_week_of_month * 7)+1;
236  }
237  $result = sprintf('%04d-%02d-%02d',$res_year,$res_month,$res_day);
238  }
239  break;
240 
241  // Every 'x' months on the 'y'th last day of the month
242  case 'MRN':
243  $target_days_from_end = days_in_month($start_month, $start_year) - $start_day;
244 
245  if (($month_diff % $interval) != 0) {
246  increment_month($res_month, $res_year, $interval - ($month_diff % $interval));
247  } else if ((days_in_month($res_month, $res_year) - $after_day) < $target_days_from_end) {
248  increment_month($res_month, $res_year, $interval);
249  }
250 
251  $result = sprintf('%04d-%02d-%02d',$res_year,$res_month,days_in_month($res_month, $res_year) - $target_days_from_end);
252 
253  while ((days_in_month($res_month, $res_year) - $target_days_from_end) < 0) {
254  increment_month($res_month, $res_year, $interval);
255 
256  $result = sprintf('%04d-%02d-%02d',$res_year,$res_month,days_in_month($res_month, $res_year) - $target_days_from_end);
257  }
258  break;
259 
260  // Every 'x' months on the 'y'th last weekday of the month
261  case 'MRW':
262  $min_day = 1;
263  $target_weeks_from_end = (int)((days_in_month($start_month, $start_year) - $start_day) / 7);
264  $target_day_of_week = (date('w', iso8601_ts($start)));
265 
266  if (($month_diff % $interval) != 0) {
267  // go to the next valid month
268  increment_month($res_month, $res_year, $interval - ($month_diff % $interval));
269  } else if ((int)((days_in_month($after_month, $after_year) - $after_day) / 7) < $target_weeks_from_end) {
270  // we're too far into this month, so go to the next valid month
271  increment_month($res_month, $res_year, $interval);
272  } else if ((int)((days_in_month($after_month, $after_year) - $after_day) / 7) == $target_weeks_from_end) {
273  // we're in the right week so start from 'now', not the begining of the week,
274  }
275  $min_day = $after_day;
276 
277  $res_day = days_in_month($res_month, $res_year) - ($target_weeks_from_end * 7);
278  $result = sprintf('%04d-%02d-%02d',$res_year,$res_month,$res_day);
279 
280  while (date('w', iso8601_ts($result)) != $target_day_of_week) {
281  // if ($res_month - $start_month > 2) die;
282  $res_day -= (7 + date('w', iso8601_ts($result)) - $target_day_of_week) % 7;
283 
284  if (($res_day < $min_day) || ($result < $after)) {
285  // can't get the nth-last xday in this month, either because
286  // it's already passed or because it doesn't exist, so goto next month
287  increment_month($res_month, $res_year, $interval);
288  $res_day = days_in_month($res_month, $res_year) - ($target_weeks_from_end * 7);
289  $min_day = 1;
290 
291  }
292  $result = sprintf('%04d-%02d-%02d',$res_year,$res_month,$res_day);
293  }
294  break;
295  }//end switch
296 
297  if (isset($this) && is_a($this, 'Calendar_Event_Recurring') && $exception_link = $this->getExceptionLink($result, $this->id)) {
298  $after = add_days_to_iso($result);
299  $result = '';
300  }
301  }//end while
302 
303  if (!empty($stop) && ($result > $stop)) {
304  return FALSE;
305  } else {
306  return $result;
307  }
308 
309  }//end getFirstOccurrenceAfter()
310 
311 
331  function hasOccurrenceOnDate($date=NULL, $start=NULL, $freq_type=NULL, $interval=NULL, $stop=NULL)
332  {
333  // if we are calling statically make sure that we have all our arguments
334  if (is_null($this) && (func_num_args() < 5)) {
335  trigger_localised_error('CAL0031', E_USER_ERROR);
336  }
337 
338  // fill in defaults if we haven't been provided with all values
339  if (is_null($start)) {
340  // called on an object
341  $freq_type = substr($this->attr('frequency'), 0, 3);
342  $interval = substr($this->attr('frequency'), 3);
343  $start = substr($this->attr('start_date'),0,10);
344  $stop = $this->attr('stop_date');
345  }
346  if (is_null($date)) $date = date('Y-m-d');
347  if (strpos($stop, '----------') !== FALSE) {
348  $stop = NULL;
349  }
350 
351  // dispose of trivial cases
352  if ($start > $date) return FALSE;
353  if ((!is_null($stop)) && ($stop < $date)) {
354  return FALSE;
355  }
356 
357  // get components and get into it
358  $date_components = Calendar_Common::getDateComponents($date);
359  $start_components = Calendar_Common::getDateComponents($start);
360 
361  if (!$interval) $interval = 1;
362  if ($interval > 1) $freq_type .= 'x';
363 
364  switch ($freq_type) {
365  case 'DED':
366  // definitely occurs
367  return TRUE;
368  break;
369 
370  case 'DEDx':
371  // days between start_date and date must be a multiple of $interval
372  return (($date_components['ds'] - $start_components['ds']) % $interval == 0);
373  break;
374 
375  case 'DWD':
376  // definitely occurs unless this is a weekend day
377  return !in_array($date_components['wday'], Array(0, 6));
378  break;
379 
380  case 'DWE':
381  // definitely occurs if this is a weekend day
382  return in_array($date_components['wday'], Array(0, 6));
383  break;
384 
385  case 'WEW':
386  // occurs if start_date and date are the same day of the week
387  return ($date_components['wday'] == $start_components['wday']);
388  break;
389 
390  case 'WEWx':
391  // occurs if start_date and date are the same day of the week
392  // AND the number of weeks between start_date and date is a multiple of $interval
393  return ($date_components['wday'] == $start_components['wday'])
394  && (((($date_components['ds'] - $start_components['ds']) / 7) % $interval) == 0);
395  break;
396 
397  case 'MFN':
398  // occurs if date and start_date are the same day of the month
399  return ($date_components['mday'] == $start_components['mday']);
400  break;
401 
402  case 'MFNx':
403  // occurs if date and start_date are the same day of the month
404  // AND the number of months in between is a multiple of $interval
405  return ($date_components['mday'] == $start_components['mday'])
406  && ((($date_components['ms'] - $start_components['ms']) % $interval) == 0);
407  break;
408 
409  case 'MFW':
410  // occurs if date and start_date are the same day of the week
411  // AND they are in the same week of the month
412  return ($date_components['wday'] == $start_components['wday'])
413  && ($date_components['wdom'] == $start_components['wdom']);
414  break;
415 
416  case 'MFWx':
417  // occurs if date and start_date are the same day of the week
418  // AND they are in the same week of the month
419  // AND the number of months in between is a multiple of $interval
420  return ($date_components['wday'] == $start_components['wday'])
421  && ($date_components['wdom'] == $start_components['wdom'])
422  && ((($date_components['ms'] - $start_components['ms']) % $interval) == 0);
423  break;
424 
425  case 'MRN':
426  // occurs if monthlength - day_of_month is equal for date and start_date
427  return ($start_components['rmday'] == $date_components['rmday']);
428  break;
429 
430  case 'MRNx':
431  // occurs if monthlength - day_of_month is equal for date and start_date
432  // AND number of months between start_date and date is a multiple of $interval
433  return ($start_components['rmday'] == $date_components['rmday'])
434  && ((($date_components['ms'] - $start_components['ms']) % $interval) == 0);
435  break;
436 
437  case 'MRW':
438  // occurs if date and start_date are the same day of the week...
439  // AND they are in the same week from the end of the month
440  return ($date_components['wday'] == $start_components['wday'])
441  && ($date_components['rwdom'] == $start_components['rwdom']);
442  break;
443 
444  case 'MRWx':
445  // occurs if date and start_date are the same day of the week...
446  // AND they are in the same week from the end of the month
447  // AND the number of months between is a multiple of $interval
448  return ($date_components['wday'] == $start_components['wday'])
449  && ($date_components['rwdom'] == $start_components['rwdom'])
450  && ((($date_components['ms'] - $start_components['ms']) % $interval) == 0);
451  break;
452  }//end switch
453  return FALSE;
454 
455  }//end hasOccurrenceOnDate()
456 
457 
472  function getInstanceDates($date)
473  {
474  if (empty($this->_tmp['instance_dates'][$date])) {
475  $res = Array();
476  if ($this->hasOccurrenceOnDate($date)) {
477  $res['start'] = $date;
478  } else if (!$this->getEndDate()) {
479  $res['start'] = $this->getFirstOccurrenceAfter($date);
480  } else {
481  $period = substr($this->attr('frequency'), 3);
482  if (!$period) $period = 1;
483  switch (substr($this->attr('frequency'), 0, 1)) {
484  case 'D':
485  $first_occur = add_days_to_iso($date, -$period);
486  break;
487 
488  case 'W':
489  $first_occur = add_days_to_iso($date, -$period*7);
490  break;
491 
492  case 'M':
493  $first_occur = add_months_to_iso($date, -$period).'-'.date('d', iso8601_ts($date));
494  break;
495 
496  }
497  $res['start'] = $this->getFirstOccurrenceAfter($first_occur);
498  }
499 
500  if ($this->getStartTime()) $res['start'] .= ' '.$this->getStartTime();
501 
502  $duration = $this->getDuration();
503  $res['end'] = date('Y-m-d', iso8601_ts($res['start']) + $duration);
504 
505  if ($date > $res['end']){
506  $res['start'] = $this->getFirstOccurrenceAfter($date);
507  if ($this->getStartTime()) $res['start'] .= ' '.$this->getStartTime();
508  $res['end'] = date('Y-m-d', iso8601_ts($res['start']) + $duration);
509  }
510 
511  if ($this->getEndTime()) $res['end'] .= ' '.$this->getEndTime();
512 
513  $this->_tmp['instance_dates'][$date] = $res;
514  }
515  return $this->_tmp['instance_dates'][$date];
516 
517  }//end getInstanceDates()
518 
519 
526  function getiCalFrequency()
527  {
528  $freq_type = substr($this->attr('frequency'), 0, 3);
529  $interval = substr($this->attr('frequency'), 3);
530  $rule = '';
531  switch ($freq_type) {
532  // every day (or x days)
533  case 'DED':
534  if ($interval) {
535  $rule .= 'FREQ=DAILY;INTERVAL='.$interval;
536  } else {
537  $rule .= 'FREQ=DAILY;INTERVAL=1';
538  }
539  break;
540 
541  // every weekday
542  case 'DWD':
543  if ($interval) {
544  $rule .= 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;INTERVAL='.$interval;
545  } else {
546  $rule .= 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;INTERVAL=1';
547  }
548  break;
549 
550  // every weekday
551  case 'DWE':
552  if ($interval) {
553  $rule .= 'FREQ=WEEKLY;BYDAY=SA,SU;INTERVAL='.$interval;
554  } else {
555  $rule .= 'FREQ=WEEKLY;BYDAY=SA,SU;INTERVAL=1';
556  }
557  break;
558 
559  // every week (or x weeks)
560  case 'WEW':
561  if ($interval) {
562  $rule .= 'FREQ=WEEKLY;INTERVAL='.$interval;
563  } else {
564  $rule .= 'FREQ=WEEKLY;INTERVAL=1';
565  }
566 
567  break;
568 
569  // every month (or x months) on the yth day of the month
570  case 'MFN':
571  $day = (int)substr($this->attr('start_date'), 8, 2);
572  if ($interval) {
573  $rule .= 'FREQ=MONTHLY;INTERVAL='.$interval;
574  } else {
575  $rule .= 'FREQ=MONTHLY;INTERVAL=1';
576  }
577  break;
578 
579  // every month (or x months) on the yth weekday of the month
580  case 'MFW':
581  $day = (int)substr($this->attr('start_date'), 8, 2);
582  $counter = (int)(($day-1) / 7) + 1;
583  $day_of_week = strtoupper(substr(date('l', iso8601_ts($this->attr('start_date'))), 0, 2));
584 
585  if ($interval) {
586  $rule .= 'FREQ=MONTHLY;BYDAY='.$counter.$day_of_week.';INTERVAL='.$interval;
587  } else {
588  $rule .= 'FREQ=MONTHLY;BYDAY='.$counter.$day_of_week.';INTERVAL=1';
589  }
590  break;
591 
592  // every month (or x months) on the yth LAST day of the month
593  case 'MRN':
594  $day = (int)substr($this->attr('start_date'), 8, 2);
595  $month = (int)substr($this->attr('start_date'), 5, 2);
596  $year = (int)substr($this->attr('start_date'), 0, 4);
597  $month_length = date('j', strtotime('yesterday', iso8601_ts($year.'-'.($month+1).'-01')));
598  $day_of_month = (int) $month_length - $day + 1;
599 
600  if ($interval) {
601  $rule .= 'FREQ=MONTHLY;INTERVAL='.$interval.';BYMONTHDAY=-'.$day_of_month;
602  } else {
603  $rule .= 'FREQ=MONTHLY;BYMONTHDAY=-'.$day_of_month;
604  }
605  break;
606 
607  // every month (or x months) on the yth LAST weekday of the month
608  case 'MRW':
609  $day = (int)substr($this->attr('start_date'), 8, 2);
610  $day_of_week = strtoupper(substr(date('l', iso8601_ts($this->attr('start_date'))), 0, 2));
611  $month = (int)substr($this->attr('start_date'), 5, 2);
612  $year = (int)substr($this->attr('start_date'), 0, 4);
613  $month_length = date('j', strtotime('yesterday', iso8601_ts($year.'-'.($month+1).'-01')));
614  $day_of_month = (int)((($month_length - $day + 1) / 7) + 1);
615 
616  if ($interval) {
617  $rule .= 'FREQ=MONTHLY;INTERVAL='.$interval.';BYDAY=-'.$day_of_month.$day_of_week;
618  } else {
619  $rule .= 'FREQ=MONTHLY;INTERVAL=1;BYDAY=-'.$day_of_month.$day_of_week;
620  }
621  break;
622 
623  }//end switch
624 
625  // recurs until (only include 'UNTIL' part if the stop date is defined)
626  $stop_date_attr = $this->getAttribute('stop_date');
627  if ($stop_date_attr->value != '---------- --:--:--') {
628  $rule .= ';UNTIL='.date('Ymd\THis', iso8601_ts($stop_date_attr->value));
629  }
630  return $rule;
631 
632  }//end getiCalFrequency()
633 
634 
642  {
643  $freq_type = substr($this->attr('frequency'), 0, 3);
644  $interval = substr($this->attr('frequency'), 3);
645  $monthly_type = '';
646  switch ($freq_type) {
647  // every day (or x days)
648  case 'DED':
649  if ($interval) {
650  $res = translate('cal_event_every_x_days', $interval);
651  } else {
652  $res = translate('cal_event_every_day');
653  }
654  break;
655 
656  // every weekday
657  case 'DWD':
658  $res = translate('cal_event_every_weekday');
659  break;
660 
661  // every weekend day
662  case 'DWE':
663  $res = translate('cal_event_every_weekend');
664  break;
665 
666  // every week (or x weeks)
667  case 'WEW':
668  $dow = date('l', iso8601_ts($this->attr('start_date')));
669  if ($interval) {
670  $res = translate('cal_event_every_x_weeks', $interval, $dow);
671  } else {
672  $res = translate('cal_event_every_week', $dow);
673  }
674  break;
675 
676  // every month (or x months) on the yth day of the month
677  case 'MFN':
678  $day = (int)substr($this->attr('start_date'), 8, 2);
679  if ($interval) {
680  $res = translate('cal_event_every_x_months_first_day', $interval, add_ordinal_suffix($day));
681  } else {
682  $res = translate('cal_event_every_month_first_day', add_ordinal_suffix($day));
683  }
684  break;
685 
686  // every month (or x months) on the yth weekday of the month
687  case 'MFW':
688  $day = (int)substr($this->attr('start_date'), 8, 2);
689  $day_of_week = date('l', iso8601_ts($this->attr('start_date')));
690  if ($interval) {
691  $res = translate('cal_event_every_x_months_first_weekday', $interval, add_ordinal_suffix((int)((($day-1) / 7) + 1)), $day_of_week);
692  } else {
693  $res = translate('cal_event_every_month_first_weekday', add_ordinal_suffix((int)((($day-1) / 7) + 1)), $day_of_week);
694  }
695  break;
696 
697  // every month (or x months) on the yth LAST day of the month
698  case 'MRN':
699  $day = (int)substr($this->attr('start_date'), 8, 2);
700  $month = (int)substr($this->attr('start_date'), 5, 2);
701  $year = (int)substr($this->attr('start_date'), 0, 4);
702  $month_length = date('j', strtotime('yesterday', iso8601_ts($year.'-'.($month+1).'-01')));
703  if ($interval) {
704  $res = translate('cal_event_every_x_months_last_day', $interval, add_reverse_ordinal_suffix($month_length - $day + 1));
705  } else {
706  $res = translate('cal_event_every_month_last_day', add_reverse_ordinal_suffix($month_length - $day + 1));
707  }
708  break;
709 
710  // every month (or x months) on the yth LAST weekday of the month
711  case 'MRW':
712  $day = (int)substr($this->attr('start_date'), 8, 2);
713  $day_of_week = date('l', iso8601_ts($this->attr('start_date')));
714  $month = (int)substr($this->attr('start_date'), 5, 2);
715  $year = (int)substr($this->attr('start_date'), 0, 4);
716  $month_length = date('j', strtotime('yesterday', iso8601_ts($year.'-'.($month+1).'-01')));
717  if ($interval) {
718  $res = translate('cal_event_every_x_months_last_weekday', $interval, add_reverse_ordinal_suffix((int)((($month_length - $day + 1) / 7) + 1)), $day_of_week);
719  } else {
720  $res = translate('cal_event_every_month_last_weekday', add_reverse_ordinal_suffix((int)((($month_length - $day + 1) / 7) + 1)), $day_of_week);
721  }
722  break;
723 
724  // unknown frequency
725  default:
726  $res = translate('cal_event_unknown_frequency', $this->attr('frequency'));
727  }//end switch
728 
729  $stop_date_attr = $this->getAttribute('stop_date');
730  if (($stop_timestamp = $stop_date_attr->getTimestamp()) == -1) {
731  return $res.', '.translate('cal_event_recur_until_further_notice');
732  } else {
733  return $res.', '.translate('cal_event_recur_until_date', date('jS M Y', $stop_timestamp));
734  }
735 
736  }//end getFriendlyFrequency()
737 
738 
747  function getEventFrequency($event=NULL)
748  {
749  // If called statically, make sure we have relevant arguments
750  if (isset($this) && is_a($this, 'Calendar_Event_Recurring')) {
751  $freq_type = substr($this->attr('frequency'), 0, 3);
752  $interval = substr($this->attr('frequency'), 3);
753  } else {
754  if (is_null($event)) {
755  return FALSE;
756  } else {
757  $freq_type = array_get_index($event, 'frequency', '');;
758  $interval = array_get_index($event, 'period', '');
759  }
760  }
761 
762  if (empty($interval)) {
763  $interval = 1;
764  }
765 
766  $frequency = FALSE;
767  switch ($freq_type) {
768  case 'DED':
769  $frequency = 1*$interval; // Daily, 1 day gap between the events
770  break;
771 
772  case 'DWD':
773  $frequency = 2*$interval; // Weekdays, 2 days gap on weekends
774  break;
775 
776  case 'DWE':
777  $frequency = 5*$interval; // Weekends, 5 days gap on weekdays
778  break;
779 
780  case 'WEW':
781  $frequency = 7*$interval; // Weekly, 7 days gap
782  break;
783 
784  case 'MFN':
785  case 'MFW':
786  case 'MRN':
787  case 'MRW':
788  $frequency = 30*$interval; // Monthly, 30 days gap
789  break;
790 
791  }//end switch
792 
793  return $frequency;
794 
795  }//end getEventFrequency()
796 
797 
806  function getiCalExceptions($id=NULL)
807  {
808  $exception_string = '';
809  if ($this->id <> 0) {
810  $cancellations = $GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, SQ_SC_LINK_ALL, Array('calendar_event_cancellation'), FALSE, 'major');
811 
812  foreach ($cancellations as $cancelled_event) {
813  $event = $GLOBALS['SQ_SYSTEM']->am->getAsset($cancelled_event['minorid'], $cancelled_event['minor_type_code']);
814  if ($event->readAccess()) {
815  $exception_string .= date('Ymd\THis', iso8601_ts($event->attr('start_date'))).',';
816  }
817  }
818  if (substr($exception_string, -1) == ',') {
819  $exception_string = substr($exception_string, 0, -1);
820  }
821  }
822  return $exception_string;
823 
824  }//end getiCalExceptions()
825 
826 
833  function printBody()
834  {
835  ?>
836  <table border="0" cellspacing="2" cellpadding="2">
837  <tr>
838  <td><b><?php echo translate('name'); ?></b></td>
839  <td><?php echo $this->attr('name'); ?></td>
840  </tr>
841  <tr>
842  <td><b><?php echo translate('cal_event_first_start'); ?></b></td>
843  <td><?php echo $this->getFriendlyStart(); ?></td>
844  </tr>
845  <tr>
846  <td><b><?php echo translate('cal_event_first_end'); ?></b></td>
847  <td><?php echo $this->getFriendlyEnd(); ?></td>
848  </tr>
849  <tr>
850  <td><b><?php echo translate('cal_event_instance_duration'); ?></b></td>
851  <td><?php
852  // give useful info if no duration - easy_time_total returns empty string
853  $total_time = easy_time_total($this->getDuration(), TRUE);
854  echo (empty($total_time) ? 'N/A' : $total_time);
855  ?></td>
856  </tr>
857  <tr>
858  <td><b><?php echo translate('frequency'); ?></b></td>
859  <td><?php echo $this->getFriendlyFrequency() ?></td>
860  </tr>
861  <tr>
862  <td><b><?php echo translate('description'); ?></b></td>
863  <td><?php echo $this->getKeywordReplacement('asset_attribute_description'); ?></td>
864  </tr>
865  </table>
866  <?php
867 
868  }//end printBody()
869 
870 
877  function _getAllowedLinks()
878  {
879  $links = parent::_getAllowedLinks();
880  $links[SQ_LINK_TYPE_2]['calendar_event_modification'] = Array('card' => 'M', 'exclusive' => FALSE);
881  $links[SQ_LINK_TYPE_2]['calendar_event_cancellation'] = Array('card' => 'M', 'exclusive' => FALSE);
882  return $links;
883 
884  }//end _getAllowedLinks()
885 
886 
907  function prepareLink(&$asset, $side_of_link, &$link_type, &$value, &$sort_order, &$dependant, &$exclusive)
908  {
909  if (is_a($asset, 'calendar_event_modification') || is_a($asset, 'calendar_event_cancellation')) {
910  $link_type = SQ_LINK_TYPE_2;
911  }
912  return TRUE;
913 
914  }//end prepareLink()
915 
916 
931  function getKeywordReplacement($keyword)
932  {
933  $keyword = parse_keyword($keyword, $modifiers);
934 
935  switch ($keyword) {
936  case 'edit_link':
937  $replacement = $this->writeAccess('') ? '<a href="'.htmlspecialchars(replace_query_string_vars(Array('SQ_CALENDAR_VIEW'=>'edit', 'SQ_CALENDAR_EVENT_ID'=>$this->id))).'">'.translate('cal_event_recurring_edit_link_text').'</a>' : '';
938  break;
939 
940  case 'event_frequency':
941  $replacement = $this->getFriendlyFrequency();
942  break;
943 
944  case 'event_frequency_summary':
945  $frequency_threshold = $GLOBALS['SQ_SYSTEM']->getUserPrefs('calendar_event_recurring', 'SQ_CALENDAR_EVENTS_FREQUENCY');
946  $event_frequency = $this->getEventFrequency();
947  // If the event's frequency less than the predefined frequency threshold, display the event's recurrance summary
948  $replacement = ($event_frequency < $frequency_threshold) ? $this->getFriendlyFrequency() : '';
949  break;
950 
951  case 'event_ical_frequency':
952  $replacement = $this->getiCalFrequency();
953  break;
954 
955  case 'event_ical_exceptions':
956  $replacement = $this->getiCalExceptions();
957  break;
958 
959  default:
960  if (in_array($keyword, Array('instance_start_datetime', 'instance_end_datetime', 'cancel_link', 'modify_link'))) {
961  if (isset($_REQUEST['SQ_CALENDAR_DATE'])) {
962  if ($exception_link = $this->getExceptionLink($_REQUEST['SQ_CALENDAR_DATE'])) {
963  // exception today
964  $replacement = '';
965  } else {
966  // we are showing a normal instance of this event
967  $instance_dates = $this->getInstanceDates($_REQUEST['SQ_CALENDAR_DATE']);
968  switch ($keyword) {
969  case 'instance_start_datetime':
970  if (!isset($this->vars['start_date_ts'])) {
971  $replacement = $instance_dates['start'];
972  } else {
973  // If the event is the shadow asset, then instance data is available as asset attribute
974  $replacement = parent::getKeywordReplacement($keyword);
975  }
976  break;
977 
978  case 'instance_end_datetime':
979  if (!isset($this->vars['start_end_ts'])) {
980  $replacement = $instance_dates['end'];
981  } else {
982  // If the event is the shadow asset, then instance data is available as asset attribute
983  $replacement = parent::getKeywordReplacement($keyword);
984  }
985  break;
986 
987  case 'cancel_link':
988  $write_access = $this->writeAccess('');
989  $replacement = $write_access ? $this->_getCancelLink() : translate('cal_event_cancel_link_default');
990  break;
991 
992  case 'modify_link':
993  $write_access = $this->writeAccess('');
994  $replacement = $write_access ? $this->_getModifyLink() : translate('cal_event_modify_link_default');
995  break;
996  }
997  }
998  } else if ($keyword == 'instance_start_datetime' || $keyword == 'instance_end_datetime') {
999  // If the event is the shadow asset, then instance data is available as asset attribute
1000  $replacement = parent::getKeywordReplacement($keyword);
1001  } else {
1002 
1003  // somehow we're just viewing the event by itself, so set the instance-specific
1004  // keywords to blank for tidiness's sake
1005  $replacement = '';
1006  }
1007 
1008  // break outta switch block
1009  break;
1010  }
1011  $matches = Array();
1012  preg_match('/^instance_start_datetime_(.+)/', $keyword, $matches);
1013  if (!empty($matches)) {
1014  if (!empty($_REQUEST['SQ_CALENDAR_DATE'])) {
1015  if ($exception_link = $this->getExceptionLink($_REQUEST['SQ_CALENDAR_DATE'])) {
1016  // exception today
1017  $replacement = '';
1018  } else {
1019  // we are showing a normal instance of this event
1020  $instance_dates = $this->getInstanceDates($_REQUEST['SQ_CALENDAR_DATE']);
1021  switch ($matches[1]){
1022  case 'ical':
1023  // If theres no time bit, then only use the date portion to get the correct iCal timestamp
1024  if (preg_match("/^(.*?) (--):(--):(--)$/", $instance_dates['start'])) {
1025  $replacement = date('Ymd', iso8601_ts($instance_dates['start']));
1026  } else {
1027  $replacement = date('Ymd\THis', iso8601_ts($instance_dates['start']));
1028  }
1029  break;
1030  default:
1031  $replacement = date($matches[1], iso8601_ts($instance_dates['start']));
1032  }
1033  }
1034  } else {
1035  // If the event is the shadow asset, then instance data is available as asset attribute
1036  $replacement = parent::getKeywordReplacement($keyword);
1037  }
1038  // break outta switch block
1039  break;
1040  }
1041  $matches = Array();
1042  preg_match('/^instance_end_datetime_(.+)/', $keyword, $matches);
1043  if (!empty($matches)) {
1044  if (!empty($_REQUEST['SQ_CALENDAR_DATE'])) {
1045  if ($exception_link = $this->getExceptionLink($_REQUEST['SQ_CALENDAR_DATE'])) {
1046  // exception today
1047  $replacement = '';
1048  } else {
1049  // we are showing a normal instance of this event
1050  $instance_dates = $this->getInstanceDates($_REQUEST['SQ_CALENDAR_DATE']);
1051  switch ($matches[1]){
1052  case 'ical':
1053  if (preg_match("/^(.*?) (--):(--):(--)$/", $instance_dates['end'])) {
1054  $replacement = date('Ymd', iso8601_ts($instance_dates['end']));
1055  } else {
1056  $replacement = date('Ymd\THis', iso8601_ts($instance_dates['end']));
1057  }
1058  break;
1059  default:
1060  $replacement = date($matches[1], iso8601_ts($instance_dates['end']));
1061  }
1062  }
1063  } else {
1064  // If the event is the shadow asset, then instance data is available as asset attribute
1065  $replacement = parent::getKeywordReplacement($keyword);
1066  }
1067  // break outta switch block
1068  break;
1069  }
1070  $matches = Array();
1071  preg_match('/^event_stop_date_(.+)/', $keyword, $matches);
1072  $stop_date = $this->attr('stop_date');
1073  if ((!empty($matches)) && !empty($stop_date) && (FALSE === strpos('----', $stop_date))) {
1074  $replacement = date($matches[1], iso8601_ts($stop_date));
1075 
1076  // break outta switch block
1077  break;
1078  }
1079  if (in_array($keyword, Array('asset_url', 'asset_href'))) {
1080  $res = parent::getKeywordReplacement($keyword);
1081  if (!empty($_REQUEST['SQ_CALENDAR_DATE'])) {
1082  $res = replace_query_string_vars(Array('SQ_CALENDAR_DATE' => $_REQUEST['SQ_CALENDAR_DATE']), $res);
1083  }
1084 
1085  // Produce some valid XHTML
1086  $replacement = htmlentities($res);
1087 
1088  // break outta switch block
1089  break;
1090  }
1091 
1092  $replacement = parent::getKeywordReplacement($keyword);
1093  $empty_replacement = (((string)$replacement) == '%'.$keyword.'%');
1094 
1095  // If we have not found an appropriate replacement, ask the parent, if it is a Calendar Multi Date Event asset
1096  if ($empty_replacement && (count($this->parent_links) > 0)) {
1097  // Only expecting one relevant link
1098  $parent_link = reset($this->parent_links);
1099 
1100  $parent_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($parent_link['majorid']);
1101  $replacement = $parent_asset->getKeywordReplacement($keyword);
1102  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($parent_asset);
1103  }
1104 
1105  }//end switch
1106 
1107  apply_keyword_modifiers($replacement, $modifiers, Array('assetid' => $this->id));
1108 
1109  return $replacement;
1110 
1111  }//end getKeywordReplacement()
1112 
1113 
1128  function getExceptionLink($date, $id=NULL)
1129  {
1130  if (is_null($id) && isset($this->_tmp['exception_link'])) {
1131  return $this->_tmp['exception_link'];
1132  }
1133  $search_id = is_null($id) ? $this->id : $id;
1134  $child_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($search_id, SQ_SC_LINK_ALL, Array('calendar_event_cancellation', 'calendar_event_modification'), FALSE, 'major', $date);
1135  $res = current($child_links);
1136  if ($res['minor_type_code'] == 'calendar_event_cancellation') {
1137  $replacement_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($res['minorid'], SQ_SC_LINK_ALL, 'calendar_event_single', FALSE);
1138  if (!empty($replacement_links)) {
1139  $res = current($replacement_links);
1140  }
1141  }
1142  if (is_null($id)) {
1143  $this->_tmp['exception_link'] = $res;
1144  }
1145  return $res;
1146 
1147  }//end getExceptionLink()
1148 
1149 
1156  function _getCancelLink()
1157  {
1158  if (isset($_REQUEST['SQ_CALENDAR_DATE'])) {
1159  return '<a href="?SQ_CALENDAR_VIEW=event&amp;SQ_CALENDAR_EVENT_ID='.$this->id.'&amp;SQ_CALENDAR_EVENT_ACTION=cance11&amp;SQ_CALENDAR_DATE='.$_REQUEST['SQ_CALENDAR_DATE'].'" onclick="return confirm(\''.translate('cal_event_cancel_this_instance_confirm').'\');">'.translate('cal_event_cancel_this_instance').'</a>';
1160  } else {
1161  return translate('cal_event_cancel_link_default');
1162  }
1163 
1164  }//end _getCancelLink()
1165 
1166 
1173  function _getModifyLink()
1174  {
1175  if (isset($_REQUEST['SQ_CALENDAR_DATE'])) {
1176  return '<a href="?SQ_CALENDAR_VIEW=event&amp;SQ_CALENDAR_EVENT_ID='.$this->id.'&amp;SQ_CALENDAR_EVENT_ACTION=modify1&amp;SQ_CALENDAR_DATE='.$_REQUEST['SQ_CALENDAR_DATE'].'">'.translate('cal_event_modify_this_instance').'</a>';
1177  } else {
1178  return translate('cal_event_modify_link_default');
1179  }
1180 
1181  }//end _getModifyLink()
1182 
1183 
1192  function processEventAction($action_name)
1193  {
1194  $res = '';
1195  switch ($action_name) {
1196  case 'cancel1':
1197  if (!isset($_REQUEST['SQ_CALENDAR_DATE'])) {
1198  trigger_localised_error('CAL0032', E_USER_WARNING);
1199  return '';
1200  }
1201  $res = '<form method="POST" action="?SQ_CALENDAR_VIEW=event&amp;SQ_CALENDAR_EVENT_ID='.$this->id.'&amp;SQ_CALENDAR_EVENT_ACTION=cancel2&amp;SQ_CALENDAR_DATE='.$_REQUEST['SQ_CALENDAR_DATE'].'"><table cellpadding="4" border="0">';
1202  $res .= '<tr><td style="vertical-align: top">'.translate('cal_event_reason_for_cancellation').':</td><td><textarea rows="4" cols="40" name="event_cancellation_reason"></textarea></td></tr>';
1203  $res .= '<tr><td colspan="2"><input type="submit" value="'.translate('cal_event_add_cancellation').'" />&nbsp;&nbsp;<input type="button" value="'.translate('go_back').'" onclick="document.location=\''.$_SERVER['HTTP_REFERER'].'\'" /></td></tr></table></form>';
1204  break;
1205 
1206  case 'cancel2':
1207  if ((!isset($_REQUEST['SQ_CALENDAR_DATE'])) || (!isset($_REQUEST['event_cancellation_reason']))) {
1208  trigger_localised_error('CAL0033', E_USER_WARNING);
1209  return '';
1210  }
1211 
1212  $cancel_event = $this->addCancellation($_REQUEST['SQ_CALENDAR_DATE'], $_REQUEST['event_cancellation_reason']);
1213 
1214  $res = '<p>'.translate('cal_event_cancel_rep_invite_message_1', $this->attr('name'), date('jS M Y', strtotime($_REQUEST['SQ_CALENDAR_DATE'].' 12:00:00 GMT'))).'</p>';
1215  $res .= '<p style="margin: 0px; ">'.translate('cal_event_cancel_rep_invite_message_2').'<p>';
1216  $res .= '<form style="margin: 0px; float: left" method="POST" action="?SQ_CALENDAR_VIEW=event&amp;SQ_CALENDAR_EVENT_ID='.$cancel_event->id.'&amp;SQ_CALENDAR_EVENT_ACTION=addreplacement&amp;SQ_CALENDAR_DATE='.$_REQUEST['SQ_CALENDAR_DATE'].'"><input type="submit" value="'.translate('yes').'" /></form><form style="margin: 0px 1ex; float: left" method="POST" action="?SQ_CALENDAR_VIEW=event&amp;SQ_CALENDAR_EVENT_ID='.$this->id.'&amp;SQ_CALENDAR_DATE='.$_REQUEST['SQ_CALENDAR_DATE'].'"><input type="submit" value="'.translate('no').'" /></form>';
1217  break;
1218 
1219  case 'modify1':
1220  if (!isset($_REQUEST['SQ_CALENDAR_DATE'])) {
1221  trigger_localised_error('CAL0034', E_USER_WARNING);
1222  return '';
1223  }
1224  $res = '<form method="POST" action="?SQ_CALENDAR_VIEW=event&amp;SQ_CALENDAR_EVENT_ID='.$this->id.'&amp;SQ_CALENDAR_EVENT_ACTION=modify2&amp;SQ_CALENDAR_DATE='.$_REQUEST['SQ_CALENDAR_DATE'].'"><table cellpadding="4" border="0">';
1225 
1226  $res .= '<tr><td style="vertical-align: top">'.translate('cal_event_start_and_end').'</td><td>';
1227  ob_start();
1228  $edit_fns = $this->getEditFns();
1229  $edit_fns->paintStartEndChooser($this, 'event');
1230  $res .= ob_get_contents();
1231  ob_end_clean();
1232  $res .= '</td></tr>';
1233 
1234  $res .= '<tr><td style="vertical-align: top">'.translate('description').':</td><td><textarea name="event_description">';
1235  $res .= $this->attr('description');
1236  $res .= '</textarea></td></tr>';
1237 
1238  $res .= '<tr><td colspan="2"><input type="submit" value="'.translate('cal_event_add_modification').'" />&nbsp;&nbsp;<input type="button" value="'.translate('cancel').'" onclick="document.location=\''.$_SERVER['HTTP_REFERER'].'\'" /></td></tr></table></form>';
1239 
1240  break;
1241 
1242  case 'modify2':
1243  // Figure out the original details of the occurrence we're modifying
1244  $occurrence_start = '';
1245  $occurrence_end = '---------- --:--:--';
1246  if ($this->hasOccurrenceOnDate($_REQUEST['SQ_CALENDAR_DATE'])) {
1247  $occurrence_start_date = $_REQUEST['SQ_CALENDAR_DATE'];
1248  } else {
1249  $occurrence_start_date = $this->getFirstOccurrenceAfter(date('Y-m-d', iso8601_ts($_REQUEST['SQ_CALENDAR_DATE']) - $this->getDuration()));
1250  }
1251  $occurrence_start = $occurrence_start_date.' ';
1252  $occurrence_start .= ($this->getStartTime() ? $this->getStartTime().':00' : '--:--:--');
1253 
1254  if ($duration = $this->getDuration()) {
1255  $occurrence_end = date('Y-m-d', iso8601_ts($occurrence_start) + $duration).' ';
1256  $occurrence_end .= ($this->getEndTime() ? $this->getEndTime().':00' : '--:--:--');
1257  }
1258 
1259  // Get the new details
1260  $edit_fns = $this->getEditFns();
1261  $new_details = $edit_fns->getStartEndChooserResult('event');
1262 
1263  // Compare old and new details
1264  if (($new_details['start'] == $occurrence_start) && ($new_details['end'] == $occurrence_end)) {
1265  // because the dates and times have not changed, we can just add a modification
1266  // with the new description
1267  $this->addModification($occurrence_start_date, $_REQUEST['event_description']);
1268  $res = '<p>'.translate('cal_event_instance_modified').'</p>';
1269  } else {
1270  // dates/times have changed, so we have to cancel and then reschedule
1271  $cancellation_event =& $this->addCancellation($occurrence_start_date, translate('cal_event_rescheduled_note'));
1272 
1273  $replacement =& $cancellation_event->addReplacement($new_details['start'], $new_details['end'] , $_REQUEST['event_description']);
1274 
1275  $res = '<p>Event rescheduled</p>';
1276  $new_query_string_vars = Array(
1277  'SQ_CALENDAR_EVENT_ID' => $replacement->id,
1278  'SQ_CALENDAR_DATE' => $replacement->getStartDate(),
1279  'SQ_CALENDAR_EVENT_ACTION' => NULL,
1280  );
1281  $res .= '<p><a href="'.htmlspecialchars(replace_query_string_vars($new_query_string_vars)).'">'.translate('cal_event_view_replacement_event').'</a></p>';
1282  }
1283  break;
1284 
1285  default:
1286  $res = parent::processEventAction($action_name);
1287  break;
1288  }//end switch
1289  return $res;
1290 
1291  }//end processEventAction()
1292 
1293 
1303  function &addCancellation($date, $reason)
1304  {
1305  if (!$this->writeAccess('')) {
1306  trigger_localised_error('CAL0035', E_USER_WARNING);
1307  return FALSE;
1308  }
1309  $GLOBALS['SQ_SYSTEM']->am->includeAsset('calendar_event_cancellation');
1310  if ($existing_link = $this->getExceptionLink($date)) {
1311  if ($existing_link['minor_type_code'] == 'calendar_event_cancellation') {
1312  $cancellation_event = $GLOBALS['SQ_SYSTEM']->am->getAsset($existing_link['minorid']);
1313  } else {
1314  $GLOBALS['SQ_SYSTEM']->am->deleteAssetLink($existing_link['linkid']);
1315  $cancellation_event = new Calendar_Event_Cancellation();
1316  }
1317  } else {
1318  $cancellation_event = new Calendar_Event_Cancellation();
1319  }
1320  $cancellation_event->setAttrValue('start_date', $date.' '.($this->getStartTime() ? $this->getStartTime().':00' : '--:--:--'));
1321  $end_date = date('Y-m-d', iso8601_ts($cancellation_event->attr('start_date')) + $this->getDuration());
1322  $cancellation_event->setAttrValue('end_date', $end_date.' '.($this->getEndTime() ? $this->getEndTime().':00' : '--:--:--'));
1323  $cancellation_event->setAttrValue('description', $reason);
1324  if (!$cancellation_event->id) {
1325  $link = Array('asset' => &$this, 'link_type' => SQ_LINK_TYPE_2, 'is_dependant' => 1, 'is_exclusive' => 0, 'value' => $date);
1326  $cancellation_event->create($link);
1327  } else {
1328  if (!$GLOBALS['SQ_SYSTEM']->am->acquireLock($cancellation_event->id, 'attributes')) {
1329  trigger_localised_error('CAL0036', E_USER_WARNING, $cancellation_event->name);
1330  return 0;
1331  }
1332  $cancellation_event->saveAttributes();
1333  $GLOBALS['SQ_SYSTEM']->am->releaseLock($cancellation_event->id, 'attributes');
1334  }
1335  $cancellation_event->processStatusChange($this->status);
1336  return $cancellation_event;
1337 
1338  }//end addCancellation()
1339 
1340 
1350  function &addModification($date, $description)
1351  {
1352  if (!$this->writeAccess('')) {
1353  trigger_localised_error('CAL0037', E_USER_WARNING);
1354  return FALSE;
1355  }
1356  $GLOBALS['SQ_SYSTEM']->am->includeAsset('calendar_event_modification');
1357 
1358  if ($existing_link = $this->getExceptionLink($date)) {
1359  if ($existing_link['minor_type_code'] == 'calendar_event_modification') {
1360  $modification_event = $GLOBALS['SQ_SYSTEM']->am->getAsset($existing_link['minorid']);
1361  } else {
1362  $GLOBALS['SQ_SYSTEM']->am->deleteAssetLink($existing_link['linkid']);
1363  $modification_event = new Calendar_Event_Modification();
1364  }
1365  } else {
1366  $modification_event = new Calendar_Event_Modification();
1367  }
1368  $modification_event->setAttrValue('start_date', $date.' --:--:--');
1369  $modification_event->setAttrValue('description', $description);
1370  if (!$modification_event->id) {
1371  $link = Array('asset' => &$this, 'link_type' => SQ_LINK_TYPE_2, 'is_dependant' => 1, 'is_exclusive' => 0, 'value' => $date);
1372  $modification_event->create($link);
1373  } else {
1374  if (!$GLOBALS['SQ_SYSTEM']->am->acquireLock($modification_event->id, 'attributes')) {
1375  trigger_localised_error('CAL0038', E_USER_WARNING, $modification_event->name);
1376  return 0;
1377  }
1378  $modification_event->saveAttributes();
1379  $GLOBALS['SQ_SYSTEM']->am->releaseLock($modification_event->id, 'attributes');
1380  }
1381  return $modification_event;
1382 
1383  }//end addModification()
1384 
1385 
1395  function cacheCalendarData($updating=TRUE)
1396  {
1397  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
1398  $db = MatrixDAL::getDb();
1399 
1400  $date_values = Array();
1401  foreach (Array('start_date', 'end_date') as $date_comp) {
1402  $date_values += Calendar_Common::getDateComponents($this->attr($date_comp), $date_comp.'_');
1403  }
1404 
1405  // frequency field => as thy will be set in the standard attribute value table
1406  $date_values['frequency'] = substr($this->attr('frequency'),0,3);
1407  if (strlen($this->attr('frequency')) > 3) {
1408  $date_values['period'] = intval(substr($this->attr('frequency'),3));
1409  } else {
1410  $date_values['period'] = 1;
1411  }
1412 
1413  // stop date
1414  if (strtotime(substr($this->attr('stop_date'),0,10)) != -1) {
1415  $date_values['stop_date'] = substr($this->attr('stop_date'),0,10);
1416  }
1417 
1418  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
1419 
1420  if (!$updating) {
1421  // set the asset ids
1422  $date_values['assetid'] = $this->id;
1423 
1424  for (reset($date_values); NULL !== ($key = key($date_values)); next($date_values)) {
1425  if (is_null($date_values[$key]))
1426  $date_values[$key] = 'NULL';
1427  else
1428  $date_values[$key] = MatrixDAL::quote($date_values[$key]);
1429  }
1430  $val_string = implode(',', $date_values);
1431 
1432  $sql = 'INSERT INTO
1433  sq_cal_date_val
1434  (
1435  '.implode(',', array_keys($date_values)).'
1436  )
1437  VALUES
1438  (
1439  '.$val_string.'
1440  )';
1441 
1442  try {
1443  $query = MatrixDAL::preparePdoQuery($sql);
1444  MatrixDAL::execPdoQuery($query);
1445  } catch (Exception $e) {
1446  throw new Exception($e->getMessage());
1447  }
1448 
1449  } else {
1450  // asset id becomes a where condition
1451  $sql = 'UPDATE
1452  sq_cal_date_val
1453  SET ';
1454 
1455  $set_array = Array();
1456  foreach ($date_values as $key => $value) {
1457  $set_array[] = $key.' = '.((is_null($value)) ? 'null' : MatrixDAL::quote($value));
1458  }
1459  $sql .= implode(',', $set_array);
1460  $sql .=' WHERE
1461  assetid = :assetid';
1462 
1463  try {
1464  $query = MatrixDAL::preparePdoQuery($sql);
1465  MatrixDAL::bindValueToPdo($query, 'assetid', $this->id);
1466  MatrixDAL::execPdoQuery($query);
1467  } catch (Exception $e) {
1468  throw new Exception($e->getMessage());
1469  }
1470  }
1471 
1472  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
1473  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1474  return TRUE;
1475 
1476  }//end cacheCalendarData()
1477 
1478 
1493  function getAvailableKeywords()
1494  {
1495  $res = parent::getAvailableKeywords();
1496 
1497  $our_keywords[] = 'asset_attribute_start_date'; // = translate('start_date_iso'); //'Start date (iso8601)';
1498  $our_keywords[] = 'asset_attribute_end_date'; // = translate('end_date_iso'); //'End date (iso8601)';
1499  $our_keywords[] = 'event_start_date'; // = translate('start_date_only_readable'); //'Event Start Date';
1500  $our_keywords[] = 'event_end_date'; // = tralsate('end_date_only_readable'); //'Event End Date';
1501  $our_keywords[] = 'event_start_datetime'; // = translate('Event start date/time';
1502  $our_keywords[] = 'event_end_datetime'; // = 'Event end date/time';
1503  $our_keywords[] = 'event_start_time_12h'; // = 'Start time (12 hour)';
1504  $our_keywords[] = 'event_end_time_12h'; // = 'End time (12 hour)';
1505  $our_keywords[] = 'event_start_time_24h'; // = 'Start time (24 hour)';
1506  $our_keywords[] = 'event_end_time_24h'; // = 'End time (24 hour)';
1507  $our_keywords[] = 'event_start_time_ical'; // = 'Start time (iCalendar)';
1508  $our_keywords[] = 'event_end_time_ical'; // = 'End time (iCalendar)';
1509  $our_keywords[] = 'event_duration'; // = 'Duration of the Event';
1510  $our_keywords[] = 'event_start_datetime_'; // = dynamic keyword to print start date and time
1511  $our_keywords[] = 'event_end_datetime_'; // = dynamic keyword to print end date and time
1512  $our_keywords[] = 'event_frequency';
1513  $our_keywords[] = 'instance_end_datetime';
1514  $our_keywords[] = 'instance_start_datetime';
1515  $our_keywords[] = 'instance_start_datetime_';
1516  $our_keywords[] = 'instance_end_datetime_';
1517  $our_keywords[] = 'event_stop_date_';
1518  $our_keywords[] = 'event_ical_frequency';
1519  $our_keywords[] = 'event_ical_exceptions';
1520  $our_keywords[] = 'event_frequency_summary';
1521 
1522  foreach ($our_keywords as $keyword) {
1523  $res[$keyword] = translate('cal_evt_recur_keyword_'.$keyword);
1524  }
1525  ksort($res);
1526  return $res;
1527 
1528  }//end getAvailableKeywords()
1529 
1530 
1531 }//end class
1532 
1533 ?>