]> source.dussan.org Git - nextcloud-server.git/commitdiff
feat(comments): Add a meta data column for comments
authorJoas Schilling <coding@schilljs.com>
Wed, 13 Dec 2023 12:29:12 +0000 (13:29 +0100)
committerJoas Schilling <coding@schilljs.com>
Thu, 14 Dec 2023 13:32:00 +0000 (14:32 +0100)
Signed-off-by: Joas Schilling <coding@schilljs.com>
apps/dav/tests/unit/Comments/CommentsNodeTest.php
core/Migrations/Version29000Date20231213104850.php [new file with mode: 0644]
lib/composer/composer/autoload_classmap.php
lib/composer/composer/autoload_static.php
lib/private/Comments/Comment.php
lib/private/Comments/Manager.php
lib/public/Comments/IComment.php
tests/lib/Comments/CommentTest.php
tests/lib/Comments/ManagerTest.php
version.php

index 15c207eafbd89524af590d5f6b370ca8b82f2c3f..f9a6e872b8055d622c3eb5720d75702e6421e699 100644 (file)
@@ -405,6 +405,11 @@ class CommentsNodeTest extends \Test\TestCase {
                        $ns . 'referenceId' => 'ref',
                        $ns . 'isUnread' => null,
                        $ns . 'reactions' => [],
+                       $ns . 'metaData' => [
+                               'last_edited_at' => 1702553770,
+                               'last_edited_by_id' => 'charly',
+                               'last_edited_by_type' => 'user',
+                       ],
                        $ns . 'expireDate' => new \DateTime('2016-01-12 19:00:00'),
                ];
 
