ebdf540146e69edf58b6f3ba9d8504bf1ec65884
[tine20] / tine20 / Tinebase / Model / Filter / Container.php
1 <?php
2 /**
3  * Tine 2.0
4  * 
5  * @package     Tinebase
6  * @subpackage  Filter
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
9  * @author      Cornelius Weiss <c.weiss@metaways.de>
10  */
11
12 /**
13  * Tinebase_Model_Filter_Container
14  * 
15  * filters by containers
16  * 
17  * NOTE: this filter accepts multiple formats for incoming container values
18  *  - id (always represents a single container)
19  *  - path (may also represent a node)
20  *  - array containing id or path
21  *  
22  * NOTE: This filter already does all ACL checks. This means a controller only 
23  *       has to make sure a containerfilter is set and if not add one
24  * 
25  * @package     Tinebase
26  * @subpackage  Filter
27  */
28 class Tinebase_Model_Filter_Container extends Tinebase_Model_Filter_Abstract implements Tinebase_Model_Filter_AclFilter 
29 {
30     /**
31      * @var array list of allowed operators
32      */
33     protected $_operators = array(
34         0 => 'equals',       // value is expected to represent a single container
35         1 => 'in',           // value is expected to be an array of container representations
36         2 => 'specialNode',  // value is one of {all|shared|otherUsers|internal} (deprecated please use equals with path instead)
37         3 => 'personalNode', // value is expected to be a user id (deprecated please use equals with path instead)
38         //4 => 'not',        // value is expected to be a single container id
39     );
40     
41     /**
42      * @var array one of these grants must be met
43      */
44     protected $_requiredGrants = array(
45         Tinebase_Model_Grants::GRANT_READ
46     );
47     
48     /**
49      * is resolved
50      *
51      * @var boolean
52      */
53     protected $_isResolved = FALSE;
54     
55     /**
56      * resolved containerIds
57      *
58      * @var array
59      */
60     protected $_containerIds = array();
61     
62     /**
63      * set options 
64      *
65      * @param  array $_options
66      * @throws Tinebase_Exception_Record_NotDefined
67      */
68     protected function _setOptions(array $_options)
69     {
70         if (! isset($_options['applicationName'])) {
71             throw new Tinebase_Exception_InvalidArgument('Container filter needs the applicationName option');
72         }
73         
74         $_options['ignoreAcl'] = isset($_options['ignoreAcl']) ? $_options['ignoreAcl'] : false;
75         
76         $this->_options = $_options;
77     }
78     
79     /**
80      * sets the grants this filter needs to assure
81      *
82      * @param array $_grants
83      */
84     public function setRequiredGrants(array $_grants)
85     {
86         $this->_requiredGrants = $_grants;
87     }
88
89     /**
90      * set operator
91      *
92      * @param string $_operator
93      */
94     public function setOperator($_operator)
95     {
96         parent::setOperator($_operator);
97         $this->_isResolved = FALSE;
98     }
99     
100     /**
101      * set value
102      * 
103      * NOTE: incoming ids will be rewritten to their corresponding paths
104      * NOTE: incoming *Node operators will be rewritten to their corresponding pathes
105      * 
106      * @param mixed $_value
107      */
108     public function setValue($_value)
109     {
110         // transform *Node operators
111         if (strpos($this->getOperator(), 'Node') !== FALSE) {
112             $_value = $this->_node2path($this->getOperator(), $_value);
113             $this->setOperator('equals');
114         }
115         
116         $this->_flatten($_value);
117             
118         $value = array();
119         foreach ((array) $_value as $v) {
120             $this->_flatten($v);
121             
122             // transform id to path
123             if (strpos($v, '/') === FALSE) {
124                 try {
125                     $v = Tinebase_Container::getInstance()->getContainerById($v, TRUE)->getPath();
126                 } catch (Tinebase_Exception_InvalidArgument $teia) {
127                     if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' ' . $teia->getMessage());
128                     if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . $teia->getTraceAsString());
129                     $v = '/';
130                 }
131             }
132             $value[] = $v;
133         }
134         
135         parent::setValue(is_array($_value) ? $value : $value[0]);
136         $this->_isResolved = FALSE;
137     }
138     
139     /**
140      * appends sql to given select statement
141      *
142      * @param  Zend_Db_Select                    $_select
143      * @param  Tinebase_Backend_Sql_Abstract     $_backend
144      * @throws Tinebase_Exception_NotFound
145      */
146     public function appendFilterSql($_select, $_backend)
147     {
148         $this->_resolve();
149         
150         $_select->where($this->_getQuotedFieldName($_backend) .  ' IN (?)', empty($this->_containerIds) ? new Zend_Db_Expr('NULL') : $this->_containerIds);
151     }
152     
153     /**
154      * returns array with the filter settings of this filter
155      *
156      * @param  bool $_valueToJson resolve value for json api
157      * @return array
158      */
159     public function toArray($_valueToJson = false)
160     {
161         $result = parent::toArray($_valueToJson);
162         
163         if ($_valueToJson == true) {
164             // NOTE: at this point operators should be equals or in and all values should be paths
165             $values = array();
166             foreach((array) $this->_value as $path) {
167                 $containerData = array('path' => $path);
168                 if (($containerId = Tinebase_Model_Container::pathIsContainer($path))) {
169                     $containerData = array_merge($containerData, Tinebase_Container::getInstance()->getContainerById($containerId, TRUE)->toArray());
170                 } else if (($ownerId = Tinebase_Model_Container::pathIsPersonalNode($path))) {
171                     // transform current user 
172                     $owner = Tinebase_User::getInstance()->getUserById($ownerId);
173                     $containerData['name']  = $owner->accountDisplayName;
174                     $containerData['path']  = "/personal/$ownerId";
175                     $containerData['owner'] = $owner->toArray();
176                 }
177                 
178                 $values[] = $containerData;
179             }
180             $result['value'] = is_array($this->_value) ? $values : $values[0];
181         }
182         
183         return $result;
184     } 
185     
186     /**
187      * return ids of containers selected by filter
188      * 
189      * @return array
190      */
191     public function getContainerIds()
192     {
193         $this->_resolve();
194         
195         return $this->_containerIds;
196     }
197     
198     /**
199      * flatten resolved records
200      * 
201      * @param mixed &$_value
202      */
203     protected function _flatten(&$_value) {
204         if (is_array($_value)) {
205             if((isset($_value['path']) || array_key_exists('path', $_value))) {
206                 $_value = $_value['path'];
207             } else if((isset($_value['id']) || array_key_exists('id', $_value))) {
208                 $_value = $_value['id'];
209             }
210         }
211     }
212     
213     /**
214      * resolve container ids
215      * 
216      * - checks grants and silently removes containers without required grants
217      * - sets internal $this->_containerIds
218      */
219     protected function _resolve()
220     {
221         if ($this->_isResolved) {
222             //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' already resolved');
223             return;
224         }
225         
226         if (! in_array($this->_operator, array('equals', 'in'))) {
227             throw new Tinebase_Exception_UnexpectedValue("Operator '{$this->_operator}' not supported.");
228         }
229         
230         $this->_containerIds = array();
231         foreach((array)$this->_value as $path) {
232             $this->_containerIds = array_merge($this->_containerIds, $this->_resolvePath($path));
233         }
234         
235         $this->_containerIds = array_unique($this->_containerIds);
236         
237         $this->_isResolved = TRUE;
238     }
239     
240     /**
241      * resolves a single path
242      * 
243      * @param  String $_path
244      * @return array of container ids
245      */
246     protected function _resolvePath($_path)
247     {
248         $containerIds = array();
249         
250         if (($containerId = Tinebase_Model_Container::pathIsContainer($_path))) {
251             if ($this->_options['ignoreAcl'] == TRUE) {
252                 $containerIds[] = $containerId;
253             } else if (Tinebase_Core::getUser()->hasGrant($containerId, $this->_requiredGrants)) {
254                 $containerIds[] = $containerId;
255             }
256         } else if (($ownerId = Tinebase_Model_Container::pathIsPersonalNode($_path))) {
257             $containerIds = $this->_resolveContainerNode('personal', $ownerId);
258         } else {
259             $node = $_path == '/' ? 'all' : substr($_path, 1);
260             $node = $node === 'personal' ? Tinebase_Model_Container::TYPE_OTHERUSERS : $node;
261
262             $containerIds = $this->_resolveContainerNode($node);
263         }
264         
265         return $containerIds;
266     }
267     
268     /**
269      * wrapper for get container functions
270      *
271      * @param  string $_function
272      * @param  string $_ownerId    => needed for $_node == 'personal'
273      * @return array of container ids
274      * @throws Tinebase_Exception_UnexpectedValue
275      */
276     protected function _resolveContainerNode($_node, $_ownerId = NULL)
277     {
278         $currentAccount = Tinebase_Core::getUser();
279         $appName = $this->_options['applicationName'];
280         
281         switch ($_node) {
282             case 'all':        return Tinebase_Container::getInstance()->getContainerByACL($currentAccount, $appName, $this->_requiredGrants, TRUE, $this->_options['ignoreAcl']);
283             case 'personal':   return Tinebase_Container::getInstance()->getPersonalContainer($currentAccount, $appName, $_ownerId, $this->_requiredGrants, $this->_options['ignoreAcl'])->getId();
284             case 'shared':     return Tinebase_Container::getInstance()->getSharedContainer($currentAccount, $appName, $this->_requiredGrants, $this->_options['ignoreAcl'])->getId();
285             case Tinebase_Model_Container::TYPE_OTHERUSERS: return Tinebase_Container::getInstance()->getOtherUsersContainer($currentAccount, $appName, $this->_requiredGrants, $this->_options['ignoreAcl'])->getId();
286             case 'internal':
287                 // @todo remove legacy code
288                 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ 
289                     . ' Trying to fetch obsolete "/internal" node. Please make sure this filter is no longer used because this is deprecated.');
290                 $adminConfigDefaults = Admin_Controller::getInstance()->getConfigSettings();
291                 return array($adminConfigDefaults[Admin_Model_Config::DEFAULTINTERNALADDRESSBOOK]);
292             default:           throw new Tinebase_Exception_UnexpectedValue('specialNode ' . $_node . ' not supported.');
293         }
294         
295         return $ids;
296     }
297     
298     /**
299      * converts given *Node params to a path
300      * 
301      * @param  String $_operator
302      * @param  String $_value
303      * @return String
304      */
305     protected function _node2path($_operator, $_value)
306     {
307         switch ($_operator) {
308             case 'specialNode':
309                 return '/' . ($_value != 'all' ?  $_value : '');
310             case 'personalNode':
311                 return "/personal/{$_value}";
312             default:
313                 throw new Tinebase_Exception_UnexpectedValue("operator '$_operator' not supported");
314         }
315     }
316 }