sometimes we have no relayEvents
[tine20] / tine20 / Tinebase / CustomField.php
1 <?php
2 /**
3  * Tine 2.0
4  * 
5  * @package     Tinebase
6  * @subpackage  CustomField
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @copyright   Copyright (c) 2007-2012 Metaways Infosystems GmbH (http://www.metaways.de)
9  * @author      Philipp Schüle <p.schuele@metaways.de>
10  * 
11  * @todo        add join to cf config to value backend to get name
12  * @todo        use recordset instead of array to store cfs of record
13  * @todo        move custom field handling from sql backend to abstract record controller
14  * @todo        remove the memory logging
15  */
16
17 /**
18  * the class provides functions to handle custom fields and custom field configs
19  * 
20  * @package     Tinebase
21  * @subpackage  CustomField
22  */
23 class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
24 {
25     /**************************** protected vars *********************/
26     
27     /**
28      * custom field config backend
29      * 
30      * @var Tinebase_CustomField_Config
31      */
32     protected $_backendConfig;
33     
34     /**
35      * custom field acl backend
36      * 
37      * @var Tinebase_Backend_Sql
38      */
39     protected $_backendACL;
40     
41     /**
42      * custom field values backend
43      * 
44      * @var Tinebase_Backend_Sql
45      */
46     protected $_backendValue;
47     
48     /**
49      * custom fields by application cache
50      * 
51      * @var array (app id + modelname => Tinebase_Record_RecordSet with cfs)
52      */
53     protected $_cfByApplicationCache = array();
54     
55     /**
56      * holds the instance of the singleton
57      *
58      * @var Tinebase_CustomField
59      */
60     private static $_instance = NULL;
61     
62     /**
63      * the constructor
64      *
65      * don't use the constructor. use the singleton 
66      */    
67     private function __construct() 
68     {
69         $this->_backendConfig = new Tinebase_CustomField_Config();
70         $this->_backendValue = new Tinebase_Backend_Sql(array(
71             'modelName' => 'Tinebase_Model_CustomField_Value', 
72             'tableName' => 'customfield',
73         ));
74         $this->_backendACL = new Tinebase_Backend_Sql(array(
75             'modelName' => 'Tinebase_Model_CustomField_Grant', 
76             'tableName' => 'customfield_acl',
77         ));
78     }
79
80     /**
81      * don't clone. Use the singleton.
82      *
83      */
84     private function __clone() 
85     {
86     }
87
88     /**
89      * Returns instance of Tinebase_CustomField
90      *
91      * @return Tinebase_CustomField
92      */
93     public static function getInstance() 
94     {
95         if (static::$_instance === NULL) {
96             static::$_instance = new Tinebase_CustomField();
97         }
98         
99         return static::$_instance;
100     }
101     
102     /**
103      * add new custom field
104      *
105      * @param Tinebase_Model_CustomField_Config $_customField
106      * @return Tinebase_Model_CustomField_Config
107      */
108     public function addCustomField(Tinebase_Model_CustomField_Config $_record)
109     {
110         $result = $this->_backendConfig->create($_record);
111         Tinebase_CustomField::getInstance()->setGrants($result, Tinebase_Model_CustomField_Grant::getAllGrants());
112         
113         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
114             . ' Created new custom field ' . $_record->name . ' for application ' . $_record->application_id);
115         
116         return $result;
117     }
118     
119     /**
120      * update custom field
121      *
122      * @param Tinebase_Model_CustomField_Config $_customField
123      * @return Tinebase_Model_CustomField_Config
124      */
125     public function updateCustomField(Tinebase_Model_CustomField_Config $_record)
126     {
127         $this->_clearCache();
128         $result = $this->_backendConfig->update($_record);
129         Tinebase_CustomField::getInstance()->setGrants($result, Tinebase_Model_CustomField_Grant::getAllGrants());
130         return $result;
131     }
132
133     /**
134      * get custom field by id
135      *
136      * @param string $_customFieldId
137      * @return Tinebase_Model_CustomField_Config
138      */
139     public function getCustomField($_customFieldId)
140     {
141         return $this->_backendConfig->get($_customFieldId);
142     }
143
144     /**
145      * get custom field by name and app
146      *
147      * @param string|Tinebase_Model_Application $applicationId application object, id or name
148      * @param string $customFieldName
149      * @param string $modelName
150      * @return Tinebase_Model_CustomField_Config|null
151      */
152     public function getCustomFieldByNameAndApplication($applicationId, $customFieldName, $modelName = null)
153     {
154         $allAppCustomfields = $this->getCustomFieldsForApplication($applicationId, $modelName);
155         return $allAppCustomfields->find('name', $customFieldName);
156     }
157     
158     /**
159      * get custom fields for an application
160      * - results are cached in class cache $_cfByApplicationCache
161      * - results are cached if caching is active (with cache tag 'customfields')
162      *
163      * @param string|Tinebase_Model_Application $_applicationId application object, id or name
164      * @param string                            $_modelName
165      * @param string                            $_requiredGrant (read grant by default)
166      * @return Tinebase_Record_RecordSet of Tinebase_Model_CustomField_Config records
167      */
168     public function getCustomFieldsForApplication($_applicationId, $_modelName = NULL, $_requiredGrant = Tinebase_Model_CustomField_Grant::GRANT_READ)
169     {
170         $applicationId = Tinebase_Model_Application::convertApplicationIdToInt($_applicationId);
171         
172         $userId = (is_object(Tinebase_Core::getUser())) ? Tinebase_Core::getUser()->getId() : 'nouser';
173         $cfIndex = $applicationId . (($_modelName !== NULL) ? $_modelName : '') . $_requiredGrant . $userId;
174         
175         if (isset($this->_cfByApplicationCache[$cfIndex])) {
176             return $this->_cfByApplicationCache[$cfIndex];
177         } 
178         
179         $cache = Tinebase_Core::getCache();
180         $cacheId = Tinebase_Helper::convertCacheId('getCustomFieldsForApplication' . $cfIndex);
181         $result = $cache->load($cacheId);
182         
183         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
184             . ' Before - MEMORY: ' . memory_get_usage(TRUE)/1024/1024 . ' MBytes');
185         
186         if (! $result) {
187             $filterValues = array(array(
188                 'field'     => 'application_id', 
189                 'operator'  => 'equals', 
190                 'value'     => $applicationId
191             ));
192             
193             if ($_modelName !== NULL) {
194                 $filterValues[] = array(
195                     'field'     => 'model', 
196                     'operator'  => 'equals', 
197                     'value'     => $_modelName
198                 );
199             }
200             
201             $filter = new Tinebase_Model_CustomField_ConfigFilter($filterValues);
202             $filter->setRequiredGrants((array)$_requiredGrant);
203             $result = $this->_backendConfig->search($filter);
204         
205             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
206                 . ' Got ' . count($result) . ' uncached custom fields for app id ' . $applicationId . ' (cacheid: ' . $cacheId . ')');
207             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE) && (count($result) > 0)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
208                 . print_r($result->toArray(), TRUE));
209             
210             $cache->save($result, $cacheId, array('customfields'));
211         }
212         
213         $this->_cfByApplicationCache[$cfIndex] = $result;
214         
215         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
216             . ' After - MEMORY: ' . memory_get_usage(TRUE)/1024/1024 . ' MBytes');
217         
218         return $result;
219     }
220     
221     /**
222      * check if app has customfield configs
223      * 
224      * @param string $applicationName
225      * @param string $modelName
226      * @return boolean 
227      */
228     public function appHasCustomFields($applicationName, $modelName = NULL)
229     {
230         if (empty($applicationName)) {
231             return FALSE;
232         }
233         $app = Tinebase_Application::getInstance()->getApplicationByName($applicationName);
234         $result = $this->getCustomFieldsForApplication($app, $modelName);
235         return (count($result) > 0);
236     }
237     
238     /**
239      * resolve config grants
240      * 
241      * @param Tinebase_Record_RecordSet $_cfConfigs
242      */
243     public function resolveConfigGrants($_cfConfigs)
244     {
245         $user = Tinebase_Core::getUser();
246         if (! is_object($user)) {
247             return; // do nothing
248         }
249         
250         $cfAcl = $this->_backendConfig->getAclForIds($user->getId(), $_cfConfigs->getArrayOfIds());
251         
252         foreach ($_cfConfigs as $config) {
253             $config->account_grants = ((isset($cfAcl[$config->getId()]) || array_key_exists($config->getId(), $cfAcl))) ? explode(',', $cfAcl[$config->getId()]) : array();
254         }
255     }
256     
257     /**
258      * delete a custom field
259      *
260      * @param string|Tinebase_Model_CustomField_Config $_customField
261      */
262     public function deleteCustomField($_customField)
263     {
264         $cfId = ($_customField instanceof Tinebase_Model_CustomField_Config) ? $_customField->getId() : $_customField;
265         
266         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
267             . ' Deleting custom field config ' . $cfId . ' and values.');
268         
269         $this->_clearCache();
270         $this->_backendValue->deleteByProperty($cfId, 'customfield_id');
271         $this->_backendConfig->delete($cfId);
272     }
273     
274     /**
275      * delete custom fields for an application
276      *
277      * @param string|Tinebase_Model_Application $_applicationId
278      * @return integer numer of deleted custom fields
279      */
280     public function deleteCustomFieldsForApplication($_applicationId)
281     {
282         $this->_clearCache();
283         $applicationId = Tinebase_Model_Application::convertApplicationIdToInt($_applicationId);
284  
285         $filterValues = array(array(
286             'field'     => 'application_id', 
287             'operator'  => 'equals', 
288             'value'     => $applicationId
289         ));
290             
291           $filter = new Tinebase_Model_CustomField_ConfigFilter($filterValues);
292           $filter->customfieldACLChecks(FALSE);
293         $customFields = $this->_backendConfig->search($filter);
294             
295         $deletedCount = 0;
296         foreach ($customFields as $customField) {
297                $this->deleteCustomField($customField);
298                $deletedCount++;
299         }
300         
301         return $deletedCount;
302     }
303     
304     /**
305      * saves multiple Custom Fields
306      * @param String $_modelName
307      * @param Array $_recordIds
308      * @param Array $_customFieldValues
309      */
310     
311     public function saveMultipleCustomFields($_modelName, $_recordIds, $_customFieldValues) 
312     {
313         $expModelName = explode('_', $_modelName);
314         $app = array_shift($expModelName);
315         $app = Tinebase_Application::getInstance()->getApplicationByName($app);
316         
317         $cF = $this->getCustomFieldsForApplication($app->getId(), $_modelName, Tinebase_Model_CustomField_Grant::GRANT_WRITE);
318         $fA = array();
319          
320         foreach($cF as $field) {
321             $fA[$field->__get('name')] = $field->__get('id');
322         }
323         
324         unset($cF);
325         
326         foreach($_recordIds as $recId) {
327             foreach($_customFieldValues as $cfKey => $cfValue) {
328                 $filterValues = array(
329                     array(
330                         'field'     => 'record_id',
331                         'operator'  => 'in',
332                         'value'     => (array) $recId
333                         ),
334                     array(
335                         'field'     => 'customfield_id',
336                         'operator'  => 'in',
337                         'value'     => (array) $fA[$cfKey]
338                         )
339                     );
340                 
341                 $filter = new Tinebase_Model_CustomField_ValueFilter($filterValues);
342                 
343                 $record = $this->_backendValue->search($filter)->getFirstRecord();
344                 
345                 if($record) {
346                     // DELETE
347                     if(empty($_customFieldValues[$cfKey])) {
348                         $this->_backendValue->delete($record);
349                     } else { // UPDATE
350                         $record->value = $_customFieldValues[$cfKey];
351                         $this->_backendValue->update($record);
352                     }
353                 } else {
354                     if(!empty($_customFieldValues[$cfKey])) {
355                         $record = new Tinebase_Model_CustomField_Value(array(
356                                 'record_id'         => $recId,
357                                 'customfield_id'    => $fA[$cfKey],
358                                 'value'             =>  $_customFieldValues[$cfKey]
359                             ));
360                         $this->_backendValue->create($record);
361                     }
362                 }
363             }
364         }
365         $this->_clearCache();
366       }
367     
368     /**
369      * save custom fields of record in its custom fields table
370      *
371      * @param Tinebase_Record_Interface $_record
372      */
373     public function saveRecordCustomFields(Tinebase_Record_Interface $_record)
374     {
375         $applicationId = Tinebase_Application::getInstance()->getApplicationByName($_record->getApplication())->getId();
376         $appCustomFields = $this->getCustomFieldsForApplication($applicationId, get_class($_record), Tinebase_Model_CustomField_Grant::GRANT_WRITE);
377         $this->resolveConfigGrants($appCustomFields);
378         
379         $existingCustomFields = $this->_getCustomFields($_record->getId());
380         $existingCustomFields->addIndices(array('customfield_id'));
381         
382         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
383             . ' Updating custom fields for record of class ' . get_class($_record));
384         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
385             . ' Record cf values: ' . print_r($_record->customfields, TRUE));
386         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
387             . ' App cf names: ' . print_r($appCustomFields->name, TRUE));
388         
389         foreach ($appCustomFields as $customField) {
390             if (is_array($_record->customfields) && (isset($_record->customfields[$customField->name]) || array_key_exists($customField->name, $_record->customfields))) {
391                 $value = $_record->customfields[$customField->name];
392                 $filtered = $existingCustomFields->filter('customfield_id', $customField->id);
393                 
394                 // we need to resolve the modelName and the record value if array is given (e.g. on updating customfield)
395                 if (strtolower($customField->definition['type']) == 'record') {
396                     $modelParts = explode('.', $customField->definition['recordConfig']['value']['records']); // get model parts from saved record class e.g. Tine.Admin.Model.Group
397                     $modelName  = $modelParts[1] . '_Model_' . $modelParts[3];
398                     
399                     if (is_array($value)) {
400                         $model = new $modelName(array(), TRUE);
401                         $value = $value[$model->getIdProperty()];
402                     }
403                     // check if customfield value is the record itself
404                     if (get_class($_record) == $modelName && $_record->getId() == $value) {
405                         throw new Tinebase_Exception_Record_Validation('It is not allowed to add the same record as customfield record!');
406                     }
407                 }
408                 
409                 switch (count($filtered)) {
410                     case 1:
411                         $cf = $filtered->getFirstRecord();
412                         if ($customField->valueIsEmpty($value)) {
413                             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Deleting cf value for ' . $customField->name);
414                             $this->_backendValue->delete($cf);
415                         } else {
416                             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Updating value for ' . $customField->name . ' to ' . $value);
417                             $cf->value = $value;
418                             $this->_backendValue->update($cf);
419                         }
420                         break;
421                     case 0:
422                         if (! $customField->valueIsEmpty($value)) {
423                             $cf = new Tinebase_Model_CustomField_Value(array(
424                                 'record_id'         => $_record->getId(),
425                                 'customfield_id'    => $customField->getId(),
426                                 'value'             => $value
427                             ));
428                             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Creating value for ' . $customField->name . ' -> ' . $value);
429                             $this->_backendValue->create($cf);
430                         }
431                         break;
432                     default:
433                         throw new Tinebase_Exception_UnexpectedValue('Oops, there should be only one custom field value here!');
434                 }
435             }
436         }
437     }
438     
439     /**
440      * get custom fields and add them to $_record->customfields arraay
441      *
442      * @param Tinebase_Record_Interface $_record
443      * @param Tinebase_Record_RecordSet $_customFields
444      * @param Tinebase_Record_RecordSet $_configs
445      */
446     public function resolveRecordCustomFields(Tinebase_Record_Interface $_record, $_customFields = NULL, $_configs = NULL)
447     {
448         $customFields = ($_customFields === NULL) ? $this->_getCustomFields($_record->getId()) : $_customFields;
449         
450         if (count($customFields) == 0) {
451             return;
452         }
453         
454         if ($_configs === NULL) {
455             $_configs = $this->getCustomFieldsForApplication(Tinebase_Application::getInstance()->getApplicationByName($_record->getApplication()));
456         };
457         
458         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
459             . ' Adding ' . count($customFields) . ' customfields to record  ' . $_record->getId());
460         
461         $result = array();
462         foreach ($customFields as $customField) {
463             $this->_setCfValueInRecord($_record, $customField, $_configs);
464         }
465     }
466     
467     /**
468      * set customfield value in record
469      * 
470      * @param Tinebase_Record_Abstract $record
471      * @param Tinebase_Model_CustomField_Value $customField
472      * @param Tinebase_Record_RecordSet $configs
473      */
474     protected function _setCfValueInRecord(Tinebase_Record_Abstract $record, Tinebase_Model_CustomField_Value $customField, Tinebase_Record_RecordSet $configs)
475     {
476         $recordCfs = $record->customfields;
477         $idx = $configs->getIndexById($customField->customfield_id);
478         if ($idx !== FALSE) {
479             $config = $configs[$idx];
480             if (strtolower($config->definition['type']) == 'record') {
481                 $value = $this->_getRecordTypeCfValue($config, $customField->value);
482             } else {
483                 $value = $customField->value;
484             }
485             $recordCfs[$config->name] = $value;
486         }
487         
488         $record->customfields = $recordCfs;
489     }
490     
491     /**
492      * get record cf value
493      * 
494      * @param Tinebase_Model_CustomField_Config $config
495      * @param string $value
496      * @return string
497      */
498     protected function _getRecordTypeCfValue($config, $value)
499     {
500         try {
501             $model = $config->definition['recordConfig']['value']['records'];
502             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
503                 . ' Fetching record customfield of type ' . $model);
504             
505             $controller = Tinebase_Core::getApplicationInstance($model);
506             $result = $controller->get($value)->toArray();
507         } catch (Exception $e) {
508             if (Tinebase_Core::isLogLevel(Zend_Log::ERR)) Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__
509                 . ' Error resolving custom field record: ' . $e->getMessage());
510             $result = $value;
511         }
512         
513         return $result;
514     }
515     
516     /**
517      * get all customfields of all given records
518      * 
519      * @param  Tinebase_Record_RecordSet $_records     records to get customfields for
520      */
521     public function resolveMultipleCustomfields(Tinebase_Record_RecordSet $_records)
522     {
523         if (count($_records) == 0) {
524             return;
525         }
526         
527         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
528             . ' Before resolving - MEMORY: ' . memory_get_usage(TRUE)/1024/1024 . ' MBytes');
529         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
530             . ' Resolving custom fields for ' . count($_records) . ' ' . $_records->getRecordClassName() . ' records.');
531         
532         $configs = $this->getCustomFieldsForApplication(Tinebase_Application::getInstance()->getApplicationByName($_records->getFirstRecord()->getApplication()));
533         
534         $customFields = $this->_getCustomFields($_records->getArrayOfIdsAsString(), $configs->getArrayOfIds());
535         $customFields->sort('record_id');
536         
537         // NOTE: as filtering is currently very slow, we have to loop the customfields and add the value to the record.
538         // @see 0007496: timeout when opening multiedit dlg and assigning records to events/projects/email
539         // @see 0007558: reactivate indices in Tinebase_Record_RecordSet
540         $record = NULL;
541         foreach ($customFields as $customField) {
542             if (! $record || $record->getId() !== $customField->record_id) {
543                 $record = $_records->getById($customField->record_id);
544             }
545             $this->_setCfValueInRecord($record, $customField, $configs);
546         }
547         
548         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
549             . ' After resolving - MEMORY: ' . memory_get_usage(TRUE)/1024/1024 . ' MBytes');
550     }
551     
552     /**
553      * get custom fields of record(s)
554      *
555      * @param string|array $_recordId
556      * @param array $_recordId
557      * @return Tinebase_Record_RecordSet of Tinebase_Model_CustomField_Value
558      */
559     protected function _getCustomFields($_recordId, $_configIds = NULL)
560     {
561         $recordIds = is_array($_recordId) ? $_recordId : array((string) $_recordId);
562         
563         $filterValues = array(array(
564             'field'     => 'record_id', 
565             'operator'  => 'in', 
566             'value'     => $recordIds
567         ));
568         if ($_configIds) {
569             $filterValues[] = array(
570                 'field'     => 'customfield_id', 
571                 'operator'  => 'in', 
572                 'value'     => (array) $_configIds
573             );
574         }
575         $filter = new Tinebase_Model_CustomField_ValueFilter($filterValues);
576         
577         $result = $this->_backendValue->search($filter);
578         
579         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
580             . ' Fetched ' . count($result) . ' customfield values.');
581         
582         return $result;
583     }
584     
585     /**
586      * set grants for custom field
587      *
588      * @param   string|Tinebase_Model_CustomField_Config $_customfieldId
589      * @param   array $_grants list of grants to add
590      * @param   string $_accountType
591      * @param   int $_accountId
592      * @return  void
593      */
594     public function setGrants($_customfieldId, $_grants = array(), $_accountType = NULL, $_accountId = NULL)
595     {
596         $cfId = ($_customfieldId instanceof Tinebase_Model_CustomField_Config) ? $_customfieldId->getId() : $_customfieldId;
597         
598         try {
599             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
600                 . ' Setting grants for custom field ' . $cfId . ' -> ' . implode(',', $_grants) . ' for '
601                 . ($_accountType !== NULL ? $_accountType : Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE) . ' (' . $_accountId . ')');
602             
603             $transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
604             $this->_backendACL->deleteByProperty($cfId, 'customfield_id');
605             
606             foreach ($_grants as $grant) {
607                 if (in_array($grant, Tinebase_Model_CustomField_Grant::getAllGrants())) {
608                     $newGrant = new Tinebase_Model_CustomField_Grant(array(
609                         'customfield_id'=> $cfId,
610                         'account_type'  => ($_accountType !== NULL) ? $_accountType : Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE,
611                         'account_id'    => ($_accountId !== NULL) ? $_accountId : 0,
612                         'account_grant' => $grant
613                     ));
614                     $this->_backendACL->create($newGrant);
615                 }
616             }
617             
618             Tinebase_TransactionManager::getInstance()->commitTransaction($transactionId);
619             $this->_clearCache();
620             
621         } catch (Exception $e) {
622             Tinebase_TransactionManager::getInstance()->rollBack();
623             throw new Tinebase_Exception_Backend($e->getMessage());
624         }
625     }
626     
627     /**
628      * get customfield config ids by grant
629      * 
630      * @param string $_grant
631      * @return array of ids
632      */
633     public function getCustomfieldConfigIdsByAcl($_grant)
634     {
635         $user = Tinebase_Core::getUser();
636         if (is_object($user)) {
637             $result = $this->_backendConfig->getByAcl($_grant, $user->getId());
638         } else {
639             $result = array();
640         }
641         
642         return $result;
643     }
644     
645     /**
646      * remove all customfield related entries from cache
647      * 
648      * @todo this needs to clear in a more efficient way
649      */
650     protected function _clearCache() 
651     {
652         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
653             . ' Clearing custom field cache.');
654         
655         $this->_cfByApplicationCache = array();
656         
657         $cache = Tinebase_Core::getCache();
658         $cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('customfields'));
659     }
660
661     /**
662     * remove related entries from cache for given cf config record
663     * 
664     * @param Tinebase_Model_CustomField_Config $record
665     * 
666     * @todo this needs to clear in a more efficient way
667     */
668     public function clearCacheForConfig(Tinebase_Model_CustomField_Config $record)
669     {
670         $this->_clearCache();
671         
672         // NOTE: this does not work as we need the user id in the cacheId
673         /*
674         $cfIndexRead  = Tinebase_Model_CustomField_Grant::GRANT_READ;
675         $cfIndexWrite = Tinebase_Model_CustomField_Grant::GRANT_WRITE;
676         $cfIndexModelRead  = $record->model . Tinebase_Model_CustomField_Grant::GRANT_READ;
677         $cfIndexModelWrite = $record->model . Tinebase_Model_CustomField_Grant::GRANT_WRITE;
678         $idsToClear = array($cfIndexRead, $cfIndexModelRead, $cfIndexWrite, $cfIndexModelWrite);
679         
680         $cache = Tinebase_Core::getCache();
681         foreach ($idsToClear as $id) {
682             $cacheId = 'getCustomFieldsForApplication' . $record->application_id . $id;
683             unset($this->_cfByApplicationCache[$record->application_id . $id]);
684             $cache->remove($cacheId);
685         }
686         */
687     }
688     
689     /******************** functions for Tinebase_Controller_SearchInterface / get custom field values ***************************/
690     
691     /**
692      * get list of custom field values
693      *
694      * @param Tinebase_Model_Filter_FilterGroup|optional $_filter
695      * @param Tinebase_Model_Pagination|optional $_pagination
696      * @param bool $_getRelations (unused)
697      * @param boolean $_onlyIds (unused)
698      * @return Tinebase_Record_RecordSet
699      */
700     public function search(Tinebase_Model_Filter_FilterGroup $_filter = NULL, Tinebase_Record_Interface $_pagination = NULL, $_getRelations = FALSE, $_onlyIds = FALSE)
701     {
702         $result = $this->_backendValue->search($_filter, $_pagination);
703         return $result;
704     }
705     
706     /**
707      * Gets total count of search with $_filter
708      * 
709      * @param Tinebase_Model_Filter_FilterGroup $_filter
710      * @return int
711      */
712     public function searchCount(Tinebase_Model_Filter_FilterGroup $_filter) 
713     {
714         $count = $this->_backendValue->searchCount($_filter);
715         return $count;
716     }
717 }