Merge branch '2013.10' into 2014.11
[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         $returnEvent = Calendar_Controller_Event::getInstance()->createRecurException($event, $deleteInstance, $deleteAllFollowing, $checkBusyConflicts);
48         
49         return $this->getEvent($returnEvent->getId());
50     }
51     
52     /**
53      * deletes existing events
54      *
55      * @param array $_ids
56      * @param string $range
57      * @return string
58      */
59     public function deleteEvents($ids, $range = Calendar_Model_Event::RANGE_THIS)
60     {
61         return $this->_delete($ids, Calendar_Controller_Event::getInstance(), array($range));
62     }
63     
64     /**
65      * deletes existing resources
66      *
67      * @param array $_ids 
68      * @return string
69      */
70     public function deleteResources($ids)
71     {
72         return $this->_delete($ids, Calendar_Controller_Resource::getInstance());
73     }
74     
75     /**
76      * deletes a recur series
77      *
78      * @param  array $recordData
79      * @return void
80      */
81     public function deleteRecurSeries($recordData)
82     {
83         $event = new Calendar_Model_Event(array(), TRUE);
84         $event->setFromJsonInUsersTimezone($recordData);
85         
86         Calendar_Controller_Event::getInstance()->deleteRecurSeries($event);
87         return array('success' => true);
88     }
89     
90     /**
91      * Return a single event
92      *
93      * @param   string $id
94      * @return  array record data
95      */
96     public function getEvent($id)
97     {
98         return $this->_get($id, Calendar_Controller_Event::getInstance());
99     }
100     
101     /**
102      * Returns registry data of the calendar.
103      *
104      * @return mixed array 'variable name' => 'data'
105      * 
106      * @todo move exception handling (no default calender found) to another place?
107      */
108     public function getRegistryData()
109     {
110         $defaultCalendarId = Tinebase_Core::getPreference('Calendar')->getValue(Calendar_Preference::DEFAULTCALENDAR);
111         try {
112             $defaultCalendarArray = Tinebase_Container::getInstance()->getContainerById($defaultCalendarId)->toArray();
113             $defaultCalendarArray['account_grants'] = Tinebase_Container::getInstance()->getGrantsOfAccount(Tinebase_Core::getUser(), $defaultCalendarId)->toArray();
114             $defaultCalendarArray['ownerContact'] = Addressbook_Controller_Contact::getInstance()->getContactByUserId($defaultCalendarArray['owner_id'])->toArray();
115         } catch (Exception $e) {
116             // remove default cal pref
117             Tinebase_Core::getPreference('Calendar')->deleteUserPref(Calendar_Preference::DEFAULTCALENDAR);
118             $defaultCalendarArray = array();
119         }
120         
121         $importDefinitions = $this->_getImportDefinitions();
122         
123         $registryData = array(
124             'defaultContainer'          => $defaultCalendarArray,
125             'defaultImportDefinition'   => $importDefinitions['default'],
126             'importDefinitions'         => $importDefinitions
127         );
128         
129         return $registryData;
130     }
131     
132     /**
133      * get default addressbook
134      * 
135      * @return array
136      */
137     public function getDefaultCalendar() 
138    {
139         $defaultCalendar = Calendar_Controller_Event::getInstance()->getDefaultCalendar();
140         $defaultCalendarArray = $defaultCalendar->toArray();
141         $defaultCalendarArray['account_grants'] = Tinebase_Container::getInstance()->getGrantsOfAccount(Tinebase_Core::getUser(), $defaultCalendar->getId())->toArray();
142         Tinebase_Core::getLogger()->notice(print_r($defaultCalendar, true));
143         return $defaultCalendarArray;
144     }
145     
146     /**
147      * import contacts
148      * 
149      * @param string $tempFileId to import
150      * @param string $definitionId
151      * @param array $importOptions
152      * @param array $clientRecordData
153      * @return array
154      */
155     public function importEvents($tempFileId, $definitionId, $importOptions, $clientRecordData = array())
156     {
157         return $this->_import($tempFileId, $definitionId, $importOptions, $clientRecordData);
158     }
159     
160     /**
161      * creates a scheduled import
162      * 
163      * @param string $remoteUrl
164      * @param string $interval
165      * @param string $importOptions
166      * @return array
167      */
168     public function importRemoteEvents($remoteUrl, $interval, $importOptions)
169     {
170         // Determine which plugin should be used to import
171         switch ($importOptions['sourceType']) {
172             case 'remote_caldav':
173                 $plugin = 'Calendar_Import_CalDAV';
174                 break;
175             default:
176                 $plugin = 'Calendar_Import_Ical';
177         }
178
179         $credentialCache = Tinebase_Auth_CredentialCache::getInstance();
180         $credentials = $credentialCache->cacheCredentials(
181             $importOptions['username'],
182             $importOptions['password'],
183             null,
184             /* persist */       true,
185             /* valid until */   Tinebase_DateTime::now()->addYear(100)
186         );
187
188         $record = Tinebase_Controller_ScheduledImport::getInstance()->createRemoteImportEvent(array(
189             'source'            => $remoteUrl,
190             'interval'          => $interval,
191             'options'           => array_replace($importOptions, array(
192                 'plugin' => $plugin,
193                 'importFileByScheduler' => $importOptions['sourceType'] != 'remote_caldav',
194                 'cid' => $credentials->getId(),
195                 'ckey' => $credentials->key
196             )),
197             'model'             => 'Calendar_Model_Event',
198             'user_id'           => Tinebase_Core::getUser()->getId(),
199             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
200         ));
201
202         $result = $this->_recordToJson($record);
203         Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . 'container_id:'  .  print_r($result['container_id'], true));
204
205         return $result;
206     }
207     
208     /**
209      * get addressbook import definitions
210      * 
211      * @return array
212      * 
213      * @todo generalize this
214      */
215     protected function _getImportDefinitions()
216     {
217         $filter = new Tinebase_Model_ImportExportDefinitionFilter(array(
218             array('field' => 'application_id',  'operator' => 'equals', 'value' => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId()),
219             array('field' => 'type',            'operator' => 'equals', 'value' => 'import'),
220         ));
221         
222         $definitionConverter = new Tinebase_Convert_ImportExportDefinition_Json();
223         
224         try {
225             $importDefinitions = Tinebase_ImportExportDefinition::getInstance()->search($filter);
226             $defaultDefinition = $this->_getDefaultImportDefinition($importDefinitions);
227             $result = array(
228                 'results'               => $definitionConverter->fromTine20RecordSet($importDefinitions),
229                 'totalcount'            => count($importDefinitions),
230                 'default'               => ($defaultDefinition) ? $definitionConverter->fromTine20Model($defaultDefinition) : array(),
231             );
232         } catch (Exception $e) {
233             Tinebase_Exception::log($e);
234             $result = array(
235                 array(
236                     'results'               => array(),
237                     'totalcount'            => 0,
238                     'default'               => array(),
239                 )
240             );
241         }
242         
243         return $result;
244     }
245     
246     /**
247      * get default definition
248      * 
249      * @param Tinebase_Record_RecordSet $_importDefinitions
250      * @return Tinebase_Model_ImportExportDefinition
251      * 
252      * @todo generalize this
253      */
254     protected function _getDefaultImportDefinition($_importDefinitions)
255     {
256         try {
257             $defaultDefinition = Tinebase_ImportExportDefinition::getInstance()->getByName('cal_import_ical');
258         } catch (Tinebase_Exception_NotFound $tenf) {
259             if (count($_importDefinitions) > 0) {
260                 $defaultDefinition = $_importDefinitions->getFirstRecord();
261             } else {
262                 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' No import definitions found for Calendar');
263                 $defaultDefinition = NULL;
264             }
265         }
266         
267         return $defaultDefinition;
268     }
269     
270     /**
271      * Return a single resouece
272      *
273      * @param   string $id
274      * @return  array record data
275      */
276     public function getResource($id)
277     {
278         return $this->_get($id, Calendar_Controller_Resource::getInstance());
279     }
280     
281     /**
282      * Search for events matching given arguments
283      *
284      * @param  array $_filter
285      * @param  array $_paging
286      * @return array
287      */
288     public function searchEvents($filter, $paging)
289     {
290         $controller = Calendar_Controller_Event::getInstance();
291         
292         $decodedPagination = is_array($paging) ? $paging : Zend_Json::decode($paging);
293         $pagination = new Tinebase_Model_Pagination($decodedPagination);
294         $clientFilter = $filter = $this->_decodeFilter($filter, 'Calendar_Model_EventFilter');
295
296         // find out if fixed calendars should be used
297         $fixedCalendars = Calendar_Config::getInstance()->get(Calendar_Config::FIXED_CALENDARS, new Tinebase_Config_Struct(array()))->toArray();
298         $useFixedCalendars = is_array($fixedCalendars) && ! empty($fixedCalendars);
299         
300         $periodFilter = $filter->getFilter('period');
301         
302         // add period filter per default to prevent endless search
303         if (! $periodFilter) {
304             $periodFilter = $this->_getDefaultPeriodFilter();
305             // periodFilter will be added to fixed filter when using fixed calendars
306             if (! $useFixedCalendars) {
307                 $filter->addFilter($periodFilter);
308             }
309         }
310         
311         // add fixed calendar on demand
312         if ($useFixedCalendars) {
313             $fixed = new Calendar_Model_EventFilter(array(), 'AND');
314             $fixed->addFilter( new Tinebase_Model_Filter_Text('container_id', 'in', $fixedCalendars));
315             
316             $fixed->addFilter($periodFilter);
317             
318             $og = new Calendar_Model_EventFilter(array(), 'OR');
319             $og->addFilterGroup($fixed);
320             $og->addFilterGroup($clientFilter);
321             
322             $filter = new Calendar_Model_EventFilter(array(), 'AND');
323             $filter->addFilterGroup($og);
324         }
325         
326         $records = $controller->search($filter, $pagination, FALSE);
327         
328         $result = $this->_multipleRecordsToJson($records, $clientFilter, $pagination);
329         
330         return array(
331             'results'       => $result,
332             'totalcount'    => count($result),
333             'filter'        => $clientFilter->toArray(TRUE),
334         );
335     }
336     
337     /**
338      * get default period filter
339      * 
340      * @return Calendar_Model_PeriodFilter
341      */
342     protected function _getDefaultPeriodFilter()
343     {
344         $now = Tinebase_DateTime::now()->setTime(0,0,0);
345         
346         $from = $now->getClone()->subMonth(Calendar_Config::getInstance()->get(Calendar_Config::MAX_JSON_DEFAULT_FILTER_PERIOD_FROM, 0));
347         $until = $now->getClone()->addMonth(Calendar_Config::getInstance()->get(Calendar_Config::MAX_JSON_DEFAULT_FILTER_PERIOD_UNTIL, 1));
348         $periodFilter = new Calendar_Model_PeriodFilter(array(
349             'field' => 'period',
350             'operator' => 'within',
351             'value' => array("from" => $from, "until" => $until)
352         ));
353         
354         return $periodFilter;
355     }
356     
357     /**
358      * Search for resources matching given arguments
359      *
360      * @param  array $_filter
361      * @param  array $_paging
362      * @return array
363      */
364     public function searchResources($filter, $paging)
365     {
366         return $this->_search($filter, $paging, Calendar_Controller_Resource::getInstance(), 'Calendar_Model_ResourceFilter');
367     }
368     
369     /**
370      * creates/updates an event / recur
371      *
372      * @param   array   $recordData
373      * @param   bool    $checkBusyConflicts
374      * @param   string  $range
375      * @return  array   created/updated event
376      */
377     public function saveEvent($recordData, $checkBusyConflicts = FALSE, $range = Calendar_Model_Event::RANGE_THIS)
378     {
379         return $this->_save($recordData, Calendar_Controller_Event::getInstance(), 'Event', 'id', array($checkBusyConflicts, $range));
380     }
381     
382     /**
383      * creates/updates a Resource
384      *
385      * @param   array   $recordData
386      * @return  array   created/updated Resource
387      */
388     public function saveResource($recordData)
389     {
390         $recordData['grants'] = new Tinebase_Record_RecordSet('Tinebase_Model_Grants', $recordData['grants']);
391         
392         return $this->_save($recordData, Calendar_Controller_Resource::getInstance(), 'Resource');
393     }
394     
395     /**
396      * sets attendee status for an attender on the given event
397      * 
398      * NOTE: for recur events we implicitly create an exceptions on demand
399      *
400      * @param  array         $eventData
401      * @param  array         $attenderData
402      * @param  string        $authKey
403      * @return array         complete event
404      */
405     public function setAttenderStatus($eventData, $attenderData, $authKey)
406     {
407         $event    = new Calendar_Model_Event($eventData);
408         $attender = new Calendar_Model_Attender($attenderData);
409         
410         Calendar_Controller_Event::getInstance()->attenderStatusUpdate($event, $attender, $authKey);
411         
412         return $this->getEvent($event->getId());
413     }
414     
415     /**
416      * updated a recur series
417      *
418      * @param  array $recordData
419      * @param  bool  $checkBusyConflicts
420      * @noparamyet  JSONstring $returnPeriod NOT IMPLEMENTED YET
421      * @return array 
422      */
423     public function updateRecurSeries($recordData, $checkBusyConflicts = FALSE /*, $returnPeriod*/)
424     {
425         $recurInstance = new Calendar_Model_Event(array(), TRUE);
426         $recurInstance->setFromJsonInUsersTimezone($recordData);
427         
428         //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(print_r($recurInstance->toArray(), true));
429         
430         $baseEvent = Calendar_Controller_Event::getInstance()->updateRecurSeries($recurInstance, $checkBusyConflicts);
431         
432         return $this->getEvent($baseEvent->getId());
433     }
434     
435     /**
436      * prepares an iMIP (RFC 6047) Message
437      * 
438      * @param array $iMIP
439      * @return array prepared iMIP part
440      */
441     public function iMIPPrepare($iMIP)
442     {
443         $iMIPMessage = $iMIP instanceof Calendar_Model_iMIP ? $iMIP : new Calendar_Model_iMIP($iMIP);
444         $iMIPFrontend = new Calendar_Frontend_iMIP();
445         
446         $iMIPMessage->preconditionsChecked = FALSE;
447         $iMIPFrontend->prepareComponent($iMIPMessage);
448         $iMIPMessage->setTimezone(Tinebase_Core::getUserTimezone());
449         return $iMIPMessage->toArray();
450     }
451     
452     /**
453      * process an iMIP (RFC 6047) Message
454      * 
455      * @param array  $iMIP
456      * @param string $status
457      * @return array prepared iMIP part
458      */
459     public function iMIPProcess($iMIP, $status=null)
460     {
461         $iMIPMessage = new Calendar_Model_iMIP($iMIP);
462         $iMIPFrontend = new Calendar_Frontend_iMIP();
463         
464         $iMIPFrontend->process($iMIPMessage, $status);
465         
466         return $this->iMIPPrepare($iMIPMessage);
467     }
468 }