0011172: optimize getGroupmemberships in Principalbackend
authorPhilipp Schüle <p.schuele@metaways.de>
Fri, 14 Aug 2015 10:02:20 +0000 (12:02 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Wed, 2 Sep 2015 12:04:42 +0000 (14:04 +0200)
* Tinebase_Container: added functionality to AND connect ACL checks
* Tinebase_WebDav_PrincipalBackend: makes use of AND connected ACL checks

https://forge.tine20.org/view.php?id=11172

Change-Id: I036276c7e718d961539a6af14ba84077d3489673
Reviewed-on: http://gerrit.tine20.com/customers/2111
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: Paul Mehrer <p.mehrer@metaways.de>
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
tine20/Tinebase/Container.php
tine20/Tinebase/WebDav/PrincipalBackend.php

index 2736966..443e812 100644 (file)
@@ -653,9 +653,10 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
      * @param  String            $_accountId
      * @param  Array|String      $_grant
      * @param  String            $_aclTableName
+     * @param  bool              $_andGrants
      * @return void
      */
-    public static function addGrantsSql($_select, $_accountId, $_grant, $_aclTableName = 'container_acl')
+    public static function addGrantsSql($_select, $_accountId, $_grant, $_aclTableName = 'container_acl', $_andGrants = FALSE, $joinCallBack = NULL)
     {
         $accountId = $_accountId instanceof Tinebase_Record_Abstract
             ? $_accountId->getId()
@@ -665,10 +666,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         
         $grants = is_array($_grant) ? $_grant : array($_grant);
         
-        // admin grant includes all other grants
-        if (! in_array(Tinebase_Model_Grants::GRANT_ADMIN, $grants)) {
-            $grants[] = Tinebase_Model_Grants::GRANT_ADMIN;
-        }
+
 
         $groupMemberships   = Tinebase_Group::getInstance()->getGroupMemberships($accountId);
         
@@ -690,12 +688,29 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
             // @todo fetch wildcard from specific db adapter
             $grants = str_replace('*', '%', $grants);
         
-            $quotedGrant   = $db->quoteIdentifier("{$_aclTableName}.account_grant");
-            
+            $quotedGrant   = $db->quoteIdentifier($_aclTableName . '.account_grant');
+
+            $iteration = 0;
             $grantsSelect = new Tinebase_Backend_Sql_Filter_GroupSelect($_select);
             foreach ($grants as $grant) {
-                $grantsSelect->orWhere("{$quotedGrant} LIKE ?", $grant);
+                if ($_andGrants) {
+                    if ($iteration > 0) {
+                        $callbackIdentifier = call_user_func($joinCallBack, $_select, $iteration);
+                        $grantsSelect->where($db->quoteIdentifier($callbackIdentifier . '.account_grant') . ' LIKE ?', $grant);
+                    } else {
+                        $grantsSelect->where($quotedGrant . ' LIKE ?', $grant);
+                    }
+                    ++$iteration;
+                } else {
+                    $grantsSelect->orWhere($quotedGrant . ' LIKE ?', $grant);
+                }
             }
+
+            // admin grant includes all other grants
+            if (! in_array(Tinebase_Model_Grants::GRANT_ADMIN, $grants)) {
+                $grantsSelect->orWhere($quotedGrant . ' LIKE ?', Tinebase_Model_Grants::GRANT_ADMIN);
+            }
+
             $grantsSelect->appendWhere(Zend_Db_Select::SQL_AND);
         }
     }
@@ -767,10 +782,11 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
      * @param   string|Tinebase_Model_Application   $recordClass
      * @param   array|string                        $_grant
      * @param   bool                                $_ignoreACL
+     * @param   bool                                $_andGrants
      * @return  Tinebase_Record_RecordSet set of Tinebase_Model_Container
      * @throws  Tinebase_Exception_NotFound
      */
