64141335fee4694b12f2af81a5c46ce2132be42b
[tine20] / tine20 / Tinebase / Controller / Abstract.php
1 <?php
2 /**
3  * Tine 2.0
4  *
5  * @package     Tinebase
6  * @subpackage  Controller
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Philipp Schuele <p.schuele@metaways.de>
9  * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
10  * 
11  */
12
13 /**
14  * controller abstract for applications
15  *
16  * @package     Tinebase
17  * @subpackage  Controller
18  */
19 abstract class Tinebase_Controller_Abstract extends Tinebase_Pluggable_Abstract implements Tinebase_Controller_Interface
20 {
21     /**
22      * default settings
23      * 
24      * @var array
25      */
26     protected $_defaultsSettings = array();
27
28     /**
29      * application models if given
30      *
31      * @var null
32      */
33     protected $_models = null;
34
35     /**
36      * holds the default Model of this application
37      * @var string
38      */
39     protected static $_defaultModel = NULL;
40     
41     /**
42      * application name (is needed in checkRight())
43      *
44      * @var string
45      */
46     protected $_applicationName = '';
47     
48     /**
49      * disable events on demand
50      * 
51      * @var mixed   false => no events filtered, true => all events filtered, array => disable only specific events
52      */
53     protected $_disabledEvents = false;
54
55     /**
56      * Models of this application that make use of Tinebase_Record_Path
57      *
58      * @var array|null
59      */
60     protected $_modelsUsingPath = null;
61
62     /**
63      * request context information
64      *
65      * @var array|null
66      */
67     protected $_requestContext = null;
68
69     public function setRequestContext(array $context)
70     {
71         $this->_requestContext = $context;
72     }
73
74     /**
75      * @return array|null
76      */
77     public function getRequestContext()
78     {
79         return $this->_requestContext;
80     }
81     
82     /**
83      * generic check admin rights function
84      * rules: 
85      * - ADMIN right includes all other rights
86      * - MANAGE_* right includes VIEW_* right 
87      * - results are cached if caching is active (with cache tag 'rights')
88      * 
89      * @param   string  $_right to check
90      * @param   boolean $_throwException [optional]
91      * @param   boolean $_includeTinebaseAdmin [optional]
92      * @return  boolean
93      * @throws  Tinebase_Exception_UnexpectedValue
94      * @throws  Tinebase_Exception_AccessDenied
95      * @throws  Tinebase_Exception
96      * 
97      * @todo move that to *_Acl_Rights
98      * @todo include Tinebase admin? atm only the application admin right is checked
99      * @todo think about moving the caching to Tinebase_Acl_Roles and use only a class cache as it is difficult (and slow?) to invalidate
100      */
101     public function checkRight($_right, $_throwException = TRUE, $_includeTinebaseAdmin = TRUE) 
102     {
103         if (empty($this->_applicationName)) {
104             throw new Tinebase_Exception_UnexpectedValue('No application name defined!');
105         }
106         if (! is_object(Tinebase_Core::getUser())) {
107             throw new Tinebase_Exception('No user found for right check!');
108         }
109         
110         $right = strtoupper($_right);
111         
112         $cache = Tinebase_Core::getCache();
113         $cacheId = Tinebase_Helper::convertCacheId('checkRight' . Tinebase_Core::getUser()->getId() . $right . $this->_applicationName);
114         $result = $cache->load($cacheId);
115         
116         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . $cacheId);
117         
118         if (!$result) {
119             $applicationRightsClass = $this->_applicationName . '_Acl_Rights';
120             
121             // array with the rights that should be checked, ADMIN is in it per default
122             $rightsToCheck = ($_includeTinebaseAdmin) ? array(Tinebase_Acl_Rights::ADMIN) : array();
123             
124             if (preg_match("/VIEW_([A-Z_]*)/", $right, $matches)) {
125                 // manage right includes view right
126                 $rightsToCheck[] = constant($applicationRightsClass. '::MANAGE_' . $matches[1]);
127             } 
128             
129             $rightsToCheck[] = constant($applicationRightsClass. '::' . $right);
130             
131             $result = FALSE;
132             
133             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
134                 . ' Checking rights: ' . print_r($rightsToCheck, TRUE));
135             
136             foreach ($rightsToCheck as $rightToCheck) {
137                 if (Tinebase_Acl_Roles::getInstance()->hasRight($this->_applicationName, Tinebase_Core::getUser()->getId(), $rightToCheck)) {
138                     $result = TRUE;
139                     break;
140                 }
141             }
142             
143             $cache->save($result, $cacheId, array('rights'), 120);
144         }
145         
146         if (!$result && $_throwException) {
147             throw new Tinebase_Exception_AccessDenied("You are not allowed to $right in application $this->_applicationName !");
148         }
149         
150         return $result;
151     }
152     
153     /**
154      * Returns default settings for app
155      *
156      * @param boolean $_resolve if some values should be resolved
157      * @return  array settings data
158      */
159     public function getConfigSettings($_resolve = FALSE)
160     {
161         $appConfig = Tinebase_Config::getAppConfig($this->_applicationName);
162         if ($appConfig != NULL) {
163             $settings = $appConfig->get(
164                 Tinebase_Config::APPDEFAULTS, 
165                 new Tinebase_Config_Struct($this->_defaultsSettings)
166             )->toArray();
167         } else { 
168             $settings = $this->_defaultsSettings;
169         }
170         return ($_resolve) ? $this->_resolveConfigSettings($settings) : $settings;
171     }
172     
173     /**
174      * resolve some settings
175      * 
176      * @param array $_settings
177      */
178     protected function _resolveConfigSettings($_settings)
179     {
180         return $_settings;
181     }
182     
183     /**
184      * save settings
185      * 
186      * @param array $_settings
187      * @return void
188      */
189     public function saveConfigSettings($_settings)
190     {
191         // only admins are allowed to do this
192         $this->checkRight(Tinebase_Acl_Rights::ADMIN);
193         
194         $appConfig = Tinebase_Config::getAppConfig($this->_applicationName);
195         
196         if ($appConfig !== NULL) {
197             $appConfig->set(Tinebase_Config::APPDEFAULTS, $_settings);
198         }
199     }
200     
201     /**
202      * returns the default model of this application
203      *
204      * @return string
205      */
206     public function getDefaultModel()
207     {
208         if (static::$_defaultModel !== null) {
209             return static::$_defaultModel;
210         }
211
212         // no default model defined, using first model of app...
213         $models = $this->getModels();
214         return (count($models) > 0) ? $models[0] : null;
215     }
216     
217     /**
218      * returns controller instance for given $_controllerName
219      * 
220      * @param string $_controllerName
221      * @return Tinebase_Controller
222      */
223     public static function getController($_controllerName)
224     {
225         if (! class_exists($_controllerName)) {
226             throw new Exception("Controller" . $_controllerName . "not found.");
227         }
228         
229         if (!in_array('Tinebase_Controller_Interface', class_implements($_controllerName))) {
230             throw new Exception("Controller $_controllerName does not implement Tinebase_Controller_Interface.");
231         }
232         
233         return call_user_func(array($_controllerName, 'getInstance'));
234     }
235
236     /**
237      * delete all personal user folders and the content associated with these folders
238      *
239      * @param Tinebase_Model_User|string $_accountId the account object
240      */
241     public function deletePersonalFolder($_accountId, $model = '')
242     {
243         if ($_accountId instanceof Tinebase_Record_Abstract) {
244             $_accountId = $_accountId->getId();
245         }
246
247         if ('' === $model) {
248             $model = static::$_defaultModel;
249         }
250         // attention, currently everybody who has admin rights on a personal container is the owner of it
251         // even if multiple users have admin rights on that personal container! (=> multiple owners)
252         $containers = Tinebase_Container::getInstance()->getPersonalContainer($_accountId, $model, $_accountId, '*', true);
253
254         foreach ($containers as $container) {
255             //Tinebase_Container::getInstance()->deleteContainerContents($container, true);
256             Tinebase_Container::getInstance()->deleteContainer($container, true);
257         }
258     }
259
260     /**
261      * get core data for this application
262      *
263      * @return Tinebase_Record_RecordSet
264      *
265      * TODO add generic approach for fetching core data from config
266      */
267     public function getCoreDataForApplication()
268     {
269         $result = new Tinebase_Record_RecordSet('CoreData_Model_CoreData');
270
271         // TODO get configured core data
272
273         return $result;
274     }
275
276     /**
277      * get all models of this application that use tinebase_record_path
278      *
279      * @return array|null
280      */
281     public function getModelsUsingPaths()
282     {
283         return $this->_modelsUsingPath;
284     }
285
286     /**
287      * @return array
288      *
289      * @param bool $MCV2only filter for new modelconfig with doctrine schema tool
290      */
291     public function getModels($MCV2only = false)
292     {
293         if ($this->_models === null && ! empty($this->_applicationName)) {
294
295             $cache = Tinebase_Core::getCache();
296             $cacheId = Tinebase_Helper::convertCacheId('getModels' . $this->_applicationName);
297             $models = $cache->load($cacheId);
298
299             if (! $models) {
300                 $models = $this->_getModelsFromAppDir();
301                 // cache for a long time only on prod
302                 $cache->save($models, $cacheId, array(), TINE20_BUILDTYPE === 'DEVELOPMENT' ? 1 : 3600);
303             }
304
305             $this->_models = $models;
306         }
307
308         if ($MCV2only) {
309             $md = new Tinebase_Record_DoctrineMappingDriver();
310             $MCv2Models = array();
311             foreach ((array)$this->_models as $model) {
312                 if ($md->isTransient($model)) {
313                     $MCv2Models[] = $model;
314                 }
315             }
316
317             return $MCv2Models;
318         }
319
320         return $this->_models;
321     }
322
323     /**
324      * get models from application directory
325      *
326      * @return array|null
327      */
328     protected function _getModelsFromAppDir()
329     {
330         try {
331             $dir = new DirectoryIterator(dirname(dirname(dirname(__FILE__))) . '/' . $this->_applicationName . '/Model/');
332         } catch (Exception $e) {
333             Tinebase_Exception::log($e);
334             return null;
335         }
336
337         $models = array();
338         foreach ($dir as $fileinfo) {
339             if (!$fileinfo->isDot() && !$fileinfo->isLink()) {
340                 if ($this->_isModelFile($fileinfo)) {
341                     $models[] = $this->_applicationName . '_Model_' . str_replace('.php', '', $fileinfo->getBasename());
342                 } else if ($fileinfo->isDir()) {
343                     // go (only) one level deeper
344                     $subdir = new DirectoryIterator($fileinfo->getPath() . '/' . $fileinfo->getFilename());
345                     foreach ($subdir as $subfileinfo) {
346                         if ($this->_isModelFile($subfileinfo)) {
347                             $models[] = $this->_applicationName . '_Model_' . $fileinfo->getBasename() . '_'
348                                 . str_replace('.php', '', $subfileinfo->getBasename());
349                         }
350                     }
351
352                 }
353             }
354         }
355
356         foreach ($models as $key => $model) {
357             if (class_exists($model)) {
358                 $reflection = new ReflectionClass($model);
359                 $interfaces = $reflection->getInterfaceNames();
360                 if (! in_array('Tinebase_Record_Interface', $interfaces)) {
361                     unset($models[$key]);
362                 }
363             } else {
364                 // interface, no php class, ...
365                 unset($models[$key]);
366             }
367         }
368
369         return $models;
370     }
371
372     /**
373      * returns true if $fileinfo describes a model file
374      *
375      * @param $fileinfo
376      * @return bool
377      */
378     protected function _isModelFile($fileinfo)
379     {
380         $isModel = (
381             ! $fileinfo->isDot() &&
382             ! $fileinfo->isLink() &&
383             $fileinfo->isFile() &&
384             ! preg_match('/filter\.php/i', $fileinfo->getBasename()) &&
385             ! preg_match('/abstract\.php/i', $fileinfo->getBasename())
386         );
387
388         return $isModel;
389     }
390 }