finalize Calendar_Frontend_Json::searchAttendee
[tine20] / tine20 / Calendar / Frontend / Json.php
1 <?php
2 /**
3  * Tine 2.0
4  * 
5  * @package     Calendar
6  * @subpackage  Frontend
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)
10  */
11
12 /**
13  * json interface for calendar
14  * 
15  * @package     Calendar
16  * @subpackage  Frontend
17  */
18 class Calendar_Frontend_Json extends Tinebase_Frontend_Json_Abstract
19 {
20     /**
21      * app name
22      * 
23      * @var string
24      */
25     protected $_applicationName = 'Calendar';
26     
27     /**
28      * creates an exception instance of a recurring event
29      *
30      * NOTE: deleting persistent exceptions is done via a normal delete action
31      *       and handled in the controller
32      * 
33      * @param  array       $recordData
34      * @param  bool        $deleteInstance
35      * @param  bool        $deleteAllFollowing
36      * @param  bool        $checkBusyConflicts
37      * @return array       exception Event | updated baseEvent
38      * 
39      * @todo replace $_allFollowing param with $range
40      * @deprecated replace with create/update/delete
41      */
42     public function createRecurException($recordData, $deleteInstance, $deleteAllFollowing, $checkBusyConflicts = FALSE)
43     {
44         $event = new Calendar_Model_Event(array(), TRUE);
45         $event->setFromJsonInUsersTimezone($recordData);
46
47         /** @noinspection PhpDeprecationInspection */
48         $returnEvent = Calendar_Controller_Event::getInstance()->createRecurException($event, $deleteInstance, $deleteAllFollowing, $checkBusyConflicts);
49         
50         return $this->getEvent($returnEvent->getId());
51     }
52     
53     /**
54      * deletes existing events
55      *
56      * @param array $ids
57      * @param string $range
58      * @return string
59      */
60     public function deleteEvents($ids, $range = Calendar_Model_Event::RANGE_THIS)
61     {
62         return $this->_delete($ids, Calendar_Controller_Event::getInstance(), array($range));
63     }
64     
65     /**
66      * deletes existing resources
67      *
68      * @param array $ids
69      * @return string
70      */
71     public function deleteResources($ids)
72     {
73         return $this->_delete($ids, Calendar_Controller_Resource::getInstance());
74     }
75     
76     /**
77      * deletes a recur series
78      *
79      * @param  array $recordData
80      * @return array
81      */
82     public function deleteRecurSeries($recordData)
83     {
84         $event = new Calendar_Model_Event(array(), TRUE);
85         $event->setFromJsonInUsersTimezone($recordData);
86         
87         Calendar_Controller_Event::getInstance()->deleteRecurSeries($event);
88         return array('success' => true);
89     }
90     
91     /**
92      * Return a single event
93      *
94      * @param   string $id
95      * @return  array record data
96      */
97     public function getEvent($id)
98     {
99         return $this->_get($id, Calendar_Controller_Event::getInstance());
100     }
101     
102     /**
103      * Returns registry data of the calendar.
104      *
105      * @return mixed array 'variable name' => 'data'
106      * 
107      * @todo move exception handling (no default calender found) to another place?
108      */
109     public function getRegistryData()
110     {
111         $defaultCalendarId = Tinebase_Core::getPreference('Calendar')->getValue(Calendar_Preference::DEFAULTCALENDAR);
112         try {
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();
117             }
118         } catch (Exception $e) {
119             // remove default cal pref
120             Tinebase_Core::getPreference('Calendar')->deleteUserPref(Calendar_Preference::DEFAULTCALENDAR);
121             $defaultCalendarArray = array();
122         }
123         
124         $importDefinitions = $this->_getImportDefinitions();
125         $allCalendarResources = Calendar_Controller_Resource::getInstance()->getAll()->toArray();
126         
127         $registryData = array(
128             'defaultContainer'          => $defaultCalendarArray,
129             'defaultImportDefinition'   => $importDefinitions['default'],
130             'importDefinitions'         => $importDefinitions,
131             'calendarResources'         => $allCalendarResources
132         );
133         
134         return $registryData;
135     }
136     
137     /**
138      * get default addressbook
139      * 
140      * @return array
141      */
142     public function getDefaultCalendar() 
143    {
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;
149     }
150     
151     /**
152      * import contacts
153      * 
154      * @param string $tempFileId to import
155      * @param string $definitionId
156      * @param array $importOptions
157      * @param array $clientRecordData
158      * @return array
159      */
160     public function importEvents($tempFileId, $definitionId, $importOptions, $clientRecordData = array())
161     {
162         return $this->_import($tempFileId, $definitionId, $importOptions, $clientRecordData);
163     }
164     
165     /**
166      * creates a scheduled import
167      * 
168      * @param string $remoteUrl
169      * @param string $interval
170      * @param string $importOptions
171      * @return array
172      */
173     public function importRemoteEvents($remoteUrl, $interval, $importOptions)
174     {
175         // Determine which plugin should be used to import
176         switch ($importOptions['sourceType']) {
177             case 'remote_caldav':
178                 $plugin = 'Calendar_Import_CalDAV';
179                 break;
180             default:
181                 $plugin = 'Calendar_Import_Ical';
182         }
183
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(
189                 'plugin' => $plugin,
190                 'importFileByScheduler' => $importOptions['sourceType'] != 'remote_caldav',
191             )),
192             'model'             => 'Calendar_Model_Event',
193             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
194         ), true));
195
196         $result = $this->_recordToJson($record);
197
198         return $result;
199     }
200     
201     /**
202      * get addressbook import definitions
203      * 
204      * @return array
205      * 
206      * @todo generalize this
207      */
208     protected function _getImportDefinitions()
209     {
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'),
213         ));
214         
215         $definitionConverter = new Tinebase_Convert_ImportExportDefinition_Json();
216         
217         try {
218             $importDefinitions = Tinebase_ImportExportDefinition::getInstance()->search($filter);
219             $defaultDefinition = $this->_getDefaultImportDefinition($importDefinitions);
220             $result = array(
221                 'results'               => $definitionConverter->fromTine20RecordSet($importDefinitions),
222                 'totalcount'            => count($importDefinitions),
223                 'default'               => ($defaultDefinition) ? $definitionConverter->fromTine20Model($defaultDefinition) : array(),
224             );
225         } catch (Exception $e) {
226             Tinebase_Exception::log($e);
227             $result = array(
228                 array(
229                     'results'               => array(),
230                     'totalcount'            => 0,
231                     'default'               => array(),
232                 )
233             );
234         }
235         
236         return $result;
237     }
238     
239     /**
240      * get default definition
241      * 
242      * @param Tinebase_Record_RecordSet $_importDefinitions
243      * @return Tinebase_Model_ImportExportDefinition
244      * 
245      * @todo generalize this
246      */
247     protected function _getDefaultImportDefinition($_importDefinitions)
248     {
249         try {
250             $defaultDefinition = Tinebase_ImportExportDefinition::getInstance()->getByName('cal_import_ical');
251         } catch (Tinebase_Exception_NotFound $tenf) {
252             if (count($_importDefinitions) > 0) {
253                 $defaultDefinition = $_importDefinitions->getFirstRecord();
254             } else {
255                 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' No import definitions found for Calendar');
256                 $defaultDefinition = NULL;
257             }
258         }
259         
260         return $defaultDefinition;
261     }
262     
263     /**
264      * Return a single resouece
265      *
266      * @param   string $id
267      * @return  array record data
268      */
269     public function getResource($id)
270     {
271         return $this->_get($id, Calendar_Controller_Resource::getInstance());
272     }
273     
274     /**
275      * Search for events matching given arguments
276      *
277      * @param  array $filter
278      * @param  array $paging
279      * @return array
280      */
281     public function searchEvents($filter, $paging)
282     {
283         $controller = Calendar_Controller_Event::getInstance();
284         
285         $decodedPagination = $this->_prepareParameter($paging);
286         $pagination = new Tinebase_Model_Pagination($decodedPagination);
287         $clientFilter = $filter = $this->_decodeFilter($filter, 'Calendar_Model_EventFilter');
288
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);
292         
293         $periodFilter = $filter->getFilter('period');
294         
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);
301             }
302         }
303         
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));
308             
309             $fixed->addFilter($periodFilter);
310             
311             $og = new Calendar_Model_EventFilter(array(), 'OR');
312             $og->addFilterGroup($fixed);
313             $og->addFilterGroup($clientFilter);
314             
315             $filter = new Calendar_Model_EventFilter(array(), 'AND');
316             $filter->addFilterGroup($og);
317         }
318         
319         $records = $controller->search($filter, $pagination, FALSE);
320         
321         $result = $this->_multipleRecordsToJson($records, $clientFilter, $pagination);
322         
323         return array(
324             'results'       => $result,
325             'totalcount'    => count($result),
326             'filter'        => $clientFilter->toArray(TRUE),
327         );
328     }
329     
330     /**
331      * get default period filter
332      * 
333      * @return Calendar_Model_PeriodFilter
334      */
335     protected function _getDefaultPeriodFilter()
336     {
337         $now = Tinebase_DateTime::now()->setTime(0,0,0);
338         
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(
342             'field' => 'period',
343             'operator' => 'within',
344             'value' => array("from" => $from, "until" => $until)
345         ));
346         
347         return $periodFilter;
348     }
349     
350     /**
351      * Search for resources matching given arguments
352      *
353      * @param  array $filter
354      * @param  array $paging
355      * @return array
356      */
357     public function searchResources($filter, $paging)
358     {
359         return $this->_search($filter, $paging, Calendar_Controller_Resource::getInstance(), 'Calendar_Model_ResourceFilter', true);
360     }
361     
362     /**
363      * creates/updates an event / recur
364      *
365      * @param   array   $recordData
366      * @param   bool    $checkBusyConflicts
367      * @param   string  $range
368      * @return  array   created/updated event
369      */
370     public function saveEvent($recordData, $checkBusyConflicts = FALSE, $range = Calendar_Model_Event::RANGE_THIS)
371     {
372         return $this->_save($recordData, Calendar_Controller_Event::getInstance(), 'Event', 'id', array($checkBusyConflicts, $range));
373     }
374     
375     /**
376      * creates/updates a Resource
377      *
378      * @param   array   $recordData
379      * @return  array   created/updated Resource
380      */
381     public function saveResource($recordData)
382     {
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;
386         }
387         
388         return $this->_save($recordData, Calendar_Controller_Resource::getInstance(), 'Resource');
389     }
390     
391     /**
392      * sets attendee status for an attender on the given event
393      * 
394      * NOTE: for recur events we implicitly create an exceptions on demand
395      *
396      * @param  array         $eventData
397      * @param  array         $attenderData
398      * @param  string        $authKey
399      * @return array         complete event
400      */
401     public function setAttenderStatus($eventData, $attenderData, $authKey)
402     {
403         $event    = new Calendar_Model_Event($eventData);
404         $attender = new Calendar_Model_Attender($attenderData);
405         
406         Calendar_Controller_Event::getInstance()->attenderStatusUpdate($event, $attender, $authKey);
407         
408         return $this->getEvent($event->getId());
409     }
410     
411     /**
412      * updated a recur series
413      *
414      * @param  array $recordData
415      * @param  bool  $checkBusyConflicts
416      * @noparamyet  JSONstring $returnPeriod NOT IMPLEMENTED YET
417      * @return array 
418      */
419     public function updateRecurSeries($recordData, $checkBusyConflicts = FALSE /*, $returnPeriod*/)
420     {
421         $recurInstance = new Calendar_Model_Event(array(), TRUE);
422         $recurInstance->setFromJsonInUsersTimezone($recordData);
423         
424         //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(print_r($recurInstance->toArray(), true));
425         
426         $baseEvent = Calendar_Controller_Event::getInstance()->updateRecurSeries($recurInstance, $checkBusyConflicts);
427         
428         return $this->getEvent($baseEvent->getId());
429     }
430     
431     /**
432      * prepares an iMIP (RFC 6047) Message
433      * 
434      * @param array|Calendar_Model_iMIP $iMIP
435      * @return array prepared iMIP part
436      */
437     public function iMIPPrepare($iMIP)
438     {
439         $iMIPMessage = $iMIP instanceof Calendar_Model_iMIP ? $iMIP : new Calendar_Model_iMIP($iMIP);
440         $iMIPFrontend = new Calendar_Frontend_iMIP();
441         
442         $iMIPMessage->preconditionsChecked = FALSE;
443         $iMIPFrontend->prepareComponent($iMIPMessage);
444         $iMIPMessage->setTimezone(Tinebase_Core::getUserTimezone());
445         return $iMIPMessage->toArray();
446     }
447     
448     /**
449      * process an iMIP (RFC 6047) Message
450      * 
451      * @param array  $iMIP
452      * @param string $status
453      * @return array prepared iMIP part
454      */
455     public function iMIPProcess($iMIP, $status=null)
456     {
457         $iMIPMessage = new Calendar_Model_iMIP($iMIP);
458         $iMIPFrontend = new Calendar_Frontend_iMIP();
459         
460         $iMIPFrontend->process($iMIPMessage, $status);
461         
462         return $this->iMIPPrepare($iMIPMessage);
463     }
464
465     /**
466      * @param array $_attendee
467      * @param array $_periods
468      * @param array $_ignoreUIDs
469      * @return array
470      */
471     public function getFreeBusyInfo($_attendee, $_periods, $_ignoreUIDs = array())
472     {
473         $attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', $_attendee);
474         $periods = new Calendar_Model_EventFilter($_periods);
475
476         $fbInfo = Calendar_Controller_Event::getInstance()->getFreeBusyInfo($periods, $attendee, $_ignoreUIDs);
477
478         return $fbInfo->toArray();
479     }
480
481     /**
482      * @param array $_filter
483      * @param array $_paging
484      * @param array $_periods
485      * @param array $_ignoreUIDs
486      * @return array
487      */
488     public function searchAttendee($_filter, $_paging, $_periods, $_ignoreUIDs)
489     {
490         $filters = array();
491         foreach($_filter as $filter) {
492             switch($filter['field']) {
493                 case 'query':
494                     $filters['query'] = $filter;
495                     break;
496                 default:
497                     $filters[$filter['field']] = $filter['value'];
498                     break;
499             }
500         }
501
502         $result = array();
503         $addressBookFE = new Addressbook_Frontend_Json();
504
505         if (!isset($filters['type']) || in_array(Calendar_Model_Attender::USERTYPE_USER, $filters['type'])) {
506             $contactFilter = array(array('condition' => 'OR', 'filters' => array(
507                 $filters['query'],
508                 array('field' => 'path', 'operator' => 'contains', 'value' => $filters['query']['value'])
509             )));
510             if (isset($filters['userFilter'])) {
511                 $contactFilter[] = $filters['userFilter'];
512             }
513             $contactPaging = $_paging;
514             $contactPaging['sort'] = 'type';
515             $result[Calendar_Model_Attender::USERTYPE_USER] = $addressBookFE->searchContacts($contactFilter, $contactPaging);
516         }
517
518         if (!isset($filters['type']) || in_array(Calendar_Model_Attender::USERTYPE_GROUP, $filters['type'])) {
519             $groupFilter = array(array('condition' => 'OR', 'filters' => array(
520                 $filters['query'],
521                 array('field' => 'path', 'operator' => 'contains', 'value' => $filters['query']['value'])
522             )));
523             if (isset($filters['groupFilter'])) {
524                 $groupFilter[] = $filters['groupFilter'];
525             }
526             $groupFilter[] = array('field' => 'type', 'operator' => 'contains', 'value' => 'group');
527             $result[Calendar_Model_Attender::USERTYPE_GROUP] = $addressBookFE->searchLists($groupFilter, $_paging);
528         }
529
530         if (!isset($filters['type']) || in_array(Calendar_Model_Attender::USERTYPE_RESOURCE, $filters['type'])) {
531             $resourceFilter = array($filters['query']);
532             if (isset($filters['resourceFilter'])) {
533                 $resourceFilter[] = $filters['resourceFilter'];
534             }
535             $result[Calendar_Model_Attender::USERTYPE_RESOURCE] = $this->searchResources($resourceFilter, $_paging);
536         }
537
538         $attendee = array();
539         foreach ($result as $type => $res) {
540             foreach ($res['results'] as $r) {
541                 $attendee[] = array(
542                     'user_id'   => $r['id'],
543                     'user_type' => $type
544                 );
545             }
546         }
547
548         $result['freeBusyInfo'] = $this->getFreeBusyInfo($attendee, $_periods, $_ignoreUIDs);
549
550         return $result;
551     }
552 }