d440c736f6de80a049357e5954fd35f1576b6751
[tine20] / tine20 / Calendar / Controller.php
1 <?php
2 /**
3  * Sql Calendar 
4  * 
5  * @package     Calendar
6  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
7  * @author      Cornelius Weiss <c.weiss@metaways.de>
8  * @copyright   Copyright (c) 2009 Metaways Infosystems GmbH (http://www.metaways.de)
9  */
10
11 /**
12  * main controller for Calendar
13  *
14  * @package     Calendar
15  */
16 class Calendar_Controller extends Tinebase_Controller_Event implements Tinebase_Application_Container_Interface
17 {
18     /**
19      * holds the instance of the singleton
20      *
21      * @var Calendar_Controller
22      */
23     private static $_instance = NULL;
24
25     /**
26      * holds the default Model of this application
27      * @var string
28      */
29     protected static $_defaultModel = 'Calendar_Model_Event';
30
31     /**
32      * application name (is needed in checkRight())
33      *
34      * @var string
35      */
36     protected $_applicationName = 'Calendar';
37
38     /**
39      * don't clone. Use the singleton.
40      *
41      */
42     private function __clone() 
43     {
44     }
45     
46     /**
47      * the singleton pattern
48      *
49      * @return Calendar_Controller
50      */
51     public static function getInstance() 
52     {
53         if (self::$_instance === NULL) {
54             self::$_instance = new Calendar_Controller();
55         }
56         
57         return self::$_instance;
58     }
59
60     /**
61      * event handler function
62      * 
63      * all events get routed through this function
64      *
65      * @param Tinebase_Event_Abstract $_eventObject the eventObject
66      */
67     protected function _handleEvent(Tinebase_Event_Abstract $_eventObject)
68     {
69         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . ' ' . __LINE__ . ' handle event of type ' . get_class($_eventObject));
70         
71         switch (get_class($_eventObject)) {
72             case 'Admin_Event_AddAccount':
73                 //$this->createPersonalFolder($_eventObject->account);
74                 Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::DEFAULTCALENDAR, $_eventObject->account->getId());
75                 break;
76                 
77             case 'Tinebase_Event_User_DeleteAccount':
78                 /**
79                  * @var Tinebase_Event_User_DeleteAccount $_eventObject
80                  */
81                 $this->_handleDeleteUserEvent($_eventObject);
82
83                 // let event bubble
84                 parent::_handleEvent($_eventObject);
85                 break;
86                 
87             case 'Admin_Event_UpdateGroup':
88                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . ' ' . __LINE__ . ' updated group ' . $_eventObject->group->name);
89                 Tinebase_ActionQueue::getInstance()->queueAction('Calendar.onUpdateGroup', $_eventObject->group->getId());
90                 break;
91             case 'Admin_Event_AddGroupMember':
92                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . ' ' . __LINE__ . ' add groupmember ' . (string) $_eventObject->userId . ' to group ' . (string) $_eventObject->groupId);
93                 Tinebase_ActionQueue::getInstance()->queueAction('Calendar.onUpdateGroup', $_eventObject->groupId);
94                 break;
95                 
96             case 'Admin_Event_RemoveGroupMember':
97                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . ' ' . __LINE__ . ' removed groupmember ' . (string) $_eventObject->userId . ' from group ' . (string) $_eventObject->groupId);
98                 Tinebase_ActionQueue::getInstance()->queueAction('Calendar.onUpdateGroup', $_eventObject->groupId);
99                 break;
100                 
101             case 'Tinebase_Event_Container_BeforeCreate':
102                 $this->_handleContainerBeforeCreateEvent($_eventObject);
103                 break;
104         }
105     }
106
107     protected function _handleDeleteUserEvent($_eventObject)
108     {
109         // this needs to happen before deletePersonalFolder. Otherwise the attender display container (the personal folder) is gone and the attendee filter doesnt work anymore!
110         if (!$_eventObject->keepAsContact()) {
111             // remove all event attenders for this contact
112             $this->_deleteEventAttenders($_eventObject);
113         }
114
115         if ($_eventObject->keepOrganizerEvents()) {
116             $this->_keepOrganizerEvents($_eventObject);
117         }
118
119         if ($_eventObject->deletePersonalContainers()) {
120             $this->deletePersonalFolder($_eventObject->account);
121         }
122     }
123
124     protected function _deleteEventAttenders($_eventObject)
125     {
126         $contactId = $_eventObject->account->contact_id;
127
128         $filter = new Calendar_Model_EventFilter(array(array(
129             'field' => 'attender', 'operator' => 'in', 'value' => array(array(
130                 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
131                 'user_id'   => $contactId,
132             ))
133         )));
134
135         $eventController = Calendar_Controller_Event::getInstance();
136         $oldAcl = $eventController->doContainerACLChecks(false);
137         $oldSendNotification = $eventController->sendNotifications(false);
138
139         $events = $eventController->search($filter);
140         /** @var Calendar_Model_Event $event */
141         foreach($events as $event)
142         {
143             $toRemove = array();
144             foreach ($event->attendee as $key => $attendee) {
145                 $attendeeUserId = $attendee->user_id instanceof Tinebase_Record_Abstract
146                     ? $attendee->user_id->getId()
147                     : $attendee->user_id;
148
149                 if ($attendeeUserId === $contactId && ($attendee->user_type === Calendar_Model_Attender::USERTYPE_USER ||
150                                                         $attendee->user_type === Calendar_Model_Attender::USERTYPE_GROUPMEMBER))
151                 {
152                     $toRemove[] = $key;
153                 }
154             }
155             if (count($toRemove) > 0) {
156                 foreach($toRemove as $index) {
157                     $event->attendee->offsetUnset($index);
158                 }
159
160                 $eventController->update($event);
161             }
162         }
163
164         $eventController->doContainerACLChecks($oldAcl);
165         $eventController->sendNotifications($oldSendNotification);
166     }
167
168     protected function _keepOrganizerEvents($_eventObject)
169     {
170         $accountId = $_eventObject->account->getId();
171         $contactId = $_eventObject->account->contact_id;
172
173         $contact = null;
174         $newContact = null;
175         $contactEmail = null;
176         if ($_eventObject->keepAsContact()) {
177             try {
178                 $contact = Addressbook_Controller_Contact::getInstance()->get($contactId);
179                 $contactEmail = $contact->getPreferredEmailAddress();
180             } catch (Tinebase_Exception_NotFound $tenf) {
181                 // ignore
182                 $contactEmail = $_eventObject->account->accountEmailAddress;
183             }
184         } else {
185             $contactEmail = $_eventObject->account->accountEmailAddress;
186         }
187
188         if (null === $contact) {
189             $newContact = Calendar_Model_Attender::resolveEmailToContact(array(
190                 'email' => $contactEmail,
191             ));
192         }
193
194         $eventController = Calendar_Controller_Event::getInstance();
195         $oldState = $eventController->doContainerACLChecks(false);
196         // delete all events where our deletee is organizer and that are private (no matter if they have attendees or not)
197         /*$filter = new Calendar_Model_EventFilter(array(
198             array('field' => 'class', 'operator' => 'equals', 'value' => Calendar_Model_Event::CLASS_PRIVATE),
199             array('field' => 'organizer', 'operator' => 'equals', 'value' => $contactId),
200         ));
201         $eventController->deleteByFilter($filter);*/
202
203         // delete all events where our deletee is organizer and that dont have any additional attenders except the organizer / deletee himself
204         $filter = new Calendar_Model_EventFilter(array(
205             array('field' => 'organizer', 'operator' => 'equals', 'value' => $contactId),
206             array('field' => 'attender', 'operator' => 'notHasSomeExcept', 'value' => array(
207                 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
208                 'user_id' => $contactId,
209             )),
210         ));
211         $eventController->deleteByFilter($filter);
212
213         $eventController->doContainerACLChecks($oldState);
214
215         // get all personal containers
216         $containers = Tinebase_Container::getInstance()->getPersonalContainer($accountId, $this->getDefaultModel(), $accountId, '*', true);
217         if ($containers->count() > 0) {
218             // take the first one and make it an invitation container
219             $container = $containers->getByIndex(0);
220             $this->convertToInvitationContainer($container, $contactEmail);
221
222             // if there are more than 1 container, move contents to invitation container, then delete them
223             $i = 1;
224             while ($containers->count() > 1) {
225                 $moveContainer = $containers->getByIndex($i);
226                 $containers->offsetUnset($i++);
227
228                 //move $moveContainer content to $container
229                 $eventController->getBackend()->moveEventsToContainer($moveContainer, $container);
230                 //delete $moveContainer
231                 Tinebase_Container::getInstance()->deleteContainer($moveContainer, true);
232             }
233         }
234
235         // replace old contactId with newContact->getId()
236         if (null !== $newContact) {
237             $eventController->getBackend()->replaceContactId($contactId, $newContact->getId());
238         }
239     }
240
241     /**
242      * Converts the calendar to be a calendar for external organizer
243      *
244      * @param Tinebase_Model_Container $container
245      */
246     public function convertToInvitationContainer(Tinebase_Model_Container $container, $emailAddress)
247     {
248         if ($container->model !== 'Calendar_Model_Event') {
249             Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . ' container provided needs to have the model Calendar_Model_Event instead of ' . $container->model);
250             throw Tinebase_Exception_UnexpectedValue('container provided needs to have the model Calendar_Model_Event instead of ' . $container->model);
251         }
252
253         $tbc = Tinebase_Container::getInstance();
254         try {
255             $oldContainer = $tbc->getContainerByName('Calendar', $emailAddress, Tinebase_Model_Container::TYPE_SHARED);
256
257             // TODO fix me!
258             // bad, we should move the events from $oldContainer to $container
259
260             $tbc->deleteContainer($oldContainer, true);
261         } catch (Tinebase_Exception_NotFound $tenf) {
262             //good, ignore
263         }
264
265         $container->name = $emailAddress;
266         $container->color = '#333399';
267         $container->type = Tinebase_Model_Container::TYPE_SHARED;
268         $tbc->update($container);
269
270         $grants = new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(
271             array(
272                 'account_id'      => '0',
273                 'account_type'    => Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE,
274                 Tinebase_Model_Grants::GRANT_ADD         => true,
275                 Tinebase_Model_Grants::GRANT_EDIT        => true,
276                 Tinebase_Model_Grants::GRANT_DELETE      => true,
277             )
278         ));
279         $tbc->setGrants($container->getId(), $grants, true, false);
280     }
281
282     /**
283      * Get/Create Calendar for external organizer
284      * 
285      * @param  Addressbook_Model_Contact $organizer organizer id
286      * @param  string $emailAddress
287      * @return Tinebase_Model_Container  container id
288      */
289     public function getInvitationContainer($organizer, $emailAddress = null)
290     {
291         if (null!==$organizer) {
292             $containerName = $organizer->getPreferredEmailAddress();
293         } else {
294             $containerName = $emailAddress;
295         }
296
297         if (empty($containerName)) {
298             Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . ' event organizer does not have an email address');
299             throw new Tinebase_Exception_UnexpectedValue('event organizer does not have an email address');
300         }
301         
302         try {
303             $container = Tinebase_Container::getInstance()->getContainerByName('Calendar', $containerName, Tinebase_Model_Container::TYPE_SHARED);
304         } catch (Tinebase_Exception_NotFound $tenf) {
305             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
306                 . ' No invitation container found. Creating a new one for organizer ' . $containerName);
307             
308             $container = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
309                 'name'              => $containerName,
310                 'color'             => '#333399',
311                 'type'              => Tinebase_Model_Container::TYPE_SHARED,
312                 'backend'           => Tinebase_User::SQL,
313                 'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
314                 'model'             => 'Calendar_Model_Event'
315             )), NULL, TRUE);
316             
317             $grants = new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(
318                 array(
319                     'account_id'      => '0',
320                     'account_type'    => Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE,
321                     Tinebase_Model_Grants::GRANT_ADD         => true,
322                     Tinebase_Model_Grants::GRANT_EDIT        => true,
323                     Tinebase_Model_Grants::GRANT_DELETE      => true,
324                 )
325             ));
326             Tinebase_Container::getInstance()->setGrants($container->getId(), $grants, true, false);
327         }
328          
329         return $container;
330     }
331     
332     /**
333      * creates the initial folder for new accounts
334      *
335      * @param mixed[int|Tinebase_Model_User] $_account   the accountd object
336      * @return Tinebase_Record_RecordSet of subtype Tinebase_Model_Container
337      * 
338      * @todo use Tinebase_Container::getDefaultContainer
339      */
340     public function createPersonalFolder($_account)
341     {
342         $translation = Tinebase_Translation::getTranslation('Calendar');
343         
344         $account = Tinebase_User::getInstance()->getUserById($_account);
345         
346         $newContainer = new Tinebase_Model_Container(array(
347             'name'              => sprintf($translation->_("%s's personal calendar"), $account->accountFullName),
348             'type'              => Tinebase_Model_Container::TYPE_PERSONAL,
349             'owner_id'          => $account->getId(),
350             'backend'           => Tinebase_User::SQL,
351             'color'             => '#FF6600',
352             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
353             'model'             => static::$_defaultModel
354         ));
355         
356         $personalContainer = Tinebase_Container::getInstance()->addContainer($newContainer);
357         $container = new Tinebase_Record_RecordSet('Tinebase_Model_Container', array($personalContainer));
358         
359         return $container;
360     }
361     
362     /**
363      * handler for Tinebase_Event_Container_BeforeCreate
364      * - give owner of personal container all grants
365      * - give freebusy grants to anyone for personal container
366      * 
367      * @param Tinebase_Event_Container_BeforeCreate $_eventObject
368      */
369     protected function _handleContainerBeforeCreateEvent(Tinebase_Event_Container_BeforeCreate $_eventObject)
370     {
371         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->INFO(__METHOD__ . ' ' . __LINE__
372             . ' about to handle Tinebase_Event_Container_BeforeCreate' );
373         
374         if ($_eventObject->container && 
375             $_eventObject->container->type === Tinebase_Model_Container::TYPE_PERSONAL &&
376             $_eventObject->container->application_id === Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId() &&
377             $_eventObject->grants instanceof Tinebase_Record_RecordSet
378             ) {
379             // get owner from initial initial grants
380             $grants = $_eventObject->grants;
381             $grants->removeAll();
382             
383             $grants->addRecord(new Tinebase_Model_Grants(array(
384                 'account_id'     => $_eventObject->accountId,
385                 'account_type'   => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER,
386                 Tinebase_Model_Grants::GRANT_READ      => true,
387                 Tinebase_Model_Grants::GRANT_ADD       => true,
388                 Tinebase_Model_Grants::GRANT_EDIT      => true,
389                 Tinebase_Model_Grants::GRANT_DELETE    => true,
390                 Tinebase_Model_Grants::GRANT_EXPORT    => true,
391                 Tinebase_Model_Grants::GRANT_SYNC      => true,
392                 Tinebase_Model_Grants::GRANT_ADMIN     => true,
393                 Tinebase_Model_Grants::GRANT_FREEBUSY  => true,
394                 Tinebase_Model_Grants::GRANT_PRIVATE   => true,
395             ), TRUE));
396             
397             if (! Tinebase_Config::getInstance()->get(Tinebase_Config::ANYONE_ACCOUNT_DISABLED)) {
398                 $grants->addRecord(new Tinebase_Model_Grants(array(
399                     'account_id'      => '0',
400                     'account_type'    => Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE,
401                     Tinebase_Model_Grants::GRANT_FREEBUSY  => true
402                 ), TRUE));
403             }
404         }
405     }
406     
407     /**
408      * send notifications 
409      * 
410      * @param Calendar_Model_Event       $_event
411      * @param Tinebase_Model_FullAccount $_updater
412      * @param Sting                      $_action
413      * @param Calendar_Model_Event       $_oldEvent
414      * @return void
415      */
416     public function sendEventNotifications($_event, $_updater, $_action, $_oldEvent = NULL)
417     {
418         Calendar_Controller_EventNotifications::getInstance()->doSendNotifications($_event, $_updater, $_action, $_oldEvent);
419     }
420     
421     /**
422      * update group events
423      * 
424      * @param string $_groupId
425      * @return void
426      */
427     public function onUpdateGroup($_groupId)
428     {
429         Calendar_Controller_Event::getInstance()->onUpdateGroup($_groupId);
430     }
431
432     /**
433      * get core data for this application
434      *
435      * @return Tinebase_Record_RecordSet
436      */
437     public function getCoreDataForApplication()
438     {
439         $result = parent::getCoreDataForApplication();
440
441         $application = Tinebase_Application::getInstance()->getApplicationByName($this->_applicationName);
442
443         if (Tinebase_Core::getUser()->hasRight($application, Calendar_Acl_Rights::MANAGE_RESOURCES)) {
444             $result->addRecord(new CoreData_Model_CoreData(array(
445                 'id' => 'cal_resources',
446                 'application_id' => $application,
447                 'model' => 'Calendar_Model_Resource',
448                 'label' => 'Resources' // _('Resources')
449             )));
450         }
451
452         return $result;
453     }
454 }