73656b44f36a7b833190140e020307c6cd9449ac

Author: Nate Abele

Date: 2009-01-22 18:38:11 -0500

Renaming CalendarController to EventsController, adding calendar routes, and implementing JSON support

diff --git a/app_controller.php b/app_controller.php index f832419..5deb5c2 100755 --- a/app_controller.php +++ b/app_controller.php @@ -1,7 +1,9 @@ <?php class AppController extends Controller { + var $helpers = array('Html', 'Javascript', 'Ajax', 'Cal','PrintMonth'); + } ?> \ No newline at end of file diff --git a/config/routes.php b/config/routes.php index 8ff5478..c9ca60b 100755 --- a/config/routes.php +++ b/config/routes.php @@ -1,6 +1,20 @@ <?php -Router::connect ('/', array('controller' => 'calendar', 'action' => 'index')); -Router::connect ('/import', array('controller' => 'calendar', 'action' => 'import')); +Router::parseExtensions('rss', 'json'); + +Router::connect('/', array('controller' => 'events', 'action' => 'index')); +Router::connect('/import', array('controller' => 'calendar', 'action' => 'import')); + +Router::connect( + '/:year/:month/:day', + array('controller' => 'events', 'action' => 'index', 'month' => null, 'day' => null), + array('year' => $Year, 'month' => $Month, 'day' => $Day) +); + +Router::connect( + '/events/:year/:month/:day', + array('controller' => 'events', 'action' => 'index', 'month' => null, 'day' => null), + array('year' => $Year, 'month' => $Month, 'day' => $Day) +); ?> \ No newline at end of file diff --git a/controllers/events_controller.php b/controllers/events_controller.php new file mode 100755 index 0000000..2e17ea0 --- /dev/null +++ b/controllers/events_controller.php @@ -0,0 +1,251 @@ +<?php + +class EventsController extends AppController { + + var $components = array('RequestHandler', 'ICal', 'Csv'); + + function beforeFilter() { + $this->RequestHandler->setContent(array( + 'ics' => 'text/calendar', + 'csv' => 'application/octet-stream' + )); + $this->set('isAjax', $this->RequestHandler->isAjax()); + + // This is debug code, kill it: + $this->Session->destroy(); + } + + function index ($year = null, $month = null, $day = null) { + if ($year == null && $this->Session->check('Calendar.View')) { + extract($this->Session->read('Calendar.View')); + (intval($year) == 1969) ? $year = $month = $day = null : null; + } + + if (empty($year)) { + $year = date('Y'); + } + if ($month == null) { + $month = date('m'); + } + if (intval($day) < 1) { + $day = null; + } + + $tmpDay = $day == null ? 1 : $day; + $stamp = strtotime($year.'-'.$month.'-'.$tmpDay); + $start = date('Y-m-d', $stamp); + $end = $start; + + if ($day == null) { + $end = date( + 'Y-m-d', strtotime('+1 month', $stamp) - (strtotime('+1 day') - strtotime('now')) + ); + } + + $events = $this->Event->find('all', array('conditions' => array( + 'Event.start <=' => $end . ' 23:59:59', + 'Event.end >=' => $start, + ))); + + $this->Session->write('Calendar.View', compact('year', 'month', 'day')); + + $isDay = ($day != null); + $day = $tmpDay; + $events = array_values($events); + $this->set(compact('isDay', 'events', 'year', 'month', 'day')); + } + + function view($id = null) { + if ($this->RequestHandler->prefers('json')) { + $event = $this->Event->findById($id); + $this->set(compact('event')); + return; + } + $this->setAction('index'); + } + + function edit($id = null) { + $this->data = $this->Event->findById($id); + + $this->data['Event'] = array_merge($this->data['Event'], array( + 'start_Date' => date('m/d/Y', strtotime($this->data['Event']['start'])), + 'start_Time' => date('g:i a', strtotime($this->data['Event']['start'])), + 'end_Date' => date('m/d/Y', strtotime($this->data['Event']['end'])), + 'end_Time' => date('g:i a', strtotime($this->data['Event']['end'])) + )); + } + + function save() { + if (!empty($this->params['form'])) { + $this->Event->create(); + + if (!empty($this->data)) { + $this->Event->save($this->data); + + if (!isset($this->data['Event']['id']) || empty($this->data['Event']['id'])) { + $id = $this->Event->id; + } else { + $data = $this->Event->findById($this->Event->id); + $data = $data['Event']; + } + } else { + // Serialized JavaScript data + $this->Event->create(); + $this->Event->save($this->params['form']); + $data = $this->Event->findById($this->Event->id); + $data['Event']['__id'] = $this->params['form']['__id']; + $data = $data['Event']; + } + } + $this->set(compact('data', 'id')); + } + + function delete($id = null) { + $this->Event->del($id); + } + + function import() { + if (!empty($this->data)) { + + $events = array(); + + if ($this->data['Event']['file']['type'] == 'text/calendar') { + // Parse as iCal + $data = $this->ICal->read($this->data['Event']['file']['tmp_name']); + if (!is_array($data) || empty($data) || !isset($data['Calendar'])) { + $this->Session->setFlash('Invalid import file'); + return; + } + $data = $data['Calendar']; + + if (!isset($data[0])) { + $data = array($data); + } + + foreach ($data as $cal) { + + if (isset($cal['Event'][0])) { + // Calendar contains + foreach($cal['Event'] as $event) { + $events[] = $this->__mapEvent($event); + } + } elseif (isset($cal['Event'])) { + $events[] = $this->__mapEvent($cal['Event']); + } + } + } else { + // Parse as CSV + $data = $this->Csv->read($this->data['Event']['file']['tmp_name']); + foreach ($data as $e) { + $events[] = $this->__mapEvent2($e); + } + } + + foreach ($events as $e) { + $this->Event->create(); + $this->Event->save($e); + } + + $this->Session->setFlash('Import success'); + $this->redirect(array('action' => 'index')); + } + } + + function __mapEvent($event) { + $data = array('sharing' => 0, 'repeat' => 0); + + if (isset($event['start_date'][0])) { + $event['start_date'] = $event['start_date'][0]; + } + if (isset($event['end_date'][0])) { + $event['end_date'] = $event['end_date'][0]; + } + + $data['start'] = $event['start_date']; + $data['title'] = $event['summary']; + + if (isset($event['duration'])) { + $offset = $this->__duration($event['duration']); + $data['end'] = date('Y-m-d H:i:s', strtotime($data['start'].' + '.$offset)); + } elseif (isset($event['end_date'])) { + $data['end'] = $event['end_date']; + } + return $data; + } + + function __mapEvent2($event) { + return array( + 'start' => date('Y-m-d H:i:s', strtotime( + $event['Start Date'] . ' ' . $event['Start Time'] + )), + 'end' => date('Y-m-d H:i:s', strtotime( + $event['End Date'] . ' ' . $event['End Time'] + )), + 'title' => $event['Subject'], + 'notes' => $event['Description'], + 'location' => $event['Location'], + 'repeat' => 0, + ); + } + + function export() { + // $this->RequestHandler->respondAs('text/calendar', array( + // 'attachment' => 'cal-export.ics' + // )); + + $this->RequestHandler->respondAs('application/octet-stream', array( + 'attachment' => 'cal-export.csv' + )); + + $data = array(); + $events = $this->Event->find('all'); + + foreach ($events as $event) { + $data[] = array( + $event['title'], + date('m/d/Y', strtotime($event['Event']['start'])), + date('H:i:s A', strtotime($event['Event']['start'])), + date('m/d/Y', strtotime($event['Event']['end'])), + date('H:i:s A', strtotime($event['Event']['end'])), + 'False', + 'False', + date('m/d/Y', strtotime($event['Event']['start'])), + date('H:i:s A', strtotime($event['Event']['start'])), + '', + '', + '', + '', + '', + '', + $event['Event']['notes'], + $event['Event']['location'], + '', + 'Normal', + $event['Event']['sharing'] == 2 ? 'False' : 'True', + 'Normal', + 2 + ); + } + + // iCal + /*foreach ($events as $event) { + $event = $event['Event']; + $tmp = array(); + $tmp['DTSTART;TZID=US/Eastern'] = date('Ymd', strtotime($event['start'])).'T'.date('His', strtotime($event['start'])); + $tmp['SUMMARY'] = $event['title']; + $tmp['DURATION'] = $this->ICal->__putDuration($event['start'], $event['end']); + $tmp['DESCRIPTION'] = r("\n", '\n', $event['notes']); + $tmp['DTSTAMP;TZID=US/Eastern'] = date('Ymd', strtotime($event['created'])).'T'.date('His', strtotime($event['created'])); + + if ($event['repeat'] && $event['repeat'] != 0) { + $tmp['RRULE'] = 'FREQ=' . ($event['repeat'] == 1 ? 'WEEKLY' : 'MONTHLY'); + } + $data[] = $tmp; + }*/ + + $this->set(compact('data')); + $this->render('outlook_export', 'ajax'); + } +} + +?> \ No newline at end of file diff --git a/views/events/csv/index.ctp b/views/events/csv/index.ctp new file mode 100755 index 0000000..bc1aea4 --- /dev/null +++ b/views/events/csv/index.ctp @@ -0,0 +1,8 @@ +"Subject","Start Date","Start Time","End Date","End Time","All day event","Reminder on/off","Reminder Date","Reminder Time","Meeting Organizer","Required Attendees","Optional Attendees","Meeting Resources","Billing Information","Categories","Description","Location","Mileage","Priority","Private","Sensitivity","Show time as" +<?php + +foreach ($data as $item) { + e('"' . join('","', $item) . '"' . "\n"); +} + +?> \ No newline at end of file diff --git a/views/events/edit.ctp b/views/events/edit.ctp new file mode 100755 index 0000000..f4ff753 --- /dev/null +++ b/views/events/edit.ctp @@ -0,0 +1,69 @@ +<div class="block"> + + <?php echo $form->create('Event', 'post', array('id' => 'EventFormData', 'onsubmit' => 'return true;')); ?> + <?php echo $html->hidden('Event/id', array('id' => 'EventId')); ?> + + <table width='100%' border='0' cellpadding='4'> + <tr><td class="win_title" colspan='2' valign="top"> + <div class='window_buttons'> + <?php echo $ajax->link('Save', '/calendar/save', array( + 'id' => 'eventFormSave', + 'complete' => 'Effect.SlideUp("eventForm");' . "\n" . 'Events.update(request.responseText);', + 'with' => 'Form.serialize("EventFormData")' + )); ?> + <?php echo $html->link('Cancel', 'javascript:void(0);', array( + 'id' => 'eventFormCancel', 'onclick' => "Effect.SlideUp('eventForm');" + )); ?> + </div> + <label>Event Edit Form</label> + </td></tr> + <tr><td class='labeltd'> + <label for="EventTitle">Title</label></td> + <td><?php echo $form->input('Event/title', array('class' => 'input_title')); ?><br /> + </td></tr> + <tr><td class='labeltd'> + <label for="EventStartDate">Start</label> + </td><td> + <?php echo $html->input('Event/start_Date', array('class' => 'date')); ?> + <label for="EventStartTime" class="label_small">@</label> + <?php echo $html->input('Event/start_Time', array('class' => 'time')); ?><br /> + </td></tr> + <tr><td class='labeltd'> + <label form="EventEndDate">End</label> + </td><td> + <?php echo $html->input('Event/end_Date', array('class' => 'date')); ?> + <label for="EventEndTime" class="label_small">@</label> + <?php echo $html->input('Event/end_Time', array('class' => 'time')); ?> + </tr><!--<tr><td class="labeltd"> + <label form="EventRepeat">Repeat</label> + </td><td> + <?php echo $html->selectTag('Event/repeat', array('don\'t repeat', 'weekly', 'monthly'), null, array('id' => 'EventRepeat'), null, false); ?> + </tr>--><tr><td class="labeltd"> + <label form="EventLocation">Location</label> + </td><td> + <?php echo $html->input('Event/location'); ?> + </tr><tr><td class="labeltd"> + <label form="EventNotes">Notes</label> + </td><td> + <?php echo $html->textarea('Event/notes'); ?> + </td></tr> + <tr><td class='labeltd'> + <label form="EventEndDate">Sharing</label> + </td><td> + <?php echo $html->selectTag('Event/sharing', array('Private (only for me)', 'Shared (selected users)', 'Public (everyone can see)', 'Public Editable (everyone can update)'), null, null, null, false); ?> + <?php echo $javascript->event('EventSharing', 'change', '$F("EventSharing") == 1 ? $("EventShare").show() : $("EventShare").hide();'); ?> + </td></tr> + <tr><td colspan="2"> + <div id="EventShare"> + Share with users (Ctrl-click to select multiple):<br /> + <?php echo $html->selectTag('SharedUsers/SharedUsers', $users, $this->data['SharedUsers'], array('id' => 'SharedUsers', 'multiple' => true), null, false); ?> + + <?php if ($this->data['Event']['sharing'] != 1) { ?> + <?php echo $javascript->codeBlock('$("EventShare").hide();'); ?> + <?php } ?> + </div> + </td></tr> + </table> + </form> + +</div> diff --git a/views/events/icaldbg.ctp b/views/events/icaldbg.ctp new file mode 100755 index 0000000..764d124 --- /dev/null +++ b/views/events/icaldbg.ctp @@ -0,0 +1,19 @@ +<div id="eventForm"> + + <?php echo $html->formTag('/calendar/icaldbg', 'file'); ?> + + <table style='width:100%;' border='0' cellpadding='10'> + <tr><td class='labeltd' style='text-align:left;background-color:#333;color:#ccc;padding:10px;' colspan='2'> + <label>Import Events</label> + <span style='float:right;'><?php echo $html->link('Cancel', '/', array('id' => 'eventFormCancel')); ?></span> + </td></tr> + <tr><td class='labeltd'> + <label for="EventTitle">File</label></td> + <td><?php echo $html->file('Event/file'); ?> + </td></tr> + <tr><td colspan='2' align='right'> + <?php echo $html->submit('Import'); ?> + </td></tr> + </table> + </form> +</div> \ No newline at end of file diff --git a/views/events/ics/index.ctp b/views/events/ics/index.ctp new file mode 100755 index 0000000..65a8e28 --- /dev/null +++ b/views/events/ics/index.ctp @@ -0,0 +1,37 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sage Systems//Calendar 2.0//EN +X-WR-TIMEZONE:US/Eastern +CALSCALE:GREGORIAN +METHOD:PUBLISH +BEGIN:VTIMEZONE +TZID:US/Eastern +LAST-MODIFIED:20060515T203558Z +BEGIN:DAYLIGHT +DTSTART:20050403T070000 +TZOFFSETTO:-0400 +TZOFFSETFROM:+0000 +TZNAME:EDT +END:DAYLIGHT +BEGIN:STANDARD +DTSTART:20051030T020000 +TZOFFSETTO:-0500 +TZOFFSETFROM:-0400 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:20060402T010000 +TZOFFSETTO:-0400 +TZOFFSETFROM:-0500 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +<?php foreach ($data as $item) { ?> +BEGIN:VEVENT +<?php foreach ($item as $key => $val) { ?> +<?php echo $key; ?>:<?php echo $val; ?> + +<?php } ?> +END:VEVENT +<?php } ?> +END:VCALENDAR diff --git a/views/events/import.ctp b/views/events/import.ctp new file mode 100755 index 0000000..bcb4e41 --- /dev/null +++ b/views/events/import.ctp @@ -0,0 +1,19 @@ +<?php echo $html->formTag('/calendar/import', 'file'); ?> + + <table style='width:100%;' border='0' cellpadding='10'> + <tr><td class='win_title' colspan='2'> + <span class='window_buttons'> + <?php echo $html->link('Cancel', 'javascript:void(0)', array('id' => 'importFormCancel')); ?> + </span> + <label>Import Events</label> + </td></tr> + <tr><td class='labeltd'> + <label for="EventTitle">File</label></td> + <td><?php echo $html->file('Event/file'); ?> + </td></tr> + <tr><td colspan='2' align='right'> + <?php echo $html->submit('Import'); ?> + </td></tr> + </table> +</form> +<?php echo $javascript->event('$("importFormCancel")', 'click', 'Effect.SlideUp("eventForm");'); ?> \ No newline at end of file diff --git a/views/events/index.ctp b/views/events/index.ctp new file mode 100755 index 0000000..aec6a10 --- /dev/null +++ b/views/events/index.ctp @@ -0,0 +1,126 @@ +<?php $javascript->cacheEvents(); ?> + +<?php if(!$isAjax) { ?> + +<div id="calLoader" style="display: none;"> + <?php echo $html->image('cal/spinner.gif'); ?> +</div> + +<div style="display: box;float:left;"> +<table id='tools'> + <tr> + <td> + <div class="tool_button" style="padding-left:5px;padding-right:5px;"> + <?php echo $html->image('cal/spacer.gif', array( + 'alt' => 'spacer','height'=>'20','width'=>'5' + )); ?> + </div> + </td><td> + <div id="btnImport" class="tool_button"> + <?php echo $html->image('cal/btn_import.gif', array('alt' => 'Import')); ?> + </div> + Import + </td><td> + <div id="btnExport" class="tool_button"> + <?php echo $html->image('cal/btn_export.gif', array('alt' => 'Export')); ?> + </div> + Export + </td><td> + <div class="tool_button" style="padding-left:5px;padding-right:5px;"> + <?php echo $html->image('cal/div.gif', array( + 'alt' => 'spacer','height' => '20', 'width' => '5' + )); ?> + </div> + </td><td> + <div id="btnPrint" class="tool_button"> + <?php echo $html->image('cal/btn_print.gif', array('alt' => 'Print')); ?> + </div> + Print + </td><td> + <div class="tool_button" style="padding-left:5px;padding-right:5px;"> + <?php echo $html->image('cal/spacer.gif', array( + 'alt' => 'spacer', 'height' => '20', 'width' => '5' + )); ?> + </div> + </td> + </tr> +</table> +</div> + +<div id="toolbar"> + + <div id="eventTools" style="display: none;"> + <span id="btnEdit" class="tool_button"> + <?php echo $html->image('cal/btn_edit.gif', array('alt' => 'Edit Event')); ?> + </span> + <span id="btnDelete" class="tool_button"> + <?php echo $html->image('cal/btn_delete.gif', array('alt' => 'Delete Event')); ?> + </span> + </div> + + <div id="dayTools" style="display: none;"> + <span id="btnAdd" class="tool_button"> + <?php echo $html->image('cal/btn_add.gif', array('alt' => 'Add Event')); ?> + </span> + <span id="btnZoom" class="tool_button"> + <?php echo $html->image('cal/btn_zoom.gif', array('alt' => 'Day View')); ?> + </span> + </div> +</div> + +<?php $javascript->event('$("btnEdit")', 'click', 'Events.edit(Calendar.Events.selected);'); ?> +<?php $javascript->event('$("btnDelete")', 'click', 'Events.remove(Calendar.Events.selected);'); ?> +<?php $javascript->event('$("btnAdd")', 'click', 'Events.add(Calendar.selected)'); ?> +<?php $javascript->event('$("btnZoom")', 'click', 'Events.view(Calendar.selected);'); ?> +<?php $javascript->event('$("btnImport")', 'click', $ajax->remoteFunction(array( + 'url' => '/calendar/import', + 'update' => 'eventForm', + 'complete' => 'Effect.SlideDown("eventForm");' +))); ?> +<?php $javascript->event('$("btnExport")', 'click', 'window.location = "'.$this->base.'/calendar/export";'); ?> +<?php $javascript->event('$("btnPrint")', 'click', $ajax->remoteFunction(array( + 'url' => '/calendar/printerForm', + 'update' => 'eventForm', + 'complete' => 'Effect.SlideDown("eventForm");' +))); ?> +<div id="eventForm" style="display: none;"></div> +<div id="log" style="display: none; line-height: 16px; position: absolute; left: 10px; top: 80%; font-size: 12px; width: 75%; height: 500px; overflow: auto; border: 1px solid #333333;"></div> + +<?php } ?> + +<?php if(!$isDay) { ?> + <?php $cal->init($month, $year); ?> + <?php echo $cal->draw('calendar', 'month', true, !$isAjax); ?> + + <?php // Set up JavaScript bindings between Calendar object and UI ?> + <?php echo $javascript->codeBlock('Object.extend(Calendar, { base : "'.$this->base.'", year : '.$year.', month : '.$month.' });'); ?> + <?php echo $javascript->codeBlock('$("tools").show();'); ?> +<?php } else { + echo $this->element('day'); +} ?> + +<?php echo $javascript->codeBlock('Calendar.bind("calendar");'); ?> +<?php echo $javascript->object($events, true, 'events = ', ';'); ?> + +<?php echo $javascript->link('bindings'); ?> +<script type="text/javascript"> + + Calendar.Events.click = Events.click; + Calendar.loading(true); + Calendar.Events.items = []; + + z = 0; + while (typeof events[z] != 'undefined') { + Calendar.Events.add(events[z]); + z++; + } + + Calendar.Events.select(null); + setTimeout('Calendar.loading(false);', 1); + Calendar.resize(); + + Event.observe(document, 'click', function(e) { Element.hide('toolbar'); }, true); + Element.hide('toolbar'); +</script> + +<?php echo $javascript->writeEvents(); ?> \ No newline at end of file diff --git a/views/events/json/index.ctp b/views/events/json/index.ctp new file mode 100644 index 0000000..bab30ca --- /dev/null +++ b/views/events/json/index.ctp @@ -0,0 +1 @@ +<?php echo $javascript->object($events); ?> \ No newline at end of file diff --git a/views/events/json/view.ctp b/views/events/json/view.ctp new file mode 100644 index 0000000..d58581f --- /dev/null +++ b/views/events/json/view.ctp @@ -0,0 +1 @@ +<?php echo $javascript->object($event); ?> \ No newline at end of file diff --git a/views/events/save.ctp b/views/events/save.ctp new file mode 100755 index 0000000..192691d --- /dev/null +++ b/views/events/save.ctp @@ -0,0 +1,9 @@ +<?php + +if (isset($data) && is_array($data)) { + e($javascript->object($data) . ' //'); // "//" for debug timestamp at the end +} elseif (isset($id)) { + e($id); +} + +?> \ No newline at end of file diff --git a/views/events/share.ctp b/views/events/share.ctp new file mode 100755 index 0000000..2430480 --- /dev/null +++ b/views/events/share.ctp @@ -0,0 +1,14 @@ +<ul class="user_list"> + <?php foreach($users as $user) { ?> + <li><?php echo $user['full_name']; ?></li> + <?php } ?> +</ul> + +<div style="padding: 2px;"> + Add a user <?php echo $html->selectTag('Event/share', $userList, null, array('id' => 'EventShare')); ?> + <?php echo $ajax->observeField('EventShare', array( + 'url' => '/calendar/share', + 'update' => 'Shared', + 'with' => 'Form.serialize("EventFormData")' + )); ?> +</div> \ No newline at end of file diff --git a/views/layouts/json/default.ctp b/views/layouts/json/default.ctp new file mode 100644 index 0000000..3f29013 --- /dev/null +++ b/views/layouts/json/default.ctp @@ -0,0 +1 @@ +<?php echo $content_for_layout; ?> \ No newline at end of file