0009918: shared (only free/busy) calendar is showing history
[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-2012 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     /**
22      * wait for changes
23      * 
24      * @todo do we still need this?
25      */
26     public function ping()
27     {
28         Zend_Session::writeClose(true);
29         sleep(10);
30         return array('changes' => 'contacts');
31     }
32
33     /**
34      * get list of translated country names
35      * 
36      * Wrapper for {@see Tinebase_Core::getCountrylist}
37      * 
38      * @return array list of countrys
39      */
40     public function getCountryList()
41     {
42         return Tinebase_Translation::getCountryList();
43     }
44
45     /**
46      * returns list of all available translations
47      *
48      * @return array list of all available translations
49      */
50     public function getAvailableTranslations()
51     {
52         $availableTranslations = Tinebase_Translation::getAvailableTranslations();
53         foreach($availableTranslations as &$info) unset($info['path']);
54
55         return array(
56             'results'    => array_values($availableTranslations),
57             'totalcount' => count($availableTranslations)
58         );
59     }
60
61     /**
62      * sets locale
63      *
64      * @param  string $localeString
65      * @param  bool   $saveaspreference
66      * @param  bool   $setcookie
67      * @return array
68      */
69     public function setLocale($localeString, $saveaspreference, $setcookie)
70     {
71         Tinebase_Core::setupUserLocale($localeString, $saveaspreference);
72         $locale = Tinebase_Core::get('locale');
73
74         // save in cookie (expires in 365 days)
75         if ($setcookie) {
76             setcookie('TINE20LOCALE', $localeString, time()+60*60*24*365);
77         }
78
79         return array(
80             'success'      => TRUE
81         );
82     }
83
84     /**
85      * sets timezone
86      *
87      * @param  string $timezoneString
88      * @param  bool   $saveaspreference
89      * @return string
90      */
91     public function setTimezone($timezoneString, $saveaspreference)
92     {
93         $timezone = Tinebase_Core::setupUserTimezone($timezoneString, $saveaspreference);
94
95         return $timezone;
96     }
97
98     /**
99      * get users
100      *
101      * @param string $filter
102      * @param string $sort
103      * @param string $dir
104      * @param int $start
105      * @param int $limit
106      * @return array with results array & totalcount (int)
107      */
108     public function getUsers($filter, $sort, $dir, $start, $limit)
109     {
110         $result = array(
111             'results'     => array(),
112             'totalcount'  => 0
113         );
114
115         if($rows = Tinebase_User::getInstance()->getUsers($filter, $sort, $dir, $start, $limit)) {
116             $result['results']    = $rows->toArray();
117             if($start == 0 && count($result['results']) < $limit) {
118                 $result['totalcount'] = count($result['results']);
119             } else {
120                 //$result['totalcount'] = $backend->getCountByAddressbookId($addressbookId, $filter);
121             }
122         }
123
124         return $result;
125     }
126
127     /**
128      * Search for roles
129      *
130      * @param  array $_filter
131      * @param  array $_paging
132      * @return array
133      */
134     public function searchRoles($filter, $paging)
135     {
136         $result = array(
137             'results'     => array(),
138             'totalcount'  => 0
139         );
140
141         $filter = new Tinebase_Model_RoleFilter(array(
142             'name'        => '%' . $filter[0]['value'] . '%',
143             'description' => '%' . $filter[0]['value'] . '%'
144         ));
145
146         $paging['sort'] = isset($paging['sort']) ? $paging['sort'] : 'name';
147         $paging['dir'] = isset($paging['dir']) ? $paging['dir'] : 'ASC';
148
149         $result['results'] = Tinebase_Acl_Roles::getInstance()->searchRoles($filter, new Tinebase_Model_Pagination($paging))->toArray();
150         $result['totalcount'] = Tinebase_Acl_Roles::getInstance()->searchCount($filter);
151
152         return $result;
153     }
154
155     /**
156      * change password of user
157      *
158      * @param  string $oldPassword the old password
159      * @param  string $newPassword the new password
160      * @return array
161      */
162     public function changePassword($oldPassword, $newPassword)
163     {
164         $response = array(
165             'success'      => TRUE
166         );
167
168         try {
169             Tinebase_Controller::getInstance()->changePassword($oldPassword, $newPassword);
170         } catch (Tinebase_Exception $e) {
171             $response = array(
172                 'success'      => FALSE,
173                 'errorMessage' => "New password could not be set! Error: " . $e->getMessage()
174             );
175         }
176
177         return $response;
178     }
179
180     /**
181      * clears state
182      *
183      * @param  string $name
184      * @return void
185      */
186     public function clearState($name)
187     {
188         Tinebase_State::getInstance()->clearState($name);
189     }
190
191     /**
192      * retuns all states
193      *
194      * @return array of name => value
195      */
196     public function loadState()
197     {
198         return Tinebase_State::getInstance()->loadStateInfo();
199     }
200
201     /**
202      * set state
203      *
204      * @param  string $name
205      * @param  string $value
206      * @return void
207      */
208     public function setState($name, $value)
209     {
210         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " Setting state: {$name} -> {$value}");
211         Tinebase_State::getInstance()->setState($name, $value);
212     }
213
214     /**
215      * adds a new personal tag
216      *
217      * @param  array $tag
218      * @return array
219      */
220     public function saveTag($tag)
221     {
222         $inTag = new Tinebase_Model_Tag($tag);
223
224         if (strlen($inTag->getId()) < 40) {
225             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' creating tag: ' . print_r($inTag->toArray(), true));
226             $outTag = Tinebase_Tags::getInstance()->createTag($inTag);
227         } else {
228             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' updating tag: ' .print_r($inTag->toArray(), true));
229             $outTag = Tinebase_Tags::getInstance()->updateTag($inTag);
230         }
231         return $outTag->toArray();
232     }
233
234     /**
235      * Used for updating multiple records
236      * 
237      * @param string $appName
238      * @param string $modelName
239      * @param array $changes
240      * @param array $filter
241      */
242     public function updateMultipleRecords($appName, $modelName, $changes, $filter)
243     {
244         // increase execution time to 30 minutes
245         $oldMaxExcecutionTime = Tinebase_Core::setExecutionLifeTime(1800);
246         
247         $filterModel = $appName . '_Model_' . $modelName . 'Filter';
248         foreach ($changes as $f) {
249             $data[preg_replace('/^customfield_/','#', $f['name'])] = $f['value'];
250         }
251         
252         return $this->_updateMultiple($filter, $data, Tinebase_Core::getApplicationInstance($appName, $modelName), $filterModel);
253     }
254
255     /**
256      * search tags
257      *
258      * @param  array $filter filter array
259      * @param  array $paging pagination info
260      * @return array
261      */
262     public function searchTags($filter, $paging)
263     {
264         $filter = new Tinebase_Model_TagFilter($filter);
265         $paging = new Tinebase_Model_Pagination($paging);
266
267         return array(
268             'results'    => Tinebase_Tags::getInstance()->searchTags($filter, $paging)->toArray(),
269             'totalCount' => Tinebase_Tags::getInstance()->getSearchTagsCount($filter)
270         );
271     }
272
273     /**
274     * search tags by foreign filter
275     *
276     * @param  array $filterData
277     * @param  string $filterName
278     * @return array
279     */
280     public function searchTagsByForeignFilter($filterData, $filterName)
281     {
282         $filter = $this->_getFilterGroup($filterData, $filterName);
283
284         $result = Tinebase_Tags::getInstance()->searchTagsByForeignFilter($filter)->toArray();
285         return array(
286             'results'    => $result,
287             'totalCount' => count($result)
288         );
289     }
290
291     /**
292      * get filter group defined by filterName and filterData
293      *
294      * @param array $_filterData
295      * @param string $_filterName
296      * @return Tinebase_Model_Filter_FilterGroup
297      * @throws Tinebase_Exception_AccessDenied
298      */
299     protected function _getFilterGroup($_filterData, $_filterName)
300     {
301         // NOTE: this function makes a new instance of a class whose name is given by user input.
302         //       we need to do some sanitising first!
303         list($appName, $modelString, $filterGroupName) = explode('_', $_filterName);
304         if ($modelString !== 'Model') {
305             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' spoofing attempt detected, affected account: ' . print_r(Tinebase_Core::getUser()->toArray(), TRUE));
306             die('go away!');
307         }
308
309         if (! Tinebase_Core::getUser()->hasRight($appName, Tinebase_Acl_Rights_Abstract::RUN)) {
310             throw new Tinebase_Exception_AccessDenied('No right to access application');
311         }
312
313         $filterGroup = new $_filterName(array());
314         if (! $filterGroup instanceof Tinebase_Model_Filter_FilterGroup) {
315             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' spoofing attempt detected, affected account: ' . print_r(Tinebase_Core::getUser()->toArray(), TRUE));
316             die('go away!');
317         }
318
319         // at this point we are sure request is save ;-)
320         $filterGroup->setFromArray($_filterData);
321
322         return $filterGroup;
323     }
324
325     /**
326      * attach tag to multiple records identified by a filter
327      *
328      * @param array  $filterData
329      * @param string $filterName
330      * @param mixed  $tag       string|array existing and non-existing tag
331      * @return void
332      */
333     public function attachTagToMultipleRecords($filterData, $filterName, $tag)
334     {
335         $this->_longRunningRequest();
336         $filter = $this->_getFilterGroup($filterData, $filterName);
337
338         Tinebase_Tags::getInstance()->attachTagToMultipleRecords($filter, $tag);
339         return array('success' => true);
340     }
341
342     /**
343      * detach tags to multiple records identified by a filter
344      *
345      * @param array  $filterData
346      * @param string $filterName
347      * @param mixed  $tag       string|array existing and non-existing tag
348      * @return void
349      */
350     public function detachTagsFromMultipleRecords($filterData, $filterName, $tag)
351     {
352         $this->_longRunningRequest();
353         $filter = $this->_getFilterGroup($filterData, $filterName);
354
355         Tinebase_Tags::getInstance()->detachTagsFromMultipleRecords($filter, $tag);
356         return array('success' => true);
357     }
358
359     /**
360      * search / get notes
361      * - used by activities grid
362      *
363      * @param  array $filter filter array
364      * @param  array $paging pagination info
365      * @return array
366      */
367     public function searchNotes($filter, $paging)
368     {
369         $filter = new Tinebase_Model_NoteFilter($filter);
370         $paging = new Tinebase_Model_Pagination($paging);
371         
372         $records = Tinebase_Notes::getInstance()->searchNotes($filter, $paging, /* ignoreACL = */ false);
373         $result = $this->_multipleRecordsToJson($records);
374
375         return array(
376             'results'       => $result,
377             'totalcount'    => Tinebase_Notes::getInstance()->searchNotesCount($filter, /* ignoreACL = */ false)
378         );
379     }
380
381     /**
382      * get note types
383      *
384      */
385     public function getNoteTypes()
386     {
387         $noteTypes = Tinebase_Notes::getInstance()->getNoteTypes();
388         $noteTypes->translate();
389
390         return array(
391             'results'       => $noteTypes->toArray(),
392             'totalcount'    => count($noteTypes)
393         );
394     }
395
396     /**
397      * deletes tags identified by an array of identifiers
398      *
399      * @param  array $ids
400      * @return array
401      */
402     public function deleteTags($ids)
403     {
404         Tinebase_Tags::getInstance()->deleteTags($ids);
405         return array('success' => true);
406     }
407
408     /**
409      * authenticate user by username and password
410      *
411      * @param  string $username the username
412      * @param  string $password the password
413      * @return array
414      */
415     public function authenticate($username, $password)
416     {
417         $authResult = Tinebase_Auth::getInstance()->authenticate($username, $password);
418
419         if ($authResult->isValid()) {
420             $response = array(
421                 'status'    => 'success',
422                 'msg'       => 'authentication succseed',
423                 //'loginUrl'  => 'someurl',
424             );
425         } else {
426             $response = array(
427                 'status'    => 'fail',
428                 'msg'       => 'authentication failed',
429             );
430         }
431
432         return $response;
433     }
434
435     /**
436      * login user with given username and password
437      *
438      * @param  string $username the username
439      * @param  string $password the password
440      * @param  string $securitycode the security code(captcha)
441      * @return array
442      */
443     public function login($username, $password, $securitycode=NULL)
444     {
445         Tinebase_Core::startSession('tinebase');
446         
447         $captcha = (isset(Tinebase_Core::getConfig()->captcha->count) && Tinebase_Core::getConfig()->captcha->count != 0) ? TRUE : FALSE; 
448         if ($captcha) {
449             $config_count = Tinebase_Core::getConfig()->captcha->count;
450             $count = (isset($_SESSION['captcha']['count']) ? $_SESSION['captcha']['count'] : 1);
451             if ($count >= $config_count) {
452                 $aux = isset($_SESSION['captcha']['code']) ? $_SESSION['captcha']['code'] : NULL;
453                 if ($aux != $securitycode) {
454                     $rets = Tinebase_Controller::getInstance()->makeCaptcha(); 
455                     $response = array(
456                         'success'      => FALSE,
457                         'errorMessage' => "Wrong username or password!",
458                         'c1' => $rets['1']
459                     );
460                     return $response;
461                    }
462             }
463         }
464         // try to login user
465         $success = (Tinebase_Controller::getInstance()->login($username, $password, $_SERVER['REMOTE_ADDR'], 'TineJson', $securitycode) === TRUE);
466         
467         if ($success == true) {
468             $response = array(
469                 'success'        => TRUE,
470                 'account'        => Tinebase_Core::getUser()->getPublicUser()->toArray(),
471                 'jsonKey'        => Tinebase_Core::get('jsonKey'),
472                 'welcomeMessage' => "Welcome to Tine 2.0!"
473             );
474              
475             if (Tinebase_Config::getInstance()->get(Tinebase_Config::REUSEUSERNAME_SAVEUSERNAME, 0)) {
476                 // save in cookie (expires in 2 weeks)
477                 setcookie('TINE20LASTUSERID', $username, time()+60*60*24*14);
478             } else {
479                 setcookie('TINE20LASTUSERID', '', 0);
480             }
481
482             $this->_setCredentialCacheCookie();
483             
484         } else {
485             $response = array(
486                 'success'      => FALSE,
487                 'errorMessage' => "Wrong username or password!",
488             );
489             Tinebase_Auth_CredentialCache::getInstance()->getCacheAdapter()->resetCache();
490             if ($captcha) {
491                 $config_count = Tinebase_Core::getConfig()->captcha->count;
492                 if (!isset($_SESSION['captcha']['count'])) {
493                     $_SESSION['captcha']['count'] = 1;
494                 } else {
495                     $_SESSION['captcha']['count'] = $_SESSION['captcha']['count'] + 1;
496                 }
497                 if ($_SESSION['captcha']['count'] >= $config_count) {
498                     $rets = Tinebase_Controller::getInstance()->makeCaptcha(); 
499                     $response = array(
500                         'success'      => FALSE,
501                         'errorMessage' => "Wrong username or password!",
502                         'c1' => $rets['1']
503                     );
504                 }
505             } else {
506                 Zend_Session::destroy(false,true);
507             }
508             
509         }
510
511         return $response;
512     }
513
514     /**
515      * set credential cache cookie
516      *
517      * @return boolean
518      */
519     protected function _setCredentialCacheCookie()
520     {
521         $result = TRUE;
522
523         if (Tinebase_Core::isRegistered(Tinebase_Core::USERCREDENTIALCACHE)) {
524             Tinebase_Auth_CredentialCache::getInstance()->getCacheAdapter()->setCache(Tinebase_Core::get(Tinebase_Core::USERCREDENTIALCACHE));
525         } else {
526             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Something went wrong with the CredentialCache / no CC registered.');
527             $success = FALSE;
528         }
529
530         return $result;
531     }
532
533     /**
534      * update user credential cache
535      *
536      * - fires Tinebase_Event_User_ChangeCredentialCache
537      *
538      * @param string $password
539      * @return array
540      */
541     public function updateCredentialCache($password)
542     {
543         $oldCredentialCache = Tinebase_Core::get(Tinebase_Core::USERCREDENTIALCACHE);
544         $credentialCache = Tinebase_Auth_CredentialCache::getInstance()->cacheCredentials(Tinebase_Core::getUser()->accountLoginName, $password);
545         Tinebase_Core::set(Tinebase_Core::USERCREDENTIALCACHE, $credentialCache);
546
547         $success = $this->_setCredentialCacheCookie();
548
549         if ($success) {
550             // close session to allow other requests
551             Zend_Session::writeClose(true);
552             $event = new Tinebase_Event_User_ChangeCredentialCache($oldCredentialCache);
553             Tinebase_Event::fireEvent($event);
554         }
555
556         return array(
557             'success'      => $success
558         );
559     }
560
561     /**
562      * destroy session
563      *
564      * @return array
565      */
566     public function logout()
567     {
568         Tinebase_Controller::getInstance()->logout($_SERVER['REMOTE_ADDR']);
569         
570         Tinebase_Auth_CredentialCache::getInstance()->getCacheAdapter()->resetCache();
571         
572         if (Zend_Session::isStarted()) {
573             Zend_Session::destroy();
574         }
575
576         $result = array(
577             'success'=> true,
578         );
579
580         return $result;
581     }
582
583     /**
584      * Returns registry data of tinebase.
585      * @see Tinebase_Application_Json_Abstract
586      *
587      * @return mixed array 'variable name' => 'data'
588      */
589     public function getRegistryData()
590     {
591         $registryData = $this->_getAnonymousRegistryData();
592         
593         if (Tinebase_Core::isRegistered(Tinebase_Core::USER)) {
594             $userRegistryData = $this->_getUserRegistryData();
595             $registryData += $userRegistryData;
596         }
597         
598         return $registryData;
599     }
600     
601     /**
602      * get anonymous registry
603      * 
604      * @return array
605      */
606     protected function _getAnonymousRegistryData()
607     {
608         $locale = Tinebase_Core::get('locale');
609         $tbFrontendHttp = new Tinebase_Frontend_Http();
610
611         // default credentials
612         if (isset(Tinebase_Core::getConfig()->login)) {
613             $loginConfig = Tinebase_Core::getConfig()->login;
614             $defaultUsername = (isset($loginConfig->username)) ? $loginConfig->username : '';
615             $defaultPassword = (isset($loginConfig->password)) ? $loginConfig->password : '';
616         } else {
617             $defaultUsername = '';
618             $defaultPassword = '';
619         }
620         
621         $numberString = Zend_Locale_Format::toFloat(1234.56);
622         
623         $registryData =  array(
624             'modSsl'           => Tinebase_Auth::getConfiguredBackend() == Tinebase_Auth::MODSSL,
625             'serviceMap'       => $tbFrontendHttp->getServiceMap(),
626             'timeZone'         => Tinebase_Core::get(Tinebase_Core::USERTIMEZONE),
627             'locale'           => array(
628                 'locale'   => $locale->toString(),
629                 'language' => Zend_Locale::getTranslation($locale->getLanguage(), 'language', $locale),
630                 'region'   => Zend_Locale::getTranslation($locale->getRegion(), 'country', $locale),
631             ),
632             'version'          => array(
633                 'buildType'     => TINE20_BUILDTYPE,
634                 'codeName'      => TINE20_CODENAME,
635                 'packageString' => TINE20_PACKAGESTRING,
636                 'releaseTime'   => TINE20_RELEASETIME,
637                 'filesHash'     => TINE20_BUILDTYPE != 'DEVELOPMENT' ? $tbFrontendHttp->getJsCssHash() : null
638             ),
639             'defaultUsername'   => $defaultUsername,
640             'defaultPassword'   => $defaultPassword,
641             'denySurveys'       => Tinebase_Core::getConfig()->denySurveys,
642             'titlePostfix'      => Tinebase_Config::getInstance()->get(Tinebase_Model_Config::PAGETITLEPOSTFIX),
643             'redirectUrl'       => Tinebase_Config::getInstance()->get(Tinebase_Model_Config::REDIRECTURL),
644             'helpUrl'           => Tinebase_Core::getConfig()->helpUrl,
645             'maxFileUploadSize' => convertToBytes(ini_get('upload_max_filesize')),
646             'maxPostSize'       => convertToBytes(ini_get('post_max_size')),
647             'thousandSeparator' => $numberString[1],
648             'decimalSeparator'  => $numberString[5],
649             'filesystemAvailable' => Setup_Controller::getInstance()->isFilesystemAvailable(),
650         );
651         
652         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
653             . ' Anonymous registry: ' . print_r($registryData, TRUE));
654         
655         return $registryData;
656     }
657     
658     /**
659      * get user registry
660      * 
661      * @return array
662      */
663     protected function _getUserRegistryData()
664     {
665         $user = Tinebase_Core::getUser();
666         $userContactArray = array();
667         if (Tinebase_Application::getInstance()->isInstalled('Addressbook') === true) {
668             try {
669                 $userContactArray = Addressbook_Controller_Contact::getInstance()->getContactByUserId($user->getId(), TRUE)->toArray();
670             } catch (Addressbook_Exception_NotFound $aenf) {
671                 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__
672                     . ' User not found in Addressbook: ' . $user->accountDisplayName);
673             }
674         }
675         
676         try {
677             $persistentFilters = Tinebase_Frontend_Json_PersistentFilter::getAllPersistentFilters();
678         } catch (Tinebase_Exception_NotFound $tenf) {
679             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__
680                 . " Failed to fetch persistent filters. Exception: \n". $tenf);
681             $persistentFilters = array();
682         }
683         
684         $userRegistryData = array(
685             'currentAccount'    => $user->toArray(),
686             'userContact'       => $userContactArray,
687             'accountBackend'    => Tinebase_User::getConfiguredBackend(),
688             'jsonKey'           => Tinebase_Core::get('jsonKey'),
689             'userApplications'  => $user->getApplications()->toArray(),
690             'NoteTypes'         => $this->getNoteTypes(),
691             'stateInfo'         => Tinebase_State::getInstance()->loadStateInfo(),
692             'mustchangepw'      => $user->mustChangePassword(),
693             'confirmLogout'     => Tinebase_Core::getPreference()->getValue(Tinebase_Preference::CONFIRM_LOGOUT, 1),
694             'persistentFilters' => $persistentFilters,
695         );
696         
697         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
698             . ' User registry: ' . print_r($userRegistryData, TRUE));
699         
700         return $userRegistryData;
701     }
702
703     /**
704      * Returns registry data of all applications current user has access to
705      * @see Tinebase_Application_Json_Abstract
706      *
707      * @return mixed array 'variable name' => 'data'
708      */
709     public function getAllRegistryData()
710     {
711         $registryData = array();
712         
713         if (Tinebase_Core::getUser()) {
714             $userApplications = Tinebase_Core::getUser()->getApplications(TRUE);
715             $clientConfig = Tinebase_Config::getInstance()->getClientRegistryConfig();
716             
717             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
718                . ' User applications to fetch registry for: ' . print_r($userApplications->name, TRUE));
719             
720             if (! in_array('Tinebase', $userApplications->name)) {
721                 Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . ' User has no permissions to run Tinebase.');
722                 $this->logout();
723                 throw new Tinebase_Exception_AccessDenied('User has no permissions to run Tinebase');
724             }
725             
726             foreach ($userApplications as $application) {
727                 $jsonAppName = $application->name . '_Frontend_Json';
728                 
729                 if (class_exists($jsonAppName)) {
730                     if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
731                         Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Getting registry data for app ' . $application->name);
732                     }
733                     
734                     try {
735                         $applicationJson = new $jsonAppName();
736                         $registryData[$application->name] = ((isset($registryData[$application->name]) || array_key_exists($application->name, $registryData)))
737                             ? array_merge_recursive($registryData[$application->name], $applicationJson->getRegistryData()) 
738                             : $applicationJson->getRegistryData();
739                     
740                     } catch (Exception $e) {
741                         Tinebase_Exception::log($e);
742                         if (! in_array($application->name, array('Tinebase', 'Addressbook', 'Admin'))) {
743                             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Disabling ' . $application->name . ': ' . $e);
744                             Tinebase_Application::getInstance()->setApplicationState(array($application->getId()), Tinebase_Application::DISABLED);
745                         }
746                         unset($registryData[$application->name]);
747                         continue;
748                     }
749                     
750                     $registryData[$application->name]['rights'] = Tinebase_Core::getUser()->getRights($application->name);
751                     $registryData[$application->name]['config'] = isset($clientConfig[$application->name]) ? $clientConfig[$application->name]->toArray() : array();
752                     $registryData[$application->name]['models'] = $applicationJson->getModelsConfiguration();
753                     $registryData[$application->name]['defaultModel'] = $applicationJson->getDefaultModel();
754                     
755                     foreach ($applicationJson->getRelatableModels() as $relModel) {
756                         $registryData[$relModel['ownApp']]['relatableModels'][] = $relModel;
757                     }
758
759                     // @todo do we need this for all apps?
760                     $exportDefinitions = Tinebase_ImportExportDefinition::getInstance()->getExportDefinitionsForApplication($application);
761                     $registryData[$application->name]['exportDefinitions'] = array(
762                         'results'               => $exportDefinitions->toArray(),
763                         'totalcount'            => count($exportDefinitions),
764                     );
765                     
766                     $customfields = Tinebase_CustomField::getInstance()->getCustomFieldsForApplication($application);
767                     Tinebase_CustomField::getInstance()->resolveConfigGrants($customfields);
768                     $registryData[$application->name]['customfields'] = $customfields->toArray();
769                     
770                     // add preferences for app
771                     $appPrefs = Tinebase_Core::getPreference($application->name);
772                     if ($appPrefs !== NULL) {
773                         $allPrefs = $appPrefs->getAllApplicationPreferences();
774                         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
775                             . ' ' . print_r($allPrefs, TRUE));
776                         
777                         foreach ($allPrefs as $pref) {
778                             try {
779                                 $registryData[$application->name]['preferences'][$pref] = $appPrefs->{$pref};
780                             } catch (Exception $e) {
781                                 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Could not get ' . $pref . '  preference: ' . $e);
782                             }
783                         }
784                     }
785                 }
786             }
787         } else {
788             $registryData['Tinebase'] = $this->getRegistryData();
789         }
790         
791         return $registryData;
792     }
793
794     /**
795      * search / get custom field values
796      *
797      * @param  array $filter filter array
798      * @param  array $paging pagination info
799      * @return array
800      */
801     public function searchCustomFieldValues($filter, $paging)
802     {
803         $result = $this->_search($filter, $paging, Tinebase_CustomField::getInstance(), 'Tinebase_Model_CustomField_ValueFilter');
804         return $result;
805     }
806
807     /************************ preferences functions ***************************/
808
809     /**
810      * search preferences
811      *
812      * @param  string $applicationName
813      * @param  array  $filter json encoded
814      * @return array
815      */
816     public function searchPreferencesForApplication($applicationName, $filter)
817     {
818         $decodedFilter = is_array($filter) ? $filter : Zend_Json::decode($filter);
819
820         $filter = new Tinebase_Model_PreferenceFilter();
821         if (! empty($decodedFilter)) {
822             $filter->setFromArrayInUsersTimezone($decodedFilter);
823         }
824         $appId = Tinebase_Application::getInstance()->getApplicationByName($applicationName)->getId();
825         $filter->addFilter($filter->createFilter(array('field'     => 'application_id',  'operator'  => 'equals', 'value'     => $appId)));
826
827         $backend = Tinebase_Core::getPreference($applicationName);
828         if ($backend) {
829             $records = $backend->search($filter);
830             
831             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
832                 . ' Got ' . count($records) . ' preferences for app ' . $applicationName);
833             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
834                 . ' ' . print_r($records->toArray(), TRUE));
835             
836             $result = $this->_multipleRecordsToJson($records, $filter);
837
838             // add translated labels and descriptions
839             $translations = $backend->getTranslatedPreferences();
840             foreach ($result as $key => $prefArray) {
841                 if (isset($translations[$prefArray['name']])) {
842                     $result[$key] = array_merge($prefArray, $translations[$prefArray['name']]);
843                 } else {
844                     $result[$key] = array_merge($prefArray, array('label' => $prefArray['name']));
845                 }
846             }
847             
848             // sort prefs by definition
849             $allPrefs = (array) $backend->getAllApplicationPreferences();
850             usort($result, function($a, $b) use ($allPrefs) {
851                 $a = (int) array_search($a['name'], $allPrefs);
852                 $b = (int) array_search($b['name'], $allPrefs);
853                 
854                 if ($a == $b) {
855                     return 0;
856                 }
857                 return ($a < $b) ? -1 : 1;
858             });
859             
860         } else {
861             $result = array();
862         }
863
864         return array(
865             'results'       => $result,
866             'totalcount'    => count($result)
867         );
868     }
869
870     /**
871      * save preferences for application
872      *
873      * @param string    $data       json encoded preferences data
874      * @param bool      $adminMode  submit in admin mode?
875      * @return array with the changed prefs
876      *
877      * @todo move saving of user values to preferences controller
878      */
879     public function savePreferences($data, $adminMode)
880     {
881         $decodedData = is_array($data) ? $data : Zend_Json::decode($data);
882
883         $result = array();
884         foreach ($decodedData as $applicationName => $data) {
885
886             if ($applicationName == 'Tinebase.UserProfile') {
887                 $userProfileData = array();
888                 foreach($data as $fieldName => $valueArray) {
889                     $userProfileData[$fieldName] = $valueArray['value'];
890                 }
891                 $this->updateUserProfile($userProfileData);
892
893             } else {
894                 $backend = Tinebase_Core::getPreference($applicationName);
895                 if ($backend !== NULL) {
896                     if ($adminMode) {
897                         $result = $backend->saveAdminPreferences($data);
898                     } else {
899                         // set user prefs
900                         foreach ($data as $name => $value) {
901                             $backend->doSpecialJsonFrontendActions($this, $name, $value['value'], $applicationName);
902                             $backend->$name = $value['value'];
903                             $result[$applicationName][] = array('name' => $name, 'value' => $backend->$name);
904                         }
905                     }
906                 }
907             }
908         }
909
910         return array(
911             'status'    => 'success',
912             'results'   => $result
913         );
914     }
915
916     /**
917      * get profile of current user
918      *
919      * @param string $userId
920      * @return array
921      */
922     public function getUserProfile($userId)
923     {
924         // NOTE: $userProfile is a contact where non readable fields are clearad out!
925         $userProfile = Tinebase_UserProfile::getInstance()->get($userId);
926
927         // NOTE: This hurts! We don't have methods to call in our frontends yet which convert
928         //       a record to the json representaion :( Thus image link will be broken!
929         $userProfile->setTimezone(Tinebase_Core::get(Tinebase_Core::USERTIMEZONE));
930
931         return array(
932             'userProfile'      => $userProfile->toArray(),
933             'readableFields'   => Tinebase_UserProfile::getInstance()->getReadableFields(),
934             'updateableFields' => Tinebase_UserProfile::getInstance()->getUpdateableFields(),
935         );
936     }
937
938     /**
939      * update user profile
940      *
941      * @param  array $profileData
942      * @return array
943      */
944     public function updateUserProfile($profileData)
945     {
946         $contact = new Addressbook_Model_Contact(array(), TRUE);
947         $contact->setFromJsonInUsersTimezone($profileData);
948
949         // NOTE: $userProfile is a contact where non readable fields are clearad out!
950         $userProfile = Tinebase_UserProfile::getInstance()->update($contact);
951
952         // NOTE: This hurts! We don't have methods to call in our frontends yet which convert
953         //       a record to the json representaion :( Thus image link will be broken!
954         $userProfile->setTimezone(Tinebase_Core::get(Tinebase_Core::USERTIMEZONE));
955         return $userProfile->toArray();
956     }
957
958     /**
959      * dummy function to measure speed of framework initialization
960      */
961     public function void()
962     {
963         return array();
964     }
965
966     /**
967      * gets the userProfile config
968      *
969      * @return @array
970      */
971     public function getUserProfileConfig()
972     {
973         return array(
974             'possibleFields'   => array_values(Tinebase_UserProfile::getInstance()->getPossibleFields()),
975             'readableFields'   => array_values(Tinebase_UserProfile::getInstance()->getReadableFields()),
976             'updateableFields' => array_values(Tinebase_UserProfile::getInstance()->getUpdateableFields()),
977         );
978     }
979
980     /**
981      * saves userProfile config
982      *
983      * @param array $configData
984      */
985     public function setUserProfileConfig($configData)
986     {
987         Tinebase_UserProfile::getInstance()->setReadableFields($configData['readableFields']);
988         Tinebase_UserProfile::getInstance()->setUpdateableFields($configData['updateableFields']);
989     }
990
991     /************************ department functions **************************/
992
993     /**
994      * search / get departments
995      *
996      * @param  array $filter filter array
997      * @param  array $paging pagination info
998      * @return array
999      */
1000     public function searchDepartments($filter, $paging)
1001     {
1002         $result = $this->_search($filter, $paging, Tinebase_Department::getInstance(), 'Tinebase_Model_DepartmentFilter');
1003         return $result;
1004     }
1005
1006     /************************ config functions ******************************/
1007
1008     /**
1009      * get config settings for application
1010      *
1011      * @param string $id application name
1012      * @return array
1013      */
1014     public function getConfig($id)
1015     {
1016         $controllerName = $id . '_Controller';
1017         $appController = Tinebase_Controller_Abstract::getController($controllerName);
1018
1019         return array(
1020             'id'        => $id,
1021             'settings'  => $appController->getConfigSettings(TRUE),
1022         );
1023     }
1024
1025     /**
1026      * save application config
1027      *
1028      * @param array $recordData
1029      * @return array
1030      */
1031     public function saveConfig($recordData)
1032     {
1033         //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . print_r($recordData, TRUE));
1034
1035         $controllerName = $recordData['id'] . '_Controller';
1036         $appController = Tinebase_Controller_Abstract::getController($controllerName);
1037         $appController->saveConfigSettings($recordData['settings']);
1038
1039         return $this->getConfig($recordData['id']);
1040     }
1041
1042     /************************ tempFile functions ******************************/
1043
1044     /**
1045      * joins all given tempfiles in given order to a single new tempFile
1046      *
1047      * @param array of tempfiles arrays $tempFiles
1048      * @return array new tempFile
1049      */
1050     public function joinTempFiles($tempFilesData)
1051     {
1052         $tempFileRecords = new Tinebase_Record_RecordSet('Tinebase_Model_TempFile');
1053         foreach($tempFilesData as $tempFileData) {
1054             $record = new Tinebase_Model_TempFile(array(), TRUE);
1055             $record->setFromJsonInUsersTimezone($tempFileData);
1056             $tempFileRecords->addRecord($record);
1057         }
1058
1059         $joinedTempFile = Tinebase_TempFile::getInstance()->joinTempFiles($tempFileRecords);
1060
1061         return $joinedTempFile->toArray();
1062     }
1063
1064     /************************ protected functions ***************************/
1065
1066     /**
1067      * returns multiple records prepared for json transport
1068      *
1069      * @param Tinebase_Record_RecordSet $_records Tinebase_Record_Abstract
1070      * @param Tinebase_Model_Filter_FilterGroup $_filter
1071      * @param Tinebase_Model_Pagination $_pagination
1072      * @return array data
1073      */
1074     protected function _multipleRecordsToJson(Tinebase_Record_RecordSet $_records, $_filter = NULL, $_pagination = NULL)
1075     {
1076         if (count($_records) == 0) {
1077             return array();
1078         }
1079
1080         switch ($_records->getRecordClassName()) {
1081             case 'Tinebase_Model_Preference':
1082                 $accountFilterArray = $_filter->getFilter('account')->toArray();
1083                 $adminMode = ($accountFilterArray['value']['accountId'] == 0 && $accountFilterArray['value']['accountType'] == Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE);
1084                 foreach ($_records as $record) {
1085                     if (! isset($app) || $record->application_id != $app->getId()) {
1086                         $app = Tinebase_Application::getInstance()->getApplicationById($record->application_id);
1087                     }
1088                     $preference = Tinebase_Core::getPreference($app->name, TRUE);
1089                     $preference->resolveOptions($record);
1090                     if ($record->type == Tinebase_Model_Preference::TYPE_DEFAULT || ! $adminMode && $record->type == Tinebase_Model_Preference::TYPE_ADMIN) {
1091                         $record->value = Tinebase_Model_Preference::DEFAULT_VALUE;
1092                     }
1093                 }
1094                 break;
1095         }
1096
1097         $result = parent::_multipleRecordsToJson($_records, $_filter, $_pagination);
1098         return $result;
1099     }
1100 }