fa9c67ba1156a7abddc652e7845f69ce2ae3350a
[tine20] / tine20 / Tinebase / Frontend / Cli.php
1 <?php
2 /**
3  * Tine 2.0
4  * @package     Tinebase
5  * @subpackage  Frontend
6  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
7  * @author      Philipp Schüle <p.schuele@metaways.de>
8  * @copyright   Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
9  */
10
11 /**
12  * cli server
13  *
14  * This class handles all requests from cli scripts
15  *
16  * @package     Tinebase
17  * @subpackage  Frontend
18  */
19 class Tinebase_Frontend_Cli extends Tinebase_Frontend_Cli_Abstract
20 {
21     /**
22      * the internal name of the application
23      *
24      * @var string
25      */
26     protected $_applicationName = 'Tinebase';
27
28     /**
29      * authentication
30      *
31      * @param string $_username
32      * @param string $_password
33      */
34     public function authenticate($_username, $_password)
35     {
36         $authResult = Tinebase_Auth::getInstance()->authenticate($_username, $_password);
37         
38         if ($authResult->isValid()) {
39             $accountsController = Tinebase_User::getInstance();
40             try {
41                 $account = $accountsController->getFullUserByLoginName($authResult->getIdentity());
42             } catch (Tinebase_Exception_NotFound $e) {
43                 echo 'account ' . $authResult->getIdentity() . ' not found in account storage'."\n";
44                 exit();
45             }
46             
47             Tinebase_Core::set('currentAccount', $account);
48
49             $ipAddress = '127.0.0.1';
50             $account->setLoginTime($ipAddress);
51
52             Tinebase_AccessLog::getInstance()->create(new Tinebase_Model_AccessLog(array(
53                 'sessionid'     => 'cli call',
54                 'login_name'    => $authResult->getIdentity(),
55                 'ip'            => $ipAddress,
56                 'li'            => Tinebase_DateTime::now()->get(Tinebase_Record_Abstract::ISO8601LONG),
57                 'lo'            => Tinebase_DateTime::now()->get(Tinebase_Record_Abstract::ISO8601LONG),
58                 'result'        => $authResult->getCode(),
59                 'account_id'    => Tinebase_Core::getUser()->getId(),
60                 'clienttype'    => 'TineCli',
61             )));
62             
63         } else {
64             echo "Wrong username and/or password.\n";
65             exit();
66         }
67     }
68     
69     /**
70      * handle request (call -ApplicationName-_Cli.-MethodName- or -ApplicationName-_Cli.getHelp)
71      *
72      * @param Zend_Console_Getopt $_opts
73      * @return boolean success
74      */
75     public function handle($_opts)
76     {
77         list($application, $method) = explode('.', $_opts->method);
78         $class = $application . '_Frontend_Cli';
79         
80         if (@class_exists($class)) {
81             $object = new $class;
82             if ($_opts->info) {
83                 $result = $object->getHelp();
84             } else if (method_exists($object, $method)) {
85                 $result = call_user_func(array($object, $method), $_opts);
86             } else {
87                 $result = FALSE;
88                 echo "Method $method not found.\n";
89             }
90         } else {
91             echo "Class $class does not exist.\n";
92             $result = FALSE;
93         }
94         
95         return $result;
96     }
97
98     /**
99      * trigger async events (for example via cronjob)
100      *
101      * @param Zend_Console_Getopt $_opts
102      * @return boolean success
103      */
104     public function triggerAsyncEvents($_opts)
105     {
106         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .' Triggering async events from CLI.');
107         
108         $userController = Tinebase_User::getInstance();
109         
110         // deactivate user plugins (like postfix/dovecot email backends) for async job user
111         $userController->unregisterAllPlugins();
112         
113         try {
114             $cronuser = $userController->getFullUserByLoginName($_opts->username);
115         } catch (Tinebase_Exception_NotFound $tenf) {
116             $cronuser = $this->_getCronuserFromConfigOrCreateOnTheFly();
117         }
118         Tinebase_Core::set(Tinebase_Core::USER, $cronuser);
119         
120         $scheduler = Tinebase_Core::getScheduler();
121         $responses = $scheduler->run();
122         
123         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .' ' . print_r(array_keys($responses), TRUE));
124         
125         $responseString = ($responses) ? implode(',', array_keys($responses)) : 'NULL';
126         echo "Tine 2.0 scheduler run (" . $responseString . ") complete.\n";
127         
128         return TRUE;
129     }
130     
131     /**
132      * try to get user for cronjob from config
133      * 
134      * @return Tinebase_Model_FullUser
135      */
136     protected function _getCronuserFromConfigOrCreateOnTheFly()
137     {
138         try {
139             $cronuserId = Tinebase_Config::getInstance()->get(Tinebase_Config::CRONUSERID);
140             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Setting user with id ' . $cronuserId . ' as cronuser.');
141             $cronuser = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountId', $cronuserId, 'Tinebase_Model_FullUser');
142         } catch (Tinebase_Exception_NotFound $tenf) {
143             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' ' . $tenf->getMessage());
144             
145             $cronuser = $this->_createCronuser();
146             Tinebase_Config::getInstance()->set(Tinebase_Config::CRONUSERID, $cronuser->getId());
147         }
148         
149         return $cronuser;
150     }
151     
152     /**
153      * create new cronuser
154      * 
155      * @return Tinebase_Model_FullUser
156      */
157     protected function _createCronuser()
158     {
159         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .' Creating new cronuser.');
160         
161         $adminGroup = Tinebase_Group::getInstance()->getDefaultAdminGroup();
162         $cronuser = new Tinebase_Model_FullUser(array(
163             'accountLoginName'      => 'cronuser',
164             'accountStatus'         => Tinebase_Model_User::ACCOUNT_STATUS_DISABLED,
165             'visibility'            => Tinebase_Model_FullUser::VISIBILITY_HIDDEN,
166             'accountPrimaryGroup'   => $adminGroup->getId(),
167             'accountLastName'       => 'cronuser',
168             'accountDisplayName'    => 'cronuser',
169             'accountExpires'        => NULL,
170         ));
171         $cronuser = Tinebase_User::getInstance()->addUser($cronuser);
172         Tinebase_Group::getInstance()->addGroupMember($cronuser->accountPrimaryGroup, $cronuser->getId());
173         
174         return $cronuser;
175     }
176     
177     /**
178      * process given queue job
179      *  --message json encoded task
180      *
181      * @TODO rework user management, jobs should be executed as the right user in future
182      * 
183      * @param Zend_Console_Getopt $_opts
184      * @return boolean success
185      */
186     public function executeQueueJob($_opts)
187     {
188         try {
189             $cronuser = Tinebase_User::getInstance()->getFullUserByLoginName($_opts->username);
190         } catch (Tinebase_Exception_NotFound $tenf) {
191             $cronuser = $this->_getCronuserFromConfigOrCreateOnTheFly();
192         }
193         
194         Tinebase_Core::set(Tinebase_Core::USER, $cronuser);
195         
196         $args = $_opts->getRemainingArgs();
197         $message = preg_replace('/^message=/', '', $args[0]);
198         
199         if (! $message) {
200             throw new Tinebase_Exception_InvalidArgument('mandatory parameter "message" is missing');
201         }
202         
203         Tinebase_ActionQueue::getInstance()->executeAction($message);
204         
205         return TRUE;
206     }
207     
208     /**
209      * clear table as defined in arguments
210      * can clear the following tables:
211      * - credential_cache
212      * - access_log
213      * - async_job
214      * - temp_files
215      * 
216      * if param date is given (date=2010-09-17), all records before this date are deleted (if the table has a date field)
217      * 
218      * @param $_opts
219      * @return boolean success
220      */
221     public function clearTable(Zend_Console_Getopt $_opts)
222     {
223         if (! $this->_checkAdminRight()) {
224             return FALSE;
225         }
226         
227         $args = $this->_parseArgs($_opts, array('tables'), 'tables');
228         $dateString = (isset($args['date']) || array_key_exists('date', $args)) ? $args['date'] : NULL;
229
230         $db = Tinebase_Core::getDb();
231         foreach ($args['tables'] as $table) {
232             switch ($table) {
233                 case 'access_log':
234                     $date = ($dateString) ? new Tinebase_DateTime($dateString) : NULL;
235                     Tinebase_AccessLog::getInstance()->clearTable($date);
236                     break;
237                 case 'async_job':
238                     $where = ($dateString) ? array(
239                         $db->quoteInto($db->quoteIdentifier('end_time') . ' < ?', $dateString)
240                     ) : array();
241                     $where[] = $db->quoteInto($db->quoteIdentifier('status') . ' < ?', 'success');
242                     
243                     echo "\nRemoving all successful async_job entries " . ($dateString ? "before $dateString " : "") . "...";
244                     $deleteCount = $db->delete(SQL_TABLE_PREFIX . $table, $where);
245                     echo "\nRemoved $deleteCount records.";
246                     break;
247                 case 'credential_cache':
248                     Tinebase_Auth_CredentialCache::getInstance()->clearCacheTable($dateString);
249                     break;
250                 case 'temp_files':
251                     Tinebase_TempFile::getInstance()->clearTableAndTempdir($dateString);
252                     break;
253                 default:
254                     echo 'Table ' . $table . " not supported or argument missing.\n";
255             }
256             echo "\nCleared table $table.";
257         }
258         echo "\n\n";
259         
260         return TRUE;
261     }
262     
263     /**
264      * purge deleted records
265      * 
266      * if param date is given (for example: date=2010-09-17), all records before this date are deleted (if the table has a date field)
267      * if table names are given, purge only records from this tables
268      * 
269      * @param $_opts
270      * @return boolean success
271      */
272     public function purgeDeletedRecords(Zend_Console_Getopt $_opts)
273     {
274         if (! $this->_checkAdminRight()) {
275             return FALSE;
276         }
277
278         $args = $this->_parseArgs($_opts, array(), 'tables');
279
280         if (! (isset($args['tables']) || array_key_exists('tables', $args)) || empty($args['tables'])) {
281             echo "No tables given.\nPurging records from all tables!\n";
282             $args['tables'] = $this->_getAllApplicationTables();
283         }
284         
285         $db = Tinebase_Core::getDb();
286         
287         if ((isset($args['date']) || array_key_exists('date', $args))) {
288             echo "\nRemoving all deleted entries before {$args['date']} ...";
289             $where = array(
290                 $db->quoteInto($db->quoteIdentifier('deleted_time') . ' < ?', $args['date'])
291             );
292         } else {
293             echo "\nRemoving all deleted entries ...";
294             $where = array();
295         }
296         $where[] = $db->quoteInto($db->quoteIdentifier('is_deleted') . ' = ?', 1);
297         
298         foreach ($args['tables'] as $table) {
299             try {
300                 $schema = Tinebase_Db_Table::getTableDescriptionFromCache(SQL_TABLE_PREFIX . $table);
301             } catch (Zend_Db_Statement_Exception $zdse) {
302                 echo "\nCould not get schema (" . $zdse->getMessage() ."). Skipping table $table";
303                 continue;
304             }
305             if (! (isset($schema['is_deleted']) || array_key_exists('is_deleted', $schema)) || ! (isset($schema['deleted_time']) || array_key_exists('deleted_time', $schema))) {
306                 continue;
307             }
308             
309             $deleteCount = 0;
310             try {
311                 $deleteCount = $db->delete(SQL_TABLE_PREFIX . $table, $where);
312             } catch (Zend_Db_Statement_Exception $zdse) {
313                 echo "\nFailed to purge deleted records for table $table. " . $zdse->getMessage();
314             }
315             if ($deleteCount > 0) {
316                 echo "\nCleared table $table (deleted $deleteCount records).";
317             }
318         }
319         echo "\n\n";
320         
321         return TRUE;
322     }
323     
324     /**
325      * get all app tables
326      * 
327      * @return array
328      */
329     protected function _getAllApplicationTables()
330     {
331         $result = array();
332         
333         $enabledApplications = Tinebase_Application::getInstance()->getApplicationsByState(Tinebase_Application::ENABLED);
334         foreach ($enabledApplications as $application) {
335             $result = array_merge($result, Tinebase_Application::getInstance()->getApplicationTables($application));
336         }
337         
338         return $result;
339     }
340     
341     /**
342      * add new customfield config
343      * 
344      * needs args like this:
345      * application="Addressbook" name="datefield" label="Date" model="Addressbook_Model_Contact" type="datefield"
346      * @see Tinebase_Model_CustomField_Config for full list 
347      * 
348      * @param $_opts
349      * @return boolean success
350      */
351     public function addCustomfield(Zend_Console_Getopt $_opts)
352     {
353         if (! $this->_checkAdminRight()) {
354             return FALSE;
355         }
356         
357         // parse args
358         $args = $_opts->getRemainingArgs();
359         $data = array();
360         foreach ($args as $idx => $arg) {
361             list($key, $value) = explode('=', $arg);
362             if ($key == 'application') {
363                 $key = 'application_id';
364                 $value = Tinebase_Application::getInstance()->getApplicationByName($value)->getId();
365             }
366             $data[$key] = $value;
367         }
368         
369         $customfieldConfig = new Tinebase_Model_CustomField_Config($data);
370         $cf = Tinebase_CustomField::getInstance()->addCustomField($customfieldConfig);
371
372         echo "\nCreated customfield: ";
373         print_r($cf->toArray());
374         echo "\n";
375         
376         return TRUE;
377     }
378     
379     /**
380      * nagios monitoring for tine 2.0 database connection
381      * 
382      * @return integer
383      * @see http://nagiosplug.sourceforge.net/developer-guidelines.html#PLUGOUTPUT
384      */
385     public function monitoringCheckDB()
386     {
387         $message = 'DB CONNECTION FAIL';
388         try {
389             if (! Setup_Core::isRegistered(Setup_Core::CONFIG)) {
390                 Setup_Core::setupConfig();
391             }
392             if (! Setup_Core::isRegistered(Setup_Core::LOGGER)) {
393                 Setup_Core::setupLogger();
394             }
395             $time_start = microtime(true);
396             $dbcheck = Setup_Core::setupDatabaseConnection();
397             $time = (microtime(true) - $time_start) * 1000;
398         } catch (Exception $e) {
399             $message .= ': ' . $e->getMessage();
400             $dbcheck = FALSE;
401         }
402         
403         if ($dbcheck) {
404             echo "DB CONNECTION OK | connecttime={$time}ms;;;;\n";
405             return 0;
406         } 
407         
408         echo $message . "\n";
409         return 2;
410     }
411     
412     /**
413      * nagios monitoring for tine 2.0 config file
414      * 
415      * @return integer
416      * @see http://nagiosplug.sourceforge.net/developer-guidelines.html#PLUGOUTPUT
417      */
418     public function monitoringCheckConfig()
419     {
420         $message = 'CONFIG FAIL';
421         $configcheck = FALSE;
422         
423         $configfile = Setup_Core::getConfigFilePath();
424         if ($configfile) {
425             $configfile = escapeshellcmd($configfile);
426             if (preg_match('/^win/i', PHP_OS)) {
427                 exec("php -l $configfile 2> NUL", $error, $code);
428             } else {
429                 exec("php -l $configfile 2> /dev/null", $error, $code);
430             }
431             if ($code == 0) {
432                 $configcheck = TRUE;
433             } else {
434                 $message .= ': CONFIG FILE SYNTAX ERROR';
435             }
436         } else {
437             $message .= ': CONFIG FILE MISSING';
438         }
439         
440         if ($configcheck) {
441             echo "CONFIG FILE OK\n";
442             return 0;
443         } else {
444             echo $message . "\n";
445             return 2;
446         }
447     }
448     
449     /**
450     * nagios monitoring for tine 2.0 async cronjob run
451     *
452     * @return integer
453     * 
454     * @see http://nagiosplug.sourceforge.net/developer-guidelines.html#PLUGOUTPUT
455     * @see 0008038: monitoringCheckCron -> check if cron did run in the last hour
456     */
457     public function monitoringCheckCron()
458     {
459         $message = 'CRON FAIL';
460         $result  = 2;
461         
462         try {
463             $lastJob = Tinebase_AsyncJob::getInstance()->getLastJob('Tinebase_Event_Async_Minutely');
464             
465             if ($lastJob === NULL) {
466                 $message .= ': NO LAST JOB FOUND';
467                 $result = 1;
468             } else {
469                 if ($lastJob->end_time instanceof Tinebase_DateTime) {
470                     $duration = $lastJob->end_time->getTimestamp() - $lastJob->start_time->getTimestamp();
471                     $valueString = ' | duration=' . $duration . 's;;;;';
472                     $valueString .= ' end=' . $lastJob->end_time->getIso() . ';;;;';
473                 } else {
474                     $valueString = '';
475                 }
476                 
477                 if ($lastJob->status === Tinebase_Model_AsyncJob::STATUS_RUNNING && Tinebase_DateTime::now()->isLater($lastJob->end_time)) {
478                     $message .= ': LAST JOB TOOK TOO LONG';
479                     $result = 1;
480                 } else if ($lastJob->status === Tinebase_Model_AsyncJob::STATUS_FAILURE) {
481                     $message .= ': LAST JOB FAILED';
482                     $result = 1;
483                 } else if (Tinebase_DateTime::now()->isLater($lastJob->start_time->addHour(1))) {
484                     $message .= ': NO JOB IN THE LAST HOUR';
485                     $result = 1;
486                 } else {
487                     $message = 'CRON OK';
488                     $result = 0;
489                 }
490                 $message .= $valueString;
491             }
492         } catch (Exception $e) {
493             $message .= ': ' . $e->getMessage();
494             $result = 2;
495         }
496         
497         echo $message . "\n";
498         return $result;
499     }
500     
501     /**
502      * nagios monitoring for tine 2.0 logins during the last 5 mins
503      * 
504      * @return number
505      * 
506      * @todo allow to configure timeslot
507      */
508     public function monitoringLoginNumber()
509     {
510         $message = 'LOGINS';
511         $result  = 0;
512         
513         try {
514             $filter = new Tinebase_Model_AccessLogFilter(array(
515                 array('field' => 'li', 'operator' => 'after', 'value' => Tinebase_DateTime::now()->subMinute(5))
516             ));
517             $accesslogs = Tinebase_AccessLog::getInstance()->search($filter, NULL, FALSE, TRUE);
518             $valueString = ' | count=' . count($accesslogs) . ';;;;';
519             $message .= ' OK' . $valueString;
520         } catch (Exception $e) {
521             $message .= ' FAIL: ' . $e->getMessage();
522             $result = 2;
523         }
524         
525         echo $message . "\n";
526         return $result;
527     }
528
529     /**
530      * nagios monitoring for tine 2.0 active users
531      *
532      * @return number
533      *
534      * @todo allow to configure timeslot / currently the active users of the last month are returned
535      */
536     public function monitoringActiveUsers()
537     {
538         $message = 'ACTIVE USERS';
539         $result  = 0;
540
541         try {
542             $userCount = Tinebase_User::getInstance()->getActiveUserCount();
543             $valueString = ' | count=' . $userCount . ';;;;';
544             $message .= ' OK' . $valueString;
545         } catch (Exception $e) {
546             $message .= ' FAIL: ' . $e->getMessage();
547             $result = 2;
548         }
549
550         echo $message . "\n";
551         return $result;
552     }
553
554     /**
555      * undo changes to records defined by certain criteria (user, date, fields, ...)
556      * 
557      * example: $ php tine20.php --username pschuele --method Tinebase.undo -d 
558      *   -- record_type=Addressbook_Model_Contact modification_time=2013-05-08 modification_account=3263
559      * 
560      * @param Zend_Console_Getopt $opts
561      */
562     public function undo(Zend_Console_Getopt $opts)
563     {
564         if (! $this->_checkAdminRight()) {
565             return FALSE;
566         }
567         
568         $data = $this->_parseArgs($opts, array('modification_time'));
569         
570         // build filter from params
571         $filterData = array();
572         $allowedFilters = array(
573             'record_type',
574             'modification_time',
575             'modification_account',
576             'record_id',
577             'modified_attribute'
578         );
579         foreach ($data as $key => $value) {
580             if (in_array($key, $allowedFilters)) {
581                 $operator = ($key === 'modification_time') ? 'within' : 'equals';
582                 $filterData[] = array('field' => $key, 'operator' => $operator, 'value' => $value);
583             }
584         }
585         $filter = new Tinebase_Model_ModificationLogFilter($filterData);
586         
587         $dryrun = $opts->d;
588         $overwrite = (isset($data['overwrite']) && $data['overwrite']) ? TRUE : FALSE;
589         $result = Tinebase_Timemachine_ModificationLog::getInstance()->undo($filter, $overwrite, $dryrun);
590         
591         if (! $dryrun) {
592             echo 'Reverted ' . $result['totalcount'] . " change(s)\n";
593         } else {
594             echo "Dry run\n";
595             echo 'Would revert ' . $result['totalcount'] . " change(s):\n";
596             foreach ($result['undoneModlogs'] as $modlog) {
597                 echo 'id ' . $modlog->record_id . ' [' . $modlog->modified_attribute . ']: ' . $modlog->new_value . ' -> ' . $modlog->old_value . "\n";
598             }
599         }
600         echo 'Failcount: ' . $result['failcount'] . "\n";
601         return 0;
602     }
603     
604     /**
605      * creates demo data for all applications
606      * accepts same arguments as Tinebase_Frontend_Cli_Abstract::createDemoData
607      * and the additional argument "skipAdmin" to force no user/group/role creation
608      * 
609      * @param Zend_Console_Getopt $_opts
610      */
611     public function createAllDemoData($_opts)
612     {
613         if (! $this->_checkAdminRight()) {
614             return FALSE;
615         }
616         
617         // fetch all applications and check if required are installed, otherwise remove app from array
618         $applications = Tinebase_Application::getInstance()->getApplicationsByState(Tinebase_Application::ENABLED)->name;
619         foreach($applications as $appName) {
620             echo 'Searching for DemoData in application "' . $appName . '"...' . PHP_EOL;
621             $className = $appName.'_Setup_DemoData';
622             if (class_exists($className)) {
623                 echo 'DemoData in application "' . $appName . '" found!' . PHP_EOL;
624                 $required = $className::getRequiredApplications();
625                 foreach($required as $requiredApplication) {
626                     if (! Tinebase_Helper::in_array_case($applications, $requiredApplication)) {
627                         echo 'Creating DemoData for Application ' . $appName . ' is impossible, because application "' . $requiredApplication . '" is not installed.' . PHP_EOL;
628                         continue 2;
629                     }
630                 }
631                 $this->_applicationsToWorkOn[$appName] = array('appName' => $appName, 'required' => $required);
632             } else {
633                 echo 'DemoData in application "' . $appName . '" not found.' . PHP_EOL . PHP_EOL;
634             }
635         }
636         
637         foreach($this->_applicationsToWorkOn as $app => $cfg) {
638             $this->_createDemoDataRecursive($app, $cfg, $_opts);
639         }
640     }
641     
642     /**
643      * creates demo data and calls itself if there are required apps
644      * 
645      * @param string $app
646      * @param array $cfg
647      * @param Zend_Console_Getopt $opts
648      */
649     protected function _createDemoDataRecursive($app, $cfg, $opts)
650     {
651         if (isset($cfg['required']) && is_array($cfg['required'])) {
652             foreach($cfg['required'] as $requiredApp) {
653                 $this->_createDemoDataRecursive($requiredApp, $this->_applicationsToWorkOn[$requiredApp], $opts);
654             }
655         }
656         
657         $className = $app . '_Frontend_Cli';
658         
659         $classNameDD = $app . '_Setup_DemoData';
660         
661         if (class_exists($className)) {
662             if (! $classNameDD::hasBeenRun()) {
663                 echo 'Creating DemoData in application "' . $app . '"...' . PHP_EOL;
664                 $class = new $className();
665                 $class->createDemoData($opts, FALSE);
666             } else {
667                 echo 'DemoData for ' . $app . ' has been run already, skipping...' . PHP_EOL;
668             }
669         } else {
670             echo 'Could not found ' . $className . ', so DemoData for application "' . $app . '" could not be created!';
671         }
672     }
673     
674     /**
675      * clears deleted files from filesystem + database
676      * @return boolean
677      */
678     public function clearDeletedFiles()
679     {
680         if (! $this->_checkAdminRight()) {
681             return FALSE;
682         }
683         
684         $this->_addOutputLogWriter();
685         
686         Tinebase_FileSystem::getInstance()->clearDeletedFiles();
687     }
688     
689     /**
690      * repair a table
691      * 
692      * @param Zend_Console_Getopt $opts
693      * 
694      * @todo add more tables
695      */
696     public function repairTable($opts)
697     {
698         if (! $this->_checkAdminRight()) {
699             return FALSE;
700         }
701         
702         $this->_addOutputLogWriter();
703         
704         $data = $this->_parseArgs($opts, array('table'));
705         
706         switch ($data['table']) {
707             case 'importexport_definition':
708                 Tinebase_ImportExportDefinition::getInstance()->repairTable();
709                 $result = 0;
710                 break;
711             default:
712                 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__
713                     . ' No repair script found for ' . $data['table']);
714                 $result = 1;
715         }
716         
717         exit($result);
718     }
719     
720     /**
721      * repairs container names
722      * 
723      * @param Zend_Console_Getopt $opts
724      */
725     public function repairContainerName($opts)
726     {
727         if (! $this->_checkAdminRight()) {
728             return FALSE;
729         }
730         $dryrun = $opts->d;
731         
732         $this->_addOutputLogWriter();
733         $args = $this->_parseArgs($opts);
734         
735         $containersWithBadNames = Tinebase_Container::getInstance()->getContainersWithBadNames();
736         
737         $locale = Tinebase_Translation::getLocale((isset($args['locale']) ?$args['locale'] : 'auto'));
738
739         if ($dryrun) {
740             print_r($containersWithBadNames->toArray());
741             echo "Using Locale " . $locale . "\n";
742         }
743         
744         $appContainerNames = array(
745             'Calendar' => 'calendar',
746             'Tasks'    => 'tasks',
747             'Addressbook'    => 'addressbook',
748         );
749         
750         foreach ($containersWithBadNames as $container) {
751             if (empty($container->owner_id)) {
752                 if ($dryrun) {
753                     echo "Don't rename shared container " . $container->id . "\n";
754                 }
755                 continue;
756             }
757             $app = Tinebase_Application::getInstance()->getApplicationById($container->application_id);
758             $appContainerName = isset($appContainerNames[$app->name]) ? $appContainerNames[$app->name] : "container";
759             $translation = Tinebase_Translation::getTranslation($app->name, $locale);
760             $account = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountId', $container->owner_id);
761             $newName = $newBaseName = sprintf($translation->_("%s's personal " . $appContainerName), $account->accountFullName);
762             
763             $count = 1;
764             do {
765                 try {
766                     Tinebase_Container::getInstance()->getContainerByName($app->name, $newName, Tinebase_Model_Container::TYPE_PERSONAL, $container->owner_id);
767                     $found = true;
768                     $newName = $newBaseName . ' ' . ++$count;
769                 } catch (Tinebase_Exception_NotFound $tenf) {
770                     $found = false;
771                 }
772                 
773             } while ($found);
774             if ($dryrun) {
775                 echo "Rename container id " . $container->id . ' to ' . $newName . "\n";
776             } else {
777                 
778                 $container->name = $newName;
779                 Tinebase_Container::getInstance()->update($container);
780             }
781         }
782         
783         $result = 0;
784         exit($result);
785     }
786     
787     /**
788      * transfer relations
789      * 
790      * @param Zend_Console_Getopt $opts
791      */
792     public function transferRelations($opts)
793     {
794         if (! $this->_checkAdminRight()) {
795             return FALSE;
796         }
797         
798         $this->_addOutputLogWriter();
799         
800         try {
801             $args = $this->_parseArgs($opts, array('oldId', 'newId', 'model'));
802         } catch (Tinebase_Exception_InvalidArgument $e) {
803             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
804                 Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Parameters "oldId", "newId" and "model" are required!');
805             }
806             exit(1);
807         }
808         
809         $skippedEntries = Tinebase_Relations::getInstance()->transferRelations($args['oldId'], $args['newId'], $args['model']);
810
811         if (! empty($skippedEntries) && Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
812             Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' ' . count($skippedEntries) . ' entries has been skipped:');
813         }
814         
815         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
816             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' The operation has been terminated successfully.');
817         }
818     }
819 }