-    public function getSharedContainer($_accountId, $recordClass, $_grant, $_ignoreACL = FALSE)
+    public function getSharedContainer($_accountId, $recordClass, $_grant, $_ignoreACL = FALSE, $_andGrants = FALSE)
     {
         // legacy handling
         $meta = $this->_resolveRecordClassArgument($recordClass);
@@ -782,7 +798,8 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
             $accountId .
             $application->getId() .
             implode('', (array)$grant) .
-            (int)$_ignoreACL
+            (int)$_ignoreACL .
+            (int)$_andGrants
         );
         
         try {
@@ -804,7 +821,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
             
             ->order('container.name');
         
-        $this->addGrantsSql($select, $accountId, $grant);
+        $this->addGrantsSql($select, $accountId, $grant, 'container_acl', $_andGrants, __CLASS__ . '::addGrantsSqlCallback');
         
         $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
         
@@ -826,19 +843,38 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
      * @param   string|Tinebase_Model_Application   $recordClass
      * @param   array|string                        $_grant
      * @param   bool                                $_ignoreACL
+     * @param   bool                                $_andGrants
      * @return  Tinebase_Record_RecordSet set of Tinebase_Model_User
      */
-    public function getOtherUsers($_accountId, $recordClass, $_grant, $_ignoreACL = FALSE)
+    public function getOtherUsers($_accountId, $recordClass, $_grant, $_ignoreACL = FALSE, $_andGrants = FALSE)
     {
         $meta = $this->_resolveRecordClassArgument($recordClass);
-        $userIds = $this->_getOtherAccountIds($_accountId, $meta['appName'], $_grant, $_ignoreACL);
+        $userIds = $this->_getOtherAccountIds($_accountId, $meta['appName'], $_grant, $_ignoreACL, $_andGrants);
         
         $users = Tinebase_User::getInstance()->getMultiple($userIds);
         $users->sort('accountDisplayName');
         
         return $users;
     }
-    
+
+    /**
+     * appends container_acl sql
+     *
+     * @param  Zend_Db_Select    $_select
+     * @param  integer           $iteration
+     * @return string table identifier to work on
+     */
+    public static function addGrantsSqlCallback($_select, $iteration)
+    {
+        $db = $_select->getAdapter();
+        $_select->join(array(
+            /* table  */ 'container_acl' . $iteration => SQL_TABLE_PREFIX . 'container_acl'),
+            /* on     */ $db->quoteIdentifier('container_acl' . $iteration . '.container_id') . ' = ' . $db->quoteIdentifier('container.id'),
+            array()
+        );
+        return 'container_acl' . $iteration;
+    }
+
     /**
      * return account ids of accounts which made personal container accessible to given account
      *
@@ -846,15 +882,16 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
      * @param   string|Tinebase_Model_Application   $_application
      * @param   array|string                        $_grant
      * @param   bool                                $_ignoreACL
+     * @param   bool                                $_andGrants
      * @return  array of array of containerData
      */
-    protected function _getOtherAccountIds($_accountId, $_application, $_grant, $_ignoreACL = FALSE)
+    protected function _getOtherAccountIds($_accountId, $_application, $_grant, $_ignoreACL = FALSE, $_andGrants = FALSE)
     {
         $accountId   = Tinebase_Model_User::convertUserIdToInt($_accountId);
         $application = Tinebase_Application::getInstance()->getApplicationByName($_application);
         $grant       = $_ignoreACL ? '*' : $_grant;
 
-        $classCacheId = Tinebase_Helper::convertCacheId($accountId . $application->getId() . implode('', (array)$grant) .(int)$_ignoreACL);
+        $classCacheId = Tinebase_Helper::convertCacheId($accountId . $application->getId() . implode('', (array)$grant) . (int)$_ignoreACL . (int)$_andGrants);
         try {
             return $this->loadFromClassCache(__FUNCTION__, $classCacheId);
         } catch (Tinebase_Exception_NotFound $tenf) {
@@ -873,9 +910,9 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
             ->where("{$this->_db->quoteIdentifier('container.application_id')} = ?", $application->getId())
             ->where("{$this->_db->quoteIdentifier('container.type')} = ?", Tinebase_Model_Container::TYPE_PERSONAL)
             ->where("{$this->_db->quoteIdentifier('container.is_deleted')} = ?", 0, Zend_Db::INT_TYPE);
-            
-        $this->addGrantsSql($select, $accountId, $grant);
-        
+
+        $this->addGrantsSql($select, $accountId, $grant, 'container_acl', $_andGrants, __CLASS__ . '::addGrantsSqlCallback');
+
         $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
         $containerIds = $stmt->fetchAll(Zend_Db::FETCH_COLUMN);
         
index a4cdb01..08a259e 100644 (file)
@@ -361,32 +361,19 @@ class Tinebase_WebDav_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend
                     }
                     
                     if (Tinebase_Core::getUser()->hasRight('Calendar', Tinebase_Acl_Rights::RUN)) {
-                        // return user only, if the containers have the sync AND read grant set
-                        $otherUsersSync = Tinebase_Container::getInstance()->getOtherUsers($user, 'Calendar', Tinebase_Model_Grants::GRANT_SYNC);
-                        
-                        if ($otherUsersSync->count() > 0) {
-                            $otherUsersRead = Tinebase_Container::getInstance()->getOtherUsers($user, 'Calendar', Tinebase_Model_Grants::GRANT_READ);
-                            
-                            $otherUsersIds = array_intersect($otherUsersSync->getArrayOfIds(), $otherUsersRead->getArrayOfIds());
-                            
-                            foreach ($otherUsersIds as $userId) {
-                                if ($otherUsersSync->getById($userId)->contact_id && $otherUsersSync->getById($userId)->visibility == Tinebase_Model_User::VISIBILITY_DISPLAYED) {
-                                    $result[] = self::PREFIX_USERS . '/' . $otherUsersSync->getById($userId)->contact_id . '/calendar-proxy-write';
-                                }
+                        // return users only, if they have the sync AND read grant set
+                        $otherUsers = Tinebase_Container::getInstance()->getOtherUsers($user, 'Calendar', array(Tinebase_Model_Grants::GRANT_SYNC, Tinebase_Model_Grants::GRANT_READ), false, true);
+                        foreach ($otherUsers as $u) {
+                            if ($u->contact_id && $u->visibility == Tinebase_Model_User::VISIBILITY_DISPLAYED) {
+                                $result[] = self::PREFIX_USERS . '/' . $u->contact_id . '/calendar-proxy-write';
                             }
                         }
                         
-                        // return user only, if the containers have the sync AND read grant set
-                        $sharedContainersSync = Tinebase_Container::getInstance()->getSharedContainer($user, 'Calendar', Tinebase_Model_Grants::GRANT_SYNC);
+                        // return containers only, if the user has the sync AND read grant set
+                        $sharedContainers = Tinebase_Container::getInstance()->getSharedContainer($user, 'Calendar', array(Tinebase_Model_Grants::GRANT_SYNC, Tinebase_Model_Grants::GRANT_READ), false, true);
                         
-                        if ($sharedContainersSync->count() > 0) {
-                            $sharedContainersRead = Tinebase_Container::getInstance()->getSharedContainer($user, 'Calendar', Tinebase_Model_Grants::GRANT_READ);
-                            
-                            $sharedContainerIds = array_intersect($sharedContainersSync->getArrayOfIds(), $sharedContainersRead->getArrayOfIds());
-                            
-                            if (count($sharedContainerIds) > 0) {
-                                $result[] = self::PREFIX_USERS . '/' . self::SHARED . '/calendar-proxy-write';
-                            }
+                        if ($sharedContainers->count() > 0) {
+                            $result[] = self::PREFIX_USERS . '/' . self::SHARED . '/calendar-proxy-write';
                         }
                     }
                     Tinebase_Cache_PerRequest::getInstance()->save(__CLASS__, __FUNCTION__, $classCacheId, $result);