Tinebase / Filemanager Replication - replicate grants, xprops
authorPaul Mehrer <p.mehrer@metaways.de>
Tue, 30 May 2017 16:22:06 +0000 (18:22 +0200)
committerPaul Mehrer <p.mehrer@metaways.de>
Thu, 1 Jun 2017 07:13:53 +0000 (09:13 +0200)
replicate folder grants, folder notification settings and
folder revision settings

Change-Id: I762230ab2c2e19c680525f17322cb4191c8a6663
Reviewed-on: http://gerrit.tine20.com/customers/4780
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: Paul Mehrer <p.mehrer@metaways.de>
Tested-by: Paul Mehrer <p.mehrer@metaways.de>
tests/tine20/Filemanager/Frontend/JsonTests.php
tests/tine20/Tinebase/Timemachine/ModificationLogTest.php
tine20/Filemanager/Controller/Node.php
tine20/Tinebase/Controller/Record/Grants.php
tine20/Tinebase/FileSystem.php
tine20/Tinebase/Model/Tree/Node.php

index 478c7e0..2f9e7b5 100644 (file)
@@ -1826,8 +1826,12 @@ class Filemanager_Frontend_JsonTests extends TestCase
          * @var Tinebase_Model_Tree_Node $node
          */
         foreach($_nodes as $key => $node) {
+            $actual = $node->xprops(Tinebase_Model_Tree_Node::XPROPS_REVISION);
+            if (!empty($actual) && isset($actual[Tinebase_Model_Tree_Node::XPROPS_REVISION_NODE_ID])) {
+                unset($actual[Tinebase_Model_Tree_Node::XPROPS_REVISION_NODE_ID]);
+            }
             $expected = isset($_resultMap[$key]) ? $_resultMap[$key] : $_defaultResult;
-            static::assertTrue($expected == $node->xprops(Tinebase_Model_Tree_Node::XPROPS_REVISION), 'node revisions don\'t match for node: ' . $node->name . ' expected: ' . print_r($expected, true) . ' actual: ' . print_r($node->xprops(Tinebase_Model_Tree_Node::XPROPS_REVISION), true));
+            static::assertTrue($expected == $actual, 'node revisions don\'t match for node: ' . $node->name . ' expected: ' . print_r($expected, true) . ' actual: ' . print_r($node->xprops(Tinebase_Model_Tree_Node::XPROPS_REVISION), true));
         }
     }
 
@@ -1836,6 +1840,8 @@ class Filemanager_Frontend_JsonTests extends TestCase
         foreach($_properties as $key => $value) {
             $_node->xprops(Tinebase_Model_Tree_Node::XPROPS_REVISION)[$key] = $value;
         }
+        $_node->xprops(Tinebase_Model_Tree_Node::XPROPS_REVISION)[Tinebase_Model_Tree_Node::XPROPS_REVISION_NODE_ID] =
+            $_node->getId();
     }
 
     public function testSetRevisionSettings()
index 1b74075..805bf66 100644 (file)
@@ -641,7 +641,20 @@ class Tinebase_Timemachine_ModificationLogTest extends PHPUnit_Framework_TestCas
 
         // create two folders
         $fmController->createNodes(array($testPath, $testPath . '/subfolder'), Tinebase_Model_Tree_FileObject::TYPE_FOLDER);
-        $filesystem->stat(Tinebase_Model_Tree_Node_Path::createFromPath($fmController->addBasePath($testPath))->statpath);
+
+        // set Grants
+        $testPathNode = $filesystem->stat(Tinebase_Model_Tree_Node_Path::createFromPath($fmController->addBasePath($testPath . '/subfolder'))->statpath);
+        $testPathNode = $fmController->resolveGrants($testPathNode)->getFirstRecord();
+        $grantRecord = $testPathNode->grants->getFirstRecord();
+        $grantRecord->id = null;
+        $testPathNode->grants = new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array($grantRecord));
+        $testPathNode->acl_node = $testPathNode->getId();
+        $fmController->update($testPathNode);
+
+        // unset Grants
+        $testPathNode->acl_node = null;
+        $fmController->update($testPathNode);
+
         // move subfolder to new name
         /*$newSubFolderNode = */$fmController->moveNodes(array($testPath . '/subfolder'), array($testPath . '/newsubfolder'))->getFirstRecord();
         // copy it back to old name
