Tinebase_Model_Tree_Node validation for revision and notifiction props
[tine20] / tine20 / Tinebase / Model / Tree / Node.php
1 <?php
2 /**
3  * Tine 2.0
4  *
5  * @package     Tinebase
6  * @subpackage  Model
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Lars Kneschke <l.kneschke@metaways.de>
9  * @copyright   Copyright (c) 2010-2017 Metaways Infosystems GmbH (http://www.metaways.de)
10  */
11
12 /**
13  * class to hold data representing one node in the tree
14  *
15  * @package     Tinebase
16  * @subpackage  Model
17  * @property    string             contenttype
18  * @property    Tinebase_DateTime  creation_time
19  * @property    string             hash
20  * @property    string             indexed_hash
21  * @property    string             name
22  * @property    Tinebase_DateTime  last_modified_time
23  * @property    string             object_id
24  * @property    string             parent_id
25  * @property    string             size
26  * @property    string             revision_size
27  * @property    string             type
28  * @property    string             revision
29  * @property    string             available_revisions
30  * @property    string             description
31  * @property    string             acl_node
32  * @property    array              revisionProps
33  * @property    array              notificationProps
34  * @property    string             preview_count
35  * @property    Tinebase_Record_RecordSet grants
36  */
37 class Tinebase_Model_Tree_Node extends Tinebase_Record_Abstract
38 {
39     const XPROPS_REVISION = 'revisionProps';
40     const XPROPS_REVISION_NODE_ID = 'nodeId';
41     const XPROPS_REVISION_ON = 'keep';
42     const XPROPS_REVISION_NUM = 'keepNum';
43     const XPROPS_REVISION_MONTH = 'keepMonth';
44
45     /**
46      * {"notificationProps":[{"active":true,....},{"active":true....},{....}]}
47      */
48     const XPROPS_NOTIFICATION = 'notificationProps';
49     const XPROPS_NOTIFICATION_ACTIVE = 'active';
50     const XPROPS_NOTIFICATION_SUMMARY = 'summary';
51     const XPROPS_NOTIFICATION_ACCOUNT_ID = 'accountId';
52     const XPROPS_NOTIFICATION_ACCOUNT_TYPE = 'accountType';
53     
54     /**
55      * key in $_validators/$_properties array for the filed which
56      * represents the identifier
57      *
58      * @var string
59      */
60     protected $_identifier = 'id';
61     
62     /**
63      * application the record belongs to
64      *
65      * @var string
66      */
67     protected $_application = 'Tinebase';
68
69     /**
70      * name of fields that should be omitted from modlog
71      *
72      * @var array list of modlog omit fields
73      */
74     protected $_modlogOmitFields = array('hash', 'available_revisions', 'revision_size', 'path', 'indexed_hash');
75
76     /**
77      * if foreign Id fields should be resolved on search and get from json
78      * should have this format:
79      *     array('Calendar_Model_Contact' => 'contact_id', ...)
80      * or for more fields:
81      *     array('Calendar_Model_Contact' => array('contact_id', 'customer_id), ...)
82      * (e.g. resolves contact_id with the corresponding Model)
83      *
84      * @var array
85      */
86     protected static $_resolveForeignIdFields = array(
87         'Tinebase_Model_User' => array('created_by', 'last_modified_by')
88     );
89
90     /**
91      * list of zend validator
92      *
93      * these validators get used when validating user generated content with Zend_Input_Filter
94      *
95      * @var array
96      */
97     protected $_validators = array(
98         // tine 2.0 generic fields
99         'id'                 => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => null),
100         'created_by'         => array(Zend_Filter_Input::ALLOW_EMPTY => true),
101         'creation_time'      => array(Zend_Filter_Input::ALLOW_EMPTY => true),
102         'last_modified_by'   => array(Zend_Filter_Input::ALLOW_EMPTY => true),
103         'last_modified_time' => array(Zend_Filter_Input::ALLOW_EMPTY => true),
104         'is_deleted'         => array(Zend_Filter_Input::ALLOW_EMPTY => true),
105         'deleted_time'       => array(Zend_Filter_Input::ALLOW_EMPTY => true),
106         'deleted_by'         => array(Zend_Filter_Input::ALLOW_EMPTY => true),
107         'seq'                => array(Zend_Filter_Input::ALLOW_EMPTY => true),
108         // model specific fields
109         'parent_id'         => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => null),
110         'object_id'         => array('presence' => 'required'),
111         'revisionProps'     => array(Zend_Filter_Input::ALLOW_EMPTY => true),
112         'notificationProps' => array(Zend_Filter_Input::ALLOW_EMPTY => true),
113         // contains id of node with acl info
114         'acl_node'          => array(Zend_Filter_Input::ALLOW_EMPTY => true),
115         'name'              => array('presence' => 'required'),
116         'islink'            => array(
117             Zend_Filter_Input::DEFAULT_VALUE => '0',
118             array('InArray', array(true, false))
119         ),
120
121         'relations' => array(Zend_Filter_Input::ALLOW_EMPTY => true),
122         'notes' => array(Zend_Filter_Input::ALLOW_EMPTY => true),
123         'tags' => array(Zend_Filter_Input::ALLOW_EMPTY => true),
124         'customfields' => array(Zend_Filter_Input::ALLOW_EMPTY => true),
125
126         // fields from filemanager_objects table (ro)
127         'type'           => array(
128             Zend_Filter_Input::ALLOW_EMPTY => true,
129             array('InArray', array(Tinebase_Model_Tree_FileObject::TYPE_FILE,
130                 Tinebase_Model_Tree_FileObject::TYPE_FOLDER, Tinebase_Model_Tree_FileObject::TYPE_PREVIEW)),
131         ),
132         'description'           => array(Zend_Filter_Input::ALLOW_EMPTY => true),
133         'contenttype'           => array(Zend_Filter_Input::ALLOW_EMPTY => true),
134         'revision'              => array(Zend_Filter_Input::ALLOW_EMPTY => true),
135         'available_revisions'   => array(Zend_Filter_Input::ALLOW_EMPTY => true),
136         'hash'                  => array(Zend_Filter_Input::ALLOW_EMPTY => true),
137         'indexed_hash'          => array(Zend_Filter_Input::ALLOW_EMPTY => true),
138         'size'                  => array(Zend_Filter_Input::ALLOW_EMPTY => true),
139         'revision_size'         => array(Zend_Filter_Input::ALLOW_EMPTY => true),
140         'preview_count'         => array(Zend_Filter_Input::ALLOW_EMPTY => true, 'Digits',
141             Zend_Filter_Input::DEFAULT_VALUE => 0),
142
143         // not persistent
144         'container_name' => array(Zend_Filter_Input::ALLOW_EMPTY => true),
145
146         // this is needed should be sent by / delivered to client (not persistent in db atm)
147         'path'           => array(Zend_Filter_Input::ALLOW_EMPTY => true),
148         'account_grants' => array(Zend_Filter_Input::ALLOW_EMPTY => true),
149         'tempFile'       => array(Zend_Filter_Input::ALLOW_EMPTY => true),
150         'stream'         => array(Zend_Filter_Input::ALLOW_EMPTY => true),
151         // acl grants
152         'grants'                => array(Zend_Filter_Input::ALLOW_EMPTY => true),
153     );
154
155     /**
156      * name of fields containing datetime or or an array of datetime information
157      *
158      * @var array list of datetime fields
159      */
160     protected $_datetimeFields = array(
161         'creation_time',
162         'last_modified_time',
163         'deleted_time'
164     );
165     
166     /**
167     * overwrite constructor to add more filters
168     *
169     * @param mixed $_data
170     * @param bool $_bypassFilters
171     * @param mixed $_convertDates
172     */
173     public function __construct($_data = null, $_bypassFilters = false, $_convertDates = true)
174     {
175         $this->_filters['size'] = new Zend_Filter_Empty(0);
176         parent::__construct($_data, $_bypassFilters, $_convertDates);
177     }
178
179     public function runConvertToRecord()
180     {
181         if (isset($this->_properties['available_revisions'])) {
182             $this->_properties['available_revisions'] = explode(',', ltrim(
183                 rtrim($this->_properties['available_revisions'], '}'), '{'));
184         }
185
186         parent::runConvertToRecord();
187     }
188
189     public function runConvertToData()
190     {
191         if (isset($this->_properties[self::XPROPS_REVISION]) && is_array($this->_properties[self::XPROPS_REVISION])) {
192             if (count($this->_properties[self::XPROPS_REVISION]) > 0) {
193                 $this->_properties[self::XPROPS_REVISION] = json_encode($this->_properties[self::XPROPS_REVISION]);
194             } else {
195                 $this->_properties[self::XPROPS_REVISION] = null;
196             }
197         }
198
199         if (isset($this->_properties[self::XPROPS_NOTIFICATION]) &&
200                 is_array($this->_properties[self::XPROPS_NOTIFICATION])) {
201             if (count($this->_properties[self::XPROPS_NOTIFICATION]) > 0) {
202                 $this->_properties[self::XPROPS_NOTIFICATION] =
203                     json_encode($this->_properties[self::XPROPS_NOTIFICATION]);
204             } else {
205                 $this->_properties[self::XPROPS_NOTIFICATION] = null;
206             }
207         }
208
209         parent::runConvertToData();
210     }
211
212     /**
213      * returns real filesystem path
214      *
215      * @param string $baseDir
216      * @throws Tinebase_Exception_NotFound
217      * @return string
218      */
219     public function getFilesystemPath($baseDir = null)
220     {
221         if (empty($this->hash)) {
222             throw new Tinebase_Exception_NotFound('file object hash is missing');
223         }
224
225         if ($baseDir === null) {
226             $baseDir = Tinebase_Core::getConfig()->filesdir;
227         }
228
229         return $baseDir . DIRECTORY_SEPARATOR . substr($this->hash, 0, 3) . DIRECTORY_SEPARATOR .
230             substr($this->hash, 3);
231     }
232
233     public function isValid($_throwExceptionOnInvalidData = false)
234     {
235         if ($this->_isValidated === true) {
236             return true;
237         }
238
239         $invalid = array();
240         if (!empty($this->revisionProps)) {
241             if (count($this->revisionProps) > 4 || !isset($this->revisionProps[static::XPROPS_REVISION_NODE_ID])
242                     || !isset($this->revisionProps[static::XPROPS_REVISION_MONTH]) ||
243                     !isset($this->revisionProps[static::XPROPS_REVISION_ON]) ||
244                     !isset($this->revisionProps[static::XPROPS_REVISION_NUM])) {
245                 $invalid[] = 'revisionProps';
246             }
247         }
248         if (!empty($this->notificationProps)) {
249             foreach ($this->notificationProps as $val) {
250                 foreach ($val as $key => $val1) {
251                     if (!in_array($key, array(
252                             static::XPROPS_NOTIFICATION_ACCOUNT_ID,
253                             static::XPROPS_NOTIFICATION_ACCOUNT_TYPE,
254                             static::XPROPS_NOTIFICATION_ACTIVE,
255                             static::XPROPS_NOTIFICATION_SUMMARY))) {
256                         $invalid[] = 'notificationProps';
257                         break 2;
258                     }
259                 }
260             }
261         }
262
263         if (!empty($invalid) && $_throwExceptionOnInvalidData) {
264             $e = new Tinebase_Exception_Record_Validation('Some fields ' . implode(',', $invalid)
265                 . ' have invalid content');
266
267             if (Tinebase_Core::isLogLevel(Zend_Log::ERR)) Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . " "
268                 . $e->getMessage()
269                 . print_r($this->_validationErrors, true));
270             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
271                 . ' Record: ' . print_r($this->toArray(), true));
272
273             throw $e;
274         }
275
276         return parent::isValid($_throwExceptionOnInvalidData);
277     }
278
279 }