Tinebase_Filesystem - implement quota check on folder size increase
authorPaul Mehrer <p.mehrer@metaways.de>
Mon, 10 Jul 2017 16:55:34 +0000 (18:55 +0200)
committerPaul Mehrer <p.mehrer@metaways.de>
Tue, 11 Jul 2017 07:44:17 +0000 (09:44 +0200)
Change-Id: Ia35348c7469e9867972cfc53774f451da0d34478
Reviewed-on: http://gerrit.tine20.com/customers/5084
Reviewed-by: Paul Mehrer <p.mehrer@metaways.de>
Tested-by: Paul Mehrer <p.mehrer@metaways.de>
tine20/Tinebase/FileSystem.php
tine20/Tinebase/Model/Application.php
tine20/Tinebase/Timemachine/ModificationLog.php

index 94d0841..a3d6c41 100644 (file)
@@ -700,10 +700,63 @@ class Tinebase_FileSystem implements
      */
     protected function _updateFolderSizesUpToRoot(Tinebase_Record_RecordSet $_nodes, $_sizeDiff, $_revisionSizeDiff)
     {
-        $objectIds = $this->_getTreeNodeBackend()->getAllFolderNodes($_nodes)->object_id;
-        if (!empty($objectIds)) {
+        $sizeIncrease = $_sizeDiff > 0 || $_revisionSizeDiff > 0;
+
+        $quotaConfig = Tinebase_Config::getInstance()->{Tinebase_Config::QUOTA};
+
+        $applicationController = Tinebase_Application::getInstance();
+        /** @var Tinebase_Model_Application $tinebaseApplication */
+        $tinebaseApplication = $applicationController->getApplicationByName('Tinebase');
+        $tinebaseState = $tinebaseApplication->xprops('state');
+        if (!isset($tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_SIZE])) {
+            $tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_SIZE] = 0;
+        }
+        if (!isset($tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_REVISION_SIZE])) {
+            $tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_REVISION_SIZE] = 0;
+        }
+
+        $tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_SIZE] += $_sizeDiff;
+        $tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_REVISION_SIZE] += $_revisionSizeDiff;
+
+        if ($tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_SIZE] < 0) {
+            if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
+                . ' root size should not become smaller than 0: ' .
+                $tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_SIZE]);
+            $tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_SIZE] = 0;
+        }
+        if ($tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_REVISION_SIZE] < 0) {
+            if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
+                . ' root revision size should not become smaller than 0: ' .
+                $tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_REVISION_SIZE]);
+            $tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_REVISION_SIZE] = 0;
+        }
+
+        if (true === $sizeIncrease && $quotaConfig->{Tinebase_Config::QUOTA_TOTALINMB} > 0) {
+            $total = $quotaConfig->{Tinebase_Config::QUOTA_TOTALINMB} * 1024 * 1024;
+            if ($quotaConfig->{Tinebase_Config::QUOTA_INCLUDE_REVISION}) {
+                if ($tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_REVISION_SIZE] > $total) {
+                    throw new Tinebase_Exception_Record_NotAllowed('quota exceeded');
+                }
+            } else {
+                if ($tinebaseState[Tinebase_Model_Application::STATE_FILESYSTEM_ROOT_SIZE] > $total) {
+                    throw new Tinebase_Exception_Record_NotAllowed('quota exceeded');
+                }
+            }
+        }
+        $applicationController->updateApplication($tinebaseApplication);
+
+        $folderNodes = $this->_getTreeNodeBackend()->getAllFolderNodes($_nodes);
+        if ($folderNodes->count() > 0) {
+
+            $userController = Tinebase_User::getInstance();
+            $totalByUser = $quotaConfig->{Tinebase_Config::QUOTA_TOTALBYUSERINMB} * 1024 * 1024;
+            $personalNode = null;
+            if (true === $sizeIncrease) {
+                $personalNode = $this->stat('/Filemanager/folders/personal');
+            }
+
             /** @var Tinebase_Model_Tree_FileObject $fileObject */
-            foreach($this->_fileObjectBackend->getMultiple($objectIds) as $fileObject) {
+            foreach($this->_fileObjectBackend->getMultiple($folderNodes->object_id) as $fileObject) {
                 $fileObject->size = (int)$fileObject->size + (int)$_sizeDiff;
                 if ($fileObject->size < 0) {
                     if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
@@ -716,6 +769,35 @@ class Tinebase_FileSystem implements
                         . ' revision_size should not become smaller than 0: ' . $fileObject->size . ' for object id: ' . $fileObject->getId());
                     $fileObject->revision_size = 0;
                 }
+
+                if (true === $sizeIncrease) {
+                    /** @var Tinebase_Model_Tree_Node $folderNode */
+                    foreach ($folderNodes->filter('object_id', $fileObject->getId()) as $folderNode) {
+                        if ($quotaConfig->{Tinebase_Config::QUOTA_INCLUDE_REVISION}) {
+                            $size = $fileObject->size;
+                        } else {
+                            $size = $fileObject->revision_size;
+                        }
+
+                        // folder quota
+                        if (null !== $folderNode->quota && $size > $folderNode->quota) {
+                            throw new Tinebase_Exception_Record_NotAllowed('quota exceeded');
+                        }
+
+                        //personal quota
+                        if ($folderNode->parent_id === $personalNode->getId()) {
+                            $user = $userController->getFullUserById($folderNode->name);
+                            $quota = isset(
+                                $user->xprops('configuration')[Tinebase_Model_FullUser::CONFIGURATION_PERSONAL_QUOTA]) ?
+                                $user->xprops('configuration')[Tinebase_Model_FullUser::CONFIGURATION_PERSONAL_QUOTA] :
+                                $totalByUser;
+                            if ($quota > 0 && $size > $quota) {
+                                throw new Tinebase_Exception_Record_NotAllowed('quota exceeded');
+                            }
+                        }
+                    }
+                }
+
                 $this->_fileObjectBackend->update($fileObject, false);
             }
         }
