7 * @license http://www.gnu.org/licenses/agpl.html AGPL Version 3
8 * @author Cornelius Weiss <c.weiss@metaways.de>
9 * @copyright Copyright (c) 2007-2013 Metaways Infosystems GmbH (http://www.metaways.de)
13 * json interface for calendar
16 * @subpackage Frontend
18 class Calendar_Frontend_Json extends Tinebase_Frontend_Json_Abstract
25 protected $_applicationName = 'Calendar';
28 * creates an exception instance of a recurring event
30 * NOTE: deleting persistent exceptions is done via a normal delete action
31 * and handled in the controller
33 * @param array $recordData
34 * @param bool $deleteInstance
35 * @param bool $deleteAllFollowing
36 * @param bool $checkBusyConflicts
37 * @return array exception Event | updated baseEvent
39 * @todo replace $_allFollowing param with $range
40 * @deprecated replace with create/update/delete
42 public function createRecurException($recordData, $deleteInstance, $deleteAllFollowing, $checkBusyConflicts = FALSE)
44 $event = new Calendar_Model_Event(array(), TRUE);
45 $event->setFromJsonInUsersTimezone($recordData);
47 /** @noinspection PhpDeprecationInspection */
48 $returnEvent = Calendar_Controller_Event::getInstance()->createRecurException($event, $deleteInstance, $deleteAllFollowing, $checkBusyConflicts);
50 return $this->getEvent($returnEvent->getId());
54 * deletes existing events
57 * @param string $range
60 public function deleteEvents($ids, $range = Calendar_Model_Event::RANGE_THIS)
62 return $this->_delete($ids, Calendar_Controller_Event::getInstance(), array($range));
66 * deletes existing resources
71 public function deleteResources($ids)
73 return $this->_delete($ids, Calendar_Controller_Resource::getInstance());
77 * deletes a recur series
79 * @param array $recordData
82 public function deleteRecurSeries($recordData)
84 $event = new Calendar_Model_Event(array(), TRUE);
85 $event->setFromJsonInUsersTimezone($recordData);
87 Calendar_Controller_Event::getInstance()->deleteRecurSeries($event);
88 return array('success' => true);
92 * Return a single event
95 * @return array record data
97 public function getEvent($id)
99 return $this->_get($id, Calendar_Controller_Event::getInstance());
103 * Returns registry data of the calendar.
105 * @return mixed array 'variable name' => 'data'
107 * @todo move exception handling (no default calender found) to another place?
109 public function getRegistryData()
111 $defaultCalendarId = Tinebase_Core::getPreference('Calendar')->getValue(Calendar_Preference::DEFAULTCALENDAR);
113 $defaultCalendarArray = Tinebase_Container::getInstance()->getContainerById($defaultCalendarId)->toArray();
114 $defaultCalendarArray['account_grants'] = Tinebase_Container::getInstance()->getGrantsOfAccount(Tinebase_Core::getUser(), $defaultCalendarId)->toArray();
115 if ($defaultCalendarArray['type'] != Tinebase_Model_Container::TYPE_SHARED) {
116 $defaultCalendarArray['ownerContact'] = Addressbook_Controller_Contact::getInstance()->getContactByUserId($defaultCalendarArray['owner_id'])->toArray();
118 } catch (Exception $e) {
119 // remove default cal pref
120 Tinebase_Core::getPreference('Calendar')->deleteUserPref(Calendar_Preference::DEFAULTCALENDAR);
121 $defaultCalendarArray = array();
124 $importDefinitions = $this->_getImportDefinitions();
125 $allCalendarResources = Calendar_Controller_Resource::getInstance()->getAll()->toArray();
127 $registryData = array(
128 'defaultContainer' => $defaultCalendarArray,
129 'defaultImportDefinition' => $importDefinitions['default'],
130 'importDefinitions' => $importDefinitions,
131 'calendarResources' => $allCalendarResources
134 return $registryData;
138 * get default addressbook
142 public function getDefaultCalendar()
144 $defaultCalendar = Calendar_Controller_Event::getInstance()->getDefaultCalendar();
145 $defaultCalendarArray = $defaultCalendar->toArray();
146 $defaultCalendarArray['account_grants'] = Tinebase_Container::getInstance()->getGrantsOfAccount(Tinebase_Core::getUser(), $defaultCalendar->getId())->toArray();
147 Tinebase_Core::getLogger()->notice(print_r($defaultCalendar, true));
148 return $defaultCalendarArray;
154 * @param string $tempFileId to import
155 * @param string $definitionId
156 * @param array $importOptions
157 * @param array $clientRecordData
160 public function importEvents($tempFileId, $definitionId, $importOptions, $clientRecordData = array())
162 return $this->_import($tempFileId, $definitionId, $importOptions, $clientRecordData);
166 * creates a scheduled import
168 * @param string $remoteUrl
169 * @param string $interval
170 * @param string $importOptions
173 public function importRemoteEvents($remoteUrl, $interval, $importOptions)
175 // Determine which plugin should be used to import
176 switch ($importOptions['sourceType']) {
177 case 'remote_caldav':
178 $plugin = 'Calendar_Import_CalDAV';
181 $plugin = 'Calendar_Import_Ical';
184 $record = Tinebase_Controller_ScheduledImport::getInstance()->create( new Tinebase_Model_Import(array(
185 'source' => $remoteUrl,
186 'sourcetype' => Tinebase_Model_Import::SOURCETYPE_REMOTE,
187 'interval' => $interval,
188 'options' => array_replace($importOptions, array(
190 'importFileByScheduler' => $importOptions['sourceType'] != 'remote_caldav',
192 'model' => 'Calendar_Model_Event',
193 'application_id' => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
196 $result = $this->_recordToJson($record);
202 * get addressbook import definitions
206 * @todo generalize this
208 protected function _getImportDefinitions()
210 $filter = new Tinebase_Model_ImportExportDefinitionFilter(array(
211 array('field' => 'application_id', 'operator' => 'equals', 'value' => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId()),
212 array('field' => 'type', 'operator' => 'equals', 'value' => 'import'),
215 $definitionConverter = new Tinebase_Convert_ImportExportDefinition_Json();
218 $importDefinitions = Tinebase_ImportExportDefinition::getInstance()->search($filter);
219 $defaultDefinition = $this->_getDefaultImportDefinition($importDefinitions);
221 'results' => $definitionConverter->fromTine20RecordSet($importDefinitions),
222 'totalcount' => count($importDefinitions),
223 'default' => ($defaultDefinition) ? $definitionConverter->fromTine20Model($defaultDefinition) : array(),
225 } catch (Exception $e) {
226 Tinebase_Exception::log($e);
229 'results' => array(),
231 'default' => array(),
240 * get default definition
242 * @param Tinebase_Record_RecordSet $_importDefinitions
243 * @return Tinebase_Model_ImportExportDefinition
245 * @todo generalize this
247 protected function _getDefaultImportDefinition($_importDefinitions)
250 $defaultDefinition = Tinebase_ImportExportDefinition::getInstance()->getByName('cal_import_ical');
251 } catch (Tinebase_Exception_NotFound $tenf) {
252 if (count($_importDefinitions) > 0) {
253 $defaultDefinition = $_importDefinitions->getFirstRecord();
255 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' No import definitions found for Calendar');
256 $defaultDefinition = NULL;
260 return $defaultDefinition;
264 * Return a single resouece
267 * @return array record data
269 public function getResource($id)
271 return $this->_get($id, Calendar_Controller_Resource::getInstance());
275 * Search for events matching given arguments
277 * @param array $filter
278 * @param array $paging
281 public function searchEvents($filter, $paging)
283 $controller = Calendar_Controller_Event::getInstance();
285 $decodedPagination = $this->_prepareParameter($paging);
286 $pagination = new Tinebase_Model_Pagination($decodedPagination);
287 $clientFilter = $filter = $this->_decodeFilter($filter, 'Calendar_Model_EventFilter');
289 // find out if fixed calendars should be used
290 $fixedCalendars = Calendar_Config::getInstance()->get(Calendar_Config::FIXED_CALENDARS);
291 $useFixedCalendars = is_array($fixedCalendars) && ! empty($fixedCalendars);
293 $periodFilter = $filter->getFilter('period');
295 // add period filter per default to prevent endless search
296 if (! $periodFilter) {
297 $periodFilter = $this->_getDefaultPeriodFilter();
298 // periodFilter will be added to fixed filter when using fixed calendars
299 if (! $useFixedCalendars) {
300 $filter->addFilter($periodFilter);
304 // add fixed calendar on demand
305 if ($useFixedCalendars) {
306 $fixed = new Calendar_Model_EventFilter(array(), 'AND');
307 $fixed->addFilter( new Tinebase_Model_Filter_Text('container_id', 'in', $fixedCalendars));
309 $fixed->addFilter($periodFilter);
311 $og = new Calendar_Model_EventFilter(array(), 'OR');
312 $og->addFilterGroup($fixed);
313 $og->addFilterGroup($clientFilter);
315 $filter = new Calendar_Model_EventFilter(array(), 'AND');
316 $filter->addFilterGroup($og);
319 $records = $controller->search($filter, $pagination, FALSE);
321 $result = $this->_multipleRecordsToJson($records, $clientFilter, $pagination);
324 'results' => $result,
325 'totalcount' => count($result),
326 'filter' => $clientFilter->toArray(TRUE),
331 * get default period filter
333 * @return Calendar_Model_PeriodFilter
335 protected function _getDefaultPeriodFilter()
337 $now = Tinebase_DateTime::now()->setTime(0,0,0);
339 $from = $now->getClone()->subMonth(Calendar_Config::getInstance()->get(Calendar_Config::MAX_JSON_DEFAULT_FILTER_PERIOD_FROM, 0));
340 $until = $now->getClone()->addMonth(Calendar_Config::getInstance()->get(Calendar_Config::MAX_JSON_DEFAULT_FILTER_PERIOD_UNTIL, 1));
341 $periodFilter = new Calendar_Model_PeriodFilter(array(
343 'operator' => 'within',
344 'value' => array("from" => $from, "until" => $until)
347 return $periodFilter;
351 * Search for resources matching given arguments
353 * @param array $filter
354 * @param array $paging
357 public function searchResources($filter, $paging)
359 return $this->_search($filter, $paging, Calendar_Controller_Resource::getInstance(), 'Calendar_Model_ResourceFilter', true);
363 * creates/updates an event / recur
365 * @param array $recordData
366 * @param bool $checkBusyConflicts
367 * @param string $range
368 * @return array created/updated event
370 public function saveEvent($recordData, $checkBusyConflicts = FALSE, $range = Calendar_Model_Event::RANGE_THIS)
372 return $this->_save($recordData, Calendar_Controller_Event::getInstance(), 'Event', 'id', array($checkBusyConflicts, $range));
376 * creates/updates a Resource
378 * @param array $recordData
379 * @return array created/updated Resource
381 public function saveResource($recordData)
383 $recordData['grants'] = new Tinebase_Record_RecordSet('Tinebase_Model_Grants', $recordData['grants']);
384 if(array_key_exists ('max_number_of_people', $recordData) && $recordData['max_number_of_people'] == '') {
385 $recordData['max_number_of_people'] = null;
388 return $this->_save($recordData, Calendar_Controller_Resource::getInstance(), 'Resource');
392 * sets attendee status for an attender on the given event
394 * NOTE: for recur events we implicitly create an exceptions on demand
396 * @param array $eventData
397 * @param array $attenderData
398 * @param string $authKey
399 * @return array complete event
401 public function setAttenderStatus($eventData, $attenderData, $authKey)
403 $event = new Calendar_Model_Event($eventData);
404 $attender = new Calendar_Model_Attender($attenderData);
406 Calendar_Controller_Event::getInstance()->attenderStatusUpdate($event, $attender, $authKey);
408 return $this->getEvent($event->getId());
412 * updated a recur series
414 * @param array $recordData
415 * @param bool $checkBusyConflicts
416 * @noparamyet JSONstring $returnPeriod NOT IMPLEMENTED YET
419 public function updateRecurSeries($recordData, $checkBusyConflicts = FALSE /*, $returnPeriod*/)
421 $recurInstance = new Calendar_Model_Event(array(), TRUE);
422 $recurInstance->setFromJsonInUsersTimezone($recordData);
424 //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(print_r($recurInstance->toArray(), true));
426 $baseEvent = Calendar_Controller_Event::getInstance()->updateRecurSeries($recurInstance, $checkBusyConflicts);
428 return $this->getEvent($baseEvent->getId());
432 * prepares an iMIP (RFC 6047) Message
434 * @param array|Calendar_Model_iMIP $iMIP
435 * @return array prepared iMIP part
437 public function iMIPPrepare($iMIP)
439 $iMIPMessage = $iMIP instanceof Calendar_Model_iMIP ? $iMIP : new Calendar_Model_iMIP($iMIP);
440 $iMIPFrontend = new Calendar_Frontend_iMIP();
442 $iMIPMessage->preconditionsChecked = FALSE;
443 $iMIPFrontend->prepareComponent($iMIPMessage);
444 $iMIPMessage->setTimezone(Tinebase_Core::getUserTimezone());
445 return $iMIPMessage->toArray();
449 * process an iMIP (RFC 6047) Message
452 * @param string $status
453 * @return array prepared iMIP part
455 public function iMIPProcess($iMIP, $status=null)
457 $iMIPMessage = new Calendar_Model_iMIP($iMIP);
458 $iMIPFrontend = new Calendar_Frontend_iMIP();
460 $iMIPFrontend->process($iMIPMessage, $status);
462 return $this->iMIPPrepare($iMIPMessage);
466 * @param array $_attendee
467 * @param array $_periods
468 * @param array $_ignoreUIDs
471 public function getFreeBusyInfo($_attendee, $_periods, $_ignoreUIDs = array())
473 $attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', $_attendee);
474 $periods = new Calendar_Model_EventFilter($_periods);
476 $fbInfo = Calendar_Controller_Event::getInstance()->getFreeBusyInfo($periods, $attendee, $_ignoreUIDs);
478 return $fbInfo->toArray();
482 * @param array $_filter
483 * @param array $_paging
484 * @param array $_periods
485 * @param array $_ignoreUIDs
488 public function searchAllAttendeeTypes($_filter, $_paging, $_periods, $_ignoreUIDs)
490 $addressBookFE = new Addressbook_Frontend_Json();
491 $contactFilter = array(array('condition' => 'OR', 'filters' => array(
493 array('field' => 'path', 'operator' => 'contains', 'value' => $_filter[0]['value'])
495 $groupFilter = $contactFilter;
496 $groupFilter[] = array('field' => 'type', 'operator' => 'contains', 'value' => 'group');
497 $contactPaging = $_paging;
498 $contactPaging['sort'] = 'type';
501 Calendar_Model_Attender::USERTYPE_USER => $addressBookFE->searchContacts($contactFilter, $contactPaging),
502 Calendar_Model_Attender::USERTYPE_GROUP => $addressBookFE->searchLists($groupFilter, $_paging),
503 Calendar_Model_Attender::USERTYPE_RESOURCE => $this->searchResources($_filter, $_paging)
507 foreach ($result as $type => $res) {
508 foreach ($res['results'] as $r) {
510 'user_id' => $r['id'],
516 $result['freeBusyInfo'] = $this->getFreeBusyInfo($attendee, $_periods, $_ignoreUIDs);