@@ -475,6 +480,10 @@ class CommentsNodeTest extends \Test\TestCase {
                        ->method('getReferenceId')
                        ->willReturn($expected[$ns . 'referenceId']);
 
+               $this->comment->expects($this->once())
+                       ->method('getMetaData')
+                       ->willReturn($expected[$ns . 'metaData']);
+
                $this->comment->expects($this->once())
                        ->method('getExpireDate')
                        ->willReturn($expected[$ns . 'expireDate']);
@@ -494,7 +503,7 @@ class CommentsNodeTest extends \Test\TestCase {
                $properties = $this->node->getProperties(null);
 
                foreach ($properties as $name => $value) {
-                       $this->assertArrayHasKey($name, $expected);
+                       $this->assertArrayHasKey($name, $expected, 'Key not found in the list of $expected');
                        $this->assertSame($expected[$name], $value);
                        unset($expected[$name]);
                }
diff --git a/core/Migrations/Version29000Date20231213104850.php b/core/Migrations/Version29000Date20231213104850.php
new file mode 100644 (file)
index 0000000..0b9ffe9
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Core\Migrations;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version29000Date20231213104850 extends SimpleMigrationStep {
+       /**
+        * @param IOutput $output
+        * @param Closure(): ISchemaWrapper $schemaClosure
+        * @param array $options
+        * @return null|ISchemaWrapper
+        */
+       public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+               /** @var ISchemaWrapper $schema */
+               $schema = $schemaClosure();
+
+               $table = $schema->getTable('comments');
+               $modified = false;
+
+               if (!$table->hasColumn('reference_id')) {
+                       $modified = true;
+                       $table->addColumn('reference_id', Types::STRING, [
+                               'notnull' => false,
+                               'length' => 64,
+                       ]);
+               }
+
+               if (!$table->hasColumn('meta_data')) {
+                       $modified = true;
+                       $table->addColumn('meta_data', Types::TEXT, [
+                               'notnull' => false,
+                               'default' => '',
+                       ]);
+               }
+
+               return $modified ? $schema : null;
+       }
+}
index c34742a864a08d3666f70526e24abbec6f0e55bb..bca214e32896b2a388b816079bb23a5e0c94599c 100644 (file)
@@ -1230,6 +1230,7 @@ return array(
     'OC\\Core\\Migrations\\Version28000Date20230906104802' => $baseDir . '/core/Migrations/Version28000Date20230906104802.php',
     'OC\\Core\\Migrations\\Version28000Date20231004103301' => $baseDir . '/core/Migrations/Version28000Date20231004103301.php',
     'OC\\Core\\Migrations\\Version28000Date20231103104802' => $baseDir . '/core/Migrations/Version28000Date20231103104802.php',
+    'OC\\Core\\Migrations\\Version29000Date20231213104850' => $baseDir . '/core/Migrations/Version29000Date20231213104850.php',
     'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
     'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',
     'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php',
index 743c5f752ce63e097771db69a090d2cc9d34f174..df74352d3cfe9500b84ab24b71062a2c9b8b6ace 100644 (file)
@@ -1263,6 +1263,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
         'OC\\Core\\Migrations\\Version28000Date20230906104802' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20230906104802.php',
         'OC\\Core\\Migrations\\Version28000Date20231004103301' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20231004103301.php',
         'OC\\Core\\Migrations\\Version28000Date20231103104802' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20231103104802.php',
+        'OC\\Core\\Migrations\\Version29000Date20231213104850' => __DIR__ . '/../../..' . '/core/Migrations/Version29000Date20231213104850.php',
         'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
         'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',
         'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php',
index 35e88c74438a31b0ab8ad07bfd4c1abcfc05064b..183821e37b1ee65697ddeae394004d1893070112 100644 (file)
@@ -42,6 +42,7 @@ class Comment implements IComment {
                'objectType' => '',
                'objectId' => '',
                'referenceId' => null,
+               'metaData' => null,
                'creationDT' => null,
                'latestChildDT' => null,
                'reactions' => null,
@@ -400,6 +401,34 @@ class Comment implements IComment {
                return $this;
        }
 
+       /**
+        * @inheritDoc
+        */
+       public function getMetaData(): ?array {
+               if ($this->data['metaData'] === null) {
+                       return null;
+               }
+
+               try {
+                       $metaData = json_decode($this->data['metaData'], true, flags: JSON_THROW_ON_ERROR);
+               } catch (\JsonException $e) {
+                       return null;
+               }
+               return is_array($metaData) ? $metaData : null;
+       }
+
+       /**
+        * @inheritDoc
+        */
+       public function setMetaData(?array $metaData):  IComment {
+               if ($metaData === null) {
+                       $this->data['metaData'] = null;
+               } else {
+                       $this->data['metaData'] = json_encode($metaData, JSON_THROW_ON_ERROR);
+               }
+               return $this;
+       }
+
        /**
         * @inheritDoc
         */
index e92cfddc60b473fd5cbae64d4e36874575eae1fc..85b56f9f25cf17db09fc4df485c4bb6dd13ed933 100644 (file)
@@ -29,7 +29,6 @@
 namespace OC\Comments;
 
 use Doctrine\DBAL\Exception\DriverException;
-use Doctrine\DBAL\Exception\InvalidFieldNameException;
 use OCP\AppFramework\Utility\ITimeFactory;
 use OCP\Comments\CommentsEvent;
 use OCP\Comments\IComment;
@@ -97,7 +96,8 @@ class Manager implements ICommentsManager {
                        $data['expire_date'] = new \DateTime($data['expire_date']);
                }
                $data['children_count'] = (int)$data['children_count'];
-               $data['reference_id'] = $data['reference_id'] ?? null;
+               $data['reference_id'] = $data['reference_id'];
+               $data['meta_data'] = json_decode($data['meta_data'], true);
                if ($this->supportReactions()) {
                        if ($data['reactions'] !== null) {
                                $list = json_decode($data['reactions'], true);
@@ -1150,22 +1150,6 @@ class Manager implements ICommentsManager {
         * @return bool
         */
        protected function insert(IComment $comment): bool {
-               try {
-                       $result = $this->insertQuery($comment, true);
-               } catch (InvalidFieldNameException $e) {
-                       // The reference id field was only added in Nextcloud 19.
-                       // In order to not cause too long waiting times on the update,
-                       // it was decided to only add it lazy, as it is also not a critical
-                       // feature, but only helps to have a better experience while commenting.
-                       // So in case the reference_id field is missing,
-                       // we simply save the comment without that field.
-                       $result = $this->insertQuery($comment, false);
-               }
-
-               return $result;
-       }
-
-       protected function insertQuery(IComment $comment, bool $tryWritingReferenceId): bool {
                $qb = $this->dbConn->getQueryBuilder();
 
                $values = [
@@ -1181,12 +1165,10 @@ class Manager implements ICommentsManager {
                        'object_type' => $qb->createNamedParameter($comment->getObjectType()),
                        'object_id' => $qb->createNamedParameter($comment->getObjectId()),
                        'expire_date' => $qb->createNamedParameter($comment->getExpireDate(), 'datetime'),
+                       'reference_id' => $qb->createNamedParameter($comment->getReferenceId()),
+                       'meta_data' => $qb->createNamedParameter(json_encode($comment->getMetaData())),
                ];
 
-               if ($tryWritingReferenceId) {
-                       $values['reference_id'] = $qb->createNamedParameter($comment->getReferenceId());
-               }
-
                $affectedRows = $qb->insert('comments')
                        ->values($values)
                        ->execute();
@@ -1289,12 +1271,7 @@ class Manager implements ICommentsManager {
                $this->sendEvent(CommentsEvent::EVENT_PRE_UPDATE, $this->get($comment->getId()));
                $this->uncache($comment->getId());
 
-               try {
-                       $result = $this->updateQuery($comment, true);
-               } catch (InvalidFieldNameException $e) {
-                       // See function insert() for explanation
-                       $result = $this->updateQuery($comment, false);
-               }
+               $result = $this->updateQuery($comment);
 
                if ($comment->getVerb() === 'reaction_deleted') {
                        $this->deleteReaction($comment);
@@ -1305,7 +1282,7 @@ class Manager implements ICommentsManager {
                return $result;
        }
 
-       protected function updateQuery(IComment $comment, bool $tryWritingReferenceId): bool {
+       protected function updateQuery(IComment $comment): bool {
                $qb = $this->dbConn->getQueryBuilder();
                $qb
                        ->update('comments')
@@ -1320,14 +1297,11 @@ class Manager implements ICommentsManager {
                        ->set('latest_child_timestamp', $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'))
                        ->set('object_type', $qb->createNamedParameter($comment->getObjectType()))
                        ->set('object_id', $qb->createNamedParameter($comment->getObjectId()))
-                       ->set('expire_date', $qb->createNamedParameter($comment->getExpireDate(), 'datetime'));
-
-               if ($tryWritingReferenceId) {
-                       $qb->set('reference_id', $qb->createNamedParameter($comment->getReferenceId()));
-               }
-
-               $affectedRows = $qb->where($qb->expr()->eq('id', $qb->createNamedParameter($comment->getId())))
-                       ->execute();
+                       ->set('expire_date', $qb->createNamedParameter($comment->getExpireDate(), 'datetime'))
+                       ->set('reference_id', $qb->createNamedParameter($comment->getReferenceId()))
+                       ->set('meta_data', $qb->createNamedParameter(json_encode($comment->getMetaData())))
+                       ->where($qb->expr()->eq('id', $qb->createNamedParameter($comment->getId())));
+               $affectedRows = $qb->executeStatement();
 
                if ($affectedRows === 0) {
                        throw new NotFoundException('Comment to update does ceased to exist');
index eb696fa5f06b4fb0a761cb423e9f2eea5ad5a181..0a0f1b1b251d886182084f63ddafcd99d90aff91 100644 (file)
@@ -279,6 +279,25 @@ interface IComment {
         */
        public function setReferenceId(?string $referenceId): IComment;
 
+       /**
+        * Returns the metadata of the comment
+        *
+        * @return array|null
+        * @since 29.0.0
+        */
+       public function getMetaData(): ?array;
+
+       /**
+        * Sets (overwrites) the metadata of the comment
+        * Data as a json encoded array
+        *
+        * @param array|null $metaData
+        * @return IComment
+        * @throws \JsonException When the metadata can not be converted to a json encoded string
+        * @since 29.0.0
+        */
+       public function setMetaData(?array $metaData): IComment;
+
        /**
         * Returns the reactions array if exists
         *
index 328e6fd5447dec6e700ee88e9ec8487d1d76fc06..a46df75897ca2fa137317971f61aee84e8cb6461 100644 (file)
@@ -23,6 +23,8 @@ class CommentTest extends TestCase {
                $creationDT = new \DateTime();
                $latestChildDT = new \DateTime('yesterday');
                $object = ['type' => 'files', 'id' => 'file64'];
+               $referenceId = sha1('referenceId');
+               $metaData = ['last_edit_actor_id' => 'admin'];
 
                $comment
                        ->setId($id)
@@ -34,7 +36,9 @@ class CommentTest extends TestCase {
                        ->setActor($actor['type'], $actor['id'])
                        ->setCreationDateTime($creationDT)
                        ->setLatestChildDateTime($latestChildDT)
-                       ->setObject($object['type'], $object['id']);
+                       ->setObject($object['type'], $object['id'])
+                       ->setReferenceId($referenceId)
+                       ->setMetaData($metaData);
 
                $this->assertSame($id, $comment->getId());
                $this->assertSame($parentId, $comment->getParentId());
@@ -48,6 +52,8 @@ class CommentTest extends TestCase {
                $this->assertSame($latestChildDT, $comment->getLatestChildDateTime());
                $this->assertSame($object['type'], $comment->getObjectType());
                $this->assertSame($object['id'], $comment->getObjectId());
+               $this->assertSame($referenceId, $comment->getReferenceId());
+               $this->assertSame($metaData, $comment->getMetaData());
        }
 
 
index 5fa1beee3742e7d15a84017d9aae66e3dce88d99..3ad7a67b2963448d3de23f06285b4f45a0a2cf81 100644 (file)
@@ -65,6 +65,8 @@ class ManagerTest extends TestCase {
                                'object_type' => $qb->createNamedParameter('files'),
                                'object_id' => $qb->createNamedParameter($objectId),
                                'expire_date' => $qb->createNamedParameter($expireDate, 'datetime'),
+                               'reference_id' => $qb->createNamedParameter('referenceId'),
+                               'meta_data' => $qb->createNamedParameter(json_encode(['last_edit_actor_id' => 'admin'])),
                        ])
                        ->execute();
 
@@ -119,6 +121,8 @@ class ManagerTest extends TestCase {
                                'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, 'datetime'),
                                'object_type' => $qb->createNamedParameter('files'),
                                'object_id' => $qb->createNamedParameter('file64'),
+                               'reference_id' => $qb->createNamedParameter('referenceId'),
+                               'meta_data' => $qb->createNamedParameter(json_encode(['last_edit_actor_id' => 'admin'])),
                        ])
                        ->execute();
 
@@ -138,6 +142,8 @@ class ManagerTest extends TestCase {
                $this->assertSame($comment->getObjectId(), 'file64');
                $this->assertEquals($comment->getCreationDateTime()->getTimestamp(), $creationDT->getTimestamp());
                $this->assertEquals($comment->getLatestChildDateTime(), $latestChildDT);
+               $this->assertEquals($comment->getReferenceId(), 'referenceId');
+               $this->assertEquals($comment->getMetaData(), ['last_edit_actor_id' => 'admin']);
        }
 
 
index df6eb7ba390879f297bcd2fb2acb82733203afca..b4b6ff2845ee6ceb428b14d0c61b757ac87d23a9 100644 (file)
@@ -30,7 +30,7 @@
 // between betas, final and RCs. This is _not_ the public version number. Reset minor/patch level
 // when updating major/minor version number.
 
-$OC_Version = [29, 0, 0, 2];
+$OC_Version = [29, 0, 0, 3];
 
 // The human-readable string
 $OC_VersionString = '29.0.0 dev';