ac09ba60bcca495941650d25dcf490cea489ef39
[tine20] / tine20 / Addressbook / Controller / List.php
1 <?php
2 /**
3  * Tine 2.0
4  *
5  * @package     Addressbook
6  * @subpackage  Controller
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Lars Kneschke <l.kneschke@metaways.de>
9  * @copyright   Copyright (c) 2010-2012 Metaways Infosystems GmbH (http://www.metaways.de)
10  * 
11  */
12
13 /**
14  * contact controller for Addressbook
15  *
16  * @package     Addressbook
17  * @subpackage  Controller
18  */
19 class Addressbook_Controller_List extends Tinebase_Controller_Record_Abstract
20 {
21     /**
22      * application name (is needed in checkRight())
23      *
24      * @var string
25      */
26     protected $_applicationName = 'Addressbook';
27
28     /**
29      * Model name
30      *
31      * @var string
32      */
33     protected $_modelName = 'Addressbook_Model_List';
34
35     /**
36      * @var null|Tinebase_Backend_Sql
37      */
38     protected $_memberRolesBackend = null;
39
40     /**
41      * the constructor
42      *
43      * don't use the constructor. use the singleton
44      */
45     private function __construct()
46     {
47         $this->_resolveCustomFields = true;
48         $this->_backend = new Addressbook_Backend_List();
49     }
50
51     /**
52      * don't clone. Use the singleton.
53      *
54      */
55     private function __clone()
56     {
57     }
58
59     /**
60      * holds the instance of the singleton
61      *
62      * @var Addressbook_Controller_List
63      */
64     private static $_instance = NULL;
65
66     protected function _getMemberRolesBackend()
67     {
68         if ($this->_memberRolesBackend === null) {
69             $this->_memberRolesBackend = new Tinebase_Backend_Sql(array(
70                 'tableName' => 'adb_list_m_role',
71                 'modelName' => 'Addressbook_Model_ListMemberRole',
72             ));
73         }
74
75         return $this->_memberRolesBackend;
76     }
77
78     /**
79      * the singleton pattern
80      *
81      * @return Addressbook_Controller_List
82      */
83     public static function getInstance()
84     {
85         if (self::$_instance === NULL) {
86             self::$_instance = new Addressbook_Controller_List();
87         }
88
89         return self::$_instance;
90     }
91
92     /**
93      * (non-PHPdoc)
94      *
95      * @see Tinebase_Controller_Record_Abstract::get()
96      */
97     public function get($_id, $_containerId = NULL)
98     {
99         $result = new Tinebase_Record_RecordSet('Addressbook_Model_List', array(parent::get($_id, $_containerId)));
100         $this->_removeHiddenListMembers($result);
101         return $result->getFirstRecord();
102     }
103
104     /**
105      * use contact search to remove hidden list members
106      *
107      * @param Tinebase_Record_RecordSet $lists
108      */
109     protected function _removeHiddenListMembers($lists)
110     {
111         if (count($lists) === 0) {
112             return;
113         }
114
115         $allMemberIds = array();
116         foreach ($lists as $list) {
117             $allMemberIds = array_merge($list->members, $allMemberIds);
118         }
119         $allMemberIds = array_unique($allMemberIds);
120
121         if (empty($allMemberIds)) {
122             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
123                 . ' No members found.');
124             return;
125         }
126
127         $allVisibleMemberIds = Addressbook_Controller_Contact::getInstance()->search(new Addressbook_Model_ContactFilter(array(array(
128             'field' => 'id',
129             'operator' => 'in',
130             'value' => $allMemberIds
131         ))), NULL, FALSE, TRUE);
132
133         $hiddenMemberids = array_diff($allMemberIds, $allVisibleMemberIds);
134
135         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
136             . ' Found ' . count($hiddenMemberids) . ' hidden members, removing them');
137         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
138             . print_r($hiddenMemberids, TRUE));
139
140         foreach ($lists as $list) {
141             $list->members = array_diff($list->members, $hiddenMemberids);
142         }
143     }
144
145     /**
146      * (non-PHPdoc)
147      *
148      * @see Tinebase_Controller_Record_Abstract::search()
149      */
150     public function search(Tinebase_Model_Filter_FilterGroup $_filter = NULL, Tinebase_Record_Interface $_pagination = NULL, $_getRelations = FALSE, $_onlyIds = FALSE, $_action = 'get')
151     {
152         $result = parent::search($_filter, $_pagination, $_getRelations, $_onlyIds, $_action);
153
154         if ($_onlyIds !== true) {
155             $this->_removeHiddenListMembers($result);
156         }
157
158         return $result;
159     }
160
161     /**
162      * (non-PHPdoc)
163      *
164      * @see Tinebase_Controller_Record_Abstract::getMultiple()
165      */
166     public function getMultiple($_ids, $_ignoreACL = FALSE)
167     {
168         $result = parent::getMultiple($_ids, $_ignoreACL);
169         $this->_removeHiddenListMembers($result);
170         return $result;
171     }
172
173     /**
174      * add new members to list
175      *
176      * @param  mixed $_listId
177      * @param  mixed $_newMembers
178      * @return Addressbook_Model_List
179      */
180     public function addListMember($_listId, $_newMembers)
181     {
182         try {
183             $list = $this->get($_listId);
184         } catch (Tinebase_Exception_AccessDenied $tead) {
185             $list = $this->_fixEmptyContainerId($_listId);
186             $list = $this->get($_listId);
187         }
188
189         $this->_checkGrant($list, 'update', TRUE, 'No permission to add list member.');
190
191         $list = $this->_backend->addListMember($_listId, $_newMembers);
192
193         return $this->get($list->getId());
194     }
195
196     /**
197      * fixes empty container ids / perhaps this can be removed later as all lists should have a container id!
198      *
199      * @param  mixed $_listId
200      * @return Addressbook_Model_List
201      */
202     protected function _fixEmptyContainerId($_listId)
203     {
204         $list = $this->_backend->get($_listId);
205
206         if (empty($list->container_id)) {
207             $list->container_id = $this->_getDefaultInternalAddressbook();
208             $list = $this->_backend->update($list);
209         }
210
211         return $list;
212     }
213
214     /**
215      * get default internal adb id
216      *
217      * @return string
218      */
219     protected function _getDefaultInternalAddressbook()
220     {
221         $appConfigDefaults = Admin_Controller::getInstance()->getConfigSettings();
222         $result = (isset($appConfigDefaults[Admin_Model_Config::DEFAULTINTERNALADDRESSBOOK])) ? $appConfigDefaults[Admin_Model_Config::DEFAULTINTERNALADDRESSBOOK] : NULL;
223
224         if (empty($result)) {
225             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__
226                 . ' Default internal addressbook not found. Creating new config setting.');
227             $result = Addressbook_Setup_Initialize::setDefaultInternalAddressbook()->getId();
228         }
229         return $result;
230     }
231
232     /**
233      * remove members from list
234      *
235      * @param  mixed $_listId
236      * @param  mixed $_newMembers
237      * @return Addressbook_Model_List
238      */
239     public function removeListMember($_listId, $_newMembers)
240     {
241         $list = $this->get($_listId);
242
243         $this->_checkGrant($list, 'update', TRUE, 'No permission to remove list member.');
244         $list = $this->_backend->removeListMember($_listId, $_newMembers);
245
246         return $this->get($list->getId());
247     }
248
249     /**
250      * inspect creation of one record
251      *
252      * @param   Tinebase_Record_Interface $_record
253      * @return  void
254      */
255     protected function _inspectBeforeCreate(Tinebase_Record_Interface $_record)
256     {
257         if (isset($record->type) && $record->type == Addressbook_Model_List::LISTTYPE_GROUP) {
258             throw new Addressbook_Exception_InvalidArgument('can not add list of type ' . Addressbook_Model_List::LISTTYPE_GROUP);
259         }
260     }
261
262     /**
263      * inspect creation of one record (after create)
264      *
265      * @param   Tinebase_Record_Interface $_createdRecord
266      * @param   Tinebase_Record_Interface $_record
267      * @return  void
268      */
269     protected function _inspectAfterCreate($_createdRecord, Tinebase_Record_Interface $_record)
270     {
271         $this->_fireChangeListeEvent($_createdRecord);
272     }
273
274     /**
275      * inspect update of one record
276      *
277      * @param   Tinebase_Record_Interface $_record the update record
278      * @param   Tinebase_Record_Interface $_oldRecord the current persistent record
279      * @return  void
280      */
281     protected function _inspectBeforeUpdate($_record, $_oldRecord)
282     {
283         if (isset($record->type) && $record->type == Addressbook_Model_List::LISTTYPE_GROUP) {
284             throw new Addressbook_Exception_InvalidArgument('can not update list of type ' . Addressbook_Model_List::LISTTYPE_GROUP);
285         }
286     }
287
288     /**
289      * inspect update of one record (after update)
290      *
291      * @param   Tinebase_Record_Interface $updatedRecord   the just updated record
292      * @param   Tinebase_Record_Interface $record          the update record
293      * @param   Tinebase_Record_Interface $currentRecord   the current record (before update)
294      * @return  void
295      */
296     protected function _inspectAfterUpdate($updatedRecord, $record, $currentRecord)
297     {
298         $this->_fireChangeListeEvent($updatedRecord);
299     }
300
301     /**
302      * fireChangeListeEvent
303      *
304      * @param Addressbook_Model_List $list
305      */
306     protected function _fireChangeListeEvent(Addressbook_Model_List $list)
307     {
308         $event = new Addressbook_Event_ChangeList();
309         $event->list = $list;
310         Tinebase_Event::fireEvent($event);
311     }
312
313     /**
314      * inspects delete action
315      *
316      * @param array $_ids
317      * @return array of ids to actually delete
318      */
319     protected function _inspectDelete(array $_ids)
320     {
321         $lists = $this->getMultiple($_ids);
322         foreach ($lists as $list) {
323             $event = new Addressbook_Event_DeleteList();
324             $event->list = $list;
325             Tinebase_Event::fireEvent($event);
326         }
327
328         return $_ids;
329     }
330
331     /**
332      * create or update list in addressbook sql backend
333      *
334      * @param  Tinebase_Model_Group $group
335      * @return Addressbook_Model_List
336      */
337     public function createOrUpdateByGroup(Tinebase_Model_Group $group)
338     {
339         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . print_r($group->toArray(), TRUE));
340
341         try {
342             if (empty($group->list_id)) {
343                 $list = $this->_backend->getByGroupName($group->name);
344                 if (!$list) {
345                     // jump to catch block => no list_id provided and no existing list for group found
346                     throw new Tinebase_Exception_NotFound('list_id is empty');
347                 }
348                 $group->list_id = $list->getId();
349             } else {
350                 $list = $this->_backend->get($group->list_id);
351             }
352
353             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
354                 . ' Update list ' . $group->name);
355
356             $list->name = $group->name;
357             $list->description = $group->description;
358             $list->email = $group->email;
359             $list->type = Addressbook_Model_List::LISTTYPE_GROUP;
360             $list->container_id = (empty($group->container_id)) ? $this->_getDefaultInternalAddressbook() : $group->container_id;
361             $list->members = (isset($group->members)) ? $this->_getContactIds($group->members) : array();
362
363             // add modlog info
364             Tinebase_Timemachine_ModificationLog::setRecordMetaData($list, 'update');
365
366             $list = $this->_backend->update($list);
367             $list = $this->get($list->getId());
368
369         } catch (Tinebase_Exception_NotFound $tenf) {
370             $list = $this->createByGroup($group);
371         }
372
373         return $list;
374     }
375
376     /**
377      * create new list by group
378      *
379      * @param Tinebase_Model_Group $group
380      * @return Addressbook_Model_List
381      */
382     public function createByGroup($group)
383     {
384         $list = new Addressbook_Model_List(array(
385             'name' => $group->name,
386             'description' => $group->description,
387             'email' => $group->email,
388             'type' => Addressbook_Model_List::LISTTYPE_GROUP,
389             'container_id' => (empty($group->container_id)) ? $this->_getDefaultInternalAddressbook() : $group->container_id,
390             'members' => (isset($group->members)) ? $this->_getContactIds($group->members) : array(),
391         ));
392
393         // add modlog info
394         Tinebase_Timemachine_ModificationLog::setRecordMetaData($list, 'create');
395
396         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
397             . ' Add new list ' . $group->name);
398         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
399             . ' ' . print_r($list->toArray(), TRUE));
400
401         $list = $this->_backend->create($list);
402
403         return $list;
404     }
405
406     /**
407      * get contact_ids of users
408      *
409      * @param  array $_userIds
410      * @return array
411      */
412     protected function _getContactIds($_userIds)
413     {
414         $contactIds = array();
415
416         if (empty($_userIds)) {
417             return $contactIds;
418         }
419
420         foreach ($_userIds as $userId) {
421             $user = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountId', $userId);
422             if (!empty($user->contact_id)) {
423                 $contactIds[] = $user->contact_id;
424             }
425         }
426
427         return $contactIds;
428     }
429
430     /**
431      * you can define default filters here
432      *
433      * @param Tinebase_Model_Filter_FilterGroup $_filter
434      */
435     protected function _addDefaultFilter(Tinebase_Model_Filter_FilterGroup $_filter = NULL)
436     {
437         if (!$_filter->isFilterSet('showHidden')) {
438             $hiddenFilter = $_filter->createFilter('showHidden', 'equals', FALSE);
439             $hiddenFilter->setIsImplicit(TRUE);
440             $_filter->addFilter($hiddenFilter);
441         }
442     }
443
444     /**
445      * set relations / tags / alarms
446      *
447      * @param   Tinebase_Record_Interface $updatedRecord the just updated record
448      * @param   Tinebase_Record_Interface $record the update record
449      * @param   Tinebase_Record_Interface $currentRecord   the original record if one exists
450      * @param   boolean                   $returnUpdatedRelatedData
451      * @return  Tinebase_Record_Interface
452      */
453     protected function _setRelatedData(Tinebase_Record_Interface $updatedRecord, Tinebase_Record_Interface $record, Tinebase_Record_Interface $currentRecord = null, $returnUpdatedRelatedData = FALSE)
454     {
455         if (isset($record->memberroles)) {
456             // get migration
457             // TODO add generic helper fn for this?
458             $memberrolesToSet = (!$record->memberroles instanceof Tinebase_Record_RecordSet)
459                 ? new Tinebase_Record_RecordSet(
460                     'Addressbook_Model_ListMemberRole',
461                     $record->memberroles,
462                     /* $_bypassFilters */ true
463                 ) : $record->memberroles;
464
465             foreach ($memberrolesToSet as $memberrole) {
466                 foreach (array('contact_id', 'list_role_id', 'list_id') as $field) {
467                     if (isset($memberrole[$field]['id'])) {
468                         $memberrole[$field] = $memberrole[$field]['id'];
469                     }
470                 }
471             }
472
473             $currentMemberroles = $this->_getMemberRoles($record);
474             $diff = $currentMemberroles->diff($memberrolesToSet);
475             if (count($diff['added']) > 0) {
476                 $diff['added']->list_id = $updatedRecord->getId();
477                 foreach ($diff['added'] as $memberrole) {
478                     $this->_getMemberRolesBackend()->create($memberrole);
479                 }
480             }
481             if (count($diff['removed']) > 0) {
482                 $this->_getMemberRolesBackend()->delete($diff['removed']->getArrayOfIds());
483             }
484         }
485
486         $result = parent::_setRelatedData($updatedRecord, $record, $currentRecord, $returnUpdatedRelatedData);
487
488         return $result;
489     }
490
491     /**
492      * add related data to record
493      *
494      * @param Tinebase_Record_Interface $record
495      */
496     protected function _getRelatedData($record)
497     {
498         $memberRoles = $this->_getMemberRoles($record);
499         if (count($memberRoles) > 0) {
500             $record->memberroles = $memberRoles;
501         }
502         parent::_getRelatedData($record);
503     }
504
505     protected function _getMemberRoles($record)
506     {
507         $result = $this->_getMemberRolesBackend()->getMultipleByProperty($record->getId(), 'list_id');
508         return $result;
509     }
510
511     /**
512      * get all lists given contact is member of
513      *
514      * @param $contact
515      * @return array
516      */
517     public function getMemberships($contact)
518     {
519         return $this->_backend->getMemberships($contact);
520     }
521 }