4264ed4b4a0d7f39e24a625e1af2e1d24023d515
[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-2016 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->_backendACL->deleteByProperty($cfId, 'customfield_id');
272         $this->_backendConfig->delete($cfId);
273     }
274     
275     /**
276      * delete custom fields for an application
277      *
278      * @param string|Tinebase_Model_Application $_applicationId
279      * @return integer numer of deleted custom fields
280      */
281     public function deleteCustomFieldsForApplication($_applicationId)
282     {
283         $this->_clearCache();
284         $applicationId = Tinebase_Model_Application::convertApplicationIdToInt($_applicationId);
285  
286         $filterValues = array(array(
287             'field'     => 'application_id', 
288             'operator'  => 'equals',
289             'value'     => $applicationId
290         ));
291             
292           $filter = new Tinebase_Model_CustomField_ConfigFilter($filterValues);
293           $filter->customfieldACLChecks(FALSE);
294         $customFields = $this->_backendConfig->search($filter);
295             
296         $deletedCount = 0;
297         foreach ($customFields as $customField) {
298                $this->deleteCustomField($customField);
299                $deletedCount++;
300         }
301         
302         return $deletedCount;
303     }
304     
305     /**
306      * saves multiple Custom Fields
307      * @param String $_modelName
308      * @param Array $_recordIds
309      * @param Array $_customFieldValues
310      */
311     
312     public function saveMultipleCustomFields($_modelName, $_recordIds, $_customFieldValues) 
313     {
314         $expModelName = explode('_', $_modelName);
315         $app = array_shift($expModelName);
316         $app = Tinebase_Application::getInstance()->getApplicationByName($app);
317         
318         $cF = $this->getCustomFieldsForApplication($app->getId(), $_modelName, Tinebase_Model_CustomField_Grant::GRANT_WRITE);
319         $fA = array();
320          
321         foreach($cF as $field) {
322             $fA[$field->__get('name')] = $field->__get('id');
323         }
324         
325         unset($cF);
326         
327         foreach($_recordIds as $recId) {
328             foreach($_customFieldValues as $cfKey => $cfValue) {
329                 $filterValues = array(
330                     array(
331                         'field'     => 'record_id',
332                         'operator'  => 'in',
333                         'value'     => (array) $recId
334                         ),
335                     array(
336                         'field'     => 'customfield_id',
337                         'operator'  => 'in',
338                         'value'     => (array) $fA[$cfKey]
339                         )
340                     );
341                 
342                 $filter = new Tinebase_Model_CustomField_ValueFilter($filterValues);
343                 
344                 $record = $this->_backendValue->search($filter)->getFirstRecord();
345                 
346                 if($record) {
347                     // DELETE
348                     if(empty($_customFieldValues[$cfKey])) {
349                         $this->_backendValue->delete($record);
350                     } else { // UPDATE
351                         $record->value = $_customFieldValues[$cfKey];
352                         $this->_backendValue->update($record);
353                     }
354                 } else {
355                     if(!empty($_customFieldValues[$cfKey])) {
356                         $record = new Tinebase_Model_CustomField_Value(array(
357                                 'record_id'         => $recId,
358                                 'customfield_id'    => $fA[$cfKey],
359                                 'value'             =>  $_customFieldValues[$cfKey]
360                             ));
361                         $this->_backendValue->create($record);
362                     }
363                 }
364             }
365         }
366         $this->_clearCache();
367       }
368     
369     /**
370      * save custom fields of record in its custom fields table
371      *
372      * @param Tinebase_Record_Interface $_record
373      */
374     public function saveRecordCustomFields(Tinebase_Record_Interface $_record)
375     {
376         $applicationId = Tinebase_Application::getInstance()->getApplicationByName($_record->getApplication())->getId();
377         $appCustomFields = $this->getCustomFieldsForApplication($applicationId, get_class($_record), Tinebase_Model_CustomField_Grant::GRANT_WRITE);
378         $this->resolveConfigGrants($appCustomFields);
379         
380         $existingCustomFields = $this->_getCustomFields($_record->getId());
381         $existingCustomFields->addIndices(array('customfield_id'));
382         
383         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
384             . ' Updating custom fields for record of class ' . get_class($_record));
385         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
386             . ' Record cf values: ' . print_r($_record->customfields, TRUE));
387         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
388             . ' App cf names: ' . print_r($appCustomFields->name, TRUE));
389         
390         foreach ($appCustomFields as $customField) {
391             if (is_array($_record->customfields) && (isset($_record->customfields[$customField->name]) || array_key_exists($customField->name, $_record->customfields))) {
392                 $value = $_record->customfields[$customField->name];
393                 $filtered = $existingCustomFields->filter('customfield_id', $customField->id);
394                 
395                 // we need to resolve the modelName and the record value if array is given (e.g. on updating customfield)
396                 if (strtolower($customField->definition['type']) == 'record') {
397                     $modelParts = explode('.', $customField->definition['recordConfig']['value']['records']); // get model parts from saved record class e.g. Tine.Admin.Model.Group
398                     $modelName  = $modelParts[1] . '_Model_' . $modelParts[3];
399                     
400                     if (is_array($value)) {
401                         $model = new $modelName(array(), TRUE);
402                         $value = $value[$model->getIdProperty()];
403                     }
404                     // check if customfield value is the record itself
405                     if (get_class($_record) == $modelName && $_record->getId() == $value) {
406                         throw new Tinebase_Exception_Record_Validation('It is not allowed to add the same record as customfield record!');
407                     }
408                 }
409                 
410                 switch (count($filtered)) {
411                     case 1:
412                         $cf = $filtered->getFirstRecord();
413                         if ($customField->valueIsEmpty($value)) {
414                             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Deleting cf value for ' . $customField->name);
415                             $this->_backendValue->delete($cf);
416                         } else {
417                             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Updating value for ' . $customField->name . ' to ' . $value);
418                             $cf->value = $value;
419                             $this->_backendValue->update($cf);
420                         }
421                         break;
422                     case 0:
423                         if (! $customField->valueIsEmpty($value)) {
424                             $cf = new Tinebase_Model_CustomField_Value(array(
425                                 'record_id'         => $_record->getId(),
426                                 'customfield_id'    => $customField->getId(),
427                                 'value'             => $value
428                             ));
429                             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Creating value for ' . $customField->name . ' -> ' . $value);
430                             $this->_backendValue->create($cf);
431                         }
432                         break;
433                     default:
434                         throw new Tinebase_Exception_UnexpectedValue('Oops, there should be only one custom field value here!');
435                 }
436             }
437         }
438     }
439     
440     /**
441      * get custom fields and add them to $_record->customfields arraay
442      *
443      * @param Tinebase_Record_Interface $_record
444      * @param Tinebase_Record_RecordSet $_customFields
445      * @param Tinebase_Record_RecordSet $_configs
446      */
447     public function resolveRecordCustomFields(Tinebase_Record_Interface $_record, $_customFields = NULL, $_configs = NULL)
448     {
449         $customFields = ($_customFields === NULL) ? $this->_getCustomFields($_record->getId()) : $_customFields;
450         
451         if (count($customFields) == 0) {
452             return;
453         }
454         
455         if ($_configs === NULL) {
456             $_configs = $this->getCustomFieldsForApplication(Tinebase_Application::getInstance()->getApplicationByName($_record->getApplication()));
457         };
458         
459         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
460             . ' Adding ' . count($customFields) . ' customfields to record  ' . $_record->getId());
461         
462         $result = array();
463         foreach ($customFields as $customField) {
464             $this->_setCfValueInRecord($_record, $customField, $_configs);
465         }
466     }
467     
468     /**
469      * set customfield value in record
470      * 
471      * @param Tinebase_Record_Abstract $record
472      * @param Tinebase_Model_CustomField_Value $customField
473      * @param Tinebase_Record_RecordSet $configs
474      */
475     protected function _setCfValueInRecord(Tinebase_Record_Abstract $record, Tinebase_Model_CustomField_Value $customField, Tinebase_Record_RecordSet $configs)
476     {
477         $recordCfs = $record->customfields;
478         $idx = $configs->getIndexById($customField->customfield_id);
479         if ($idx !== FALSE) {
480             $config = $configs[$idx];
481             if (strtolower($config->definition['type']) == 'record') {
482                 $value = $this->_getRecordTypeCfValue($config, $customField->value);
483             } else {
484                 $value = $customField->value;
485             }
486             $recordCfs[$config->name] = $value;
487         }
488
489         // sort customfields by key
490         if (is_array($recordCfs)) {
491             ksort($recordCfs);
492         }
493         
494         $record->customfields = $recordCfs;
495     }
496     
497     /**
498      * get record cf value
499      * 
500      * @param Tinebase_Model_CustomField_Config $config
501      * @param string $value
502      * @return string
503      */
504     protected function _getRecordTypeCfValue($config, $value)
505     {
506         try {
507             $model = $config->definition['recordConfig']['value']['records'];
508             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
509                 . ' Fetching record customfield of type ' . $model);
510             
511             $controller = Tinebase_Core::getApplicationInstance($model);
512             $result = $controller->get($value)->toArray();
513         } catch (Exception $e) {
514             if (Tinebase_Core::isLogLevel(Zend_Log::ERR)) Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__
515                 . ' Error resolving custom field record: ' . $e->getMessage());
516             $result = $value;
517         }
518         
519         return $result;
520     }
521     
522     /**
523      * get all customfields of all given records
524      * 
525      * @param  Tinebase_Record_RecordSet $_records     records to get customfields for
526      */
527     public function resolveMultipleCustomfields(Tinebase_Record_RecordSet $_records)
528     {
529         if (count($_records) == 0) {
530             return;
531         }
532         
533         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
534             . ' Before resolving - MEMORY: ' . memory_get_usage(TRUE)/1024/1024 . ' MBytes');
535         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
536             . ' Resolving custom fields for ' . count($_records) . ' ' . $_records->getRecordClassName() . ' records.');
537         
538         $configs = $this->getCustomFieldsForApplication(Tinebase_Application::getInstance()->getApplicationByName($_records->getFirstRecord()->getApplication()));
539         
540         $customFields = $this->_getCustomFields($_records->getArrayOfIdsAsString(), $configs->getArrayOfIds());
541         $customFields->sort('record_id');
542         
543         // NOTE: as filtering is currently very slow, we have to loop the customfields and add the value to the record.
544         // @see 0007496: timeout when opening multiedit dlg and assigning records to events/projects/email
545         // @see 0007558: reactivate indices in Tinebase_Record_RecordSet
546         $record = NULL;
547         foreach ($customFields as $customField) {
548             if (! $record || $record->getId() !== $customField->record_id) {
549                 $record = $_records->getById($customField->record_id);
550             }
551             $this->_setCfValueInRecord($record, $customField, $configs);
552         }
553         
554         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
555             . ' After resolving - MEMORY: ' . memory_get_usage(TRUE)/1024/1024 . ' MBytes');
556     }
557     
558     /**
559      * get custom fields of record(s)
560      *
561      * @param string|array $_recordId
562      * @param array $_recordId
563      * @return Tinebase_Record_RecordSet of Tinebase_Model_CustomField_Value
564      */
565     protected function _getCustomFields($_recordId, $_configIds = NULL)
566     {
567         $recordIds = is_array($_recordId) ? $_recordId : array((string) $_recordId);
568         
569         $filterValues = array(array(
570             'field'     => 'record_id', 
571             'operator'  => 'in', 
572             'value'     => $recordIds
573         ));
574         if ($_configIds) {
575             $filterValues[] = array(
576                 'field'     => 'customfield_id', 
577                 'operator'  => 'in', 
578                 'value'     => (array) $_configIds
579             );
580         }
581         $filter = new Tinebase_Model_CustomField_ValueFilter($filterValues);
582         
583         $result = $this->_backendValue->search($filter);
584         
585         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
586             . ' Fetched ' . count($result) . ' customfield values.');
587         
588         return $result;
589     }
590     
591     /**
592      * set grants for custom field
593      *
594      * @param   string|Tinebase_Model_CustomField_Config $_customfieldId
595      * @param   array $_grants list of grants to add
596      * @param   string $_accountType
597      * @param   int $_accountId
598      * @return  void
599      */
600     public function setGrants($_customfieldId, $_grants = array(), $_accountType = NULL, $_accountId = NULL)
601     {
602         $cfId = ($_customfieldId instanceof Tinebase_Model_CustomField_Config) ? $_customfieldId->getId() : $_customfieldId;
603         
604         try {
605             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
606                 . ' Setting grants for custom field ' . $cfId . ' -> ' . implode(',', $_grants) . ' for '
607                 . ($_accountType !== NULL ? $_accountType : Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE) . ' (' . $_accountId . ')');
608             
609             $transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
610             $this->_backendACL->deleteByProperty($cfId, 'customfield_id');
611             
612             foreach ($_grants as $grant) {
613                 if (in_array($grant, Tinebase_Model_CustomField_Grant::getAllGrants())) {
614                     $newGrant = new Tinebase_Model_CustomField_Grant(array(
615                         'customfield_id'=> $cfId,
616                         'account_type'  => ($_accountType !== NULL) ? $_accountType : Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE,
617                         'account_id'    => ($_accountId !== NULL) ? $_accountId : 0,
618                         'account_grant' => $grant
619                     ));
620                     $this->_backendACL->create($newGrant);
621                 }
622             }
623             
624             Tinebase_TransactionManager::getInstance()->commitTransaction($transactionId);
625             $this->_clearCache();
626             
627         } catch (Exception $e) {
628             Tinebase_TransactionManager::getInstance()->rollBack();
629             throw new Tinebase_Exception_Backend($e->getMessage());
630         }
631     }
632     
633     /**
634      * get customfield config ids by grant
635      * 
636      * @param string $_grant
637      * @return array of ids
638      */
639     public function getCustomfieldConfigIdsByAcl($_grant)
640     {
641         $user = Tinebase_Core::getUser();
642         if (is_object($user)) {
643             $result = $this->_backendConfig->getByAcl($_grant, $user->getId());
644         } else {
645             $result = array();
646         }
647         
648         return $result;
649     }
650     
651     /**
652      * remove all customfield related entries from cache
653      * 
654      * @todo this needs to clear in a more efficient way
655      */
656     protected function _clearCache() 
657     {
658         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
659             . ' Clearing custom field cache.');
660         
661         $this->_cfByApplicationCache = array();
662         
663         $cache = Tinebase_Core::getCache();
664         $cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('customfields'));
665     }
666
667     /**
668     * remove related entries from cache for given cf config record
669     * 
670     * @param Tinebase_Model_CustomField_Config $record
671     * 
672     * @todo this needs to clear in a more efficient way
673     */
674     public function clearCacheForConfig(Tinebase_Model_CustomField_Config $record)
675     {
676         $this->_clearCache();
677         
678         // NOTE: this does not work as we need the user id in the cacheId
679         /*
680         $cfIndexRead  = Tinebase_Model_CustomField_Grant::GRANT_READ;
681         $cfIndexWrite = Tinebase_Model_CustomField_Grant::GRANT_WRITE;
682         $cfIndexModelRead  = $record->model . Tinebase_Model_CustomField_Grant::GRANT_READ;
683         $cfIndexModelWrite = $record->model . Tinebase_Model_CustomField_Grant::GRANT_WRITE;
684         $idsToClear = array($cfIndexRead, $cfIndexModelRead, $cfIndexWrite, $cfIndexModelWrite);
685         
686         $cache = Tinebase_Core::getCache();
687         foreach ($idsToClear as $id) {
688             $cacheId = 'getCustomFieldsForApplication' . $record->application_id . $id;
689             unset($this->_cfByApplicationCache[$record->application_id . $id]);
690             $cache->remove($cacheId);
691         }
692         */
693     }
694     
695     /******************** functions for Tinebase_Controller_SearchInterface / get custom field values ***************************/
696     
697     /**
698      * get list of custom field values
699      *
700      * @param Tinebase_Model_Filter_FilterGroup $_filter
701      * @param Tinebase_Model_Pagination $_pagination
702      * @param bool $_getRelations (unused)
703      * @param boolean $_onlyIds (unused)
704      * @return Tinebase_Record_RecordSet
705      */
706     public function search(Tinebase_Model_Filter_FilterGroup $_filter = NULL, Tinebase_Record_Interface $_pagination = NULL, $_getRelations = FALSE, $_onlyIds = FALSE)
707     {
708         $result = $this->_backendValue->search($_filter, $_pagination);
709         return $result;
710     }
711     
712     /**
713      * Gets total count of search with $_filter
714      * 
715      * @param Tinebase_Model_Filter_FilterGroup $_filter
716      * @return int
717      */
718     public function searchCount(Tinebase_Model_Filter_FilterGroup $_filter) 
719     {
720         $count = $this->_backendValue->searchCount($_filter);
721         return $count;
722     }
723
724     /**
725      * get list of custom field values
726      *
727      * @param Tinebase_Model_Filter_FilterGroup $_filter
728      * @param Tinebase_Model_Pagination $_pagination
729      * @param bool $_getRelations (unused)
730      * @param boolean $_onlyIds (unused)
731      * @return Tinebase_Record_RecordSet
732      */
733     public function searchConfig(Tinebase_Model_Filter_FilterGroup $_filter = NULL, Tinebase_Record_Interface $_pagination = NULL, $_getRelations = FALSE, $_onlyIds = FALSE)
734     {
735         $result = $this->_backendConfig->search($_filter, $_pagination);
736         return $result;
737     }
738
739     public function deleteCustomFieldValue($_ids)
740     {
741         return $this->_backendValue->delete($_ids);
742     }
743
744     public function saveCustomFieldValue(Tinebase_Model_CustomField_Value $_record)
745     {
746         if (!empty($_record->getId())) {
747             return $this->_backendValue->update($_record);
748         }
749         return $this->_backendValue->create($_record);
750     }
751 }