diff --git a/composer.json b/composer.json index f3a47cde2..131a08bff 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "require": { "php": ">=7.4", "bedita/i18n": "^4.4.3", - "bedita/web-tools": "^3.10.1", + "bedita/web-tools": "^4.0.2", "cakephp/authentication": "^2.9", "cakephp/cakephp": "~4.5.0", "cakephp/plugin-installer": "^1.3", diff --git a/src/Controller/Model/TagsController.php b/src/Controller/Model/TagsController.php index 3ecb2b532..3fb077c13 100644 --- a/src/Controller/Model/TagsController.php +++ b/src/Controller/Model/TagsController.php @@ -54,12 +54,15 @@ public function initialize(): void public function index(): ?Response { $this->getRequest()->allowMethod(['get']); - $options = $this->getRequest()->getQueryParams(); + $params = $this->getRequest()->getQueryParams(); + $options = $params; $options['page_size'] = empty($options['page_size']) ? 20 : $options['page_size']; $options['sort'] = empty($options['sort']) ? 'name' : $options['sort']; $response = ApiClientProvider::getApiClient()->get('/model/tags', $options); $resources = Hash::combine((array)Hash::get($response, 'data'), '{n}.id', '{n}'); - CacheTools::setModuleCount((array)$response, 'tags'); + if (empty($params['q']) && empty($params['filter'])) { + CacheTools::setModuleCount((array)$response, 'tags'); + } $roots = $this->Categories->getAvailableRoots($resources); $tagsTree = $this->Categories->tree($resources); $this->set(compact('resources', 'roots', 'tagsTree')); diff --git a/src/Controller/ModulesController.php b/src/Controller/ModulesController.php index db7e342c5..68f776f4f 100644 --- a/src/Controller/ModulesController.php +++ b/src/Controller/ModulesController.php @@ -103,8 +103,11 @@ public function index(): ?Response } try { - $response = $this->apiClient->getObjects($this->objectType, $this->Query->index()); - CacheTools::setModuleCount((array)$response, $this->Modules->getConfig('currentModuleName')); + $params = $this->Query->index(); + $response = $this->apiClient->getObjects($this->objectType, $params); + if (empty($params['q']) && empty($params['filter'])) { + CacheTools::setModuleCount((array)$response, $this->Modules->getConfig('currentModuleName')); + } } catch (BEditaClientException $e) { $this->log($e->getMessage(), LogLevel::ERROR); $this->Flash->error($e->getMessage(), ['params' => $e]); @@ -379,28 +382,25 @@ public function clone($id): ?Response public function delete(): ?Response { $this->getRequest()->allowMethod(['post']); - $ids = []; - if (!empty($this->getRequest()->getData('ids'))) { - if (is_string($this->getRequest()->getData('ids'))) { - $ids = explode(',', (string)$this->getRequest()->getData('ids')); - } - } elseif (!empty($this->getRequest()->getData('id'))) { - $ids = [$this->getRequest()->getData('id')]; - } - foreach ($ids as $id) { - try { - $this->apiClient->deleteObject($id, $this->objectType); + $id = $this->getRequest()->getData('id'); + $ids = $this->getRequest()->getData('ids'); + $ids = is_string($ids) ? explode(',', $ids) : $ids; + $ids = empty($ids) ? [$id] : $ids; + try { + $this->apiClient->deleteObjects($ids, $this->objectType); + $eventManager = $this->getEventManager(); + foreach ($ids as $id) { $event = new Event('Controller.afterDelete', $this, ['id' => $id, 'type' => $this->objectType]); - $this->getEventManager()->dispatch($event); - } catch (BEditaClientException $e) { - $this->log($e->getMessage(), LogLevel::ERROR); - $this->Flash->error($e->getMessage(), ['params' => $e]); - if (!empty($this->getRequest()->getData('id'))) { - return $this->redirect(['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $this->getRequest()->getData('id')]); - } - - return $this->redirect(['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $id]); + $eventManager->dispatch($event); } + } catch (BEditaClientException $e) { + $this->log($e->getMessage(), LogLevel::ERROR); + $this->Flash->error($e->getMessage(), ['params' => $e]); + if (!empty($this->getRequest()->getData('id'))) { + return $this->redirect(['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $this->getRequest()->getData('id')]); + } + + return $this->redirect($this->referer()); } $this->Flash->success(__('Object(s) deleted')); diff --git a/src/Controller/TrashController.php b/src/Controller/TrashController.php index c7128ad9a..75bf46321 100644 --- a/src/Controller/TrashController.php +++ b/src/Controller/TrashController.php @@ -63,8 +63,11 @@ public function index(): ?Response $this->getRequest()->allowMethod(['get']); try { - $response = $this->apiClient->getObjects('trash', $this->getRequest()->getQueryParams()); - CacheTools::setModuleCount($response, 'trash'); + $params = $this->getRequest()->getQueryParams(); + $response = $this->apiClient->getObjects('trash', $params); + if (empty($params['q']) && empty($params['filter'])) { + CacheTools::setModuleCount($response, 'trash'); + } } catch (BEditaClientException $e) { // Error! Back to dashboard. $this->log($e->getMessage(), LogLevel::ERROR); @@ -127,29 +130,16 @@ public function view($id): ?Response public function restore(): ?Response { $this->getRequest()->allowMethod(['post']); - $ids = []; - if (!empty($this->getRequest()->getData('ids'))) { - $ids = $this->getRequest()->getData('ids'); - if (is_string($ids)) { - $ids = explode(',', (string)$this->getRequest()->getData('ids')); - } - } else { - $ids = [$this->getRequest()->getData('id')]; - } - foreach ($ids as $id) { - try { - $this->apiClient->restoreObject($id, 'objects'); - } catch (BEditaClientException $e) { - // Error! Back to object view. - $this->log($e->getMessage(), LogLevel::ERROR); - $this->Flash->error($e->getMessage(), ['params' => $e]); - - if (!empty($this->getRequest()->getData('ids'))) { - return $this->redirect(['_name' => 'trash:list'] + $this->listQuery()); - } - - return $this->redirect(['_name' => 'trash:view', 'id' => $id]); - } + $id = $this->getRequest()->getData('id'); + $ids = $this->getRequest()->getData('ids'); + $ids = is_string($ids) ? explode(',', $ids) : $ids; + $ids = empty($ids) ? [$id] : $ids; + try { + $this->apiClient->restoreObjects($ids); + } catch (BEditaClientException $e) { + // Error! Back to object view. + $this->log($e->getMessage(), LogLevel::ERROR); + $this->Flash->error($e->getMessage(), ['params' => $e]); } return $this->redirect(['_name' => 'trash:list'] + $this->listQuery()); @@ -163,33 +153,14 @@ public function restore(): ?Response public function delete(): ?Response { $this->getRequest()->allowMethod(['post']); - $ids = []; - if (!empty($this->getRequest()->getData('ids'))) { - $ids = $this->getRequest()->getData('ids'); - if (is_string($ids)) { - $ids = explode(',', (string)$this->getRequest()->getData('ids')); - } - } else { - $ids = [$this->getRequest()->getData('id')]; - } - foreach ($ids as $id) { - try { - $this->deleteData($id); - } catch (BEditaClientException $e) { - // Error! Back to object view. - $this->log($e->getMessage(), LogLevel::ERROR); - $this->Flash->error($e->getMessage(), ['params' => $e]); - - if (!empty($this->getRequest()->getData('ids'))) { - return $this->redirect(['_name' => 'trash:list'] + $this->listQuery()); - } - - return $this->redirect(['_name' => 'trash:view', 'id' => $id]); - } + $id = $this->getRequest()->getData('id'); + $ids = $this->getRequest()->getData('ids'); + $ids = is_string($ids) ? explode(',', $ids) : $ids; + $ids = empty($ids) ? [$id] : $ids; + if ($this->deleteMulti($ids)) { + $this->Flash->success(__('Object(s) deleted from trash')); } - $this->Flash->success(__('Object(s) deleted from trash')); - return $this->redirect(['_name' => 'trash:list'] + $this->listQuery()); } @@ -224,18 +195,11 @@ public function emptyTrash(): ?Response $response = $this->apiClient->getObjects('trash', $query); $counter = 0; while (Hash::get($response, 'meta.pagination.count', 0) > 0) { - foreach ($response['data'] as $data) { - try { - $this->deleteData($data['id']); - $counter++; - } catch (BEditaClientException $e) { - // Error! Back to trash index. - $this->log($e->getMessage(), LogLevel::ERROR); - $this->Flash->error($e->getMessage(), ['params' => $e]); - - return $this->redirect(['_name' => 'trash:list'] + $this->listQuery()); - } + $ids = Hash::extract($response, 'data.{n}.id'); + if (!$this->deleteMulti($ids)) { + return $this->redirect(['_name' => 'trash:list'] + $this->listQuery()); } + $counter += count($ids); $response = $this->apiClient->getObjects('trash', $query); } $this->Flash->success(__(sprintf('%d objects deleted from trash', $counter))); @@ -255,12 +219,50 @@ public function deleteData(string $id): void $this->apiClient->remove($id); // this for BE versions < 5.25.1, where streams are not deleted with media on delete $streams = (array)Hash::get($response, 'data'); - foreach ($streams as $stream) { - $search = $this->apiClient->get('/streams', ['filter' => ['uuid' => $stream['id']]]); - $count = (int)Hash::get($search, 'meta.pagination.count', 0); - if ($count === 1) { - $this->apiClient->delete(sprintf('/streams/%s', $stream['id'])); - } + $this->removeStreams($streams); + } + + /** + * Delete multiple data and related streams, if any. + * + * @param array $ids Object IDs + * @return bool + */ + public function deleteMulti(array $ids): bool + { + try { + $response = $this->apiClient->get('/streams', ['filter' => ['object_id' => $ids]]); + $this->apiClient->removeObjects($ids); + $streams = (array)Hash::get($response, 'data'); + $this->removeStreams($streams); + } catch (BEditaClientException $e) { + // Error! Back to object view. + $this->log($e->getMessage(), LogLevel::ERROR); + $this->Flash->error($e->getMessage(), ['params' => $e]); + + return false; + } + + return true; + } + + /** + * Remove streams + * + * @param array $streams The streams to remove + * @return void + */ + public function removeStreams(array $streams): void + { + // this for BE versions < 5.25.1, where streams are not deleted with media on delete + $uuids = (array)Hash::extract($streams, '{n}.id'); + if (empty($uuids)) { + return; + } + $search = $this->apiClient->get('/streams', ['filter' => ['uuid' => $uuids]]); + $search = (array)Hash::get($search, 'data'); + foreach ($search as $stream) { + $this->apiClient->delete(sprintf('/streams/%s', $stream['id'])); } } } diff --git a/src/Utility/PermissionsTrait.php b/src/Utility/PermissionsTrait.php index 93af4d6be..6f845114a 100644 --- a/src/Utility/PermissionsTrait.php +++ b/src/Utility/PermissionsTrait.php @@ -75,9 +75,7 @@ public function addPermissions(string $objectId, array $roleIds): void */ public function removePermissions(array $objectPermissionIds): void { - foreach ($objectPermissionIds as $id) { - ApiClientProvider::getApiClient()->deleteObject($id, 'object_permissions'); - } + ApiClientProvider::getApiClient()->deleteObjects($objectPermissionIds, 'object_permissions'); } /** diff --git a/tests/TestCase/Controller/AppControllerTest.php b/tests/TestCase/Controller/AppControllerTest.php index 86ab55f2e..6e4d83b4d 100644 --- a/tests/TestCase/Controller/AppControllerTest.php +++ b/tests/TestCase/Controller/AppControllerTest.php @@ -195,11 +195,9 @@ public function testBeforeFilterCorrectTokens(): void /** @var \Authentication\Identity|null $user */ $user = $this->AppController->Authentication->getIdentity(); $expectedtokens = $user->get('tokens'); - - $event = $this->AppController->dispatchEvent('Controller.initialize'); - + $this->AppController->dispatchEvent('Controller.initialize'); $apiClient = $this->accessProperty($this->AppController, 'apiClient'); - $apiClientTokens = $this->accessProperty($apiClient, 'tokens'); + $apiClientTokens = $apiClient->getTokens(); static::assertEquals($expectedtokens, $apiClientTokens); } diff --git a/tests/TestCase/Controller/TrashControllerTest.php b/tests/TestCase/Controller/TrashControllerTest.php index ef29a5f2e..ba7ebdcad 100644 --- a/tests/TestCase/Controller/TrashControllerTest.php +++ b/tests/TestCase/Controller/TrashControllerTest.php @@ -179,11 +179,12 @@ public function testRestoreMultiFailure(): void } /** - * Test `delete` and `deleteData` methods + * Test `delete` method * * @return void * @covers ::delete() - * @covers ::deleteData() + * @covers ::deleteMulti() + * @covers ::removeStreams() */ public function testDelete(): void { @@ -206,12 +207,78 @@ public function testDelete(): void } /** - * Test `delete` and `deleteData` methods with media object + * Test `deleteData` method. For coverage and retrocompatibility only. * * @return void - * @covers ::delete() * @covers ::deleteData() */ + public function testDeleteData(): void + { + $id = $this->setupControllerAndData(); + $this->Trash->deleteData((string)$id); + + try { + $this->client->getObject($id); + } catch (BEditaClientException $e) { + $expected = new BEditaClientException('Not Found', 404); + static::assertEquals($expected->getCode(), $e->getCode()); + } + + try { + $this->client->get(sprintf('/trash/%d', $id)); + } catch (BEditaClientException $e) { + $expected = new BEditaClientException('Not Found', 404); + static::assertEquals($expected->getCode(), $e->getCode()); + } + } + + /** + * Test `deleteMulti` method. + * + * @return void + * @covers ::deleteMulti() + */ + public function testDeleteMulti(): void + { + $id = $this->setupControllerAndData(true, false, true); + $actual = $this->Trash->deleteMulti([$id]); + static::assertTrue($actual); + + try { + $this->client->getObject($id); + } catch (BEditaClientException $e) { + $expected = new BEditaClientException('Not Found', 404); + static::assertEquals($expected->getCode(), $e->getCode()); + } + + try { + $this->client->get(sprintf('/trash/%d', $id)); + } catch (BEditaClientException $e) { + $expected = new BEditaClientException('Not Found', 404); + static::assertEquals($expected->getCode(), $e->getCode()); + } + } + + /** + * Test `deleteMulti` method with exception + * + * @return void + * @covers ::deleteMulti() + */ + public function testDeleteMultiException(): void + { + $this->setupControllerAndData(true, false, true); + $actual = $this->Trash->deleteMulti(['abc']); + static::assertFalse($actual); + } + + /** + * Test `delete` method with media object + * + * @return void + * @covers ::delete() + * @covers ::removeStreams() + */ public function testDeleteMediaWithStream(): void { // setup and auth @@ -278,16 +345,15 @@ public function testDeleteUnauthorized(): void } /** - * Test `restore` method with multiple items + * Test `delete` method passing ids in POST data * * @return void * @covers ::delete() */ - public function testDeleteMulti(): void + public function testDeleteByIds(): void { $id = $this->setupControllerAndData(true, false, true); $this->Trash->delete(); - $expected = new BEditaClientException('Not Found', 404); static::expectException(get_class($expected)); static::expectExceptionCode($expected->getCode()); @@ -300,7 +366,7 @@ public function testDeleteMulti(): void * @return void * @covers ::delete() */ - public function testDeleteMultiFailure(): void + public function testDeleteByIdsFailure(): void { $id = $this->setupControllerAndData(true, false, true); $this->client->remove($id); @@ -309,7 +375,13 @@ public function testDeleteMultiFailure(): void static::assertEquals('/trash', $response->getHeaderLine('Location')); $message = $this->Trash->getRequest()->getSession()->read('Flash.flash.0.message'); - static::assertEquals('[404] Not Found', $message); + $beditaApiVersion = (string)Hash::get((array)$this->client->get('/home'), 'meta.version'); + $apiMajor = substr($beditaApiVersion, 0, strpos($beditaApiVersion, '.')); + if ($apiMajor === '4') { + static::assertEquals('[404] Not Found', $message); + } else { + static::assertEquals('Object(s) deleted from trash', $message); + } } /** @@ -318,6 +390,8 @@ public function testDeleteMultiFailure(): void * @return void * @covers ::emptyTrash() * @covers ::listQuery() + * @covers ::deleteMulti() + * @covers ::removeStreams() */ public function testEmpty(): void {