@@ -685,6 +698,22 @@ class Tinebase_Timemachine_ModificationLogTest extends PHPUnit_Framework_TestCas
         $this->assertTrue($result, 'applyReplactionModLogs failed');
         $filesystem->stat(Tinebase_Model_Tree_Node_Path::createFromPath($fmController->addBasePath($testPath . '/subfolder'))->statpath);
 
+        // set grants
+        $mod = $fmModifications->getFirstRecord();
+        $fmModifications->removeRecord($mod);
+        $result = Tinebase_Timemachine_ModificationLog::getInstance()->applyReplicationModLogs(new Tinebase_Record_RecordSet('Tinebase_Model_ModificationLog', array($mod)));
+        $this->assertTrue($result, 'applyReplactionModLogs failed');
+        $testPathNode = $filesystem->stat(Tinebase_Model_Tree_Node_Path::createFromPath($fmController->addBasePath($testPath . '/subfolder'))->statpath);
+        static::assertEquals($testPathNode->getId(), $testPathNode->acl_node, 'grants not set');
+
+        // unset grants
+        $mod = $fmModifications->getFirstRecord();
+        $fmModifications->removeRecord($mod);
+        $result = Tinebase_Timemachine_ModificationLog::getInstance()->applyReplicationModLogs(new Tinebase_Record_RecordSet('Tinebase_Model_ModificationLog', array($mod)));
+        $this->assertTrue($result, 'applyReplactionModLogs failed');
+        $testPathNode = $filesystem->stat(Tinebase_Model_Tree_Node_Path::createFromPath($fmController->addBasePath($testPath . '/subfolder'))->statpath);
+        static::assertNotEquals($testPathNode->getId(), $testPathNode->acl_node, 'grants still set');
+
         // move subfolder to new name
         $mod = $fmModifications->getFirstRecord();
         $fmModifications->removeRecord($mod);
index 0980cca..e2734c1 100644 (file)
@@ -117,7 +117,7 @@ class Filemanager_Controller_Node extends Tinebase_Controller_Record_Abstract
     /**
      * inspect update of one record (before update)
      *
-     * @param   Tinebase_Record_Interface $_record      the update record
+     * @param   Filemanager_Model_Node $_record      the update record
      * @param   Tinebase_Record_Interface $_oldRecord   the current persistent record
      * @return  void
      */
