0011336: support backup and restore via cli
[tine20] / tine20 / Setup / Frontend / Cli.php
1 <?php
2 /**
3  * Tine 2.0
4  * @package     Tinebase
5  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
6  * @author      Philipp Schüle <p.schuele@metaways.de>
7  * @copyright   Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
8  * 
9  * @todo        add ext check again
10  */
11
12 /**
13  * cli server
14  *
15  * This class handles all requests from cli scripts
16  *
17  * @package     Tinebase
18  */
19 class Setup_Frontend_Cli
20 {
21     /**
22      * the internal name of the application
23      *
24      * @var string
25      */
26     protected $_appname = 'Setup';
27
28     /**
29      * authentication
30      *
31      * @param string $_username
32      * @param string $_password
33      * 
34      * @return boolean
35      */
36     public function authenticate($_username, $_password)
37     {
38         return false;
39     }
40     
41     /**
42      * handle request (call -ApplicationName-_Cli.-MethodName- or -ApplicationName-_Cli.getHelp)
43      *
44      * @param Zend_Console_Getopt $_opts
45      * @param boolean $exitAfterHandle
46      * @return void
47      */
48     public function handle(Zend_Console_Getopt $_opts, $exitAfterHandle = true)
49     {
50         Setup_Core::set(Setup_Core::USER, 'setupuser');
51         
52         $result = 0;
53         if (isset($_opts->install)) {
54             $this->_install($_opts);
55         } elseif(isset($_opts->update)) {
56             $result = $this->_update($_opts);
57         } elseif(isset($_opts->uninstall)) {
58             $this->_uninstall($_opts);
59         } elseif(isset($_opts->list)) {
60             $result = $this->_listInstalled();
61         } elseif(isset($_opts->sync_accounts_from_ldap)) {
62             $this->_importAccounts($_opts);
63         } elseif(isset($_opts->sync_passwords_from_ldap)) {
64             $this->_syncPasswords($_opts);
65         } elseif(isset($_opts->egw14import)) {
66             $this->_egw14Import($_opts);
67         } elseif(isset($_opts->check_requirements)) {
68             $this->_checkRequirements($_opts);
69         } elseif(isset($_opts->setconfig)) {
70             $this->_setConfig($_opts);
71         } elseif(isset($_opts->create_admin)) {
72             $this->_createAdminUser($_opts);
73         } elseif(isset($_opts->getconfig)) {
74             $this->_getConfig($_opts);
75         } elseif(isset($_opts->reset_demodata)) {
76             $this->_resetDemodata($_opts);
77         } elseif(isset($_opts->updateAllImportExportDefinitions)) {
78             $this->_updateAllImportExportDefinitions($_opts);
79         } elseif(isset($_opts->backup)) {
80             $this->_backup($_opts);
81         } elseif(isset($_opts->restore)) {
82             $this->_restore($_opts);
83         }
84         
85         if ($exitAfterHandle) {
86             exit($result);
87         }
88     }
89     
90     /**
91      * install new applications
92      *
93      * @param Zend_Console_Getopt $_opts
94      */
95     protected function _install(Zend_Console_Getopt $_opts)
96     {
97         $controller = Setup_Controller::getInstance();
98         
99         if($_opts->install === true) {
100             $applications = $controller->getInstallableApplications();
101             $applications = array_keys($applications);
102         } else {
103             $applications = array();
104             $applicationNames = explode(',', $_opts->install);
105             foreach($applicationNames as $applicationName) {
106                 $applicationName = ucfirst(trim($applicationName));
107                 try {
108                     $controller->getSetupXml($applicationName);
109                     $applications[] = $applicationName;
110                 } catch (Setup_Exception_NotFound $e) {
111                     echo "Application $applicationName not found! Skipped...\n";
112                 }
113             }
114         }
115         
116         $options = $this->_parseRemainingArgs($_opts->getRemainingArgs());
117         $this->_promptRemainingOptions($applications, $options);
118         
119         $controller->installApplications($applications, $options);
120         
121         if ((isset($options['acceptedTermsVersion']) || array_key_exists('acceptedTermsVersion', $options))) {
122             Setup_Controller::getInstance()->saveAcceptedTerms($options['acceptedTermsVersion']);
123         }
124         
125         echo "Successfully installed " . count($applications) . " applications.\n";
126     }
127
128     /**
129      * prompt remaining options
130      * 
131      * @param array $_applications
132      * @param array $_options
133      * @return void
134      * 
135      * @todo add required version server side
136      */
137     protected function _promptRemainingOptions($_applications, &$_options) {
138         if (in_array('Tinebase', $_applications)) {
139             
140             if (! isset($_options['acceptedTermsVersion'])) {
141                 fwrite(STDOUT, PHP_EOL . file_get_contents(dirname(dirname(dirname(__FILE__))) . '/LICENSE' ));
142                 $licenseAnswer = Tinebase_Server_Cli::promptInput('I have read the license agreement and accept it (type "yes" to accept)');
143                 
144                 
145                 fwrite(STDOUT, PHP_EOL . file_get_contents(dirname(dirname(dirname(__FILE__))) . '/PRIVACY' ));
146                 $privacyAnswer = Tinebase_Server_Cli::promptInput('I have read the privacy agreement and accept it (type "yes" to accept)');
147             
148                 if (! (strtoupper($licenseAnswer) == 'YES' && strtoupper($privacyAnswer) == 'YES')) {
149                     echo "error: you need to accept the terms! exiting \n";
150                     exit (1);
151                 }
152                 
153                 $_options['acceptedTermsVersion'] = 1;
154             }
155             
156             
157             // initial username
158             if (! isset($_options['adminLoginName'])) {
159                 $_options['adminLoginName'] = Tinebase_Server_Cli::promptInput('Inital Admin Users Username');
160                 if (! $_options['adminLoginName']) {
161                     echo "error: username must be given! exiting \n";
162                     exit (1);
163                 }
164             }
165             
166             // initial password / can be empty => will trigger password change dialogue
167             if (! array_key_exists('adminPassword', $_options)) {
168                 $_options['adminPassword'] = $this->_promptPassword();
169             }
170         }
171     }
172     
173     /**
174      * prompt password
175      * 
176      * @return string
177      */
178     protected function _promptPassword()
179     {
180         $password1 = Tinebase_Server_Cli::promptInput('Admin user password', TRUE);
181         if (! $password1) {
182             echo "Error: Password must not be empty! Exiting ... \n";
183             exit (1);
184         }
185         $password2 = Tinebase_Server_Cli::promptInput('Confirm password', TRUE);
186         if ($password1 !== $password2) {
187             echo "Error: Passwords do not match! Exiting ... \n";
188             exit (1);
189         }
190         
191         return $password1;
192     }
193     
194     /**
195      * update existing applications
196      *
197      * @param Zend_Console_Getopt $_opts
198      * @return integer
199      */
200     protected function _update(Zend_Console_Getopt $_opts)
201     {
202         $maxLoops = 50;
203         do {
204             $result = $this->_updateApplications();
205             if ($_opts->v && ! empty($result['messages'])) {
206                 echo "Messages:\n";
207                 foreach ($result['messages'] as $message) {
208                     echo "  " . $message . "\n";
209                 }
210             }
211             $maxLoops--;
212         } while ($result['updated'] > 0 && $maxLoops > 0);
213         
214         return ($maxLoops > 0) ? 0 : 1;
215     }
216     
217     /**
218      * update all applications
219      * 
220      * @return array
221      */
222     protected function _updateApplications()
223     {
224         $controller = Setup_Controller::getInstance();
225         $applications = Tinebase_Application::getInstance()->getApplications(NULL, 'id');
226         
227         foreach ($applications as $key => &$application) {
228             try {
229                 if (! $controller->updateNeeded($application)) {
230                     unset($applications[$key]);
231                 }
232             } catch (Setup_Exception_NotFound $e) {
233                 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ 
234                     . ' Failed to check if an application needs an update:' . $e->getMessage());
235                 unset($applications[$key]);
236             }
237         }
238
239         $result = array();
240         if (count($applications) > 0) {
241             $result = $controller->updateApplications($applications);
242             echo "Updated " . $result['updated'] . " application(s).\n";
243         } else {
244             $result['updated'] = 0;
245         }
246         
247         return $result;
248     }
249
250     /**
251      * uninstall applications
252      *
253      * @param Zend_Console_Getopt $_opts
254      */
255     protected function _uninstall(Zend_Console_Getopt $_opts)
256     {
257         $controller = Setup_Controller::getInstance();
258         
259         if($_opts->uninstall === true) {
260             $applications = Tinebase_Application::getInstance()->getApplications(NULL, 'id');
261         } else {
262             $applications = new Tinebase_Record_RecordSet('Tinebase_Model_Application');
263             $applicationNames = explode(',', $_opts->uninstall);
264             foreach($applicationNames as $applicationName) {
265                 $applicationName = ucfirst(trim($applicationName));
266                 try {
267                     $application = Tinebase_Application::getInstance()->getApplicationByName($applicationName);
268                     $applications->addRecord($application);
269                 } catch (Tinebase_Exception_NotFound $e) {
270                 }
271             }
272         }
273         
274         $controller->uninstallApplications($applications->name);
275         
276         echo "Successfully uninstalled " . count($applications) . " applications.\n";
277     }
278     
279     /**
280      * reinstall applications
281      * and reset Demodata
282      * php setup.php --reset_demodata USERNAME
283      * 
284      * @param Zend_Console_Getopt $_opts
285      */
286     protected function _resetDemodata(Zend_Console_Getopt $_opts)
287     {
288         $controller = Setup_Controller::getInstance();
289         $userController = Admin_Controller_User::getInstance();
290         $containerController = Tinebase_Container::getInstance();
291         $cli = new Tinebase_Frontend_Cli();
292         
293         //Don't reset this applications
294         $fixedApplications = array('Tinebase', 'Admin', 'Addressbook');
295         
296         //Log in
297         $opts = $_opts->getRemainingArgs();
298         $username = $opts[0];
299         if (empty($username)) {
300             echo "Username is missing!\n";
301             exit;
302         }
303         $user = Tinebase_User::getInstance()->getUserByLoginName($username);
304         Tinebase_Core::set(Tinebase_Core::USER, $user);
305         
306         //get all applications and remove some
307         $applications = Tinebase_Application::getInstance()->getApplications(NULL, 'id');
308         
309         foreach ($applications as $key => &$application) {
310             if (in_array($application, $fixedApplications)) {
311                 unset($applications[$key]);
312             }
313         }
314         
315         //get set rights
316         $users = Tinebase_Acl_Roles::getInstance()->getRoleByName('user role');
317         $rights = Tinebase_Acl_Roles::getInstance()->getRoleRights($users->getId());
318         
319         //Uninstall Applications
320         try {
321             $controller->uninstallApplications($applications->name);
322             echo "Successfully uninstalled " . count($applications) . " applications.\n";
323         } catch (Tinebase_Exception_NotFound $e) {
324         }
325         //Install Applications
326         try {
327             $controller->installApplications($applications->name);
328             echo "Successfully installed " . count($applications) . " applications.\n";
329         } catch (Tinebase_Exception_NotFound $e) {
330         }
331         
332         //set rights
333         foreach ($applications as &$application) {
334             $newApplicationId = Tinebase_Application::getInstance()->getApplicationByName($application->name)->getId();
335             
336             foreach ($rights as &$right) {
337                 if ($right['application_id'] == $application->id) {
338                     $right['application_id'] = $newApplicationId;
339                 }
340             }
341             
342         }
343         
344         Tinebase_Acl_Roles::getInstance()->setRoleRights($users->getId(), $rights);
345         echo "Successfully restored user rights.\n";
346         
347         //Clean up addressbooks
348         $internalContacts = $userController->getDefaultInternalAddressbook();
349         $containers = $containerController->getAll();
350         foreach ($containers as $key => &$container) {
351             if ($container->id == $internalContacts) {
352                 // Do nothing
353             } else {
354                 try {
355                     $containerController->deleteContainer($container, true);
356                 } catch (Tinebase_Exception_NotFound $e) {
357                 }
358             }
359         }
360         echo "Successfully cleand up containers.\n";
361         
362         //remove state
363         $db = Tinebase_Core::getDb();
364         $statement = "TRUNCATE TABLE " . $db->quoteIdentifier(SQL_TABLE_PREFIX . 'state');
365         $db->query($statement);
366         echo "Successfully truncated state table.\n";
367         
368         //Get Demodata
369         $cli->createAllDemoData();
370         
371         //clear Cache
372         Tinebase_Core::getCache()->clean(Zend_Cache::CLEANING_MODE_ALL);
373         echo "Successfully cleared Cache.\n";
374         
375         echo "Every thing done!\n";
376     }
377
378     /**
379      * Update Import Export Definitions for all applications
380      */
381     protected function _updateAllImportExportDefinitions(Zend_Console_Getopt $_opts){
382         //get all applications
383         $applications = Tinebase_Application::getInstance()->getApplications(NULL, 'id');
384         foreach ($applications as $application) {
385             Setup_Controller::getInstance()->createImportExportDefinitions($application);
386             echo "Update definitions for " . $application->name . "...\n";
387         }
388     }
389     
390     /**
391      * list installed apps
392      */
393     protected function _listInstalled()
394     {
395         try {
396             $applications = Tinebase_Application::getInstance()->getApplications(NULL, 'id');
397         } catch (Zend_Db_Statement_Exception $e) {
398             echo "No applications installed\n";
399             return 1;
400         }
401         
402         echo "Currently installed applications:\n";
403         foreach($applications as $application) {
404             echo "* $application\n";
405         }
406         
407         return 0;
408     }
409     
410     /**
411      * import accounts from ldap
412      *
413      * @param Zend_Console_Getopt $_opts
414      */
415     protected function _importAccounts(Zend_Console_Getopt $_opts)
416     {
417         // disable timelimit during import of user accounts
418         Setup_Core::setExecutionLifeTime(0);
419         
420         // import groups
421         if (! $_opts->onlyusers) {
422             Tinebase_Group::syncGroups();
423         }
424         
425         // import users
426         $options = array('syncContactData' => TRUE);
427         if ($_opts->dbmailldap) {
428             $options['ldapplugins'] = array(
429                 new Tinebase_EmailUser_Imap_LdapDbmailSchema(),
430                 new Tinebase_EmailUser_Smtp_LdapDbmailSchema()
431             );
432         }
433
434         if ($_opts->syncdeletedusers) {
435             $options['deleteUsers'] = true;
436         }
437
438         Tinebase_User::syncUsers($options);
439     }
440     
441     /**
442      * sync ldap passwords
443      * 
444      * @param Zend_Console_Getopt $_opts
445      */
446     protected function _syncPasswords(Zend_Console_Getopt $_opts)
447     {
448         Tinebase_User::syncLdapPasswords();
449     }
450     
451     /**
452      * import from egw14
453      * 
454      * @param Zend_Console_Getopt $_opts
455      */
456     protected function _egw14Import(Zend_Console_Getopt $_opts)
457     {
458         $args = $_opts->getRemainingArgs();
459         
460         if (count($args) < 1 || ! is_readable($args[0])) {
461             echo "can not open config file \n";
462             echo "see tine20.org/wiki/EGW_Migration_Howto for details \n\n";
463             echo "usage: ./setup.php --egw14import /path/to/config.ini (see Tinebase/Setup/Import/Egw14/config.ini)\n\n";
464             exit(1);
465         }
466         
467         try {
468             $config = new Zend_Config(array(), TRUE);
469             $config->merge(new Zend_Config_Ini($args[0]));
470             $config = $config->merge($config->all);
471         } catch (Zend_Config_Exception $e) {
472             fwrite(STDERR, "Error while parsing config file($args[0]) " .  $e->getMessage() . PHP_EOL);
473             exit(1);
474         }
475         
476         $writer = new Zend_Log_Writer_Stream('php://output');
477         $logger = new Zend_Log($writer);
478         
479         $filter = new Zend_Log_Filter_Priority((int) $config->loglevel);
480         $logger->addFilter($filter);
481         
482         $importer = new Tinebase_Setup_Import_Egw14($config, $logger);
483         $importer->import();
484     }
485     
486     /**
487      * do the environment check
488      *
489      * @return array
490      */
491     protected function _checkRequirements(Zend_Console_Getopt $_opts)
492     {
493         $results = Setup_Controller::getInstance()->checkRequirements();
494         if ($results['success']) {
495           echo "OK - All requirements are met\n";
496         } else {
497           echo "ERRORS - The following requirements are not met: \n";
498           foreach ($results['results'] as $result) {
499             if (!empty($result['message'])) {
500               echo "- " . strip_tags($result['message']) . "\n";
501             }
502           }
503         }
504     }
505     
506     /**
507      * set config
508      *
509      * @return array
510      */
511     protected function _setConfig(Zend_Console_Getopt $_opts)
512     {
513         $options = $this->_parseRemainingArgs($_opts->getRemainingArgs());
514         $errors = array();
515         if (empty($options['configkey'])) {
516             $errors[] = 'Missing argument: configkey';
517         }
518         if (! isset($options['configvalue'])) {
519             $errors[] = 'Missing argument: configvalue';
520         }
521         $configKey = (string)$options['configkey'];
522         $configValue = self::parseConfigValue($options['configvalue']);
523         $applicationName = (isset($options['app'])) ? $options['app'] : 'Tinebase';
524         
525         if (empty($errors)) {
526            Setup_Controller::getInstance()->setConfigOption($configKey, $configValue, $applicationName);
527            echo "OK - Updated configuration option $configKey for application $applicationName\n";
528         } else {
529             echo "ERRORS - The following errors occured: \n";
530             foreach ($errors as $error) {
531                 echo "- " . $error . "\n";
532             }
533         }
534     }
535     
536     /**
537      * get config
538      *
539      */
540     protected function _getConfig(Zend_Console_Getopt $_opts)
541     {
542         $options = $this->_parseRemainingArgs($_opts->getRemainingArgs());
543         $applicationName = (isset($options['app'])) ? $options['app'] : 'Tinebase';
544         $config = Tinebase_Config_Abstract::factory($applicationName);
545         
546         $errors = array();
547         if (empty($options['configkey'])) {
548             $errors[] = 'Missing argument: configkey';
549             $errors[] = 'Available config settings:';
550             $errors[] = print_r($config::getProperties(), true);
551         }
552         $configKey = (string)$options['configkey'];
553         
554         if (empty($errors)) {
555             $value = $config->get($configKey);
556             $value = is_string($value) ? $value : Zend_Json::encode($value);
557             echo $value . " \n";
558         } else {
559             echo "ERRORS - The following errors occured: \n";
560             foreach ($errors as $error) {
561                 echo "- " . $error . "\n";
562             }
563         }
564     }
565     
566     /**
567      * create admin user / activate existing user / allow to reset password
568      * 
569      * @param Zend_Console_Getopt $_opts
570      * 
571      * @todo check role by rights and not by name
572      * @todo replace echos with stdout logger
573      */
574     protected function _createAdminUser(Zend_Console_Getopt $_opts)
575     {
576         if (! Setup_Controller::getInstance()->isInstalled('Tinebase')) {
577             die('Install Tinebase first.');
578         }
579         
580         echo "Please enter a username. If the user already exists, he is reactivated and you can reset the password.\n";
581         $username = strtolower(Tinebase_Server_Cli::promptInput('Username'));
582         $tomorrow = Tinebase_DateTime::now()->addDay(1);
583         
584         try {
585             $user = Tinebase_User::getInstance()->getFullUserByLoginName($username);
586             echo "User $username already exists.\n";
587             Tinebase_User::getInstance()->setStatus($user->getId(), Tinebase_Model_User::ACCOUNT_STATUS_ENABLED);
588             echo "Activated admin user '$username'.\n";
589             
590             $expire = Tinebase_Server_Cli::promptInput('Should the admin user expire tomorrow (default: "no", "y" or "yes" for expiry)?');
591             if ($expire === 'y' or $expire === 'yes') {
592                 Tinebase_User::getInstance()->setExpiryDate($user->getId(), $tomorrow);
593                 echo "User expires tomorrow at $tomorrow.\n";
594             }
595             
596             $resetPw = Tinebase_Server_Cli::promptInput('Do you want to reset the password (default: "no", "y" or "yes" for reset)?');
597             if ($resetPw === 'y' or $resetPw === 'yes') {
598                 $password = $this->_promptPassword();
599                 Tinebase_User::getInstance()->setPassword($user, $password);
600                 echo "User password has been reset.\n";
601             }
602             
603             $this->_checkAdminGroupMembership($user);
604             $this->_checkAdminRole($user);
605             
606         } catch (Tinebase_Exception_NotFound $tenf) {
607             // create new admin user that expires tomorrow
608             $password = $this->_promptPassword();
609             Tinebase_User::createInitialAccounts(array(
610                 'adminLoginName' => $username,
611                 'adminPassword'  => $password,
612                 'expires'        => $tomorrow,
613             ));
614             echo "Created new admin user '$username' that expires tomorrow.\n";
615         }
616     }
617
618     /**
619      * check admin group membership
620      * 
621      * @param Tinebase_Model_FullUser $user
622      */
623     protected function _checkAdminGroupMembership($user)
624     {
625         $adminGroup = Tinebase_Group::getInstance()->getDefaultAdminGroup();
626         $memberships = Tinebase_Group::getInstance()->getGroupMemberships($user);
627         if (! in_array($adminGroup->getId(), $memberships)) {
628             try {
629                 Tinebase_Group::getInstance()->addGroupMember($adminGroup, $user);
630                 echo "Added user to default admin group\n";
631                 // @todo clear roles/groups cache
632             } catch (Exception $e) {
633                 Tinebase_Exception::log($e);
634                 echo "Could not add user to default admin group: " . $e->getMessage();
635             }
636         }
637     }
638     
639     /**
640      * check admin role membership
641      * 
642      * @param Tinebase_Model_FullUser $user
643      */
644     protected function _checkAdminRole($user)
645     {
646         $roleMemberships = Tinebase_Acl_Roles::getInstance()->getRoleMemberships($user->getId());
647         $adminRoleFound = FALSE;
648         // TODO allow to configure this / pass it as param
649         $adminRoleName = 'admin role';
650
651         foreach ($roleMemberships as $roleId) {
652             $role = Tinebase_Acl_Roles::getInstance()->getRoleById($roleId);
653             if ($role->name === $adminRoleName) {
654                 $adminRoleFound = TRUE;
655                 break;
656             }
657         }
658
659         if (! $adminRoleFound || ! Tinebase_Acl_Roles::getInstance()->hasRight('Admin', $user->getId(), Tinebase_Acl_Rights::ADMIN)) {
660             echo "Admin role not found for user " . $user->accountLoginName . ".\n";
661
662             try {
663                 $adminRole = Tinebase_Acl_Roles::getInstance()->getRoleByName($adminRoleName);
664             } catch (Tinebase_Exception_NotFound $tenf) {
665                 $adminRole = $this->_createNewAdminRoleForAdmin($adminRoleName);
666             }
667
668             Tinebase_Acl_Roles::getInstance()->setRoleMembers($adminRole->getId(), array(
669                 array(
670                     'id'    => $user->getId(),
671                     'type'  => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER, 
672                 )
673             ));
674             
675             echo "Added user " . $user->accountLoginName . " to role '$adminRoleName''.\n";
676             // @todo clear roles/groups cache
677         }
678     }
679
680     protected function _createNewAdminRoleForAdmin($adminRoleName)
681     {
682         $adminRole = new Tinebase_Model_Role(array(
683             'name'                  => $adminRoleName,
684             'description'           => 'admin role for tine. this role has all rights per default.',
685         ));
686
687         $adminRole = Tinebase_Acl_Roles::getInstance()->createRole($adminRole);
688         // add all rights for all apps
689         $enabledApps = Tinebase_Application::getInstance()->getApplicationsByState(Tinebase_Application::ENABLED);
690         $roleRights = array();
691         foreach ($enabledApps as $application) {
692             $allRights = Tinebase_Application::getInstance()->getAllRights($application->getId());
693             foreach ($allRights as $right) {
694                 $roleRights[] = array(
695                     'application_id' => $application->getId(),
696                     'right'          => $right,
697                 );
698             }
699         }
700         Tinebase_Acl_Roles::getInstance()->setRoleRights($adminRole->getId(), $roleRights);
701
702         return $adminRole;
703     }
704
705     protected function _backup(Zend_Console_Getopt $_opts)
706     {
707         $options = $this->_parseRemainingArgs($_opts->getRemainingArgs());
708         Setup_Controller::getInstance()->backup($options);
709     }
710
711     protected function _restore(Zend_Console_Getopt $_opts)
712     {
713         $options = $this->_parseRemainingArgs($_opts->getRemainingArgs());
714         Setup_Controller::getInstance()->restore($options);
715     }
716
717     /**
718      * parse options
719      * 
720      * @param string $_value
721      * @return array|string
722      */
723     public static function parseConfigValue($_value)
724     {
725         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . print_r($_value, TRUE));
726         
727         // check value is json encoded
728         if (Tinebase_Helper::is_json($_value)) {
729             return Zend_Json::decode($_value); 
730         }
731         
732         $result = array(
733             'active' => 1
734         );
735
736         // keep spaces, \: and \,
737         $_value = preg_replace(array('/ /', '/\\\:/', '/\\\,/', '/\s*/'), array('§', '@', ';', ''), $_value);
738         
739         $parts = explode(',', $_value);
740         
741         foreach ($parts as $part) {
742             $part = str_replace(';', ',', $part);
743             $part = str_replace('§', ' ', $part);
744             $part = str_replace('@', ':', $part);
745             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . $part);
746             if (strpos($part, '_') !== FALSE) {
747                 list($key, $sub) = preg_split('/_/', $part, 2);
748                 if (preg_match('/:/', $sub)) {
749                     list($subKey, $value) = explode(':', $sub);
750                     $result[$key][$subKey] = $value;
751                 } else {
752                     // might be a '_' in the value
753                     if (preg_match('/:/', $part)) {
754                         $exploded = explode(':', $part);
755                         $key = array_shift($exploded);
756                         $result[$key] = implode(':', $exploded);
757                     } else {
758                         throw new Timetracker_Exception_UnexpectedValue('You have an error in the config syntax (":" expected): ' . $part);
759                     }
760                 }
761             } else {
762                 if (strpos($part, ':') !== FALSE) {
763                     list($key, $value) = preg_split('/:/', $part, 2);
764                     $result[$key] = $value;
765                 } else {
766                     $result = $part;
767                 }
768             }
769         }
770
771         return $result;
772     }
773     
774     /**
775      * parse remaining args
776      * 
777      * @param string $_args
778      * @return array
779      */
780     protected function _parseRemainingArgs($_args)
781     {
782         $options = array();
783         foreach ($_args as $arg) {
784             if (strpos($arg, '=') !== FALSE) {
785                 if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . $arg);
786                 list($key, $value) = preg_split('/=/', $arg, 2);
787                 $options[$key] = $value;
788             }
789         }
790         
791         return $options;
792     }
793 }