7e4792beb59594ac5145b3e987e10c853cb5f419
[tine20] / tine20 / Tinebase / Frontend / Json.php
1 <?php
2 /**
3  * Tine 2.0
4  * 
5  * @package     Tinebase
6  * @subpackage  Server
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @copyright   Copyright (c) 2007-2014 Metaways Infosystems GmbH (http://www.metaways.de)
9  * @author      Lars Kneschke <l.kneschke@metaways.de>
10  * 
11  */
12
13 /**
14  * Json interface to Tinebase
15  * 
16  * @package     Tinebase
17  * @subpackage  Server
18  */
19 class Tinebase_Frontend_Json extends Tinebase_Frontend_Json_Abstract
20 {
21     const REQUEST_TYPE = 'JSON-RPC';
22     
23     /**
24      *
25      * @var boolean
26      */
27     protected $_hasCaptcha = null;
28     
29     /**
30      * wait for changes
31      * 
32      * @todo do we still need this?
33      */
34     public function ping()
35     {
36         Tinebase_Session::writeClose(true);
37         sleep(10);
38         return array('changes' => 'contacts');
39     }
40     
41     /**
42      * get list of translated country names
43      * 
44      * Wrapper for {@see Tinebase_Core::getCountrylist}
45      * 
46      * @return array list of countrys
47      */
48     public function getCountryList()
49     {
50         return Tinebase_Translation::getCountryList();
51     }
52     
53     /**
54      * returns list of all available translations
55      *
56      * @return array list of all available translations
57      */
58     public function getAvailableTranslations()
59     {
60         $availableTranslations = Tinebase_Translation::getAvailableTranslations();
61         foreach($availableTranslations as &$info) {
62             unset($info['path']);
63         }
64         
65         return array(
66             'results'    => array_values($availableTranslations),
67             'totalcount' => count($availableTranslations)
68         );
69     }
70     
71     /**
72      * sets locale
73      *
74      * @param  string $localeString
75      * @param  bool   $saveaspreference
76      * @param  bool   $setcookie
77      * @return array
78      */
79     public function setLocale($localeString, $saveaspreference, $setcookie)
80     {
81         Tinebase_Core::setupUserLocale($localeString);
82         
83         if ($saveaspreference && is_object(Tinebase_Core::getUser())) {
84             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
85                 . " Saving locale: " . $localeString);
86             Tinebase_Core::getPreference()->{Tinebase_Preference::LOCALE} = $localeString;
87         }
88         
89         // save in cookie (expires in 365 days)
90         if ($setcookie) {
91             setcookie('TINE20LOCALE', $localeString, time()+60*60*24*365);
92         }
93         
94         return array(
95             'success'      => TRUE
96         );
97     }
98     
99     /**
100      * sets timezone
101      *
102      * @param  string $timezoneString
103      * @param  bool   $saveaspreference
104      * @return string
105      */
106     public function setTimezone($timezoneString, $saveaspreference)
107     {
108         $timezone = Tinebase_Core::setupUserTimezone($timezoneString, $saveaspreference);
109         
110         return $timezone;
111     }
112     
113     /**
114      * get users
115      *
116      * @param string $filter
117      * @param string $sort
118      * @param string $dir
119      * @param int $start
120      * @param int $limit
121      * @return array with results array & totalcount (int)
122      */
123     public function getUsers($filter, $sort, $dir, $start, $limit)
124     {
125         $result = array(
126             'results'     => array(),
127             'totalcount'  => 0
128         );
129         
130         if($rows = Tinebase_User::getInstance()->getUsers($filter, $sort, $dir, $start, $limit)) {
131             $result['results']    = $rows->toArray();
132             if($start == 0 && count($result['results']) < $limit) {
133                 $result['totalcount'] = count($result['results']);
134             } else {
135                 //$result['totalcount'] = $backend->getCountByAddressbookId($addressbookId, $filter);
136             }
137         }
138         
139         return $result;
140     }
141     
142     /**
143      * Search for roles
144      *
145      * @param  array $_filter
146      * @param  array $_paging
147      * @return array
148      */
149     public function searchRoles($filter, $paging)
150     {
151         $result = array(
152             'results'     => array(),
153             'totalcount'  => 0
154         );
155         
156         $filter = new Tinebase_Model_RoleFilter(array(
157             'name'        => '%' . $filter[0]['value'] . '%',
158             'description' => '%' . $filter[0]['value'] . '%'
159         ));
160         
161         $paging['sort'] = isset($paging['sort']) ? $paging['sort'] : 'name';
162         $paging['dir'] = isset($paging['dir']) ? $paging['dir'] : 'ASC';
163         
164         $result['results'] = Tinebase_Acl_Roles::getInstance()->searchRoles($filter, new Tinebase_Model_Pagination($paging))->toArray();
165         $result['totalcount'] = Tinebase_Acl_Roles::getInstance()->searchCount($filter);
166         
167         return $result;
168     }
169     
170     /**
171      * change password of user
172      *
173      * @param  string $oldPassword the old password
174      * @param  string $newPassword the new password
175      * @return array
176      */
177     public function changePassword($oldPassword, $newPassword)
178     {
179         $response = array(
180             'success'      => TRUE
181         );
182         
183         try {
184             Tinebase_Controller::getInstance()->changePassword($oldPassword, $newPassword);
185         } catch (Tinebase_Exception $e) {
186             $response = array(
187                 'success'      => FALSE,
188                 'errorMessage' => "New password could not be set! Error: " . $e->getMessage()
189             );
190         }
191         
192         return $response;
193     }
194     
195     /**
196      * clears state
197      *
198      * @param  string $name
199      * @return void
200      */
201     public function clearState($name)
202     {
203         Tinebase_State::getInstance()->clearState($name);
204     }
205     
206     /**
207      * retuns all states
208      *
209      * @return array of name => value
210      */
211     public function loadState()
212     {
213         return Tinebase_State::getInstance()->loadStateInfo();
214     }
215     
216     /**
217      * set state
218      *
219      * @param  string $name
220      * @param  string $value
221      * @return void
222      */
223     public function setState($name, $value)
224     {
225         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " Setting state: {$name} -> {$value}");
226         Tinebase_State::getInstance()->setState($name, $value);
227     }
228     
229     /**
230      * adds a new personal tag
231      *
232      * @param  array $tag
233      * @return array
234      */
235     public function saveTag($tag)
236     {
237         $inTag = new Tinebase_Model_Tag($tag);
238         
239         if (strlen($inTag->getId()) < 40) {
240             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' creating tag: ' . print_r($inTag->toArray(), true));
241             $outTag = Tinebase_Tags::getInstance()->createTag($inTag);
242         } else {
243             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' updating tag: ' .print_r($inTag->toArray(), true));
244             $outTag = Tinebase_Tags::getInstance()->updateTag($inTag);
245         }
246         
247         return $outTag->toArray();
248     }
249     
250     /**
251      * Used for updating multiple records
252      * 
253      * @param string $appName
254      * @param string $modelName
255      * @param array $changes
256      * @param array $filter
257      */
258     public function updateMultipleRecords($appName, $modelName, $changes, $filter)
259     {
260         // increase execution time to 30 minutes
261         Tinebase_Core::setExecutionLifeTime(1800);
262         
263         $filterModel = $appName . '_Model_' . $modelName . 'Filter';
264         $data = array();
265         foreach ($changes as $f) {
266             $data[preg_replace('/^customfield_/','#', $f['name'])] = $f['value'];
267         }
268         
269         return $this->_updateMultiple($filter, $data, Tinebase_Core::getApplicationInstance($appName, $modelName), $filterModel);
270     }
271
272     /**
273      * search tags
274      *
275      * @param  array $filter filter array
276      * @param  array $paging pagination info
277      * @return array
278      */
279     public function searchTags($filter, $paging)
280     {
281         $filter = new Tinebase_Model_TagFilter($filter);
282         $paging = new Tinebase_Model_Pagination($paging);
283         
284         return array(
285             'results'    => Tinebase_Tags::getInstance()->searchTags($filter, $paging)->toArray(),
286             // TODO we normally use 'totalcount' (all lower case) - this should be streamlined
287             'totalCount' => Tinebase_Tags::getInstance()->getSearchTagsCount($filter)
288         );
289     }
290     
291     /**
292     * search tags by foreign filter
293     *
294     * @param  array $filterData
295     * @param  string $filterName
296     * @return array
297     */
298     public function searchTagsByForeignFilter($filterData, $filterName)
299     {
300         $filter = $this->_getFilterGroup($filterData, $filterName);
301         
302         $result = Tinebase_Tags::getInstance()->searchTagsByForeignFilter($filter)->toArray();
303         return array(
304             'results'    => $result,
305             'totalCount' => count($result)
306         );
307     }
308     
309     /**
310      * get filter group defined by filterName and filterData
311      *
312      * @param array $_filterData
313      * @param string $_filterName
314      * @return Tinebase_Model_Filter_FilterGroup
315      * @throws Tinebase_Exception_AccessDenied
316      */
317     protected function _getFilterGroup($_filterData, $_filterName)
318     {
319         // NOTE: this function makes a new instance of a class whose name is given by user input.
320         //       we need to do some sanitising first!
321         /** @noinspection PhpUnusedLocalVariableInspection */
322         list($appName, $modelString, $filterGroupName) = explode('_', $_filterName);
323         if ($modelString !== 'Model') {
324             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' spoofing attempt detected, affected account: ' . print_r(Tinebase_Core::getUser()->toArray(), TRUE));
325             die('go away!');
326         }
327         
328         if (! Tinebase_Core::getUser()->hasRight($appName, Tinebase_Acl_Rights_Abstract::RUN)) {
329             throw new Tinebase_Exception_AccessDenied('No right to access application');
330         }
331         
332         $filterGroup = new $_filterName(array());
333         if (! $filterGroup instanceof Tinebase_Model_Filter_FilterGroup) {
334             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' spoofing attempt detected, affected account: ' . print_r(Tinebase_Core::getUser()->toArray(), TRUE));
335             die('go away!');
336         }
337         
338         // at this point we are sure request is save ;-)
339         $filterGroup->setFromArray($_filterData);
340         
341         return $filterGroup;
342     }
343     
344     /**
345      * attach tag to multiple records identified by a filter
346      *
347      * @param array  $filterData
348      * @param string $filterName
349      * @param mixed  $tag       string|array existing and non-existing tag
350      * @return array
351      */
352     public function attachTagToMultipleRecords($filterData, $filterName, $tag)
353     {
354         $this->_longRunningRequest();
355         $filter = $this->_getFilterGroup($filterData, $filterName);
356         
357         Tinebase_Tags::getInstance()->attachTagToMultipleRecords($filter, $tag);
358         return array('success' => true);
359     }
360     
361     /**
362      * attach multiple tags to multiple records identified by a filter
363      *
364      * @param array  $filterData
365      * @param string $filterName
366      * @param mixed  $tags         array of existing and non-existing tags
367      * @return void
368      */
369     public function attachMultipleTagsToMultipleRecords($filterData, $filterName, $tags)
370     {
371         $this->_longRunningRequest();
372         $filter = $this->_getFilterGroup($filterData, $filterName);
373
374         foreach ($tags as $tag) {
375             Tinebase_Tags::getInstance()->attachTagToMultipleRecords(clone $filter, $tag);
376         }
377
378         return array('success' => true);
379     }
380
381     /**
382      * detach tags to multiple records identified by a filter
383      *
384      * @param array  $filterData
385      * @param string $filterName
386      * @param mixed  $tag       string|array existing and non-existing tag
387      * @return array
388      */
389     public function detachTagsFromMultipleRecords($filterData, $filterName, $tag)
390     {
391         $this->_longRunningRequest();
392         $filter = $this->_getFilterGroup($filterData, $filterName);
393         
394         Tinebase_Tags::getInstance()->detachTagsFromMultipleRecords($filter, $tag);
395         return array('success' => true);
396     }
397     
398     /**
399      * search / get notes
400      * - used by activities grid
401      *
402      * @param  array $filter filter array
403      * @param  array $paging pagination info
404      * @return array
405      */
406     public function searchNotes($filter, $paging)
407     {
408         $filter = new Tinebase_Model_NoteFilter($filter);
409         $paging = new Tinebase_Model_Pagination($paging);
410         
411         $records = Tinebase_Notes::getInstance()->searchNotes($filter, $paging, /* ignoreACL = */ false);
412         $result = $this->_multipleRecordsToJson($records);
413         
414         return array(
415             'results'       => $result,
416             'totalcount'    => Tinebase_Notes::getInstance()->searchNotesCount($filter, /* ignoreACL = */ false)
417         );
418     }
419     
420     /**
421      * get note types
422      *
423      */
424     public function getNoteTypes()
425     {
426         $noteTypes = Tinebase_Notes::getInstance()->getNoteTypes();
427         $noteTypes->translate();
428         
429         return array(
430             'results'       => $noteTypes->toArray(),
431             'totalcount'    => count($noteTypes)
432         );
433     }
434     
435     /**
436      * deletes tags identified by an array of identifiers
437      *
438      * @param  array $ids
439      * @return array
440      */
441     public function deleteTags($ids)
442     {
443         Tinebase_Tags::getInstance()->deleteTags($ids);
444         return array('success' => true);
445     }
446
447     /**
448      * authenticate user by username and password
449      *
450      * @param  string $username the username
451      * @param  string $password the password
452      * @return array
453      */
454     public function authenticate($username, $password)
455     {
456         $authResult = Tinebase_Auth::getInstance()->authenticate($username, $password);
457         
458         if ($authResult->isValid()) {
459             $response = array(
460                 'status'    => 'success',
461                 'msg'       => 'authentication succseed',
462                 //'loginUrl'  => 'someurl',
463             );
464         } else {
465             $response = array(
466                 'status'    => 'fail',
467                 'msg'       => 'authentication failed',
468             );
469         }
470         
471         return $response;
472     }
473
474     /**
475      * login user with given username and password
476      *
477      * @param  string $username the username
478      * @param  string $password the password
479      * @param  string $securitycode the security code(captcha)
480      * @param  string $otp  the second factor password
481      * @return array
482      */
483     public function login($username, $password, $securitycode = null, $otp = null)
484     {
485         Tinebase_Core::startCoreSession();
486         
487         if (is_array(($response = $this->_getCaptchaResponse($securitycode)))) {
488             return $response;
489         }
490
491         // TODO move security code here and use params
492         Tinebase_Controller::getInstance()->setRequestContext(array(
493             'otp' => $otp
494         ));
495
496         // try to login user
497         $success = Tinebase_Controller::getInstance()->login(
498             $username,
499             $password,
500             Tinebase_Core::get(Tinebase_Core::REQUEST),
501             self::REQUEST_TYPE,
502             $securitycode
503         );
504         
505         if ($success === true) {
506             return $this->_getLoginSuccessResponse($username);
507         } else {
508             return $this->_getLoginFailedResponse();
509         }
510     }
511     
512     /**
513      * Returns TRUE if there is a captcha
514      * @return boolean
515      */
516     protected function _hasCaptcha()
517     {
518         if ($this->_hasCaptcha === null){
519             $this->_hasCaptcha = isset(Tinebase_Core::getConfig()->captcha->count) && Tinebase_Core::getConfig()->captcha->count != 0;
520         }
521         
522         return $this->_hasCaptcha;
523     }
524     
525     /**
526      *
527      * @param string $securitycode
528      * @return array | NULL
529      */
530     protected function _getCaptchaResponse($securitycode)
531     {
532         if ($this->_hasCaptcha()) {
533             $config_count = Tinebase_Core::getConfig()->captcha->count;
534             
535             $count = (isset(Tinebase_Session::getSessionNamespace()->captcha['count'])
536                 ? Tinebase_Session::getSessionNamespace()->captcha['count']
537                 : 1
538             );
539             
540             if ($count >= $config_count) {
541                 $aux = isset(Tinebase_Session::getSessionNamespace()->captcha['code'])
542                     ? Tinebase_Session::getSessionNamespace()->captcha['code']
543                     : null;
544                 
545                 if ($aux != $securitycode) {
546                     $rets = Tinebase_Controller::getInstance()->makeCaptcha(); 
547                     $response = array(
548                         'success'      => false,
549                         'errorMessage' => "Wrong username or password!",
550                         'c1'           => $rets['1']
551                     );
552                     
553                     return $response;
554                 }
555             }
556         }
557         
558         return null;
559     }
560     
561     /**
562      *
563      * @param string $username
564      * @return array
565      */
566     protected function _getLoginSuccessResponse($username)
567     {
568             $response = array(
569                 'success'        => true,
570                 'account'        => Tinebase_Core::getUser()->getPublicUser()->toArray(),
571                 'jsonKey'        => Tinebase_Core::get('jsonKey'),
572                 'welcomeMessage' => "Welcome to Tine 2.0!"
573             );
574              
575             if (Tinebase_Config::getInstance()->get(Tinebase_Config::REUSEUSERNAME_SAVEUSERNAME, 0)) {
576                 // save in cookie (expires in 2 weeks)
577                 setcookie('TINE20LASTUSERID', $username, time()+60*60*24*14);
578             } else {
579                 setcookie('TINE20LASTUSERID', '', 0);
580             }
581
582             $this->_setCredentialCacheCookie();
583             
584         return $response;
585     }
586     
587     /**
588      *
589      * @return array
590      */
591     protected function _getLoginFailedResponse()
592     {
593         $response = array(
594             'success'      => false,
595             'errorMessage' => "Wrong username or password!",
596         );
597         
598         Tinebase_Auth_CredentialCache::getInstance()->getCacheAdapter()->resetCache();
599         
600         if ($this->_hasCaptcha()) {
601             $config_count = Tinebase_Core::getConfig()->captcha->count;
602             
603             if (!isset(Tinebase_Session::getSessionNamespace()->captcha['count'])) {
604                 Tinebase_Session::getSessionNamespace()->captcha['count'] = 1;
605             } else {
606                 Tinebase_Session::getSessionNamespace()->captcha['count'] = Tinebase_Session::getSessionNamespace()->captcha['count'] + 1;
607             }
608             
609             if (Tinebase_Session::getSessionNamespace()->captcha['count'] >= $config_count) {
610                 $rets = Tinebase_Controller::getInstance()->makeCaptcha(); 
611                 $response = array(
612                     'success'      => false,
613                     'errorMessage' => "Wrong username or password!",
614                     'c1'           => $rets['1']
615                 );
616             }
617         } else {
618             Tinebase_Session::destroyAndMantainCookie();
619         }
620         
621         return $response;
622     }
623
624     /**
625      * set credential cache cookie
626      *
627      * @return boolean
628      */
629     protected function _setCredentialCacheCookie()
630     {
631         if (!Tinebase_Core::isRegistered(Tinebase_Core::USERCREDENTIALCACHE)) {
632             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Something went wrong with the CredentialCache / no CC registered.');
633             
634             return false;
635         }
636         
637         Tinebase_Auth_CredentialCache::getInstance()->getCacheAdapter()->setCache(Tinebase_Core::getUserCredentialCache());
638         
639         return true;
640     }
641
642     /**
643      * update user credential cache
644      *
645      * - fires Tinebase_Event_User_ChangeCredentialCache
646      *
647      * @param string $password
648      * @return array
649      */
650     public function updateCredentialCache($password)
651     {
652         $oldCredentialCache = Tinebase_Core::getUserCredentialCache();
653         $credentialCache = Tinebase_Auth_CredentialCache::getInstance()->cacheCredentials(Tinebase_Core::getUser()->accountLoginName, $password);
654         Tinebase_Core::set(Tinebase_Core::USERCREDENTIALCACHE, $credentialCache);
655         
656         $success = $this->_setCredentialCacheCookie();
657         
658         if ($success) {
659             // close session to allow other requests
660             Tinebase_Session::writeClose(true);
661             $event = new Tinebase_Event_User_ChangeCredentialCache($oldCredentialCache);
662             Tinebase_Event::fireEvent($event);
663         }
664         
665         return array(
666             'success'      => $success
667         );
668     }
669
670     /**
671      * destroy session
672      *
673      * @return array
674      */
675     public function logout()
676     {
677         Tinebase_Controller::getInstance()->logout($_SERVER['REMOTE_ADDR']);
678         
679         Tinebase_Auth_CredentialCache::getInstance()->getCacheAdapter()->resetCache();
680         
681         if (Tinebase_Session::isStarted()) {
682             Tinebase_Session::destroyAndRemoveCookie();
683         }
684         
685         $result = array(
686             'success'=> true,
687         );
688         
689         return $result;
690     }
691     
692     /**
693      * Returns registry data of tinebase.
694      * @see Tinebase_Application_Json_Abstract
695      *
696      * @return mixed array 'variable name' => 'data'
697      */
698     public function getRegistryData()
699     {
700         $registryData = $this->_getAnonymousRegistryData();
701         
702         if (Tinebase_Core::isRegistered(Tinebase_Core::USER)) {
703             $userRegistryData = $this->_getUserRegistryData();
704             $registryData += $userRegistryData;
705         }
706         
707         return $registryData;
708     }
709     
710     /**
711      * get anonymous registry
712      * 
713      * @return array
714      */
715     protected function _getAnonymousRegistryData()
716     {
717         $locale = Tinebase_Core::get('locale');
718         $tbFrontendHttp = new Tinebase_Frontend_Http();
719         
720         // default credentials
721         if (isset(Tinebase_Core::getConfig()->login)) {
722             $loginConfig = Tinebase_Core::getConfig()->login;
723             $defaultUsername = (isset($loginConfig->username)) ? $loginConfig->username : '';
724             $defaultPassword = (isset($loginConfig->password)) ? $loginConfig->password : '';
725         } else {
726             $defaultUsername = '';
727             $defaultPassword = '';
728         }
729         
730         $symbols = Zend_Locale::getTranslationList('symbols', $locale);
731         $secondFactorConfig = Tinebase_Config::getInstance()->get(Tinebase_Config::AUTHENTICATIONSECONDFACTOR);
732         
733         $registryData =  array(
734             'modSsl'           => Tinebase_Auth::getConfiguredBackend() == Tinebase_Auth::MODSSL,
735             'secondFactor'     => $secondFactorConfig && $secondFactorConfig->active,
736             'serviceMap'       => $tbFrontendHttp->getServiceMap(),
737             'locale'           => array(
738                 'locale'   => $locale->toString(),
739                 'language' => Zend_Locale::getTranslation($locale->getLanguage(), 'language', $locale),
740                 'region'   => Zend_Locale::getTranslation($locale->getRegion(), 'country', $locale),
741             ),
742             'version'          => array(
743                 'buildType'     => TINE20_BUILDTYPE,
744                 'codeName'      => TINE20_CODENAME,
745                 'packageString' => TINE20_PACKAGESTRING,
746                 'releaseTime'   => TINE20_RELEASETIME,
747                 'filesHash'     => TINE20_BUILDTYPE != 'DEVELOPMENT' ? $tbFrontendHttp->getJsCssHash() : null
748             ),
749             'defaultUsername'   => $defaultUsername,
750             'defaultPassword'   => $defaultPassword,
751             'denySurveys'       => Tinebase_Core::getConfig()->denySurveys,
752             'titlePostfix'      => Tinebase_Config::getInstance()->get(Tinebase_Config::PAGETITLEPOSTFIX),
753             'redirectUrl'       => Tinebase_Config::getInstance()->get(Tinebase_Config::REDIRECTURL),
754             'helpUrl'           => Tinebase_Core::getConfig()->helpUrl,
755             'maxFileUploadSize' => Tinebase_Helper::convertToBytes(ini_get('upload_max_filesize')),
756             'maxPostSize'       => Tinebase_Helper::convertToBytes(ini_get('post_max_size')),
757             'thousandSeparator' => $symbols['group'],
758             'decimalSeparator'  => $symbols['decimal'],
759             'filesystemAvailable' => Tinebase_Core::isFilesystemAvailable(),
760             'brandingWeburl'    => Tinebase_Config::getInstance()->get(Tinebase_Config::BRANDING_WEBURL),
761             'brandingLogo'      => Tinebase_Config::getInstance()->get(Tinebase_Config::BRANDING_LOGO),
762             'brandingFavicon'   => Tinebase_Config::getInstance()->get(Tinebase_Config::BRANDING_FAVICON),
763             'brandingTitle'   => Tinebase_Config::getInstance()->get(Tinebase_Config::BRANDING_TITLE),
764         );
765         
766         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
767             . ' Anonymous registry: ' . print_r($registryData, TRUE));
768         
769         return $registryData;
770     }
771     
772     /**
773      * get user registry
774      * 
775      * @return array
776      */
777     protected function _getUserRegistryData()
778     {
779         $user = Tinebase_Core::getUser();
780         $userContactArray = array();
781         if (Tinebase_Application::getInstance()->isInstalled('Addressbook') === true) {
782             try {
783                 $userContactArray = Addressbook_Controller_Contact::getInstance()->getContactByUserId($user->getId(), TRUE)->toArray();
784             } catch (Addressbook_Exception_NotFound $aenf) {
785                 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) /** @noinspection PhpUndefinedMethodInspection */
786                     Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__
787                     . ' User not found in Addressbook: ' . $user->accountDisplayName);
788             }
789         }
790         
791         try {
792             $persistentFilters = Tinebase_Frontend_Json_PersistentFilter::getAllPersistentFilters();
793         } catch (Tinebase_Exception_NotFound $tenf) {
794             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__
795                 . " Failed to fetch persistent filters. Exception: \n". $tenf);
796             $persistentFilters = array();
797         }  catch (Exception $e) {
798             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__
799                 . " Failed to fetch persistent filters. Exception: \n". $e);
800             $persistentFilters = array();
801         }
802
803         
804         $userRegistryData = array(
805             'timeZone'           => Tinebase_Core::getUserTimezone(),
806             'currentAccount'     => $user->toArray(),
807             'userContact'        => $userContactArray,
808             'accountBackend'     => Tinebase_User::getConfiguredBackend(),
809             'jsonKey'            => Tinebase_Core::get('jsonKey'),
810             'userApplications'   => $user->getApplications()->toArray(),
811             'NoteTypes'          => $this->getNoteTypes(),
812             'stateInfo'          => Tinebase_State::getInstance()->loadStateInfo(),
813             'mustchangepw'       => $user->mustChangePassword(),
814             'confirmLogout'      => Tinebase_Core::getPreference()->getValue(Tinebase_Preference::CONFIRM_LOGOUT, 1),
815             'advancedSearch'     => Tinebase_Core::getPreference()->getValue(Tinebase_Preference::ADVANCED_SEARCH, 0),
816             'persistentFilters'  => $persistentFilters,
817             'userAccountChanged' => Tinebase_Controller::getInstance()->userAccountChanged(),
818         );
819         
820         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
821             . ' User registry: ' . print_r($userRegistryData, TRUE));
822         
823         return $userRegistryData;
824     }
825
826     /**
827      * Returns registry data of all applications current user has access to
828      * @see Tinebase_Application_Json_Abstract
829      *
830      * @return mixed array 'variable name' => 'data'
831      */
832     public function getAllRegistryData()
833     {
834         $registryData = array();
835         
836         if (Tinebase_Core::getUser()) {
837             $userApplications = Tinebase_Core::getUser()->getApplications(/* $_anyRight */ TRUE);
838             $clientConfig = Tinebase_Config::getInstance()->getClientRegistryConfig();
839             
840             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE))
841                 /** @noinspection PhpUndefinedFieldInspection */
842                 Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
843                . ' User applications to fetch registry for: ' . print_r($userApplications->name, TRUE));
844
845             /** @noinspection PhpUndefinedFieldInspection */
846             if (! in_array('Tinebase', $userApplications->name)) {
847                 Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . ' User has no permissions to run Tinebase.');
848                 $this->logout();
849                 throw new Tinebase_Exception_AccessDenied('User has no permissions to run Tinebase');
850             }
851             
852             foreach ($userApplications as $application) {
853                 $appRegistry = array();
854                 $appRegistry['rights'] = Tinebase_Core::getUser()->getRights($application->name);
855                 $appRegistry['allrights'] = Tinebase_Application::getInstance()->getAllRights($application->getId());
856                 $appRegistry['config'] = isset($clientConfig[$application->name])
857                     ? $clientConfig[$application->name]->toArray()
858                     : array();
859
860                 // @todo do we need this for all apps?
861                 $exportDefinitions = Tinebase_ImportExportDefinition::getInstance()->getExportDefinitionsForApplication($application);
862                 $appRegistry['exportDefinitions'] = array(
863                     'results'               => $exportDefinitions->toArray(),
864                     'totalcount'            => count($exportDefinitions),
865                 );
866
867                 $customfields = Tinebase_CustomField::getInstance()->getCustomFieldsForApplication($application);
868                 Tinebase_CustomField::getInstance()->resolveConfigGrants($customfields);
869                 $appRegistry['customfields'] = $customfields->toArray();
870
871                 // add preferences for app
872                 try {
873                     $prefRegistry = $this->_getAppPreferencesForRegistry($application);
874                     $appRegistry = array_merge_recursive($appRegistry, $prefRegistry);
875                 } catch (Tinebase_Exception_AccessDenied $tead) {
876                     // do not add prefs if user has no run right
877                 }
878
879                 $customAppRegistry = $this->_getCustomAppRegistry($application);
880                 if (empty($customAppRegistry)) {
881                     // TODO always get this from app controller (and remove from _getCustomAppRegistry)
882                     $appController = Tinebase_Core::getApplicationInstance($application->name);
883                     $models = $appController->getModels();
884                     $appRegistry['models'] = Tinebase_ModelConfiguration::getFrontendConfigForModels($models);
885                     $appRegistry['defaultModel'] = $appController->getDefaultModel();
886
887                 } else {
888                     $appRegistry = array_merge_recursive($appRegistry, $customAppRegistry);
889                 }
890
891                 $registryData[$application->name] = $appRegistry;
892             }
893         } else {
894             $registryData['Tinebase'] = $this->getRegistryData();
895         }
896         
897         return $registryData;
898     }
899
900     /**
901      * get app preferences for registry
902      *
903      * @param Tinebase_Model_Application $application
904      * @return array
905      * @throws Tinebase_Exception_NotFound
906      */
907     protected function _getAppPreferencesForRegistry(Tinebase_Model_Application $application)
908     {
909         $registryData = array();
910         $appPrefs = Tinebase_Core::getPreference($application->name);
911         if ($appPrefs !== NULL) {
912             $allPrefs = $appPrefs->getAllApplicationPreferences();
913             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
914                 . ' ' . print_r($allPrefs, TRUE));
915
916             foreach ($allPrefs as $pref) {
917                 try {
918                     $registryData['preferences'][$pref] = $appPrefs->{$pref};
919                 } catch (Exception $e) {
920                     Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Could not get ' . $pref . '  preference: ' . $e);
921                 }
922             }
923         }
924
925         return $registryData;
926     }
927
928     /**
929      * get registry data from application frontend json class
930      *
931      * @param Tinebase_Model_Application $application
932      * @return array
933      * @throws Tinebase_Exception_InvalidArgument
934      */
935     protected function _getCustomAppRegistry(Tinebase_Model_Application $application)
936     {
937         $jsonAppName = $application->name . '_Frontend_Json';
938         if (! class_exists($jsonAppName)) {
939             return array();
940         }
941
942         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
943             Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
944                 . ' Getting registry data for app ' . $application->name);
945         }
946
947         try {
948             $applicationJson = new $jsonAppName();
949             $registryData = $applicationJson->getRegistryData();
950
951         } catch (Exception $e) {
952             Tinebase_Exception::log($e);
953             if (! $e instanceof Tinebase_Exception_AccessDenied && ! in_array($application->name, array('Tinebase', 'Addressbook', 'Admin'))) {
954                 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Disabling ' . $application->name . ': ' . $e);
955                 Tinebase_Application::getInstance()->setApplicationState(array($application->getId()), Tinebase_Application::DISABLED);
956             }
957             return array();
958         }
959
960         // TODO get this from app controller / modelconfig
961         foreach ($applicationJson->getRelatableModels() as $relModel) {
962             $registryData[$relModel['ownApp']]['relatableModels'][] = $relModel;
963         }
964         $registryData['models'] = $applicationJson->getModelsConfiguration();
965         $registryData['defaultModel'] = $applicationJson->getDefaultModel();
966
967         return $registryData;
968     }
969     
970     /**
971      * search / get custom field values
972      *
973      * @param  array $filter filter array
974      * @param  array $paging pagination info
975      * @return array
976      */
977     public function searchCustomFieldValues($filter, $paging)
978     {
979         $result = $this->_search($filter, $paging, Tinebase_CustomField::getInstance(), 'Tinebase_Model_CustomField_ValueFilter');
980         return $result;
981     }
982     
983     /************************ preferences functions ***************************/
984     
985     /**
986      * search preferences
987      *
988      * @param  string $applicationName
989      * @param  array  $filter json encoded
990      * @return array
991      */
992     public function searchPreferencesForApplication($applicationName, $filter)
993     {
994         $decodedFilter = $this->_prepareParameter($filter);
995         
996         $filter = new Tinebase_Model_PreferenceFilter();
997         if (! empty($decodedFilter)) {
998             $filter->setFromArrayInUsersTimezone($decodedFilter);
999         }
1000         $appId = Tinebase_Application::getInstance()->getApplicationByName($applicationName)->getId();
1001         $filter->addFilter($filter->createFilter(array('field'     => 'application_id',  'operator'  => 'equals', 'value'     => $appId)));
1002         
1003         $backend = Tinebase_Core::getPreference($applicationName);
1004         if ($backend) {
1005             $records = $backend->search($filter);
1006             
1007             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
1008                 . ' Got ' . count($records) . ' preferences for app ' . $applicationName);
1009             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
1010                 . ' ' . print_r($records->toArray(), TRUE));
1011             
1012             $result = $this->_multipleRecordsToJson($records, $filter);
1013             
1014             // add translated labels and descriptions
1015             $translations = $backend->getTranslatedPreferences();
1016             foreach ($result as $key => $prefArray) {
1017                 if (isset($translations[$prefArray['name']])) {
1018                     $result[$key] = array_merge($prefArray, $translations[$prefArray['name']]);
1019                 } else {
1020                     $result[$key] = array_merge($prefArray, array('label' => $prefArray['name']));
1021                 }
1022             }
1023             
1024             // sort prefs by definition
1025             $allPrefs = (array) $backend->getAllApplicationPreferences();
1026             usort($result, function($a, $b) use ($allPrefs) {
1027                 $a = (int) array_search($a['name'], $allPrefs);
1028                 $b = (int) array_search($b['name'], $allPrefs);
1029                 
1030                 if ($a == $b) {
1031                     return 0;
1032                 }
1033                 return ($a < $b) ? -1 : 1;
1034             });
1035             
1036         } else {
1037             $result = array();
1038         }
1039         
1040         return array(
1041             'results'       => $result,
1042             'totalcount'    => count($result)
1043         );
1044     }
1045     
1046     /**
1047      * save preferences for application
1048      *
1049      * @param string    $data       json encoded preferences data
1050      * @param bool      $adminMode  submit in admin mode?
1051      * @return array with the changed prefs
1052      *
1053      * @todo move saving of user values to preferences controller
1054      */
1055     public function savePreferences($data, $adminMode)
1056     {
1057         $decodedData = $this->_prepareParameter($data);
1058         
1059         $result = array();
1060         foreach ($decodedData as $applicationName => $data) {
1061             
1062             if ($applicationName == 'Tinebase.UserProfile') {
1063                 $userProfileData = array();
1064                 foreach($data as $fieldName => $valueArray) {
1065                     $userProfileData[$fieldName] = $valueArray['value'];
1066                 }
1067                 $this->updateUserProfile($userProfileData);
1068                 
1069             } else {
1070                 $backend = Tinebase_Core::getPreference($applicationName);
1071                 if ($backend !== NULL) {
1072                     if ($adminMode) {
1073                         $backend->saveAdminPreferences($data);
1074                         // TODO return preference values?
1075                         $result = array();
1076                     } else {
1077                         // set user prefs
1078                         foreach ($data as $name => $value) {
1079                             try {
1080                                 $backend->doSpecialJsonFrontendActions($this, $name, $value['value'], $applicationName);
1081                                 $backend->$name = $value['value'];
1082                                 $result[$applicationName][] = array('name' => $name, 'value' => $backend->$name);
1083                             } catch (Exception $e) {
1084                                 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE))
1085                                     Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' '
1086                                         . 'Could not save preference '. $name . ' -> ' . $e->getMessage());
1087                             }
1088                         }
1089                     }
1090                 }
1091             }
1092         }
1093         
1094         return array(
1095             'status'    => 'success',
1096             'results'   => $result
1097         );
1098     }
1099
1100     /**
1101      * get profile of current user
1102      *
1103      * @param string $userId
1104      * @return array
1105      */
1106     public function getUserProfile($userId)
1107     {
1108         // NOTE: $userProfile is a contact where non readable fields are clearad out!
1109         $userProfile = Tinebase_UserProfile::getInstance()->get($userId);
1110         
1111         // NOTE: This hurts! We don't have methods to call in our frontends yet which convert
1112         //       a record to the json representaion :( Thus image link will be broken!
1113         $userProfile->setTimezone(Tinebase_Core::getUserTimezone());
1114         
1115         return array(
1116             'userProfile'      => $userProfile->toArray(),
1117             'readableFields'   => Tinebase_UserProfile::getInstance()->getReadableFields(),
1118             'updateableFields' => Tinebase_UserProfile::getInstance()->getUpdateableFields(),
1119         );
1120     }
1121     
1122     /**
1123      * update user profile
1124      *
1125      * @param  array $profileData
1126      * @return array
1127      */
1128     public function updateUserProfile($profileData)
1129     {
1130         $contact = new Addressbook_Model_Contact(array(), TRUE);
1131         $contact->setFromJsonInUsersTimezone($profileData);
1132         
1133         // NOTE: $userProfile is a contact where non readable fields are clearad out!
1134         $userProfile = Tinebase_UserProfile::getInstance()->update($contact);
1135         
1136         // NOTE: This hurts! We don't have methods to call in our frontends yet which convert
1137         //       a record to the json representaion :( Thus image link will be broken!
1138         $userProfile->setTimezone(Tinebase_Core::getUserTimezone());
1139         return $userProfile->toArray();
1140     }
1141     
1142     /**
1143      * dummy function to measure speed of framework initialization
1144      */
1145     public function void()
1146     {
1147         return array();
1148     }
1149     
1150     /**
1151      * gets the userProfile config
1152      *
1153      * @return @array
1154      */
1155     public function getUserProfileConfig()
1156     {
1157         return array(
1158             'possibleFields'   => array_values(Tinebase_UserProfile::getInstance()->getPossibleFields()),
1159             'readableFields'   => array_values(Tinebase_UserProfile::getInstance()->getReadableFields()),
1160             'updateableFields' => array_values(Tinebase_UserProfile::getInstance()->getUpdateableFields()),
1161         );
1162     }
1163     
1164     /**
1165      * saves userProfile config
1166      *
1167      * @param array $configData
1168      */
1169     public function setUserProfileConfig($configData)
1170     {
1171         Tinebase_UserProfile::getInstance()->setReadableFields($configData['readableFields']);
1172         Tinebase_UserProfile::getInstance()->setUpdateableFields($configData['updateableFields']);
1173     }
1174     
1175     /**
1176      * switch to another user's account
1177      * 
1178      * @param string $loginName
1179      * @return array
1180      */
1181     public function changeUserAccount($loginName)
1182     {
1183         $result = Tinebase_Controller::getInstance()->changeUserAccount($loginName);
1184         return array(
1185             'success' => $result
1186         );
1187     }
1188     
1189     /************************ department functions **************************/
1190     
1191     /**
1192      * search / get departments
1193      *
1194      * @param  array $filter filter array
1195      * @param  array $paging pagination info
1196      * @return array
1197      */
1198     public function searchDepartments($filter, $paging)
1199     {
1200         $result = $this->_search($filter, $paging, Tinebase_Department::getInstance(), 'Tinebase_Model_DepartmentFilter');
1201         return $result;
1202     }
1203     
1204     /************************* relation functions ***************************/
1205     
1206     /**
1207      * get all relations of a given record
1208      *
1209      * @param  string       $model         own model to get relations for
1210      * @param  string       $id            own id to get relations for
1211      * @param  string       $degree        only return relations of given degree
1212      * @param  array        $type          only return relations of given type
1213      * @param  string       $relatedModel  only return relations having this related model
1214      * @return array
1215      */
1216     public function getRelations($model, $id, $degree = NULL, $type = array(), $relatedModel = NULL)
1217     {
1218         if (! is_array($type)) {
1219             $type = array();
1220         }
1221         $relations = Tinebase_Relations::getInstance()->getRelations($model, 'Sql', $id, $degree, $type, false, $relatedModel);
1222
1223         // @TODO we still have no converter for relations :-(
1224         // -> related records returned here are different to the records returned by the apps itself!
1225         // -> this problem also applies to to generic json converter!
1226         if (count($relations) > 0) {
1227             $relations->setTimezone(Tinebase_Core::getUserTimezone());
1228             $relations->bypassFilters = true;
1229             $result = $relations->toArray();
1230         } else {
1231             $result = array();
1232         }
1233         return array(
1234             'results'       => array_values($result),
1235             'totalcount'    => count($result),
1236         );
1237     }
1238     
1239     /************************ config functions ******************************/
1240     
1241     /**
1242      * get config settings for application
1243      *
1244      * @param string $id application name
1245      * @return array
1246      */
1247     public function getConfig($id)
1248     {
1249         $controllerName = $id . '_Controller';
1250         $appController = Tinebase_Controller_Abstract::getController($controllerName);
1251         
1252         return array(
1253             'id'        => $id,
1254             'settings'  => $appController->getConfigSettings(TRUE),
1255         );
1256     }
1257     
1258     /**
1259      * save application config
1260      *
1261      * @param array $recordData
1262      * @return array
1263      */
1264     public function saveConfig($recordData)
1265     {
1266         //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . print_r($recordData, TRUE));
1267         
1268         $controllerName = $recordData['id'] . '_Controller';
1269         $appController = Tinebase_Controller_Abstract::getController($controllerName);
1270         $appController->saveConfigSettings($recordData['settings']);
1271         
1272         return $this->getConfig($recordData['id']);
1273     }
1274     
1275     /************************ tempFile functions ******************************/
1276     
1277     /**
1278      * joins all given tempfiles in given order to a single new tempFile
1279      *
1280      * @param array $tempFilesData of tempfiles arrays $tempFiles
1281      * @return array new tempFile
1282      */
1283     public function joinTempFiles($tempFilesData)
1284     {
1285         $tempFileRecords = new Tinebase_Record_RecordSet('Tinebase_Model_TempFile');
1286         foreach($tempFilesData as $tempFileData) {
1287             $record = new Tinebase_Model_TempFile(array(), TRUE);
1288             $record->setFromJsonInUsersTimezone($tempFileData);
1289             $tempFileRecords->addRecord($record);
1290         }
1291         
1292         $joinedTempFile = Tinebase_TempFile::getInstance()->joinTempFiles($tempFileRecords);
1293         
1294         return $joinedTempFile->toArray();
1295     }
1296     
1297     /************************ protected functions ***************************/
1298     
1299     /**
1300      * returns multiple records prepared for json transport
1301      *
1302      * @param Tinebase_Record_RecordSet $_records Tinebase_Record_Abstract
1303      * @param Tinebase_Model_Filter_FilterGroup $_filter
1304      * @param Tinebase_Model_Pagination $_pagination
1305      * @return array data
1306      */
1307     protected function _multipleRecordsToJson(Tinebase_Record_RecordSet $_records, $_filter = NULL, $_pagination = NULL)
1308     {
1309         if (count($_records) == 0) {
1310             return array();
1311         }
1312         
1313         switch ($_records->getRecordClassName()) {
1314             case 'Tinebase_Model_Preference':
1315                 $accountFilterArray = $_filter->getFilter('account')->toArray();
1316                 $adminMode = ($accountFilterArray['value']['accountId'] == 0 && $accountFilterArray['value']['accountType'] == Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE);
1317                 foreach ($_records as $record) {
1318                     if (! isset($app) || $record->application_id != $app->getId()) {
1319                         $app = Tinebase_Application::getInstance()->getApplicationById($record->application_id);
1320                     }
1321                     $preference = Tinebase_Core::getPreference($app->name, TRUE);
1322                     $preference->resolveOptions($record);
1323                     if ($record->type == Tinebase_Model_Preference::TYPE_DEFAULT || ! $adminMode && $record->type == Tinebase_Model_Preference::TYPE_ADMIN) {
1324                         $record->value = Tinebase_Model_Preference::DEFAULT_VALUE;
1325                     }
1326                 }
1327                 break;
1328         }
1329         
1330         $result = parent::_multipleRecordsToJson($_records, $_filter, $_pagination);
1331         return $result;
1332     }
1333     
1334     /**
1335      * return autocomplete suggestions for a given recordclass, the property and value
1336      *
1337      * @param string $appName
1338      * @param string $modelName
1339      * @param string $property
1340      * @param string $startswith
1341      * 
1342      * @return array
1343      */
1344     public function autoComplete($appName, $modelName, $property, $startswith)
1345     {
1346         $recordClassName = $appName . '_Model_' . $modelName;
1347         $controller      = Tinebase_Core::getApplicationInstance($appName, $modelName);
1348         $filterClassName = $recordClassName . 'Filter';
1349         
1350         if (! class_exists($recordClassName)) {
1351             throw new Tinebase_Exception_InvalidArgument('A record class for the given appName and modelName does not exist!');
1352         }
1353         
1354         if (! $controller) {
1355             throw new Tinebase_Exception_InvalidArgument('A controller for the given appName and modelName does not exist!');
1356         }
1357         
1358         if (! class_exists($filterClassName)) {
1359             throw new Tinebase_Exception_InvalidArgument('A filter for the given appName and modelName does not exist!');
1360         }
1361         
1362         if (! in_array($property, $recordClassName::getAutocompleteFields())) {
1363             throw new Tinebase_Exception_UnexpectedValue('bad property name');
1364         }
1365         
1366         $filter = new $filterClassName(array(
1367             array('field' => $property, 'operator' => 'startswith', 'value' => $startswith),
1368         ));
1369         
1370         $paging = new Tinebase_Model_Pagination(array('sort' => $property));
1371         
1372         $values = array_unique($controller->search($filter, $paging)->{$property});
1373         
1374         $result = array(
1375             'results'   => array(),
1376             'totalcount' => count($values)
1377         );
1378         
1379         foreach($values as $value) {
1380             $result['results'][] = array($property => $value);
1381         }
1382         
1383         return $result;
1384     }
1385
1386     /**
1387      * Toogles advanced search preference
1388      *
1389      * @param $state
1390      * @return true
1391      */
1392     public function toogleAdvancedSearch($state)
1393     {
1394         Tinebase_Core::getPreference()->setValue(Tinebase_Preference::ADVANCED_SEARCH, (int)$state);
1395         return $state == Tinebase_Core::getPreference()->getValue(Tinebase_Preference::ADVANCED_SEARCH, 0);
1396     }
1397 }