0011370: repair function for persistent filters (favorites) without grants
[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
461         try {
462             $lastJob = Tinebase_AsyncJob::getInstance()->getLastJob('Tinebase_Event_Async_Minutely');
463             
464             if ($lastJob === NULL) {
465                 $message .= ': NO LAST JOB FOUND';
466                 $result = 1;
467             } else {
468                 if ($lastJob->end_time instanceof Tinebase_DateTime) {
469                     $duration = $lastJob->end_time->getTimestamp() - $lastJob->start_time->getTimestamp();
470                     $valueString = ' | duration=' . $duration . 's;;;;';
471                     $valueString .= ' end=' . $lastJob->end_time->getIso() . ';;;;';
472                 } else {
473                     $valueString = '';
474                 }
475                 
476                 if ($lastJob->status === Tinebase_Model_AsyncJob::STATUS_RUNNING && Tinebase_DateTime::now()->isLater($lastJob->end_time)) {
477                     $message .= ': LAST JOB TOOK TOO LONG';
478                     $result = 1;
479                 } else if ($lastJob->status === Tinebase_Model_AsyncJob::STATUS_FAILURE) {
480                     $message .= ': LAST JOB FAILED';
481                     $result = 1;
482                 } else if (Tinebase_DateTime::now()->isLater($lastJob->start_time->addHour(1))) {
483                     $message .= ': NO JOB IN THE LAST HOUR';
484                     $result = 1;
485                 } else {
486                     $message = 'CRON OK';
487                     $result = 0;
488                 }
489                 $message .= $valueString;
490             }
491         } catch (Exception $e) {
492             $message .= ': ' . $e->getMessage();
493             $result = 2;
494         }
495         
496         echo $message . "\n";
497         return $result;
498     }
499     
500     /**
501      * nagios monitoring for tine 2.0 logins during the last 5 mins
502      * 
503      * @return number
504      * 
505      * @todo allow to configure timeslot
506      */
507     public function monitoringLoginNumber()
508     {
509         $message = 'LOGINS';
510         $result  = 0;
511         
512         try {
513             $filter = new Tinebase_Model_AccessLogFilter(array(
514                 array('field' => 'li', 'operator' => 'after', 'value' => Tinebase_DateTime::now()->subMinute(5))
515             ));
516             $accesslogs = Tinebase_AccessLog::getInstance()->search($filter, NULL, FALSE, TRUE);
517             $valueString = ' | count=' . count($accesslogs) . ';;;;';
518             $message .= ' OK' . $valueString;
519         } catch (Exception $e) {
520             $message .= ' FAIL: ' . $e->getMessage();
521             $result = 2;
522         }
523         
524         echo $message . "\n";
525         return $result;
526     }
527
528     /**
529      * nagios monitoring for tine 2.0 active users
530      *
531      * @return number
532      *
533      * @todo allow to configure timeslot / currently the active users of the last month are returned
534      */
535     public function monitoringActiveUsers()
536     {
537         $message = 'ACTIVE USERS';
538         $result  = 0;
539
540         try {
541             $userCount = Tinebase_User::getInstance()->getActiveUserCount();
542             $valueString = ' | count=' . $userCount . ';;;;';
543             $message .= ' OK' . $valueString;
544         } catch (Exception $e) {
545             $message .= ' FAIL: ' . $e->getMessage();
546             $result = 2;
547         }
548
549         echo $message . "\n";
550         return $result;
551     }
552
553     /**
554      * undo changes to records defined by certain criteria (user, date, fields, ...)
555      * 
556      * example: $ php tine20.php --username pschuele --method Tinebase.undo -d 
557      *   -- record_type=Addressbook_Model_Contact modification_time=2013-05-08 modification_account=3263
558      * 
559      * @param Zend_Console_Getopt $opts
560      */
561     public function undo(Zend_Console_Getopt $opts)
562     {
563         if (! $this->_checkAdminRight()) {
564             return FALSE;
565         }
566         
567         $data = $this->_parseArgs($opts, array('modification_time'));
568         
569         // build filter from params
570         $filterData = array();
571         $allowedFilters = array(
572             'record_type',
573             'modification_time',
574             'modification_account',
575             'record_id',
576             'modified_attribute'
577         );
578         foreach ($data as $key => $value) {
579             if (in_array($key, $allowedFilters)) {
580                 $operator = ($key === 'modification_time') ? 'within' : 'equals';
581                 $filterData[] = array('field' => $key, 'operator' => $operator, 'value' => $value);
582             }
583         }
584         $filter = new Tinebase_Model_ModificationLogFilter($filterData);
585         
586         $dryrun = $opts->d;
587         $overwrite = (isset($data['overwrite']) && $data['overwrite']) ? TRUE : FALSE;
588         $result = Tinebase_Timemachine_ModificationLog::getInstance()->undo($filter, $overwrite, $dryrun);
589         
590         if (! $dryrun) {
591             echo 'Reverted ' . $result['totalcount'] . " change(s)\n";
592         } else {
593             echo "Dry run\n";
594             echo 'Would revert ' . $result['totalcount'] . " change(s):\n";
595             foreach ($result['undoneModlogs'] as $modlog) {
596                 echo 'id ' . $modlog->record_id . ' [' . $modlog->modified_attribute . ']: ' . $modlog->new_value . ' -> ' . $modlog->old_value . "\n";
597             }
598         }
599         echo 'Failcount: ' . $result['failcount'] . "\n";
600         return 0;
601     }
602     
603     /**
604      * creates demo data for all applications
605      * accepts same arguments as Tinebase_Frontend_Cli_Abstract::createDemoData
606      * and the additional argument "skipAdmin" to force no user/group/role creation
607      * 
608      * @param Zend_Console_Getopt $_opts
609      */
610     public function createAllDemoData($_opts)
611     {
612         if (! $this->_checkAdminRight()) {
613             return FALSE;
614         }
615         
616         // fetch all applications and check if required are installed, otherwise remove app from array
617         $applications = Tinebase_Application::getInstance()->getApplicationsByState(Tinebase_Application::ENABLED)->name;
618         foreach($applications as $appName) {
619             echo 'Searching for DemoData in application "' . $appName . '"...' . PHP_EOL;
620             $className = $appName.'_Setup_DemoData';
621             if (class_exists($className)) {
622                 echo 'DemoData in application "' . $appName . '" found!' . PHP_EOL;
623                 $required = $className::getRequiredApplications();
624                 foreach($required as $requiredApplication) {
625                     if (! Tinebase_Helper::in_array_case($applications, $requiredApplication)) {
626                         echo 'Creating DemoData for Application ' . $appName . ' is impossible, because application "' . $requiredApplication . '" is not installed.' . PHP_EOL;
627                         continue 2;
628                     }
629                 }
630                 $this->_applicationsToWorkOn[$appName] = array('appName' => $appName, 'required' => $required);
631             } else {
632                 echo 'DemoData in application "' . $appName . '" not found.' . PHP_EOL . PHP_EOL;
633             }
634         }
635         
636         foreach($this->_applicationsToWorkOn as $app => $cfg) {
637             $this->_createDemoDataRecursive($app, $cfg, $_opts);
638         }
639
640         return 0;
641     }
642     
643     /**
644      * creates demo data and calls itself if there are required apps
645      * 
646      * @param string $app
647      * @param array $cfg
648      * @param Zend_Console_Getopt $opts
649      */
650     protected function _createDemoDataRecursive($app, $cfg, $opts)
651     {
652         if (isset($cfg['required']) && is_array($cfg['required'])) {
653             foreach($cfg['required'] as $requiredApp) {
654                 $this->_createDemoDataRecursive($requiredApp, $this->_applicationsToWorkOn[$requiredApp], $opts);
655             }
656         }
657         
658         $className = $app . '_Frontend_Cli';
659         
660         $classNameDD = $app . '_Setup_DemoData';
661         
662         if (class_exists($className)) {
663             if (! $classNameDD::hasBeenRun()) {
664                 echo 'Creating DemoData in application "' . $app . '"...' . PHP_EOL;
665                 $class = new $className();
666                 $class->createDemoData($opts, FALSE);
667             } else {
668                 echo 'DemoData for ' . $app . ' has been run already, skipping...' . PHP_EOL;
669             }
670         } else {
671             echo 'Could not found ' . $className . ', so DemoData for application "' . $app . '" could not be created!';
672         }
673     }
674     
675     /**
676      * clears deleted files from filesystem + database
677      * @return boolean
678      */
679     public function clearDeletedFiles()
680     {
681         if (! $this->_checkAdminRight()) {
682             return FALSE;
683         }
684         
685         $this->_addOutputLogWriter();
686         
687         Tinebase_FileSystem::getInstance()->clearDeletedFiles();
688
689         return 0;
690     }
691     
692     /**
693      * repair a table
694      * 
695      * @param Zend_Console_Getopt $opts
696      * 
697      * @todo add more tables
698      */
699     public function repairTable($opts)
700     {
701         if (! $this->_checkAdminRight()) {
702             return FALSE;
703         }
704         
705         $this->_addOutputLogWriter();
706         
707         $data = $this->_parseArgs($opts, array('table'));
708         
709         switch ($data['table']) {
710             case 'importexport_definition':
711                 Tinebase_ImportExportDefinition::getInstance()->repairTable();
712                 $result = 0;
713                 break;
714             default:
715                 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__
716                     . ' No repair script found for ' . $data['table']);
717                 $result = 1;
718         }
719         
720         exit($result);
721     }
722     
723     /**
724      * repairs container names
725      * 
726      * @param Zend_Console_Getopt $opts
727      */
728     public function repairContainerName($opts)
729     {
730         if (! $this->_checkAdminRight()) {
731             return FALSE;
732         }
733         $dryrun = $opts->d;
734         
735         $this->_addOutputLogWriter();
736         $args = $this->_parseArgs($opts);
737         
738         $containersWithBadNames = Tinebase_Container::getInstance()->getContainersWithBadNames();
739         
740         $locale = Tinebase_Translation::getLocale((isset($args['locale']) ?$args['locale'] : 'auto'));
741
742         if ($dryrun) {
743             print_r($containersWithBadNames->toArray());
744             echo "Using Locale " . $locale . "\n";
745         }
746         
747         $appContainerNames = array(
748             'Calendar' => 'calendar',
749             'Tasks'    => 'tasks',
750             'Addressbook'    => 'addressbook',
751         );
752         
753         foreach ($containersWithBadNames as $container) {
754             if (empty($container->owner_id)) {
755                 if ($dryrun) {
756                     echo "Don't rename shared container " . $container->id . "\n";
757                 }
758                 continue;
759             }
760             $app = Tinebase_Application::getInstance()->getApplicationById($container->application_id);
761             $appContainerName = isset($appContainerNames[$app->name]) ? $appContainerNames[$app->name] : "container";
762             $translation = Tinebase_Translation::getTranslation($app->name, $locale);
763             $account = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountId', $container->owner_id);
764             $newName = $newBaseName = sprintf($translation->_("%s's personal " . $appContainerName), $account->accountFullName);
765             
766             $count = 1;
767             do {
768                 try {
769                     Tinebase_Container::getInstance()->getContainerByName($app->name, $newName, Tinebase_Model_Container::TYPE_PERSONAL, $container->owner_id);
770                     $found = true;
771                     $newName = $newBaseName . ' ' . ++$count;
772                 } catch (Tinebase_Exception_NotFound $tenf) {
773                     $found = false;
774                 }
775                 
776             } while ($found);
777             if ($dryrun) {
778                 echo "Rename container id " . $container->id . ' to ' . $newName . "\n";
779             } else {
780                 
781                 $container->name = $newName;
782                 Tinebase_Container::getInstance()->update($container);
783             }
784         }
785         
786         $result = 0;
787         exit($result);
788     }
789     
790     /**
791      * transfer relations
792      * 
793      * @param Zend_Console_Getopt $opts
794      */
795     public function transferRelations($opts)
796     {
797         if (! $this->_checkAdminRight()) {
798             return FALSE;
799         }
800         
801         $this->_addOutputLogWriter();
802         
803         try {
804             $args = $this->_parseArgs($opts, array('oldId', 'newId', 'model'));
805         } catch (Tinebase_Exception_InvalidArgument $e) {
806             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
807                 Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Parameters "oldId", "newId" and "model" are required!');
808             }
809             exit(1);
810         }
811         
812         $skippedEntries = Tinebase_Relations::getInstance()->transferRelations($args['oldId'], $args['newId'], $args['model']);
813
814         if (! empty($skippedEntries) && Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
815             Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' ' . count($skippedEntries) . ' entries has been skipped:');
816         }
817         
818         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
819             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' The operation has been terminated successfully.');
820         }
821
822         return 0;
823     }
824
825     /**
826      * repair function for persistent filters (favorites) without grants: this adds default grants for those filters.
827      *
828      * @return int
829      */
830     public function setDefaultGrantsOfPersistentFilters()
831     {
832         if (! $this->_checkAdminRight()) {
833             return -1;
834         }
835
836         $this->_addOutputLogWriter(6);
837
838         // get all persistent filters without grants
839         // TODO this could be enhanced by allowing to set default grants for other filters, too
840
841         $filters = Tinebase_PersistentFilter::getInstance()->search(new Tinebase_Model_PersistentFilterFilter());
842         $filtersWithoutGrants = 0;
843
844         foreach ($filters as $filter) {
845             if (count($filter->grants) == 0) {
846                 // update to set default grants
847                 $filter = Tinebase_PersistentFilter::getInstance()->update($filter);
848                 $filtersWithoutGrants++;
849
850                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
851                     Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
852                         . ' Updated filter: ' . print_r($filter->toArray(), true));
853                 }
854             }
855         }
856
857         if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
858             Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__
859                 . ' Set default grants for ' . $filtersWithoutGrants . ' filters'
860                 . ' (checked ' . count($filters) . ' in total).');
861         }
862
863         return 0;
864     }
865 }