diff --git a/appinfo/info.xml b/appinfo/info.xml
index 2e534010c..96abbdf62 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -20,7 +20,7 @@
- 🚀 Get your project organized
- 4.0.0-dev.1
+ 4.0.0-dev.2
agpl
Julius Härtl
Deck
diff --git a/lib/Db/Acl.php b/lib/Db/Acl.php
index 735f9a19f..52aba43b1 100644
--- a/lib/Db/Acl.php
+++ b/lib/Db/Acl.php
@@ -21,6 +21,10 @@
* @method void setOwner(int $owner)
* @method void setToken(string $token)
* @method string getToken()
+ * @method int getCreatedAt()
+ * @method void setCreatedAt(int $createdAt)
+ * @method int getLastModifiedAt()
+ * @method void setLastModifiedAt(int $lastModifiedAt)
*
*/
class Acl extends RelationalEntity {
@@ -42,6 +46,8 @@ class Acl extends RelationalEntity {
protected $permissionManage = false;
protected $owner = false;
protected $token = null;
+ protected $createdAt = 0;
+ protected $lastModifiedAt = 0;
public function __construct() {
$this->addType('id', 'integer');
@@ -52,6 +58,8 @@ public function __construct() {
$this->addType('type', 'integer');
$this->addType('owner', 'boolean');
$this->addType('token', 'string');
+ $this->addType('createdAt', 'integer');
+ $this->addType('lastModifiedAt', 'integer');
$this->addRelation('owner');
$this->addResolvable('participant');
}
diff --git a/lib/Db/AclMapper.php b/lib/Db/AclMapper.php
index 669ca7a5e..defba19d1 100644
--- a/lib/Db/AclMapper.php
+++ b/lib/Db/AclMapper.php
@@ -8,6 +8,7 @@
namespace OCA\Deck\Db;
use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
@@ -20,7 +21,7 @@ public function __construct(IDBConnection $db) {
public function findByAccessToken(string $accessToken) {
$qb = $this->db->getQueryBuilder();
- $qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage', 'token')
+ $qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage', 'token', 'created_at', 'last_modified_at')
->from('deck_board_acl')
->where($qb->expr()->eq('token', $qb->createNamedParameter($accessToken, IQueryBuilder::PARAM_STR)))
->setMaxResults(1);
@@ -34,7 +35,7 @@ public function findByAccessToken(string $accessToken) {
*/
public function findAll(int $boardId, ?int $limit = null, ?int $offset = null) {
$qb = $this->db->getQueryBuilder();
- $qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage', 'token')
+ $qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage', 'token', 'created_at', 'last_modified_at')
->from('deck_board_acl')
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
->setMaxResults($limit)
@@ -45,7 +46,7 @@ public function findAll(int $boardId, ?int $limit = null, ?int $offset = null) {
public function findIn(array $boardIds, ?int $limit = null, ?int $offset = null): array {
$qb = $this->db->getQueryBuilder();
- $qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage')
+ $qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage', 'created_at', 'last_modified_at')
->from('deck_board_acl')
->where($qb->expr()->in('board_id', $qb->createParameter('boardIds')))
->setMaxResults($limit)
@@ -127,4 +128,18 @@ public function findByType(int $type): array {
->where($qb->expr()->eq('type', $qb->createNamedParameter($type, IQueryBuilder::PARAM_INT)));
return $this->findEntities($qb);
}
+
+ public function insert(Entity $entity): Entity {
+ /** @var Acl $entity */
+ $now = time();
+ $entity->setCreatedAt($now);
+ $entity->setLastModifiedAt($now);
+ return parent::insert($entity);
+ }
+
+ public function update(Entity $entity): Entity {
+ /** @var Acl $entity */
+ $entity->setLastModifiedAt(time());
+ return parent::update($entity);
+ }
}
diff --git a/lib/Migration/Version11002Date20260611000000.php b/lib/Migration/Version11002Date20260611000000.php
new file mode 100644
index 000000000..79b1cd601
--- /dev/null
+++ b/lib/Migration/Version11002Date20260611000000.php
@@ -0,0 +1,41 @@
+getTable('deck_board_acl');
+
+ if (!$table->hasColumn('created_at')) {
+ $table->addColumn('created_at', 'integer', [
+ 'notnull' => true,
+ 'default' => 0,
+ 'unsigned' => true,
+ ]);
+ }
+
+ if (!$table->hasColumn('last_modified_at')) {
+ $table->addColumn('last_modified_at', 'integer', [
+ 'notnull' => true,
+ 'default' => 0,
+ 'unsigned' => true,
+ ]);
+ }
+
+ return $schema;
+ }
+}
diff --git a/lib/Service/BoardService.php b/lib/Service/BoardService.php
index 44bc4e29b..1610352ae 100644
--- a/lib/Service/BoardService.php
+++ b/lib/Service/BoardService.php
@@ -400,6 +400,9 @@ public function addAcl(int $boardId, int $type, $participant, bool $edit, bool $
$acl->setPermissionEdit($edit);
$acl->setPermissionShare($share);
$acl->setPermissionManage($manage);
+ $now = time();
+ $acl->setCreatedAt($now);
+ $acl->setLastModifiedAt($now);
$newAcl = $this->aclMapper->insert($acl);
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $newAcl, ActivityManager::SUBJECT_BOARD_SHARE, [], $this->userId);
@@ -451,6 +454,7 @@ public function updateAcl(int $id, bool $edit, bool $share, bool $manage): Acl {
$acl->setPermissionEdit($edit);
$acl->setPermissionShare($share);
$acl->setPermissionManage($manage);
+ $acl->setLastModifiedAt(time());
$this->boardMapper->mapAcl($acl);
$acl = $this->aclMapper->update($acl);
$this->changeHelper->boardChanged($acl->getBoardId());
diff --git a/tests/integration/base-query-count.txt b/tests/integration/base-query-count.txt
index ce8c60422..627806590 100644
--- a/tests/integration/base-query-count.txt
+++ b/tests/integration/base-query-count.txt
@@ -1 +1 @@
-93102
+96706
\ No newline at end of file
diff --git a/tests/integration/import/ImportExportTest.php b/tests/integration/import/ImportExportTest.php
index a9e00e0cb..586c11c3a 100644
--- a/tests/integration/import/ImportExportTest.php
+++ b/tests/integration/import/ImportExportTest.php
@@ -134,7 +134,7 @@ public function testReimportOcc() {
);
}
- public static function writeArrayStructure(string $prefix = '', array $array = [], array $skipKeyList = ['id', 'boardId', 'cardId', 'stackId', 'ETag', 'permissions', 'shared', 'version', 'done', 'referenceData', 'token']): string {
+ public static function writeArrayStructure(string $prefix = '', array $array = [], array $skipKeyList = ['id', 'boardId', 'cardId', 'stackId', 'ETag', 'permissions', 'shared', 'version', 'done', 'referenceData', 'token', 'createdAt', 'lastModifiedAt']): string {
$output = '';
$arrayIsList = array_keys($array) === range(0, count($array) - 1);
foreach ($array as $key => $value) {
diff --git a/tests/unit/Db/AclMapperTest.php b/tests/unit/Db/AclMapperTest.php
index af3f03196..0154ee62b 100644
--- a/tests/unit/Db/AclMapperTest.php
+++ b/tests/unit/Db/AclMapperTest.php
@@ -120,6 +120,34 @@ public function testFindBoardIdDatabase() {
$this->assertEquals($this->boards[0]->getId(), $this->aclMapper->findBoardId($this->acls[1]->getId()));
}
+ public function testInsertSetsCreatedAtAndLastModifiedAt(): void {
+ $before = time();
+ $acl = $this->getAcl('user', 'timestamps_user', false, false, false, $this->boards[0]->getId());
+ $inserted = $this->aclMapper->insert($acl);
+ $after = time();
+
+ $this->assertGreaterThanOrEqual($before, $inserted->getCreatedAt());
+ $this->assertLessThanOrEqual($after, $inserted->getCreatedAt());
+ $this->assertGreaterThanOrEqual($before, $inserted->getLastModifiedAt());
+ $this->assertLessThanOrEqual($after, $inserted->getLastModifiedAt());
+
+ $this->aclMapper->delete($inserted);
+ }
+
+ public function testUpdateChangesLastModifiedAtButNotCreatedAt(): void {
+ $acl = $this->getAcl('user', 'timestamps_user2', false, false, false, $this->boards[0]->getId());
+ $inserted = $this->aclMapper->insert($acl);
+ $originalCreatedAt = $inserted->getCreatedAt();
+
+ $inserted->setPermissionEdit(true);
+ $updated = $this->aclMapper->update($inserted);
+
+ $this->assertSame($originalCreatedAt, $updated->getCreatedAt());
+ $this->assertGreaterThan(0, $updated->getLastModifiedAt());
+
+ $this->aclMapper->delete($updated);
+ }
+
public function tearDown(): void {
parent::tearDown();
foreach ($this->acls as $acl) {
diff --git a/tests/unit/Db/AclTest.php b/tests/unit/Db/AclTest.php
index e1538215e..069cb98dd 100644
--- a/tests/unit/Db/AclTest.php
+++ b/tests/unit/Db/AclTest.php
@@ -59,7 +59,9 @@ public function testJsonSerialize() {
'permissionEdit' => true,
'permissionShare' => true,
'permissionManage' => true,
- 'owner' => false
+ 'owner' => false,
+ 'createdAt' => 0,
+ 'lastModifiedAt' => 0,
], $acl->jsonSerialize());
$acl = $this->createAclGroup();
$this->assertEquals([
@@ -70,7 +72,9 @@ public function testJsonSerialize() {
'permissionEdit' => true,
'permissionShare' => true,
'permissionManage' => true,
- 'owner' => false
+ 'owner' => false,
+ 'createdAt' => 0,
+ 'lastModifiedAt' => 0,
], $acl->jsonSerialize());
}
@@ -85,7 +89,9 @@ public function testSetOwner() {
'permissionEdit' => true,
'permissionShare' => true,
'permissionManage' => true,
- 'owner' => true
+ 'owner' => true,
+ 'createdAt' => 0,
+ 'lastModifiedAt' => 0,
], $acl->jsonSerialize());
}
diff --git a/tests/unit/Service/BoardServiceTest.php b/tests/unit/Service/BoardServiceTest.php
index 549021bf0..e28a133a7 100644
--- a/tests/unit/Service/BoardServiceTest.php
+++ b/tests/unit/Service/BoardServiceTest.php
@@ -301,7 +301,16 @@ public function testAddAcl() {
->method('sendBoardShared');
$this->aclMapper->expects($this->once())
->method('insert')
- ->with($acl)
+ ->with($this->callback(function (Acl $actual) use ($acl) {
+ return $actual->getBoardId() === $acl->getBoardId()
+ && $actual->getType() === $acl->getType()
+ && $actual->getParticipant() === $acl->getParticipant()
+ && $actual->getPermissionEdit() === $acl->getPermissionEdit()
+ && $actual->getPermissionShare() === $acl->getPermissionShare()
+ && $actual->getPermissionManage() === $acl->getPermissionManage()
+ && $actual->getCreatedAt() > 0
+ && $actual->getLastModifiedAt() > 0;
+ }))
->willReturn($acl);
$this->permissionService->expects($this->any())
->method('findUsers')
@@ -407,11 +416,30 @@ public function testAddAclExtendPermission($currentUserAcl, $providedAcl, $resul
$expected = clone $acl;
$this->aclMapper->expects($this->once())
->method('insert')
- ->with($acl)
+ ->with($this->callback(function (Acl $actual) use ($acl) {
+ return $actual->getBoardId() === $acl->getBoardId()
+ && $actual->getType() === $acl->getType()
+ && $actual->getParticipant() === $acl->getParticipant()
+ && $actual->getPermissionEdit() === $acl->getPermissionEdit()
+ && $actual->getPermissionShare() === $acl->getPermissionShare()
+ && $actual->getPermissionManage() === $acl->getPermissionManage()
+ && $actual->getCreatedAt() > 0
+ && $actual->getLastModifiedAt() > 0;
+ }))
->willReturn($acl);
$this->eventDispatcher->expects(self::once())
->method('dispatchTyped')
- ->with(new AclCreatedEvent($acl));
+ ->with($this->callback(function (AclCreatedEvent $event) use ($acl) {
+ $eventAcl = $event->getAcl();
+ return $eventAcl->getBoardId() === $acl->getBoardId()
+ && $eventAcl->getType() === $acl->getType()
+ && $eventAcl->getParticipant() === $acl->getParticipant()
+ && $eventAcl->getPermissionEdit() === $acl->getPermissionEdit()
+ && $eventAcl->getPermissionShare() === $acl->getPermissionShare()
+ && $eventAcl->getPermissionManage() === $acl->getPermissionManage()
+ && $eventAcl->getCreatedAt() > 0
+ && $eventAcl->getLastModifiedAt() > 0;
+ }));
$this->assertEquals($expected, $this->service->addAcl(
123, Acl::PERMISSION_TYPE_USER, 'admin', $providedAcl[0], $providedAcl[1], $providedAcl[2]
));