4011fde9c7f9d72cc8a0e02b626d92200d0342a2
[tine20] / tine20 / Tinebase / Preference / Abstract.php
1 <?php
2 /**
3  * Tine 2.0
4  *
5  * @package     Tinebase
6  * @subpackage  Preference
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Philipp Schüle <p.schuele@metaways.de>
9  * @copyright   Copyright (c) 2009-2013 Metaways Infosystems GmbH (http://www.metaways.de)
10  *
11  * @todo        make this a real controller + singleton (create extra sql backend)
12  * @todo        add getAllprefsForApp (similar to config) to get all prefs for the registry in one request
13  * @todo        add getPreference function that returns the complete record
14  * @todo        allow free-form preferences
15  * @todo        support group preferences
16  */
17
18 /**
19  * abstract backend for preferences
20  *
21  * @package     Tinebase
22  * @subpackage  Preference
23  */
24 abstract class Tinebase_Preference_Abstract extends Tinebase_Backend_Sql_Abstract
25 {
26     /**
27      * yes no options
28      *
29      * @staticvar string
30      */
31     const YES_NO_OPTIONS = 'yesnoopt';
32
33     /**
34      * default persistent filter
35      */
36     const DEFAULTPERSISTENTFILTER = 'defaultpersistentfilter';
37     
38     /**
39      * name of the filter representing the last used filter
40      */
41     const LASTUSEDFILTER = '_lastusedfilter_';
42     
43     /**
44      * default container options
45      *
46      * @staticvar string
47      */
48     const DEFAULTCONTAINER_OPTIONS = 'defaulcontaineropt';
49     
50     /**************************** backend settings *********************************/
51
52     /**
53      * Table name without prefix
54      *
55      * @var string
56      */
57     protected $_tableName = 'preferences';
58
59     /**
60      * Model name
61      *
62      * @var string
63      */
64     protected $_modelName = 'Tinebase_Model_Preference';
65     
66     /**
67      * application
68      *
69      * @var string
70      */
71     protected $_application = 'Tinebase';
72     
73     /**
74      * preference names that have no default option
75      * 
76      * @var array
77      */
78     protected $_skipDefaultOption = array();
79     
80     /**************************** public abstract functions *********************************/
81
82     /**
83      * get all possible application prefs
84      * - every app should overwrite this
85      *
86      * @return  array   all application prefs
87      */
88     abstract public function getAllApplicationPreferences();
89
90     /**
91      * get translated right descriptions
92      *
93      * @return  array with translated descriptions for this applications preferences
94      */
95     abstract public function getTranslatedPreferences();
96
97     /**
98      * get preference defaults if no default is found in the database
99      *
100      * @param string $_preferenceName
101      * @param string|Tinebase_Model_User $_accountId
102      * @param string $_accountType
103      * @return Tinebase_Model_Preference
104      */
105     abstract public function getApplicationPreferenceDefaults($_preferenceName, $_accountId = NULL, $_accountType = Tinebase_Acl_Rights::ACCOUNT_TYPE_USER);
106
107     /**************************** public interceptior functions *********************************/
108
109     /**
110      * get interceptor (alias for getValue())
111      *
112      * @param string $_preferenceName
113      * @return string
114      */
115     public function __get($_preferenceName)
116     {
117         return $this->getValue($_preferenceName);
118     }
119
120     /**
121      * set interceptor (alias for setValue())
122      *
123      * @param string $_preferenceName
124      * @param string $_value
125      */
126     public function __set($_preferenceName, $_value) {
127         if (in_array($_preferenceName, $this->getAllApplicationPreferences())) {
128             $this->setValue($_preferenceName, $_value);
129         }
130     }
131
132     /**************************** public functions *********************************/
133
134     /**
135      * search for preferences
136      * 
137      * @param  Tinebase_Model_Filter_FilterGroup    $_filter
138      * @param  Tinebase_Model_Pagination            $_pagination
139      * @param  boolean                              $_onlyIds
140      * @return Tinebase_Record_RecordSet|array of preferences / pref ids
141      */
142     public function search(Tinebase_Model_Filter_FilterGroup $_filter = NULL, Tinebase_Model_Pagination $_pagination = NULL, $_onlyIds = FALSE)
143     {
144         if ($_filter === null) {
145             $_filter = new Tinebase_Model_PreferenceFilter();
146         }
147         
148         // make sure account is set in filter
149         $userId = Tinebase_Core::getUser()->getId();
150         if (! $_filter->isFilterSet('account')) {
151             $accountFilter = $_filter->createFilter('account', 'equals', array(
152                 'accountId'   => (string) $userId, 
153                 'accountType' => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER
154             ));
155             $_filter->addFilter($accountFilter);
156         } else {
157             // only admins can search for other users prefs
158             $accountFilter = $_filter->getAccountFilter();
159             $accountFilterValue = $accountFilter->getValue();
160             if ($accountFilterValue['accountId'] != $userId && $accountFilterValue['accountType'] == Tinebase_Acl_Rights::ACCOUNT_TYPE_USER) {
161                 if (!Tinebase_Acl_Roles::getInstance()->hasRight($this->_application, Tinebase_Core::getUser()->getId(), Tinebase_Acl_Rights_Abstract::ADMIN)) {
162                     return new Tinebase_Record_RecordSet('Tinebase_Model_Preference');
163                 }
164             }
165         }
166         
167         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . print_r($_filter->toArray(), TRUE));
168         
169         $paging = new Tinebase_Model_Pagination(array(
170             'dir'       => 'ASC',
171             'sort'      => array('name')
172         ));
173         
174         $allPrefs = parent::search($_filter, $_pagination, $_onlyIds);
175
176         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . print_r((is_array($allPrefs)) ? $allPrefs : $allPrefs->toArray(), TRUE));
177         
178         if (! $_onlyIds) {
179             $this->_addDefaultAndRemoveUndefinedPrefs($allPrefs, $_filter);
180             
181             // get single matching preferences for each different pref
182             $result = $this->getMatchingPreferences($allPrefs);
183         } else {
184             $result = $allPrefs;
185         }
186         
187         return $result;
188     }
189     
190     /**
191      * add default preferences to and remove undefined preferences from record set
192      * 
193      * @param Tinebase_Record_RecordSet $_prefs
194      * @param Tinebase_Model_Filter_FilterGroup $_filter
195      */
196     protected function _addDefaultAndRemoveUndefinedPrefs(Tinebase_Record_RecordSet $_prefs, Tinebase_Model_Filter_FilterGroup $_filter)
197     {
198         $allAppPrefs = $this->getAllApplicationPreferences();
199         
200         // add default prefs if not already in array (only if no name or type filters are set)
201         if (! $_filter->isFilterSet('name') && ! $_filter->isFilterSet('type')) {
202             $missingDefaultPrefs = array_diff($allAppPrefs, $_prefs->name);
203             foreach ($missingDefaultPrefs as $prefName) {
204                 $_prefs->addRecord($this->getApplicationPreferenceDefaults($prefName));
205             }
206         }
207         // remove all prefs that are not defined
208         $undefinedPrefs = array_diff($_prefs->name, $allAppPrefs);
209         if (count($undefinedPrefs) > 0) {
210             $_prefs->addIndices(array('name'));
211             foreach ($undefinedPrefs as $undefinedPrefName) {
212                 $record = $_prefs->find('name', $undefinedPrefName);
213                 $_prefs->removeRecord($record);
214                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Removed undefined preference from result: ' . $undefinedPrefName);
215             }
216         }
217     }
218     
219     /**
220      * do some call json functions if preferences name match
221      * - every app should define its own special handlers
222      *
223      * @param Tinebase_Frontend_Json_Abstract $_jsonFrontend
224      * @param string $name
225      * @param string $value
226      * @param string $appName
227      */
228     public function doSpecialJsonFrontendActions(Tinebase_Frontend_Json_Abstract $_jsonFrontend, $name, $value, $appName)
229     {
230     }
231
232     /**
233      * get value of preference
234      *
235      * @param string $_preferenceName
236      * @param string $_default return this if no preference found and default given
237      * @return string
238      * @throws Tinebase_Exception_NotFound if no default given and no pref found
239      */
240     public function getValue($_preferenceName, $_default = NULL)
241     {
242         $accountId = $this->_getAccountId();
243
244         try {
245             $result = $this->getValueForUser(
246                 $_preferenceName, $accountId,
247                 ($accountId === '0')
248                 ? Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE
249                 : Tinebase_Acl_Rights::ACCOUNT_TYPE_USER
250             );
251         } catch (Tinebase_Exception_NotFound $tenf) {
252             if ($_default !== NULL) {
253                 $result = $_default;
254             } else {
255                 throw $tenf;
256             }
257         }
258         
259         if ($result == Tinebase_Model_Preference::DEFAULT_VALUE) {
260             $result = $_default;
261         }
262
263         return $result;
264     }
265     
266     /**
267      * get account id
268      * 
269      * @return string
270      */
271     protected function _getAccountId()
272     {
273         return (is_object(Tinebase_Core::getUser())) ? Tinebase_Core::getUser()->getId() : '0';
274     }
275
276     /**
277      * get value of preference for a user/group
278      *
279      * @param string $_preferenceName
280      * @param integer $_accountId
281      * @param string $_accountType
282      * @return string
283      * @throws Tinebase_Exception_NotFound
284      */
285     public function getValueForUser($_preferenceName, $_accountId, $_accountType = Tinebase_Acl_Rights::ACCOUNT_TYPE_USER)
286     {
287         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
288             . ' Get value for ' . $_preferenceName . ' of account id '. $_accountId . ' / ' . $_accountType);
289         
290         $queryResult = $this->_getPrefs($_preferenceName, $_accountId, $_accountType);
291         
292         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
293             . ' ' . print_r($queryResult, true));
294         
295         if (! $queryResult) {
296             $pref = $this->getApplicationPreferenceDefaults($_preferenceName, $_accountId, $_accountType);
297         } else {
298             $pref = $this->_getMatchingPreference($this->_rawDataToRecordSet($queryResult));
299         }
300         
301         $result = $pref->value;
302         
303         return $result;
304     }
305
306     /**
307      * get preferences
308      * 
309      * @param string $_preferenceName
310      * @param string $_accountId
311      * @param string $_accountType
312      * @return array result
313      */
314     protected function _getPrefs($_preferenceName, $_accountId = '0', $_accountType = Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE)
315     {
316         $select = $this->_getSelect();
317         
318         $appId = Tinebase_Application::getInstance()->getApplicationByName($this->_application)->getId();
319         $filter = new Tinebase_Model_PreferenceFilter(array(
320             array('field'     => 'account',         'operator'  => 'equals', 'value'     => array(
321                     'accountId' => $_accountId, 'accountType' => $_accountType)
322             ),
323             array('field'     => 'name',            'operator'  => 'equals', 'value'     => $_preferenceName),
324             array('field'     => 'application_id',  'operator'  => 'equals', 'value'     => $appId),
325         ));
326         Tinebase_Backend_Sql_Filter_FilterGroup::appendFilters($select, $filter, $this);
327         
328         $stmt = $this->_db->query($select);
329         $queryResult = $stmt->fetchAll();
330         
331         return $queryResult;
332     }
333     
334     /**
335      * get all users who have the preference $_preferenceName = $_value
336      *
337      * @param string $_preferenceName
338      * @param string $_value
339      * @param array $_limitToUserIds [optional]
340      * @return array of user ids
341      */
342     public function getUsersWithPref($_preferenceName, $_value, $_limitToUserIds = array())
343     {
344         $result = array();
345
346         $queryResult = $this->_getPrefs($_preferenceName);
347
348         if (empty($queryResult)) {
349             $pref = $this->getApplicationPreferenceDefaults($_preferenceName);
350         } else {
351             $pref = new Tinebase_Model_Preference($queryResult[0]);
352         }
353
354         if ($pref->value == $_value) {
355
356             if (! empty($_limitToUserIds)) {
357                 $result = Tinebase_User::getInstance()->getMultiple($_limitToUserIds)->getArrayOfIds();
358             } else {
359                 $result = Tinebase_User::getInstance()->getUsers()->getArrayOfIds();
360             }
361
362             if ($pref->type == Tinebase_Model_Preference::TYPE_FORCED) {
363                 // forced: get all users -> do nothing here
364
365             } else if ($pref->type == Tinebase_Model_Preference::TYPE_DEFAULT) {
366                 // default: remove all users/groups who don't have default
367                 $filter = new Tinebase_Model_PreferenceFilter(array(
368                     array('field'   => 'account_type',    'operator'  => 'equals', 'value' => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER),
369                     array('field'   => 'name',            'operator'  => 'equals', 'value' => $_preferenceName),
370                     array('field'   => 'value',           'operator'  => 'not',    'value' => $_value),
371                 ));
372                 $accountsWithOtherValues = $this->search($filter)->account_id;
373                 $result = array_diff($result, $accountsWithOtherValues);
374
375             } else {
376                 throw new Tinebase_Exception_UnexpectedValue('Preference should be of type "forced" or "default".');
377             }
378
379         } else {
380             // not default or forced: get all users/groups who have the setting
381             $filter = new Tinebase_Model_PreferenceFilter(array(
382                 array('field'   => 'account_type',    'operator'  => 'equals', 'value' => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER),
383                 array('field'   => 'name',            'operator'  => 'equals', 'value' => $_preferenceName),
384                 array('field'   => 'value',           'operator'  => 'equals', 'value' => $_value),
385             ));
386             $result = $this->search($filter)->account_id;
387         }
388
389         return $result;
390     }
391
392     /**
393      * set value of preference
394      *
395      * @param string $_preferenceName
396      * @param string $_value
397      */
398     public function setValue($_preferenceName, $_value)
399     {
400         $accountId = $this->_getAccountId();
401         return $this->setValueForUser($_preferenceName, $_value, $accountId);
402     }
403
404     /**
405      * set value of preference for a user/group
406      *
407      * @param string $_preferenceName
408      * @param string $_value
409      * @param integer $_userId
410      * @param boolean $_ignoreAcl
411      * @return void
412      * 
413      * @todo use generic savePreference fn
414      */
415     public function setValueForUser($_preferenceName, $_value, $_accountId, $_ignoreAcl = FALSE)
416     {
417         // check acl first
418         $userId = $this->_getAccountId();
419         if(!$_ignoreAcl){
420             if (
421                 $_accountId !== $userId
422                 && !Tinebase_Acl_Roles::getInstance()->hasRight($this->_application, $userId, Tinebase_Acl_Rights_Abstract::ADMIN)
423             ) {
424                 throw new Tinebase_Exception_AccessDenied('You are not allowed to change the preferences.');
425             }
426         }
427         // check if already there -> update
428         $queryResult = $this->_getPrefs($_preferenceName, $_accountId, Tinebase_Acl_Rights::ACCOUNT_TYPE_USER);
429         $prefArray = NULL;
430         // need to fetch preference for user account as _getPrefs() returns prefs for ANYONE, too
431         foreach ($queryResult as $row) {
432             if ($row['account_type'] === Tinebase_Acl_Rights::ACCOUNT_TYPE_USER) {
433                 $prefArray = $row;
434                 break;
435             }
436         }
437         
438         if ($prefArray === NULL) {
439             if ($_value !== Tinebase_Model_Preference::DEFAULT_VALUE) {
440                 // no preference yet -> create
441                 $preference = new Tinebase_Model_Preference(array(
442                     'application_id'    => $appId = Tinebase_Application::getInstance()->getApplicationByName($this->_application)->getId(),
443                     'name'              => $_preferenceName,
444                     'value'             => $_value,
445                     'account_id'        => $_accountId,
446                     'account_type'      => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER,
447                     'type'              => Tinebase_Model_Preference::TYPE_USER
448                 ));
449                 $this->create($preference);
450                 $action = 'Created';
451             } else {
452                 $action = 'No action required';
453             }
454
455         } else {
456             $preference = $this->_rawDataToRecord($prefArray);
457
458             if ($preference->locked && ! $_ignoreAcl
459                 // TODO allow this for admins?
460                 /* && !Tinebase_Acl_Roles::getInstance()->hasRight(
461                     $this->_application,
462                     $userId,
463                     Tinebase_Acl_Rights_Abstract::ADMIN
464                 ) */) {
465                 throw new Tinebase_Exception_AccessDenied('You are not allowed to change the locked preference.');
466             }
467
468             if ($_value === Tinebase_Model_Preference::DEFAULT_VALUE) {
469                 // delete if new value = use default
470                 $this->delete($preference->getId());
471                 $action = 'Reset';
472             } else {
473                 $preference->value = $_value;
474                 $this->update($preference);
475                 $action = 'Updated';
476             }
477         }
478         
479         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
480             . ' ' . $action . ': ' . $_preferenceName . ' for user ' . $_accountId . ' -> ' . $_value);
481     }
482
483     /**
484      * get matching preferences from recordset with multiple prefs)
485      *
486      * @param Tinebase_Record_RecordSet $_preferences
487      */
488     public function getMatchingPreferences(Tinebase_Record_RecordSet $_preferences)
489     {
490         $_preferences->addIndices(array('name'));
491
492         // get unique names, the matching preference and add it to result set
493         $result = new Tinebase_Record_RecordSet('Tinebase_Model_Preference');
494         $uniqueNames = array_unique($_preferences->name);
495         foreach ($uniqueNames as $name) {
496             $singlePrefSet = $_preferences->filter('name', $name);
497             $result->addRecord($this->_getMatchingPreference($singlePrefSet));
498         }
499
500         return $result;
501     }
502
503     /**
504      * resolve preference options and add 'use default'
505      * 
506      * @param Tinebase_Model_Preference $_preference
507      */
508     public function resolveOptions(Tinebase_Model_Preference $_preference)
509     {
510         $options = array();
511         if (! empty($_preference->options)) {
512              $options = $this->_convertXmlOptionsToArray($_preference->options);
513         }
514         
515         // get default pref
516         if (! in_array($_preference->name, $this->_skipDefaultOption)) {
517             $default = $this->_getDefaultPreference($_preference->name);
518             
519             // check if value is in options and use that label
520             $valueLabel = $default->value;
521             foreach ($options as $option) {
522                 if ($default->value == $option[0]) {
523                     $valueLabel = $option[1];
524                     break;
525                 }
526             }
527             // add default setting to the top of options
528             $defaultLabel = Tinebase_Translation::getTranslation('Tinebase')->_('default') . 
529                 ' (' . $valueLabel . ')';
530             
531             array_unshift($options, array(
532                 Tinebase_Model_Preference::DEFAULT_VALUE,
533                 $defaultLabel,
534             ));
535         }
536         
537         $_preference->options = $options;
538     }
539     
540     /**
541      * convert options xml string to array
542      *
543      * @param string $_xmlOptions
544      * @return array
545      */
546     protected function _convertXmlOptionsToArray($_xmlOptions)
547     {
548         $result = array();
549         $optionsXml = new SimpleXMLElement($_xmlOptions);
550
551         if ($optionsXml->special) {
552            $result = $this->_getSpecialOptions($optionsXml->special);
553         } else {
554             foreach ($optionsXml->option as $option) {
555                 $result[] = array((string)$option->value, (string)$option->label);
556             }
557         }
558
559         return $result;
560     }
561
562     /**
563      * delete user preference by name
564      *
565      * @param string $_preferenceName
566      * @return void
567      */
568     public function deleteUserPref($_preferenceName)
569     {
570         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Deleting pref ' . $_preferenceName);
571
572         $where = array(
573         $this->_db->quoteInto($this->_db->quoteIdentifier('name')           . ' = ?', $_preferenceName),
574         $this->_db->quoteInto($this->_db->quoteIdentifier('account_id')     . ' = ?', Tinebase_Core::getUser()->getId()),
575         $this->_db->quoteInto($this->_db->quoteIdentifier('account_type')   . ' = ?', Tinebase_Acl_Rights::ACCOUNT_TYPE_USER)
576         );
577
578         $this->_db->delete($this->_tablePrefix . $this->_tableName, $where);
579     }
580
581     /**
582      * Creates new entry
583      *
584      * @param   Tinebase_Record_Interface $_record
585      * @return  Tinebase_Record_Interface
586      * @throws  Tinebase_Exception_UnexpectedValue
587      */
588     public function create(Tinebase_Record_Interface $_record)
589     {
590         // check if personal only and account type=anyone -> throw exception
591         if ($_record->personal_only && $_record->account_type == Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE) {
592             $message = 'It is not allowed to set this preference for anyone.';
593             Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . ' ' . $message);
594             throw new Tinebase_Exception_UnexpectedValue($message);
595         }
596
597         return parent::create($_record);
598     }
599
600     /**
601      * save admin preferences for this app
602      * 
603      * @param array $_data
604      * @param boolean $_adminMode
605      * @return void
606      * 
607      * @todo use generic savePreference fn
608      */
609     public function saveAdminPreferences($_data)
610     {
611         // only admins are allowed to update app pref defaults/forced prefs
612         if (! Tinebase_Acl_Roles::getInstance()->hasRight($this->_application, Tinebase_Core::getUser()->getId(), Tinebase_Acl_Rights_Abstract::ADMIN)) {
613             throw new Tinebase_Exception_AccessDenied('You are not allowed to change the preference defaults.');
614         }
615         
616         // create prefs that don't exist in the db
617         foreach ($_data as $id => $prefData) {
618             if (preg_match('/^default/', $id)
619                 && (isset($prefData['name']) || array_key_exists('name', $prefData))
620                 && ($prefData['type'] == Tinebase_Model_Preference::TYPE_FORCED || (string)$prefData['value'] != Tinebase_Model_Preference::DEFAULT_VALUE)
621             ) {
622                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
623                     . ' Create admin pref: ' . $prefData['name'] . ' = ' . $prefData['value']);
624                 $newPref = $this->getApplicationPreferenceDefaults($prefData['name']);
625                 $newPref->value = $prefData['value'];
626                 $newPref->type = ($prefData['type'] == Tinebase_Model_Preference::TYPE_FORCED) ? $prefData['type'] : Tinebase_Model_Preference::TYPE_ADMIN;
627                 unset($newPref->id);
628                 $this->create($newPref);
629                 
630                 unset($_data[$id]);
631             }
632         }
633         
634         // update default/forced preferences
635         $records = $this->getMultiple(array_keys($_data));
636         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
637             . ' Saving admin prefs: ' . print_r($records->name, TRUE));
638         foreach ($records as $preference) {
639             if ($_data[$preference->getId()]['value'] == Tinebase_Model_Preference::DEFAULT_VALUE) {
640                 $this->delete($preference->getId());
641             } else {
642                 $preference->value = $_data[$preference->getId()]['value'];
643                 $preference->type = ($_data[$preference->getId()]['type'] == Tinebase_Model_Preference::TYPE_FORCED) ? $_data[$preference->getId()]['type'] : Tinebase_Model_Preference::TYPE_ADMIN;
644                 $this->update($preference);
645             }
646         }
647     }
648
649     /**************************** protected functions *********************************/
650
651     /**
652      * get matching preference from result set
653      * - order: forced > user > group > default
654      * - get options xml from default pref if available
655      *
656      * @param Tinebase_Record_RecordSet $_preferences
657      * @return Tinebase_Model_Preference
658      */
659     protected function _getMatchingPreference(Tinebase_Record_RecordSet $_preferences)
660     {
661         //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . print_r($_preferences->toArray(), TRUE));
662         $_preferences->addIndices(array('type', 'account_type'));
663
664         if (count($_preferences) == 1) {
665             $result = $_preferences->getFirstRecord();
666         } else {
667             // check forced
668             $forced = $_preferences->filter('type', Tinebase_Model_Preference::TYPE_FORCED);
669             if (count($forced) > 0) {
670                 $_preferences = $forced;
671             }
672
673             // check user
674             $user = $_preferences->filter('account_type', Tinebase_Acl_Rights::ACCOUNT_TYPE_USER);
675             if (count($user) > 0) {
676                 $result = $user->getFirstRecord();
677             } else {
678                 // check group
679                 $group = $_preferences->filter('account_type', Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP);
680                 if (count($group) > 0) {
681                     $result = $group->getFirstRecord();
682                 } else {
683                     // get first record of the remaining result set (defaults/anyone)
684                     $result = $_preferences->getFirstRecord();
685                 }
686             }
687         }
688
689         // add options and perhaps value from default preference
690         if ($result->type !== Tinebase_Model_Preference::TYPE_DEFAULT && is_object(Tinebase_Core::getUser())) {
691             $defaultPref = $this->_getDefaultPreference($result->name, $_preferences);
692             $result->options = $defaultPref->options;
693         }
694
695         return $result;
696     }
697     
698     /**
699      * get default preference (from recordset, db or app defaults)
700      * 
701      * @param string $_preferenceName
702      * @param Tinebase_Record_RecordSet $_preferences
703      */
704     protected function _getDefaultPreference($_preferenceName, $_preferences = NULL)
705     {
706         if ($_preferences !== NULL) {
707             $defaults = $_preferences->filter('type', Tinebase_Model_Preference::TYPE_ADMIN);
708         } else {
709             $defaults = $this->search(new Tinebase_Model_PreferenceFilter(array(array(
710                 'field'     => 'type',
711                 'operator'  => 'equals',
712                 'value'     => Tinebase_Model_Preference::TYPE_ADMIN
713             ), array(
714                 'field'     => 'name',
715                 'operator'  => 'equals',
716                 'value'     => $_preferenceName
717             ), array(
718                 'field'     => 'account_id',
719                 'operator'  => 'equals',
720                 'value'     => '0'
721             ), array(
722                 'field'     => 'application_id',
723                 'operator'  => 'equals',
724                 'value'     => Tinebase_Application::getInstance()->getApplicationByName($this->_application)->getId()
725             ))));
726         }
727         
728         if (count($defaults) > 0) {
729             $defaultPref = $defaults->getFirstRecord();
730         } else {
731             $defaultPref = $this->getApplicationPreferenceDefaults($_preferenceName);
732         }
733         
734         return $defaultPref;
735     }
736
737     /**
738      * return base default preference
739      *
740      * @param string $_preferenceName
741      * @return Tinebase_Model_Preference
742      */
743     protected function _getDefaultBasePreference($_preferenceName)
744     {
745         return new Tinebase_Model_Preference(array(
746             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName($this->_application)->getId(),
747             'name'              => $_preferenceName,
748             'account_id'        => 0,
749             'account_type'      => Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE,
750             'type'              => Tinebase_Model_Preference::TYPE_DEFAULT,
751             'options'           => '<?xml version="1.0" encoding="UTF-8"?>
752                 <options>
753                     <special>' . $_preferenceName . '</special>
754                 </options>',
755             'id'                => 'default' . Tinebase_Record_Abstract::generateUID(33),
756             'value'             => Tinebase_Model_Preference::DEFAULT_VALUE,
757         ), TRUE);
758     }
759
760     /**
761      * overwrite this to add more special options for other apps
762      *
763      * - result array has to have the following format:
764      *  array(
765      *      array('value1', 'label1'),
766      *      array('value2', 'label2'),
767      *      ...
768      *  )
769      *
770      * @param  string $_value
771      * @return array
772      */
773     protected function _getSpecialOptions($_value)
774     {
775         $result = array();
776
777         switch ($_value) {
778
779             case self::YES_NO_OPTIONS:
780                 $locale = Tinebase_Core::get(Tinebase_Core::LOCALE);
781                 $question = Zend_Locale::getTranslationList('Question', $locale);
782
783                 list($yes, $dummy) = explode(':', $question['yes']);
784                 list($no, $dummy) = explode(':', $question['no']);
785
786                 $result[] = array(0, $no);
787                 $result[] = array(1, $yes);
788                 break;
789
790             case self::DEFAULTCONTAINER_OPTIONS:
791                 $result = $this->_getDefaultContainerOptions();
792                 break;
793                 
794             case self::DEFAULTPERSISTENTFILTER:
795                 $result = Tinebase_PersistentFilter::getPreferenceValues($this->_application);
796                 break;
797                     
798             default:
799                 throw new Tinebase_Exception_NotFound("Special option '{$_value}' not found.");
800         }
801
802         return $result;
803     }
804     
805     /**
806      * get all containers of current user and shared containers for app
807      * 
808      * @param string $_appName
809      * @return array
810      */
811     protected function _getDefaultContainerOptions($_appName = NULL)
812     {
813         $result = array();
814         $appName = ($_appName !== NULL) ? $_appName : $this->_application;
815         
816         $myContainers = Tinebase_Container::getInstance()->getPersonalContainer(Tinebase_Core::getUser(), $appName, Tinebase_Core::getUser(), Tinebase_Model_Grants::GRANT_ADD);
817         $sharedAddContainers = Tinebase_Container::getInstance()->getSharedContainer(Tinebase_Core::getUser(), $appName, Tinebase_Model_Grants::GRANT_ADD);
818         $sharedReadContainers = Tinebase_Container::getInstance()->getSharedContainer(Tinebase_Core::getUser(), $appName, Tinebase_Model_Grants::GRANT_READ);
819
820         foreach ($myContainers as $container) {
821             $result[] = array($container->getId(), $container->name);
822         }
823         foreach($sharedAddContainers as $container) {
824             if ($sharedReadContainers->getById($container->getId())) {
825                 $result[] = array($container->getId(), $container->name);
826             }
827         }
828         return $result;
829     }
830     
831     /**
832      * adds defaults to default container pref
833      * 
834      * @param Tinebase_Model_Preference $_preference
835      * @param string|Tinebase_Model_User $_accountId
836      * @param string $_appName
837      * @param string $_optionName
838      */
839     protected function _getDefaultContainerPreferenceDefaults(Tinebase_Model_Preference $_preference, $_accountId, $_appName = NULL, $_optionName = self::DEFAULTCONTAINER_OPTIONS)
840     {
841         $appName = ($_appName !== NULL) ? $_appName : $this->_application;
842         
843         $accountId = ($_accountId) ? $_accountId : Tinebase_Core::getUser()->getId();
844         $containers = Tinebase_Container::getInstance()->getPersonalContainer($accountId, $appName, $accountId, 0, true);
845         
846         $_preference->value  = $containers->sort('creation_time')->getFirstRecord()->getId();
847         $_preference->options = '<?xml version="1.0" encoding="UTF-8"?>
848             <options>
849                 <special>' . $_optionName . '</special>
850             </options>';
851     }
852 }