CMS  Version 3.9
calendar_view.inc
Go to the documentation of this file.
1 <?php
7 /**************************************************************
8 
9  Copyright (c) 2010 Sonjara, Inc
10 
11  Permission is hereby granted, free of charge, to any person
12  obtaining a copy of this software and associated documentation
13  files (the "Software"), to deal in the Software without
14  restriction, including without limitation the rights to use,
15  copy, modify, merge, publish, distribute, sublicense, and/or sell
16  copies of the Software, and to permit persons to whom the
17  Software is furnished to do so, subject to the following
18  conditions:
19 
20  The above copyright notice and this permission notice shall be
21  included in all copies or substantial portions of the Software.
22 
23  Except as contained in this notice, the name(s) of the above
24  copyright holders shall not be used in advertising or otherwise
25  to promote the sale, use or other dealings in this Software
26  without prior written authorization.
27 
28  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
30  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
32  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
33  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
34  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
35  OTHER DEALINGS IN THE SOFTWARE.
36 
37 *****************************************************************/
38 
39 Fakoli::using("settings");
40 
41 abstract class EventHandler
42 {
43 
44  function filter($event, $date)
45  {
46  return (strncmp($event->start_date, $date, strlen($date)) == 0);
47  }
48 
49  abstract function formatSummary($event, $tagRowCallbacks);
50 
51  abstract function formatFilteringTags($event, $tagRowCallbacks);
52  abstract function summary($event);
53  abstract function details($event);
54  abstract function buildForm($event);
55 
61  {
62  throw new FakoliException("No iCal formatter provided");
63  }
64 
65  function getEventClass()
66  {
67  return Event;
68  }
69 
76  {
77  $event_id = $event->get("event_id");
78 
79  if(checkRole("admin"))
80  {
81  $out = "<br/><button class='button' onclick='editEvent($event_id)'>Edit this Event</button>\n";
82  }
83 
84  return $out;
85  }
86 }
87 
89 {
98  function filter($event, $date)
99  {
100  $start = explode(" ", $event->start_date);
101  $end = explode(" ", $event->end_date);
102 
103  $startTimeStamp = strtotime($start[0]);
104  $endTimeStamp = strtotime($end[0]);
105  $dateTimeStamp = strtotime($date);
106 
107  $diff = abs($endTimeStamp - $startTimeStamp);
108  $days = floor($diff/(60*60*24));
109 
110  $inDate = new DateTime($date."T00:00:00");
111  $startDate = new DateTime($event->start_date);
112 
113  $showEvent = false;
114  if($inDate->format("j") == 1 && ($startDate->format("Y-m-d") <= $inDate->format("Y-m-d")))
115  {
116  $showEvent = true;
117  }
118 
119  if($days <= 5 || $showEvent)
120  {
121  return ($dateTimeStamp >= $startTimeStamp && $dateTimeStamp <= $endTimeStamp);
122  }
123  else
124  {
125  return ($dateTimeStamp == $startTimeStamp);
126  }
127  }
128 
129  function formatFilteringTags($event, $tagRowCallbacks)
130  {
131  $pk = $event->getPrimaryKey();
132  if(!count($tagRowCallbacks)) return "";
133 
134  $id = $event->format("id='{$pk}_{{$pk}}'");
135 
136  foreach($tagRowCallbacks as $cb)
137  {
138  $dataAttrs = call_user_func($cb, $event, $dataAttrs);
139  }
140 
141  $attrs = "";
142  foreach($dataAttrs as $name => $value)
143  {
144  $attrs .= " ".$name."='".$value."'";
145  }
146 
147  return "{$id}{$attrs}";
148  }
149 
150  function summary($event)
151  {
152  return "<h4>{$event->title}</h4><p>{$event->event_type}</p>";
153  }
154 
155  function details($event, $byProgram = false)
156  {
157  ob_start();
158 ?>
159 <div class="event_details">
160 <h3><?echo $event->title?></h3>
161 <div class="event_contents">
162 <p><em><?echo $event->event_type?></em></p>
163 <p><label style="width: 80px">Start: </label><?echo $event->format("{start_date:short}") ?><br/>
164 <label style="width: 80px">End: </label><?echo $event->format("{end_date:short}") ?></p>
165 <?
166  if ($event->location)
167  {
168 ?>
169 <p><label style="width: 80px">Location: </label><?echo $event->location?></p>
170 <?
171  }
172 ?>
173 <p><?echo $event->description?></p>
174 <?
175 
176  echo $this->formatButtons($event);
177 ?>
178 </div>
179 </div>
180 <?
181  $out = ob_get_contents();
182  ob_end_clean();
183 
184  return $out;
185  }
186 
187  function formatiCalendar($event, $sequence_id = 0)
188  {
189  return new iCalendarEventManager($event->event_id, $event->start_date, $event->end_date, $event->title, $event->description, $event->location, $sequence_id, $event->TimeZone());
190  }
191 
192  function buildForm($event)
193  {
194  $form = new AutoForm($event);
195  $form->required("title", "start_date", "end_date");
196  $form->hide("composite_class", "event_invitation_id", "allow_access", "time_zone_id", "owner_id");
197 
198  $form->allowDelete = true;
199 
200  $sites = query(Site, "ORDER BY site_name");
201  if (count($sites) > 1)
202  {
203  $siteSelect = new CrossReferenceSelectFieldRenderer($form, "sites", "Publish to Sites", $sites, "site_name", EventSiteXref);
204  }
205 
206  $calendarSelect = new RelatedItemSelectFieldRenderer($form, "calendar_id", "Calendar", Calendar, "ORDER BY name", "{name}", "calendar_id", false, false, 80);
207  $eventTypeSelect = new SelectFieldRenderer($form, "event_type", "Event Type");
208  $eventTypeSelect->allowAddEntry();
209 
210  return $form;
211  }
212 
213  function formatSummary($event, $tagRowCallacks)
214  {
215  $handler_class = get_class($this);
216  $pk = $event->getPrimaryKey();
217  $tags = $this->formatFilteringTags($event, $tagRowCallacks);
218  $content .= "<div {$tags} class='event_summary' onclick='showEventDetail({$event->$pk}, \"{$handler_class}\")'>";
219  $content .= $this->summary($event);
220  $content .= "</div>";
221 
222  return $content;
223  }
224 
225 }
226 
227 class CalendarView implements FacetFilterable
228 {
230  var $events;
231  var $id;
232  var $month;
233  var $year;
234  var $handlerMap = array(); // key is event class, value is handler class name
235  var $calendarLink = "/calendar";
236  var $listLink = "/event_list";
237  var $listDialog = ""; // javascript function for bubble popup
238  var $detailLink = "/event_detail";
239  var $yearParam = "year";
240  var $monthParam = "month";
241  var $maxEventsPerDay = null;
243 
244  function CalendarView($events, $id = null)
245  {
246  $this->events = $events;
247  $this->id = $id;
248  $this->month = date("n");
249  $this->year = date("Y");
250  $this->tagRowCallbacks = array();
251  $this->addHandler(Event, new StandardEventHandler());
252  ComponentManager::fireEvent("RegisterCalendarEventHandlers", $this);
253 
254  $max = Settings::getValue("calendar", "max_events_per_day");
255  if($max)
256  {
257  $this->maxEventsPerDay = $max;
258  }
259  }
260 
261  function getID()
262  {
263  return $this->id;
264  }
265 
273  {
274  $this->handlerMap[$class] = $handler;
275  }
276 
277  /*
278  * Preferred approach is for subscribes to RegisterCalendarEventHandlers
279  * to respond with function that maps a composite or event class to
280  * a handler class. Example:
281  * static function registerCalendarEventHandlers(&$view)
282  * {
283  * $view->addHandler(CompositeRegistrationEvent, new RegistrationEventHandler());
284  * return $view;
285  * }
286  *
287  */
288  function getHandler($event)
289  {
290  if(get_class($event) == "Event" && $event->composite_class)
291  {
292  $event_class = $event->composite_class;
293  }
294  else
295  {
296  $event_class = get_class($event);
297  }
298 
299  if(array_key_exists($event_class, $this->handlerMap))
300  {
301  $handler = $this->handlerMap[$event_class];
302  }
303  else
304  {
306  }
307 
308  return $handler;
309  }
310 
316  {
317  $this->tagRowCallbacks[] = $handler;
318  }
319 
320  function summary($event)
321  {
322  return $this->getHandler($event)->summary($event);
323  }
324 
325  function details($event, $param = null)
326  {
327  return $this->getHandler($event)->details($event, $param);
328  }
329 
330  function filterEvents($events, $currentDay)
331  {
332  $ret = array();
333 
334  foreach($events as $event)
335  {
336  $handler = $this->getHandler($event);
337  if ($handler AND $handler->filter($event, $currentDay))
338  {
339  $ret[] = $event;
340  }
341  // If not found - function getHandler outputs trace
342  }
343 
344  return $ret;
345  }
346 
348  {
349  global $isAdmin;
350 
351  $qs = $_SERVER['QUERY_STRING'];
352 
353  $identifier = $_REQUEST["identifier"];
354 
355  if ($isAdmin) $identifier = "/admin/$identifier";
356 
357  $qs = preg_replace("/identifier=[\\w_]+&*/", "", $qs);
358  $qs = preg_replace("/{$this->yearParam}=\\d+&*/", "", $qs);
359  $qs = preg_replace("/{$this->monthParam}=\\d+&*/", "", $qs);
360 
361  return "$identifier".appendToQueryString($qs, "{$this->yearParam}=$year&{$this->monthParam}=$month");
362  }
363 
365  {
366  if ($this->year < 2005 || $this->year > 2038 || $this->month < 1 || $this->month > 12)
367  {
368  die("Date out of range");
369  }
370 
371  $this->startStr = sprintf("%d-%02d-01", $this->year, $this->month);
372  $this->start = strtotime($this->startStr);
373 
374  $this->dayOfWeek = date("w", $this->start);
375  $this->daysInMonth = getMonthDays($this->month, $this->year);
376 
377  $this->prevMonth = $this->month - 1;
378  $this->prevYear = $this->year;
379 
380  if ($this->prevMonth <= 0)
381  {
382  $this->prevYear--;
383  $this->prevMonth = 12;
384  }
385 
386  $this->nextMonth = $this->month + 1;
387  $this->nextYear = $this->year;
388  if ($this->nextMonth > 12)
389 
390  {
391  $this->nextMonth = 1;
392  $this->nextYear++;
393  }
394 
395  $this->endStr = sprintf("%d-%02d-01", $this->nextYear, $this->nextMonth);
396 
397  $this->prevLink = $this->rewriteQueryString($this->prevYear, $this->prevMonth);
398  $this->nextLink = $this->rewriteQueryString($this->nextYear, $this->nextMonth);
399  }
400 
401  function drawMiniCalendar()
402  {
403  $this->createEventDialog();
404 
405  global $script;
406  $script .= <<<ENDSCRIPT
407 <script type="text/javascript" src="/components/calendar/js/calendar.js"></script>
408 ENDSCRIPT;
409 
410  $this->calculateMonthRange();
411 
412  $bubbles = "";
413 
414 ?>
415 <div id="minicalendar">
416 <table>
417  <tr>
418  <td colspan="7" class="month"><a class="calendar_nav" href="<?echo $this->prevLink?>">&lt;</a>&nbsp; <?echo date("F Y", $this->start)?>&nbsp; <a class="calendar_nav" href="<?echo $this->nextLink?>">&gt;</a> </td>
419  </tr>
420  <tr>
421  <th width="14%">Su</th>
422  <th width="14%">Mo</th>
423  <th width="14%">Tu</th>
424  <th width="14%">We</th>
425  <th width="14%">Th</th>
426  <th width="14%">Fr</th>
427  <th width="14%">Sa</th>
428  </tr>
429  <tr>
430 <?
431  for($c = 0; $c < $this->dayOfWeek; ++$c)
432  {
433 ?><td class="empty">&nbsp;</td><?
434  }
435 
436  $today = date("Y-m-d");
437 
438  for($i = 1; $i <= $this->daysInMonth; ++$i)
439  {
440 
441  $currentDay = sprintf("%d-%02d-%02d", $this->year, $this->month, $i);
442  $eventsToday = $this->filterEvents($this->events, $currentDay);
443 
444  if (count($eventsToday) > 0)
445  {
446  $class = "event";
447  $event = $eventsToday[0];
448  $eventBubbleID = "event_bubble_".str_replace("-", "_", $currentDay);
449  $dayTitle = date("F jS Y", strtotime($currentDay));
450 
451  $link = $this->formatEventListLink($this->listLink, $this->listDialog, $currentDay, $eventBubbleID, $i);
452 
453  $bubbles .=
454  "<div id='$eventBubbleID' class='event_bubble'>
455  <div class='event_bubble_content'>
456  <h4>{$dayTitle}</h4>";
457 
458  foreach($eventsToday as $event)
459  {
460  $handler = $this->getHandler($event);
461  $bubbles .= $handler->summary($event);
462  }
463 
464  $bubbles .=
465  " </div>
466  <div class='event_bubble_bottom'>&nbsp;</div></div>";
467  }
468  else
469  {
470  $class = (($c % 7) == 0 || ($c % 7) == 6) ? "weekend" : "weekday";
471  $link = $i;
472  }
473 
474  if ($currentDay == $today) $class="today";
475 ?>
476  <td class="<?echo $class ?>" ><?echo $link ?></td>
477 <?
478  ++$c;
479  if (($c % 7) == 0) echo "</tr><tr>";
480  }
481 
482  while ($c % 7)
483  {
484 
485 ?><td class="empty">&nbsp;</td>
486 <?
487  $c++;
488  }
489 ?>
490 </tr>
491 </table>
492 </div>
493 <?echo $bubbles?>
494 <?
495  }
496 
497  function createEventDialog()
498  {
499  }
500 
501  function drawMonth()
502  {
503  $this->createEventDialog();
504 
505  $this->calculateMonthRange();
506 ?>
507 <div id="fullcalendar">
508 <table id=<?php echo $this->id ?>>
509  <tr>
510  <td class="month" colspan="7"><?echo date("F Y", $this->start)?> <a class="calendar_nav" href="<?echo $this->prevLink?>"><img src="/components/calendar/images/calendar_arrow_l.png" alt="left" width="22" height="22" border="0"></a> <a class="calendar_nav" href="<?echo $this->nextLink?>"><img src="/components/calendar/images/calendar_arrow_r.png" alt="right" width="22" height="22" border="0"></a> </td>
511  </tr>
512  <tr>
513  <th width="14.28%">Su</th>
514  <th width="14.28%">Mo</th>
515  <th width="14.28%">Tu</th>
516  <th width="14.28%">We</th>
517  <th width="14.28%">Th</th>
518  <th width="14.28%">Fr</th>
519  <th width="14.28%">Sa</th>
520  </tr>
521  <tr>
522 <?
523  for($c = 0; $c < $this->dayOfWeek; ++$c)
524  {
525 ?><td class="empty">&nbsp;</td><?
526  }
527 
528  $today = date("Y-m-d");
529 
530  for($i = 1; $i <= $this->daysInMonth; ++$i)
531  {
532 
533  $currentDay = sprintf("%d-%02d-%02d", $this->year, $this->month, $i);
534  $eventsToday = $this->filterEvents($this->events, $currentDay);
535  $content = "";
536 
537  if (count($eventsToday) > 0)
538  {
539  $class = "event";
540  foreach($eventsToday as $event)
541  {
542  $handler = $this->getHandler($event);
543  $content .= $handler->formatSummary($event, $this->tagRowCallbacks);
544  }
545 
546  $link = $i;
547  }
548  else
549  {
550  $class = (($c % 7) == 0 || ($c % 7) == 6) ? "weekend" : "weekday";
551  $link = $i;
552  }
553 
554  if ($currentDay == $today) $class="today";
555 ?>
556  <td class="<?echo $class ?>" data-day="<?php echo $i ?>"><?echo $link ?><? echo $content?></td>
557 <?
558  ++$c;
559  if (($c % 7) == 0) echo "</tr><tr>";
560  }
561 
562  while ($c % 7)
563  {
564 
565 ?><td class="empty">&nbsp;</td>
566 <?
567  $c++;
568  }
569 ?>
570 </tr>
571 </table>
572 </div>
573 <?
574  }
575 
576  function writeScript()
577  {
578  if(!count($this->tagRowCallbacks) || !$this->maxEventsPerDay) return;
579 
580  $options = "{}";
581  if($this->maxEventsPerDay)
582  {
583  $options = "{per_day: {$this->maxEventsPerDay}}";
584  }
585 
586  ob_start();
587  ?>
588  <script type="text/javascript" src="/components/calendar/js/filtering_calendar.js"></script>
589  <script type="text/javascript">
590  window.addEvent('domready', function()
591  {
592  var <?echo $this->id?> = new FilteringCalendar('<?echo $this->id ?>', <?php echo $options ?>);
593  });
594  </script>
595  <?
596  $script = ob_get_contents();
597  ob_end_clean();
598  return $script;
599  }
600 
601  function header($alt = event_display)
602  {
603  $link = $this->rewriteQueryString($this->year, $this->month, $alt);
604  $altTitle = ($alt == event_display) ? "List of Events" : "Calendar";
605 ?>
606 <table style='width: 100%' class="layout">
607  <tr>
608  <td><p><a href="<? echo $link ?>">View <? echo $altTitle ?></a></p></td>
609  <td align="right">
610  <form method="get" action="">
611  <?php
612  $calendar_id = htmlSafe(checkNumeric($_GET["calendar_id"]));
613  if($calendar_id)
614  {
615  echo "<input id='calendar_id' type='hidden' name='calendar_id' value='$calendar_id'/>\n";
616  }
617  ?>
618  <select name="month">
619 <?
620  for($i = 1; $i <= 12; ++$i)
621  {
622  $monthName = date("F", strtotime("$i/1/2007"));
623  option($i, $monthName, $i == $this->month);
624  }
625 ?>
626  </select>&nbsp;<select name="year">
627 <?
628  $endYear = date("Y") + 2;
629  for($i = 2006; $i <= $endYear; ++$i)
630  {
631  option($i, $i, (int)$this->year);
632  }
633 ?>
634  </select>
635  <input type="submit" class="button" value=" GO "/>
636  </form>
637  </td>
638  </tr>
639 </table>
640 <?
641 
642  }
643 
644  /*
645  * Link to the day's events from mini calendar
646  */
647  function formatEventListLink($link, $dLink, $currentDay, $eventBubbleID, $idx)
648  {
649  if($link)
650  {
651  $link .= (preg_match("/\?/", $link)) ? "&" : "?";
652  $link .= "date=$currentDay";
653  }
654  else
655  $link = "#";
656 
657  if($dLink)
658  {
659  if(preg_match("/[\‍(.*?\‍)]$/", $dLink))
660  $dLink = preg_replace("/[\‍(.*?\‍)]$/", ", '$currentDay')", $dLink);
661  else
662  $dLink .= "('$currentDay')";
663  $onclick = "onclick=\"$dLink; return false;\"";
664  }
665 
666  return "<a class='eventlink' href=\"{$link}\" $onclick;
667  onmouseover='showEventBubble(\"$eventBubbleID\", this);' onmouseout='hideEventBubble(\"$eventBubbleID\");'>{$idx}</a>";
668  }
669 
670 
671  function drawEventList()
672  {
673  $this->createEventDialog();
674 
675  $this->calculateMonthRange();
676 
677  $this->header(calendar_display);
678 
679  if (count($this->events) > 0)
680  {
681  foreach($this->events as $event)
682  {
683  $handler = $this->getHandler($event);
684  $content .= $handler->details($event);
685  $content .= "<br>";
686  }
687  echo $content;
688  }
689  else
690  {
691 ?>
692 <p><i>No events have been scheduled for this month.</i></p>
693 <?
694  }
695  }
696 
697 }?>
& nbsp
Definition: index.inc:49
$form
$calendar_id
$start
$view month
$view year
$event
Definition: event_form.inc:46
$handler
Definition: event_form.inc:62
$event_id
Definition: event_form.inc:41
$out
Definition: page.inc:66
$helpTree style
Definition: tree.inc:46
$helpTree width
Definition: tree.inc:45
$form action
Definition: edit.inc:67
$name
Definition: upload.inc:54
Defines the Event class.
Definition: calendar.inc:43
$id
The HTML tag id for the calendar view.
CalendarView($events, $id=null)
$maxEventsPerDay
The maximum number of events to display per day on monthly view.
getHandler($event)
formatEventListLink($link, $dLink, $currentDay, $eventBubbleID, $idx)
rewriteQueryString($year, $month)
$tagRowCallbacks
Array of callbacks for adding extra attributes to each row.
details($event, $param=null)
addFacetTaggingHandler($handler)
Adds a row tagging handler.
header($alt=event_display)
filterEvents($events, $currentDay)
addHandler($class, $handler)
static fireEvent($event, $parameter=null, $mustBeConsumed=false)
Fire an event to all subscribers as detailed in their manifests.
formatFilteringTags($event, $tagRowCallbacks)
formatiCalendar($event)
format an event for sending through email as iCal event.
formatButtons($event)
Called by function details.
details($event)
filter($event, $date)
buildForm($event)
formatSummary($event, $tagRowCallbacks)
summary($event)
Defines the Event class.
Definition: event.inc:43
FakoliException is the base exception class for all Fakoli errors.
Definition: core.inc:53
static using()
Import the datamodels, views and manifest for the specified component(s).
Definition: core.inc:116
static getValue($component, $name)
Retrieve the value of the specified Setting.
Definition: settings.inc:104
Definition: site.inc:40
formatiCalendar($event, $sequence_id=0)
filter($event, $date)
If an event spans over more than 5 days, only show it on the first day.
formatSummary($event, $tagRowCallacks)
formatFilteringTags($event, $tagRowCallbacks)
details($event, $byProgram=false)
Note: we may wish to add someting like this to description:
if(!checkRole("admin")) $c
$sites
Definition: event_edit.inc:83
if(count($sites) > 1) $calendarSelect
Definition: event_edit.inc:92
$desc height
Definition: event_edit.inc:64
$date
Definition: event_list.inc:51
$topicList colspan
Definition: group_form.inc:54
$max
$identifier
$script
text align
Definition: redirects.inc:13
if(array_key_exists("HTTP_IF_MODIFIED_SINCE", $_SERVER)) $content
Definition: styles.css.inc:24