index bde69d2..60ec144 100644 (file)
@@ -5,7 +5,7 @@
  * @package     Tinebase
  * @subpackage  Record
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
- * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2017 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Lars Kneschke <l.kneschke@metaways.de>
  */
 
@@ -29,6 +29,8 @@ class Tinebase_Model_Application extends Tinebase_Record_Abstract
      * @var string
      */
     const STATE_REPLICATION_MASTER_ID = 'replicationMasterId';
+    const STATE_FILESYSTEM_ROOT_SIZE = 'filesystemRootSize';
+    const STATE_FILESYSTEM_ROOT_REVISION_SIZE = 'filesystemRootRevisionSize';
 
     /**
      * holds the configuration object (must be declared in the concrete class)
index 8be5ee5..9f5a4d9 100644 (file)
@@ -1303,9 +1303,6 @@ class Tinebase_Timemachine_ModificationLog implements Tinebase_Controller_Interf
         $applicationController = Tinebase_Application::getInstance();
         /** @var Tinebase_Model_Application $tinebaseApplication */
         $tinebaseApplication = $applicationController->getApplicationByName('Tinebase');
-        if (!is_array($tinebaseApplication->state)) {
-            $tinebaseApplication->state = array();
-        }
 
         /** @var Tinebase_Model_ModificationLog $modification */
         foreach($modifications as $modification)
@@ -1331,9 +1328,8 @@ class Tinebase_Timemachine_ModificationLog implements Tinebase_Controller_Interf
                     static::defaultApply($modification, $controller);
                 }
 
-                $state = $tinebaseApplication->state;
-                $state[Tinebase_Model_Application::STATE_REPLICATION_MASTER_ID] = $modification->instance_seq;
-                $tinebaseApplication->state = $state;
+                $tinebaseApplication->xprops['state'][Tinebase_Model_Application::STATE_REPLICATION_MASTER_ID] =
+                    $modification->instance_seq;
                 $tinebaseApplication = $applicationController->updateApplication($tinebaseApplication);
 
                 $transactionManager->commitTransaction($transactionId);