Merge branch '2014.11' into 2014.11-develop
[tine20] / tine20 / Tinebase / Controller / Record / Abstract.php
index 2326cb9..ad8be76 100644 (file)
@@ -108,7 +108,7 @@ abstract class Tinebase_Controller_Record_Abstract
      *
      * @var boolean
      */
-    protected $_sendNotifications = FALSE;
+    protected $_sendNotifications = false;
 
     /**
      * if some of the relations should be deleted
@@ -122,8 +122,15 @@ abstract class Tinebase_Controller_Record_Abstract
      * 
      * @var boolean
      */
-    protected $_inspectRelatedRecords  = FALSE;
-    
+    protected $_inspectRelatedRecords  = false;
+
+    /**
+     * set this to true to check (duplicate/freebusy/...) in create/update of related records
+     *
+     * @var boolean
+     */
+    protected $_doRelatedCreateUpdateCheck  = false;
+
     /**
      * record alarm field
      *
@@ -138,6 +145,8 @@ abstract class Tinebase_Controller_Record_Abstract
      */
     protected $_duplicateCheckFields = NULL;
 
+    protected $_duplicateCheckConfig = array();
+    
     /**
      * holds new relation on update multiple
      * @var array
@@ -209,9 +218,9 @@ abstract class Tinebase_Controller_Record_Abstract
             . ' Got ' . count($result) . ' search results');
         
         if (! $_onlyIds) {
-            if ($_getRelations) {
+            if ($_getRelations && count($result) > 0 && $result->getFirstRecord()->has('relations')) {
                 // if getRelations is true, all relations should be fetched
-                if ($_getRelations === TRUE) {
+                if ($_getRelations === true) {
                     $_getRelations = NULL;
                 }
                 $result->setByIndices('relations', Tinebase_Relations::getInstance()->getMultipleRelations($this->_modelName, $this->_getBackendType(), $result->getId(), NULL, array(), FALSE, $_getRelations));
@@ -220,7 +229,7 @@ abstract class Tinebase_Controller_Record_Abstract
                 Tinebase_CustomField::getInstance()->resolveMultipleCustomfields($result);
             }
         }
-
+        
         return $result;
     }
     
@@ -285,6 +294,16 @@ abstract class Tinebase_Controller_Record_Abstract
     }
 
     /**
+     * setter for $relatedObjectsToDelete
+     *
+     * @param array $relatedObjectNames
+     */
+    public function setRelatedObjectsToDelete(array $relatedObjectNames)
+    {
+        $this->_relatedObjectsToDelete = $relatedObjectNames;
+    }
+
+    /**
      * set/get purging of record when deleting
      *
      * @param  boolean optional
@@ -488,7 +507,7 @@ abstract class Tinebase_Controller_Record_Abstract
         if ($this->resolveCustomfields()) {
             Tinebase_CustomField::getInstance()->resolveRecordCustomFields($record);
         }
-        if ($record->has('attachments') && Setup_Controller::getInstance()->isFilesystemAvailable()) {
+        if ($record->has('attachments') && Tinebase_Core::isFilesystemAvailable()) {
             Tinebase_FileSystem_RecordAttachments::getInstance()->getRecordAttachments($record);
         }
     }
@@ -661,7 +680,7 @@ abstract class Tinebase_Controller_Record_Abstract
         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .
             ' Doing duplicate check.');
 
-        $duplicates = $this->search($duplicateFilter, new Tinebase_Model_Pagination(array('limit' => 5)));
+        $duplicates = $this->search($duplicateFilter, new Tinebase_Model_Pagination(array('limit' => 5)), /* $_getRelations = */ true);
 
         if (count($duplicates) > 0) {
             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .
@@ -696,9 +715,20 @@ abstract class Tinebase_Controller_Record_Abstract
         $filters = array();
         foreach ($this->_duplicateCheckFields as $group) {
             $addFilter = array();
+            if (! is_array($group)) {
+                $group = array($group);
+            }
             foreach ($group as $field) {
                 if (! empty($_record->{$field})) {
-                    $addFilter[] = array('field' => $field, 'operator' => 'equals', 'value' => $_record->{$field});
+                    if ($field === 'relations') {
+                        $relationFilter = $this->_getRelationDuplicateFilter($_record);
+                        if ($relationFilter) {
+                            $addFilter[] = $relationFilter;
+                        }
+                    } else {
+                        $addFilter[] = array('field' => $field, 'operator' => 'equals', 'value' => $_record->{$field});
+                    }
+                    
                 } else if (isset($_record->customfields[$field])) {
                     $customFieldConfig = Tinebase_CustomField::getInstance()->getCustomFieldByNameAndApplication($this->_applicationName, $field, $this->_modelName);
                     if ($customFieldConfig) {
@@ -734,6 +764,36 @@ abstract class Tinebase_Controller_Record_Abstract
         
         return $filter;
     }
+    
+    protected function _getRelationDuplicateFilter($record)
+    {
+        $filter = null;
+        $relations = $record->relations;
+        
+        if (count($relations) === 0 || ! isset($this->_duplicateCheckConfig['relations']['filterField'])) {
+            return $filter;
+        }
+        
+        if (! $relations instanceof Tinebase_Record_RecordSet) {
+            $relations = new Tinebase_Record_RecordSet('Tinebase_Model_Relation', $relations, /* $_bypassFilters = */ true);
+        }
+        
+        // check for relation and add relation filter
+        $type = isset($this->_duplicateCheckConfig['relations']['type']) ? $this->_duplicateCheckConfig['relations']['type'] : '';
+        $relations = $relations->filter('type', $type);
+        if (count($relations) > 0) {
+            $duplicateRelation = $relations->getFirstRecord();
+            if ($duplicateRelation->related_id) {
+                $filter = array(
+                    'field' => $this->_duplicateCheckConfig['relations']['filterField'],
+                    'operator' => 'AND',
+                    'value' => array(array('field' => ':id', 'operator' => 'equals', 'value' => $duplicateRelation->related_id))
+                );
+            }
+        }
+        
+        return $filter;
+    }
 
     /**
      * inspect creation of one record (after create)
@@ -979,7 +1039,14 @@ abstract class Tinebase_Controller_Record_Abstract
         // an empty array on the relations property will remove all relations
         if ($record->has('relations') && isset($record->relations) && is_array($record->relations)) {
             $type = $this->_getBackendType();
-            Tinebase_Relations::getInstance()->setRelations($this->_modelName, $type, $updatedRecord->getId(), $record->relations, FALSE, $this->_inspectRelatedRecords);
+            Tinebase_Relations::getInstance()->setRelations(
+                $this->_modelName,
+                $type,
+                $updatedRecord->getId(),
+                $record->relations,
+                FALSE,
+                $this->_inspectRelatedRecords,
+                $this->_doRelatedCreateUpdateCheck);
         }
         if ($record->has('tags') && isset($record->tags) && (is_array($record->tags) || $record->tags instanceof Tinebase_Record_RecordSet)) {
             $updatedRecord->tags = $record->tags;
@@ -988,7 +1055,7 @@ abstract class Tinebase_Controller_Record_Abstract
         if ($record->has('alarms') && isset($record->alarms)) {
             $this->_saveAlarms($record);
         }
-        if ($record->has('attachments') && isset($record->attachments) && Setup_Controller::getInstance()->isFilesystemAvailable()) {
+        if ($record->has('attachments') && isset($record->attachments) && Tinebase_Core::isFilesystemAvailable()) {
             $updatedRecord->attachments = $record->attachments;
             Tinebase_FileSystem_RecordAttachments::getInstance()->setRecordAttachments($updatedRecord);
         }
@@ -1513,33 +1580,54 @@ abstract class Tinebase_Controller_Record_Abstract
         if ($_record->has('notes')) {
             Tinebase_Notes::getInstance()->deleteNotesOfRecord($this->_modelName, $this->_getBackendType(), $_record->getId());
         }
+        
         if ($_record->has('relations')) {
-            $relations = Tinebase_Relations::getInstance()->getRelations($this->_modelName, $this->_getBackendType(), $_record->getId());
-            if (!empty($relations)) {
-                // remove relations
-                Tinebase_Relations::getInstance()->setRelations($this->_modelName, $this->_getBackendType(), $_record->getId(), array());
-
-                // remove related objects
-                if (!empty($this->_relatedObjectsToDelete)) {
-                    foreach ($relations as $relation) {
-                        if (in_array($relation->related_model, $this->_relatedObjectsToDelete)) {
-                            list($appName, $i, $itemName) = explode('_', $relation->related_model);
-                            $appController = Tinebase_Core::getApplicationInstance($appName, $itemName);
-
-                            try {
-                                $appController->delete($relation->related_id);
-                            } catch (Exception $e) {
-                                Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Error deleting: ' . $e->getMessage());
-                            }
-                        }
-                    }
-                }
-            }
+            $this->_deleteLinkedRelations($_record);
         }
-        if ($_record->has('attachments') && Setup_Controller::getInstance()->isFilesystemAvailable()) {
+
+        if ($_record->has('attachments') && Tinebase_Core::isFilesystemAvailable()) {
             Tinebase_FileSystem_RecordAttachments::getInstance()->deleteRecordAttachments($_record);
         }
     }
+    
+    /**
+     * delete linked relations
+     * 
+     * @param Tinebase_Record_Interface $_record
+     * 
+     * TODO check if this needs to be done, as we might already deleting this "from the other side"
+     */
+    protected function _deleteLinkedRelations(Tinebase_Record_Interface $_record)
+    {
+        $relations = Tinebase_Relations::getInstance()->getRelations($this->_modelName, $this->_getBackendType(), $_record->getId());
+        if (empty($relations)) {
+            return;
+        }
+
+        // remove relations
+        Tinebase_Relations::getInstance()->setRelations($this->_modelName, $this->_getBackendType(), $_record->getId(), array());
+
+        if (empty($this->_relatedObjectsToDelete)) {
+            return;
+        }
+        
+        // remove related objects
+        Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Deleting all '
+                . implode(',', $this->_relatedObjectsToDelete) . ' relations.');
+
+        foreach ($relations as $relation) {
+            if (in_array($relation->related_model, $this->_relatedObjectsToDelete)) {
+                list($appName, $i, $itemName) = explode('_', $relation->related_model);
+                $appController = Tinebase_Core::getApplicationInstance($appName, $itemName);
+
+                try {
+                    $appController->delete($relation->related_id);
+                } catch (Exception $e) {
+                    Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Error deleting: ' . $e->getMessage());
+                }
+            }
+        }
+    }
 
     /**
      * check grant for action (CRUD)
@@ -1628,7 +1716,7 @@ abstract class Tinebase_Controller_Record_Abstract
         $aclFilters = $_filter->getAclFilters();
 
         if (! $aclFilters) {
-            if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ 
+            if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
                 . ' Force a standard containerFilter (specialNode = all) as ACL filter.');
             
             $containerFilter = $_filter->createFilter('container_id', 'specialNode', 'all', array('applicationName' => $_filter->getApplicationName()));
@@ -1871,7 +1959,7 @@ abstract class Tinebase_Controller_Record_Abstract
                 if (! $record->getId() || strlen($record->getId()) != 40) {
                     $record->{$record->getIdProperty()} = NULL;
                 }
-                $new->add($controller->create($record));
+                $new->addRecord($controller->create($record));
             }
     
             $_createdRecord->{$_property} = $new->toArray();