Merge branch 'pu/2013.03/fileattach' into pu/2013.03/modelconfig-hr
[tine20] / tine20 / Tinebase / Convert / Json.php
1 <?php
2 /**
3  * convert functions for records from/to json (array) format
4  * 
5  * @package     Tinebase
6  * @subpackage  Convert
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Philipp Schüle <p.schuele@metaways.de>
9  * @copyright   Copyright (c) 2011-2013 Metaways Infosystems GmbH (http://www.metaways.de)
10  */
11
12 /**
13  * convert functions for records from/to json (array) format
14  *
15  * @package     Tinebase
16  * @subpackage  Convert
17  */
18 class Tinebase_Convert_Json implements Tinebase_Convert_Interface
19 {
20     /**
21      * converts external format to Tinebase_Record_Abstract
22      * 
23      * @param  mixed                     $_blob   the input data to parse
24      * @param  Tinebase_Record_Abstract  $_record  update existing record
25      * @return Tinebase_Record_Abstract
26      */
27     public function toTine20Model($_blob, Tinebase_Record_Abstract $_record = NULL)
28     {
29         throw new Tinebase_Exception_NotImplemented('From json to record is not implemented yet');
30     }
31     
32     /**
33      * converts Tinebase_Record_Abstract to external format
34      * 
35      * @param  Tinebase_Record_Abstract $_record
36      * @return mixed
37      */
38     public function fromTine20Model(Tinebase_Record_Abstract $_record)
39     {
40         if (! $_record) {
41             return array();
42         }
43         
44         // for resolving we'll use recordset
45         $records = new Tinebase_Record_RecordSet(get_class($_record), array($_record));
46         
47         Tinebase_Frontend_Json_Abstract::resolveContainerTagsUsers($records);
48         
49         // use modern record resolving, if the model was configured using Tinebase_ModelConfiguration
50         // at first, resolve all single record fields
51         $this->_resolveSingleRecordFields($records);
52         // next step, resolve all multiple records fields
53         $this->_resolveMultipleRecordFields($records);
54         
55         // resolve the traditional way, if model hasn't been configured with Tinebase_ModelConfiguration
56         self::resolveMultipleIdFields($records);
57         
58         $_record = $records->getFirstRecord();
59         $_record->setTimezone(Tinebase_Core::get(Tinebase_Core::USERTIMEZONE));
60         $_record->bypassFilters = true;
61         
62         return $_record->toArray();
63     }
64
65     /**
66 <<<<<<< HEAD
67      * resolves single record fields (Tinebase_ModelConfiguration._recordsFields)
68      * 
69      * @param Tinebase_Record_RecordSet $_records the records
70      */
71     protected function _resolveSingleRecordFields(Tinebase_Record_RecordSet $_records)
72     {
73         $ownRecordClass = $_records->getRecordClassName();
74         
75         if (! $cfg = $ownRecordClass::getConfiguration()) {
76             return;
77         }
78         $resolveFields = $cfg->recordFields;
79         if ($resolveFields && is_array($resolveFields)) {
80             // don't search twice if the same recordClass gets resolved on multiple fields
81             foreach ($resolveFields as $fieldKey => $fieldConfig) {
82                 $resolveRecords[$fieldConfig['config']['recordClassName']][] = $fieldKey;
83             }
84             
85             foreach ($resolveRecords as $foreignRecordClassName => $fields) {
86                 $foreignIds = array();
87                 $fields = (array) $fields;
88                 
89                 foreach($fields as $field) {
90                     $foreignIds = array_unique(array_merge($foreignIds, $_records->{$field}));
91                 }
92                 
93                 if (! Tinebase_Core::getUser()->hasRight(substr($foreignRecordClassName, 0, strpos($foreignRecordClassName, "_")), Tinebase_Acl_Rights_Abstract::RUN)) {
94                     continue;
95                 }
96                 
97                 $cfg = $resolveFields[$fields[0]];
98                 
99                 if ($cfg['type'] == 'user') {
100                     $foreignRecords = Tinebase_User::getInstance()->getUsers();
101                 } elseif ($cfg['type'] == 'container') {
102                     $foreignRecords = new Tinebase_Record_RecordSet('Tinebase_Model_Container');
103                     $foreignRecords->addRecord(Tinebase_Container::getInstance()->get($_id));
104                 // TODO: resolve recursive records of records better in controller
105                 // TODO: resolve containers
106                 } else {
107                     $controller = array_key_exists('controllerClassName', $cfg['config']) ? $cfg['config']['controllerClassName']::getInstance() : Tinebase_Core::getApplicationInstance($foreignRecordClassName);
108                     $foreignRecords = $controller->getMultiple($foreignIds);
109                 }
110                 
111                 $foreignRecords->setTimezone(Tinebase_Core::get(Tinebase_Core::USERTIMEZONE));
112                 $foreignRecords->convertDates = true;
113                 Tinebase_Frontend_Json_Abstract::resolveContainerTagsUsers($foreignRecords);
114                 $fr = $foreignRecords->getFirstRecord();
115                 if ($fr && $fr->has('notes')) {
116                     Tinebase_Notes::getInstance()->getMultipleNotesOfRecords($foreignRecords);
117                 }
118                 
119                 if ($foreignRecords->count()) {
120                     foreach ($_records as $record) {
121                         foreach ($fields as $field) {
122                             $idx = $foreignRecords->getIndexById($record->{$field});
123                             if (isset($idx) && $idx !== FALSE) {
124                                 $record->{$field} = $foreignRecords[$idx];
125                             }
126                         }
127                     }
128                 }
129             }
130         }
131     }
132     
133     /**
134      * resolves multiple records (fallback)
135      * 
136      * @deprecated use Tinebase_ModelConfiguration to configure your models, so this won't be used anymore 
137      * @param Tinebase_Record_RecordSet $_records the records
138      * @param array $resolveFields
139      */
140     public static function resolveMultipleIdFields($records, $resolveFields = NULL)
141     {
142         if (! $records instanceof Tinebase_Record_RecordSet) {
143             return;
144         }
145         
146         $ownRecordClass = $records->getRecordClassName();
147         if ($resolveFields === NULL) {
148             $resolveFields = $ownRecordClass::getResolveForeignIdFields();
149         }
150         
151         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
152             . ' Resolving ' . $ownRecordClass . ' fields: ' . print_r($resolveFields, TRUE));
153         
154         foreach ((array) $resolveFields as $foreignRecordClassName => $fields) {
155             if ($foreignRecordClassName === 'recursive') {
156                 foreach ($fields as $field => $model) {
157                     foreach ($records->$field as $subRecords) {
158                         self::resolveMultipleIdFields($subRecords);
159                     }
160                 }
161             } else {
162                 self::_resolveForeignIdFields($records, $foreignRecordClassName, (array) $fields);
163             }
164         }
165     }
166     
167     /**
168      * resolve foreign fields for records
169      * 
170      * @param Tinebase_Record_RecordSet $records
171      * @param string $foreignRecordClassName
172      * @param array $fields
173      */
174     protected static function _resolveForeignIdFields($records, $foreignRecordClassName, $fields)
175     {
176         $options = array_key_exists('options', $fields) ? $fields['options'] : array();
177         $fields = array_key_exists('fields', $fields) ? $fields['fields'] : $fields;
178         
179         $foreignIds = array();
180         foreach ($fields as $field) {
181             $foreignIds = array_unique(array_merge($foreignIds, $records->{$field}));
182         }
183         
184         if (! Tinebase_Core::getUser()->hasRight(substr($foreignRecordClassName, 0, strpos($foreignRecordClassName, "_")), Tinebase_Acl_Rights_Abstract::RUN)) {
185             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
186                 . ' Not resolving ' . $foreignRecordClassName . ' records because user has no right to run app.');
187             return;
188         }
189         
190         $controller = Tinebase_Core::getApplicationInstance($foreignRecordClassName);
191         
192         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
193             . ' Fetching ' . $foreignRecordClassName . ' by id: ' . print_r($foreignIds, TRUE));
194         
195         if (array_key_exists('ignoreAcl', $options) && $options['ignoreAcl']) {
196             // @todo make sure that second param of getMultiple() is $ignoreAcl
197             $foreignRecords = $controller->getMultiple($foreignIds, TRUE);
198         } else {
199             $foreignRecords = $controller->getMultiple($foreignIds);
200         }
201         
202         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
203             . ' Foreign records found: ' . print_r($foreignRecords->toArray(), TRUE));
204         
205         if (count($foreignRecords) === 0) {
206             return;
207         }
208         
209         foreach ($records as $record) {
210             foreach ($fields as $field) {
211                 if (is_scalar($record->{$field})) {
212                     $idx = $foreignRecords->getIndexById($record->{$field});
213                     if (isset($idx) && $idx !== FALSE) {
214                         $record->{$field} = $foreignRecords[$idx];
215                     } else {
216                         switch ($foreignRecordClassName) {
217                             case 'Tinebase_Model_User':
218                             case 'Tinebase_Model_FullUser':
219                                 $record->{$field} = Tinebase_User::getInstance()->getNonExistentUser();
220                                 break;
221                             default:
222                                 // skip
223                         }
224                     }
225                 }
226             }
227         }
228     }
229     
230     /**
231      * resolve multiple record fields (Tinebase_ModelConfiguration._recordsFields)
232      * 
233      * @param Tinebase_Record_RecordSet $_records
234      */
235     protected function _resolveMultipleRecordFields(Tinebase_Record_RecordSet $_records)
236     {
237         // show if there is something to resolve
238         $ownRecordClass = $_records->getRecordClassName();
239         
240         if (! $_records->count()) {
241             return;
242         }
243         
244         if (! (($config = $ownRecordClass::getConfiguration()) && ($resolveFields = $config->recordsFields))) {
245             return;
246         }
247         
248         $ownIds = $_records->{$config->idProperty};
249         
250         // iterate fields to resolve
251         foreach ($resolveFields as $fieldKey => $c) {
252             $config = $c['config'];
253             // fetch the fields by the refIfField
254             $controller = array_key_exists('controllerClassName', $config) ? $config['controllerClassName']::getInstance() : Tinebase_Core::getApplicationInstance($foreignRecordClassName);
255             $filterName = $config['filterClassName'];
256             
257             $filterArray = array();
258             
259             // addFilters can be added and must be added if the same model resides in more than one records fields
260             if (array_key_exists('addFilters', $config) && is_array($config['addFilters'])) {
261                 $useaddFilters = true;
262                 $filterArray = $config['addFilters'];
263             }
264             
265             $filter = new $filterName($filterArray);
266             $filter->addFilter(new Tinebase_Model_Filter_Id(array('field' => $config['refIdField'], 'operator' => 'in', 'value' => $ownIds)));
267             
268             $paging = NULL;
269             if (array_key_exists('paging', $config) && is_array($config['paging'])) {
270                 $paging = new Tinebase_Model_Pagination($config['paging']);
271             }
272             
273             $foreignRecords = $controller->search($filter, $paging);
274             $foreignRecords->setTimezone(Tinebase_Core::get(Tinebase_Core::USERTIMEZONE));
275             $foreignRecords->convertDates = true;
276             Tinebase_Frontend_Json_Abstract::resolveContainerTagsUsers($foreignRecords);
277             $fr = $foreignRecords->getFirstRecord();
278             if ($fr && $fr->has('notes')) {
279                 Tinebase_Notes::getInstance()->getMultipleNotesOfRecords($foreignRecords);
280             }
281             if ($foreignRecords->count() > 0) {
282                 foreach ($_records as $record) {
283                     $filtered = $foreignRecords->filter($config['refIdField'], $record->getId());
284                     $record->{$fieldKey} = $filtered->toArray();
285                 }
286             } else {
287                 $_records->{$fieldKey} = NULL;
288             }
289         }
290         
291     }
292     
293     /**
294      * converts Tinebase_Record_RecordSet to external format
295      * 
296      * @param  Tinebase_Record_RecordSet  $_records
297      * @param Tinebase_Model_Filter_FilterGroup $_filter
298      * @param Tinebase_Model_Pagination $_pagination
299      * 
300      * @return mixed
301      */
302     public function fromTine20RecordSet(Tinebase_Record_RecordSet $_records, $_filter = NULL, $_pagination = NULL)
303     {
304         if (count($_records) == 0) {
305             return array();
306         }
307         
308         Tinebase_Frontend_Json_Abstract::resolveContainerTagsUsers($_records);
309         
310         self::resolveMultipleIdFields($_records);
311         $this->_resolveSingleRecordFields($_records);
312         $this->_resolveMultipleRecordFields($_records);
313         
314         $_records->setTimezone(Tinebase_Core::get(Tinebase_Core::USERTIMEZONE));
315         $_records->convertDates = true;
316
317         $result = $_records->toArray();
318
319         return $result;
320     }
321 }