Merge branch 'tine20.com/2012.10' into 2013.03
[tine20] / tine20 / Calendar / Backend / Sql.php
index b841356..34263e8 100644 (file)
@@ -15,6 +15,8 @@
  * Events consists of the properties of Calendar_Model_Event except Tags and Notes 
  * which are as always handles by their controllers/backends
  * 
+ * @TODO rework fetch handling. all fetch operations should be based on search.
+ *       remove old grant sql when done
  * 
  * @package     Calendar 
  * @subpackage  Backend
@@ -82,8 +84,6 @@ class Calendar_Backend_Sql extends Tinebase_Backend_Sql_Abstract
      * @return  Tinebase_Record_Interface
      * @throws  Tinebase_Exception_InvalidArgument
      * @throws  Tinebase_Exception_UnexpectedValue
-     * 
-     * @todo    remove autoincremental ids later
      */
     public function create(Tinebase_Record_Interface $_record) 
     {
@@ -118,9 +118,6 @@ class Calendar_Backend_Sql extends Tinebase_Backend_Sql_Abstract
         $filter = new Tinebase_Model_Filter_Text($_property, 'equals', $_value);
         $filters->addFilter($filter);
         
-        // for get operations we need to take all attendee into account
-        $filters->addFilter($filters->createFilter('attender', 'specialNode', 'all'));
-        
         $resultSet = $this->search($filters, NULL, FALSE, $_getDeleted);
         
         switch (count($resultSet)) {
@@ -139,6 +136,13 @@ class Calendar_Backend_Sql extends Tinebase_Backend_Sql_Abstract
     
     /**
      * Calendar optimized search function
+     * 
+     * 1. get all events neglecting grants filter
+     * 2. get all related container grants (via resolveing)
+     * 3. compute effective grants in PHP and only keep events 
+     *    user has required grant for
+     * 
+     * @TODO rethink if an outer container filter could help
      *
      * @param  Tinebase_Model_Filter_FilterGroup    $_filter
      * @param  Tinebase_Model_Pagination            $_pagination
@@ -152,101 +156,111 @@ class Calendar_Backend_Sql extends Tinebase_Backend_Sql_Abstract
             $_pagination = new Tinebase_Model_Pagination();
         }
         
-        // we use a an extra select to reduce data amount where grants etc. have to be computed for.
-        // the exdate is already appended here, to reduce virtual row numbers later
-        $subselect = $this->_getSelectSimple('id', $_getDeleted);
+        $select = parent::_getSelect('*', $_getDeleted);
         
-        $subselect->joinLeft(
+        $select->joinLeft(
             /* table  */ array('exdate' => $this->_tablePrefix . 'cal_exdate'),
             /* on     */ $this->_db->quoteIdentifier('exdate.cal_event_id') . ' = ' . $this->_db->quoteIdentifier($this->_tableName . '.id'),
             /* select */ array('exdate' => $this->_dbCommand->getAggregate('exdate.exdate')));
         
-        // this attendee join has nothing to do with grants but is here for attendee/status/... filters
-        $subselect->joinLeft(
+        $select->joinLeft(
             /* table  */ array('attendee' => $this->_tablePrefix . 'cal_attendee'),
             /* on     */ $this->_db->quoteIdentifier('attendee.cal_event_id') . ' = ' . $this->_db->quoteIdentifier('cal_events.id'),
             /* select */ array());
         
         if (! $_getDeleted) {
-            $subselect->joinLeft(
+            $select->joinLeft(
                 /* table  */ array('dispcontainer' => $this->_tablePrefix . 'container'), 
                 /* on     */ $this->_db->quoteIdentifier('dispcontainer.id') . ' = ' . $this->_db->quoteIdentifier('attendee.displaycontainer_id'),
                 /* select */ array());
             
-            $subselect->where($this->_db->quoteIdentifier('dispcontainer.is_deleted') . ' = 0 OR ' . $this->_db->quoteIdentifier('dispcontainer.is_deleted') . 'IS NULL');
+            $select->where($this->_db->quoteIdentifier('dispcontainer.is_deleted') . ' = 0 OR ' . $this->_db->quoteIdentifier('dispcontainer.is_deleted') . 'IS NULL');
         }
         
-        // remove grantsfilter here as we need it in the main select
+        // remove grantsfilter here as we do grants computation in PHP
         $grantsFilter = $_filter->getFilter('grants');
         if ($grantsFilter) {
             $_filter->removeFilter('grants');
         }
         
-        $this->_addFilter($subselect, $_filter);
-        $_pagination->appendPaginationSql($subselect);
-        $subselect->group($this->_tableName . '.' . 'id');
-
-        Tinebase_Backend_Sql_Abstract::traitGroup($subselect);
-        
-        $stmt = $this->_db->query($subselect);
-        $rows = (array)$stmt->fetchAll(Zend_Db::FETCH_ASSOC);
-        $ids = array();
-        $exdates = array();
-        foreach($rows as $row) {
-            $ids[] = $row['id'];
-            $exdates[$row['id']] = $row['exdate'];
-        }
-
-        $select = $this->_getSelectSimple('*', $_getDeleted);
-        $select->where($this->_db->quoteInto("{$this->_db->quoteIdentifier('cal_events.id')} IN (?)", !empty($ids) ? $ids : ' ' ));
+        $this->_addFilter($select, $_filter);
         $_pagination->appendPaginationSql($select);
         
-        // append grants filters : only take limited set of attendee into account for grants computation
-        $attenderFilter = $_filter->getFilter('attender', TRUE, TRUE);
-        if (! $attenderFilter) {
-            // if a container filter is set, take owners of personal containers (solve secretary scenario)
-            $containerFilters = $_filter->getFilter('container_id', TRUE, TRUE);
-            if ($containerFilters) {
-                foreach ($containerFilters as $containerFilter) {
-                     if ($containerFilter instanceof Calendar_Model_CalendarFilter) {
-                         $attenderFilter[] = $containerFilter->getRelatedAttendeeFilter();
-                     }
-                }
-            } else {
-                $attenderFilter = array(new Calendar_Model_AttenderFilter('attender', 'equals', array(
-                   'user_type' => Calendar_Model_Attender::USERTYPE_USER,
-                   'user_id'   =>  Tinebase_Core::getUser()->contact_id
-                )));
-            }
-            
-        }
-        $this->_appendEffectiveGrantCalculationSql($select, $attenderFilter);
-        if ($grantsFilter) {
-            $grantsFilter->appendFilterSql($select, $this);
-        }
         $select->group($this->_tableName . '.' . 'id');
-        
         Tinebase_Backend_Sql_Abstract::traitGroup($select);
         
-        $stmt = null; // solve PHP bug @see {http://bugs.php.net/bug.php?id=35793}
         $stmt = $this->_db->query($select);
         $rows = (array)$stmt->fetchAll(Zend_Db::FETCH_ASSOC);
-        
-        if ($_onlyIds) {
-            $identifier = is_bool($_onlyIds) ? $this->_getRecordIdentifier() : $_onlyIds;
-            $result = array();
-            foreach ($rows as $row) {
-                $result[] = $row[$identifier];
-            }
-        } else {
-            foreach ($rows as &$row) {
-                $row['exdate'] = $exdates[$row[$this->_getRecordIdentifier()]];
+        $result = $this->_rawDataToRecordSet($rows);
+        $clones = clone $result;
+        
+        Tinebase_Container::getInstance()->getGrantsOfRecords($clones, Tinebase_Core::getUser());
+        Calendar_Model_Attender::resolveAttendee($clones->attendee, TRUE, $clones);
+        
+        $me = Tinebase_Core::getUser()->contact_id;
+        $inheritableGrants = array(
+            Tinebase_Model_Grants::GRANT_FREEBUSY,
+            Tinebase_Model_Grants::GRANT_READ, 
+            Tinebase_Model_Grants::GRANT_SYNC, 
+            Tinebase_Model_Grants::GRANT_EXPORT, 
+            Tinebase_Model_Grants::GRANT_PRIVATE,
+        );
+        $toRemove = array();
+        
+        foreach($result as $event) {
+            $clone = $clones->getById($event->getId());
+            if ($event->organizer == $me) {
+                foreach($this->_recordBasedGrants as $grant) {
+                    $event->{$grant}     = TRUE;
+                }
+            } else {
+                // grants to original container
+                if ($clone->container_id instanceof Tinebase_Model_Container && $clone->container_id->account_grants) {
+                    foreach($this->_recordBasedGrants as $grant) {
+                        $event->{$grant} =     $clone->container_id->account_grants[$grant] 
+                                            || $clone->container_id->account_grants[Tinebase_Model_Grants::GRANT_ADMIN];
+                    }
+                }
+                
+                // check grant inheritance
+                foreach($inheritableGrants as $grant) {
+                    if (! $event->{$grant} && $clone->attendee instanceof Tinebase_Record_RecordSet) {
+                        foreach($clone->attendee as $attendee) {
+                            if (   $attendee->displaycontainer_id instanceof Tinebase_Model_Container
+                                && $attendee->displaycontainer_id->account_grants 
+                                && (    $attendee->displaycontainer_id->account_grants[$grant]
+                                     || $attendee->displaycontainer_id->account_grants[Tinebase_Model_Grants::GRANT_ADMIN]
+                                   )
+                            ){
+                                $event->{$grant} = TRUE;
+                                break;
+                            }
+                        }
+                    }
+                }
+                
+                if ($grantsFilter) {
+                    $requiredGrants = array_intersect($grantsFilter->getRequiredGrants(), $this->_recordBasedGrants);
+                    
+                    $hasGrant = FALSE;
+                    foreach($requiredGrants as $requiredGrant) {
+                        if ($event->{$requiredGrant}) {
+                            $hasGrant |= $event->{$requiredGrant};
+                        }
+                    }
+                    
+                    if (! $hasGrant) {
+                        $toRemove[] = $event;
+                    }
+                }
             }
-
-            $result = $this->_rawDataToRecordSet($rows);
         }
         
-        return $result;
+        foreach($toRemove as $event) {
+            $result->removeRecord($event);
+        }
+        
+        return $_onlyIds ? $result->{is_bool($_onlyIds) ? $this->_getRecordIdentifier() : $_onlyIds} : $result;
     }
     
     /**
@@ -298,6 +312,11 @@ class Calendar_Backend_Sql extends Tinebase_Backend_Sql_Abstract
         if ($_record->rrule) {
             $_record->rrule = (string) $_record->rrule;
         }
+        
+        if ($_record->container_id instanceof Tinebase_Model_Container) {
+            $_record->container_id = $_record->container_id->getId();
+        }
+        
         $_record->rrule   = !empty($_record->rrule)   ? $_record->rrule   : NULL;
         $_record->recurid = !empty($_record->recurid) ? $_record->recurid : NULL;