@@ -125,28 +125,91 @@ class Filemanager_Controller_Node extends Tinebase_Controller_Record_Abstract
     {
         // protect against file object spoofing
         foreach (array_keys($_record->toArray()) as $property) {
-            if (! in_array($property, array('name', 'description', 'relations', 'customfields', 'tags', 'notes', 'revisionProps', 'acl_node', 'grants'))) {
+            if (! in_array($property, array('name', 'description', 'relations', 'customfields', 'tags', 'notes', 'acl_node', 'grants', Tinebase_Model_Tree_Node::XPROPS_NOTIFICATION, Tinebase_Model_Tree_Node::XPROPS_REVISION))) {
                 $_record->{$property} = $_oldRecord->{$property};
             }
         }
 
+        $nodePath = null;
+        if (Tinebase_Model_Tree_FileObject::TYPE_FOLDER === $_record->type) {
+            $nodePath = Tinebase_Model_Tree_Node_Path::createFromStatPath($this->_backend->getPathOfNode($_record->getId(), true));
+            $modlogNode = new Filemanager_Model_Node(array(
+                'id' => $_record->getId(),
+                'path' => $nodePath->statpath,
+                'type' => Tinebase_Model_Tree_FileObject::TYPE_FOLDER,
+                Tinebase_Model_Tree_Node::XPROPS_NOTIFICATION => $_record->xprops(Tinebase_Model_Tree_Node::XPROPS_NOTIFICATION),
+                // do not set acl_node, this will be calculated on the client side
+            ), true);
+            if (isset($_record->xprops(Tinebase_Model_Tree_Node::XPROPS_REVISION)[Tinebase_Model_Tree_Node::XPROPS_REVISION_NODE_ID]) &&
+                    $_record->xprops(Tinebase_Model_Tree_Node::XPROPS_REVISION)[Tinebase_Model_Tree_Node::XPROPS_REVISION_NODE_ID] === $_record->getId() &&
+                    $_record->xprops(Tinebase_Model_Tree_Node::XPROPS_REVISION) != $_oldRecord->xprops(Tinebase_Model_Tree_Node::XPROPS_REVISION)) {
+                $revisions = $_record->xprops(Tinebase_Model_Tree_Node::XPROPS_REVISION);
+                unset($revisions[Tinebase_Model_Tree_Node::XPROPS_REVISION_NODE_ID]);
+                $modlogNode->{Tinebase_Model_Tree_Node::XPROPS_REVISION} = $revisions;
+            }
+            $modlogOldNode = new Filemanager_Model_Node(array(
+                'id' => $_record->getId(),
+                'path' => $nodePath->statpath,
+                'type' => Tinebase_Model_Tree_FileObject::TYPE_FOLDER,
+                Tinebase_Model_Tree_Node::XPROPS_NOTIFICATION => $_oldRecord->xprops(Tinebase_Model_Tree_Node::XPROPS_NOTIFICATION),
+            ), true);
+            $this->_omitModLog = false;
+            $this->_writeModLog($modlogNode, $modlogOldNode);
+            $this->_omitModLog = true;
+        }
+
         // update node acl
         $aclNode = $_oldRecord->acl_node;
         if (Tinebase_Model_Tree_FileObject::TYPE_FOLDER === $_record->type
             && Tinebase_Core::getUser()->hasGrant($_record, Tinebase_Model_Grants::GRANT_ADMIN, 'Tinebase_Model_Tree_Node')
         ) {
-            $nodePath = Tinebase_Model_Tree_Node_Path::createFromStatPath($this->_backend->getPathOfNode($_record->getId(), true));
             if (! $nodePath->isSystemPath()) {
 
+                $modlogOldNode = $modlogNode = null;
                 if ($_record->acl_node === null && ! $nodePath->isToplevelPath()) {
                     // acl_node === null -> remove acl
                     $node = $this->_backend->setAclFromParent($nodePath->statpath);
                     $aclNode = $node->acl_node;
 
-                } else if ($_record->acl_node === $_record->getId()) {
-                    $this->_backend->setGrantsForNode($_record, $_record->grants);
+                    $modlogNode = new Filemanager_Model_Node(array(
+                        'id' => $_record->getId(),
+                        'path' => $nodePath->statpath,
+                        'type' => Tinebase_Model_Tree_FileObject::TYPE_FOLDER,
+                        'grants' => 'unset'
+                        // do not set acl_node, this will be calculated on the client side
+                    ), true);
+                    $modlogOldNode = new Filemanager_Model_Node(array(
+                        'id' => $_record->getId(),
+                        'type' => Tinebase_Model_Tree_FileObject::TYPE_FOLDER
+                    ), true);
+
+                } elseif ($_record->acl_node === $_record->getId() && isset($_record->grants)) {
+                    $oldGrants = Tinebase_Tree_NodeGrants::getInstance()->getGrantsForRecord($_oldRecord);
+                    if (is_array($_record->grants)) {
+                        $_record->grants = new Tinebase_Record_RecordSet('Tinebase_Model_Grants', $_record->grants);
+                    }
+                    $diff = $_record->grants->diff($oldGrants);
+                    if (!$diff->isEmpty() || $_oldRecord->acl_node !== $_record->acl_node) {
+                        $this->_backend->setGrantsForNode($_record, $_record->grants);
+                        $modlogNode = new Filemanager_Model_Node(array(
+                            'id' => $_record->getId(),
+                            'path' => $nodePath->statpath,
+                            'type' => Tinebase_Model_Tree_FileObject::TYPE_FOLDER,
+                            'grants' => $_record->grants
+                            // do not set acl_node, this will be calculated on the client side
+                        ), true);
+                        $modlogOldNode = new Filemanager_Model_Node(array(
+                            'id' => $_record->getId(),
+                            'type' => Tinebase_Model_Tree_FileObject::TYPE_FOLDER
+                        ), true);
+                    }
                     $aclNode = $_record->acl_node;
-                    // TODO only update if grants differ from old grants
+                }
+
+                if (null !== $modlogNode) {
+                    $this->_omitModLog = false;
+                    $this->_writeModLog($modlogNode, $modlogOldNode);
+                    $this->_omitModLog = true;
                 }
             }
         }
@@ -638,20 +701,27 @@ class Filemanager_Controller_Node extends Tinebase_Controller_Record_Abstract
 
         $newNodePath = $parentPathRecord->statpath . '/' . $path->name;
         $newNode = $this->_createNodeInBackend($newNodePath, $_type, $_tempFileId);
-        $this->_writeModlogForNewNode($newNode, $existingNode, $_type, $_path);
+        $this->_writeModlogForNewNode($newNode, /*$existingNode,*/ $_type, $_path);
 
         $this->resolvePath($newNode, $parentPathRecord);
         $this->resolveGrants($newNode);
         return $newNode;
     }
 
-    protected function _writeModlogForNewNode($_newNode, $_existingNode, $_type, $_path)
+    /**
+     * @param Tinebase_Model_Tree_Node $_newNode
+     * @param string $_type
+     * @param string $_path
+     */
+    protected function _writeModlogForNewNode($_newNode, /*$_existingNode,*/ $_type, $_path)
     {
         if (Tinebase_Model_Tree_FileObject::TYPE_FOLDER === $_type && false === $this->_inCopyOrMoveNode) {
             $modlogNode = new Filemanager_Model_Node(array(
                 'id' => $_newNode->getId(),
                 'path' => ($_path instanceof Tinebase_Model_Tree_Node_Path) ? $this->removeBasePath($_path->flatpath) : $_path,
-                'type' => Tinebase_Model_Tree_FileObject::TYPE_FOLDER
+                'type' => Tinebase_Model_Tree_FileObject::TYPE_FOLDER,
+                // no grants, no acl_node, it will all be handled on client side
+                //'grants' => Tinebase_Tree_NodeGrants::getInstance()->getGrantsForRecord($_newNode)
             ), true);
             $this->_omitModLog = false;
             $this->_writeModLog($modlogNode, null);
@@ -908,7 +978,8 @@ class Filemanager_Controller_Node extends Tinebase_Controller_Record_Abstract
                             'id' => $node->getId(),
                             'path' => is_array($_destinationFilenames) ? array($_destinationFilenames[$idx]) : $_destinationFilenames,
                             'type' => Tinebase_Model_Tree_FileObject::TYPE_FOLDER,
-                            'name' => $_action
+                            'name' => $_action,
+                            // do not set acl_node, this will be calculated on the client side
                         ), true);
                         $modlogOldNode = new Filemanager_Model_Node(array(
                             'id' => $node->getId(),
@@ -1402,8 +1473,15 @@ class Filemanager_Controller_Node extends Tinebase_Controller_Record_Abstract
                 $diff = new Tinebase_Record_Diff(json_decode($modification->new_value, true));
                 if (isset($diff->diff['name'])) {
                     $this->_copyOrMoveNodes(array($diff->oldData['path']), $diff->diff['path'], $diff->diff['name']);
+                } elseif(isset($diff->diff['grants'])) {
+                    $record = $this->_backend->stat($diff->diff['path']);
+                    if ('unset' === $diff->diff['grants']) {
+                        $this->_backend->removeAclFromNode($record);
+                    } else {
+                        $this->_backend->setGrantsForNode($record, $diff->diff['grants']);
+                    }
                 } else {
-                    throw new Tinebase_Exception_InvalidArgument('update modlogs need the property name containing copy or move');
+                    throw new Tinebase_Exception_InvalidArgument('update modlogs need the property name containing copy or move or grants for grants update');
                 }
                 break;
 
index e195aaf..49da163 100644 (file)
@@ -351,7 +351,7 @@ abstract class Tinebase_Controller_Record_Grants extends Tinebase_Controller_Rec
     /**
      * returns grants of record
      *
-     * @param Tinebase_Record_Abstract $record
+     * @param Tinebase_Record_Interface $record
      * @return  Tinebase_Record_RecordSet subtype Tinebase_Model_Grants
      */
     public function getGrantsForRecord($record)
index 224b170..c8e6c59 100644 (file)
@@ -1710,6 +1710,9 @@ class Tinebase_FileSystem implements
 
             $oldValue = $currentNodeObject->xprops(Tinebase_Model_Tree_Node::XPROPS_REVISION);
             $newValue = $_node->xprops(Tinebase_Model_Tree_Node::XPROPS_REVISION);
+            if (!empty($newValue) && !isset($newValue[Tinebase_Model_Tree_Node::XPROPS_REVISION_NODE_ID])) {
+                $newValue[Tinebase_Model_Tree_Node::XPROPS_REVISION_NODE_ID] = $_node->getId();
+            }
 
             if ($oldValue != $newValue) {
                 $oldValue = count($oldValue) > 0 ? json_encode($oldValue) : null;
index cacacfd..e79d629 100644 (file)
@@ -31,6 +31,7 @@
  * @property    string             acl_node
  * @property    string             revisionProps
  * @property    string             preview_count
+ * @property    Tinebase_Record_RecordSet grants
  */
 class Tinebase_Model_Tree_Node extends Tinebase_Record_Abstract
 {