Skip to content

Commit da8ad6c

Browse files
authored
[Translations] Add missing translation endpoints (#947)
* add missing translation endpoints * Apply php-cs-fixer changes * fix: controller method * fix: codeception * Apply php-cs-fixer changes * fix: schema
1 parent eed19e4 commit da8ad6c

16 files changed

Lines changed: 515 additions & 13 deletions

config/translation.yaml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,16 @@ services:
1111
public: true
1212
tags: [ 'controller.service_arguments' ]
1313

14+
#
15+
# Services
16+
#
17+
1418
Pimcore\Bundle\StudioBackendBundle\Translation\Service\TranslatorServiceInterface:
15-
class: Pimcore\Bundle\StudioBackendBundle\Translation\Service\TranslatorService
19+
class: Pimcore\Bundle\StudioBackendBundle\Translation\Service\TranslatorService
20+
21+
#
22+
# Repositories
23+
#
24+
25+
Pimcore\Bundle\StudioBackendBundle\Translation\Repository\TranslationRepositoryInterface:
26+
class: Pimcore\Bundle\StudioBackendBundle\Translation\Repository\TranslationRepository

src/ExecutionEngine/AutomationAction/AbstractHandler.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Model\ExecuteActionData;
3131
use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Model\FullExecuteActionData;
3232
use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config;
33-
use Pimcore\Bundle\StudioBackendBundle\Translation\Service\TranslatorService;
33+
use Pimcore\Bundle\StudioBackendBundle\Translation\Service\TranslatorServiceInterface;
3434
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementPermissions;
3535
use Pimcore\Model\Element\ElementDescriptor;
3636
use Pimcore\Model\Element\ElementInterface;
@@ -117,7 +117,7 @@ protected function abort(AbortActionData $abortActionData): void
117117
$this->abortAction(
118118
$abortActionData->getTranslationKey(),
119119
$abortActionData->getTranslationParameters(),
120-
TranslatorService::DOMAIN,
120+
TranslatorServiceInterface::DOMAIN,
121121
$abortActionData->getExceptionClassName()
122122
);
123123
}

src/Installer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
use Pimcore\Bundle\StudioBackendBundle\Entity\Grid\GridConfigurationFavorite;
2727
use Pimcore\Bundle\StudioBackendBundle\Entity\Grid\GridConfigurationShare;
2828
use Pimcore\Bundle\StudioBackendBundle\Entity\Perspective\UserPerspectiveData;
29-
use Pimcore\Bundle\StudioBackendBundle\Translation\Service\TranslatorService;
29+
use Pimcore\Bundle\StudioBackendBundle\Translation\Service\TranslatorServiceInterface;
3030
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions;
3131
use Pimcore\Extension\Bundle\Installer\Exception\InstallationException;
3232
use Pimcore\Extension\Bundle\Installer\SettingsStoreAwareInstaller;
@@ -102,7 +102,7 @@ public function uninstall(): void
102102
*/
103103
private function createTranslationTable(Schema $schema): void
104104
{
105-
$translationsDomainTableName = 'translations_' . TranslatorService::DOMAIN;
105+
$translationsDomainTableName = 'translations_' . TranslatorServiceInterface::DOMAIN;
106106
if (!$schema->hasTable($translationsDomainTableName)) {
107107
$translationDomainTable = $schema->createTable($translationsDomainTableName);
108108

src/Translation/Attribute/Request/TranslationRequestBody.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@
2424
#[Attribute(Attribute::TARGET_METHOD)]
2525
final class TranslationRequestBody extends RequestBody
2626
{
27-
public function __construct()
27+
public function __construct(string $content = Translation::class)
2828
{
2929
parent::__construct(
3030
required: true,
31-
content: new JsonContent(ref: Translation::class)
31+
content: new JsonContent(ref: $content)
3232
);
3333
}
3434
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* Pimcore
6+
*
7+
* This source file is available under two different licenses:
8+
* - GNU General Public License version 3 (GPLv3)
9+
* - Pimcore Commercial License (PCL)
10+
* Full copyright and license information is available in
11+
* LICENSE.md which is distributed with this source code.
12+
*
13+
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
14+
* @license http://www.pimcore.org/license GPLv3 and PCL
15+
*/
16+
17+
namespace Pimcore\Bundle\StudioBackendBundle\Translation\Controller;
18+
19+
use OpenApi\Attributes\Delete;
20+
use Pimcore\Bundle\StudioBackendBundle\Controller\AbstractApiController;
21+
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\NotFoundException;
22+
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Path\StringParameter;
23+
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses;
24+
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\SuccessResponse;
25+
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags;
26+
use Pimcore\Bundle\StudioBackendBundle\Translation\Service\TranslatorServiceInterface;
27+
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
28+
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions;
29+
use Symfony\Component\HttpFoundation\Response;
30+
use Symfony\Component\Routing\Attribute\Route;
31+
use Symfony\Component\Security\Http\Attribute\IsGranted;
32+
use Symfony\Component\Serializer\SerializerInterface;
33+
34+
/**
35+
* @internal
36+
*/
37+
final class DeleteController extends AbstractApiController
38+
{
39+
private const string ROUTE = '/translations/{key}';
40+
41+
public function __construct(
42+
SerializerInterface $serializer,
43+
private readonly TranslatorServiceInterface $translatorService
44+
) {
45+
parent::__construct($serializer);
46+
}
47+
48+
/**
49+
* @throws NotFoundException
50+
*/
51+
#[Route(self::ROUTE, name: 'pimcore_studio_api_delete_translation', methods: ['DELETE'])]
52+
#[IsGranted(UserPermissions::TRANSLATIONS->value)]
53+
#[Delete(
54+
path: self::PREFIX . self::ROUTE,
55+
operationId: 'translation_delete_by_key',
56+
description: 'translation_delete_by_key_description',
57+
summary: 'translation_delete_by_key_summary',
58+
tags: [Tags::Translation->name]
59+
)]
60+
#[StringParameter('key', 'some_key', description: 'Delete translations by matching key')]
61+
#[SuccessResponse(
62+
description: 'translation_delete_by_key_success_description',
63+
)]
64+
#[DefaultResponses([
65+
HttpResponseCodes::NOT_FOUND,
66+
HttpResponseCodes::UNAUTHORIZED,
67+
])]
68+
public function deleteTranslation(string $key): Response
69+
{
70+
$this->translatorService->deleteTranslationByKey($key);
71+
72+
return new Response();
73+
}
74+
}

src/Translation/Controller/TranslationController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
*/
3939
final class TranslationController extends AbstractApiController
4040
{
41-
private const ROUTE = '/translations';
41+
private const string ROUTE = '/translations';
4242

4343
public function __construct(
4444
SerializerInterface $serializer,
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* Pimcore
6+
*
7+
* This source file is available under two different licenses:
8+
* - GNU General Public License version 3 (GPLv3)
9+
* - Pimcore Commercial License (PCL)
10+
* Full copyright and license information is available in
11+
* LICENSE.md which is distributed with this source code.
12+
*
13+
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
14+
* @license http://www.pimcore.org/license GPLv3 and PCL
15+
*/
16+
17+
namespace Pimcore\Bundle\StudioBackendBundle\Translation\Controller;
18+
19+
use OpenApi\Attributes\Put;
20+
use Pimcore\Bundle\StudioBackendBundle\Controller\AbstractApiController;
21+
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidLocaleException;
22+
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses;
23+
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\SuccessResponse;
24+
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags;
25+
use Pimcore\Bundle\StudioBackendBundle\Translation\Attribute\Request\TranslationRequestBody;
26+
use Pimcore\Bundle\StudioBackendBundle\Translation\Schema\UpdateTranslation;
27+
use Pimcore\Bundle\StudioBackendBundle\Translation\Service\TranslatorServiceInterface;
28+
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
29+
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions;
30+
use Symfony\Component\HttpFoundation\Response;
31+
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
32+
use Symfony\Component\Routing\Attribute\Route;
33+
use Symfony\Component\Security\Http\Attribute\IsGranted;
34+
use Symfony\Component\Serializer\SerializerInterface;
35+
36+
/**
37+
* @internal
38+
*/
39+
final class UpdateController extends AbstractApiController
40+
{
41+
private const string ROUTE = '/translations';
42+
43+
public function __construct(
44+
SerializerInterface $serializer,
45+
private readonly TranslatorServiceInterface $translatorService
46+
) {
47+
parent::__construct($serializer);
48+
}
49+
50+
/**
51+
* @throws InvalidLocaleException
52+
*/
53+
#[Route(self::ROUTE, name: 'pimcore_studio_api_translations_update', methods: ['PUT'])]
54+
#[IsGranted(UserPermissions::TRANSLATIONS->value)]
55+
#[Put(
56+
path: self::PREFIX . self::ROUTE,
57+
operationId: 'translation_update',
58+
description: 'translation_update_description',
59+
summary: 'translation_update_summary',
60+
tags: [Tags::Translation->name]
61+
)]
62+
#[TranslationRequestBody(UpdateTranslation::class)]
63+
#[SuccessResponse(
64+
description: 'translation_update_success_response'
65+
)]
66+
#[DefaultResponses([
67+
HttpResponseCodes::NOT_FOUND,
68+
HttpResponseCodes::UNAUTHORIZED,
69+
])]
70+
public function updateTranslations(
71+
#[MapRequestPayload] UpdateTranslation $translation
72+
): Response {
73+
$this->translatorService->updateTranslations($translation);
74+
75+
return new Response();
76+
}
77+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* Pimcore
6+
*
7+
* This source file is available under two different licenses:
8+
* - GNU General Public License version 3 (GPLv3)
9+
* - Pimcore Commercial License (PCL)
10+
* Full copyright and license information is available in
11+
* LICENSE.md which is distributed with this source code.
12+
*
13+
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
14+
* @license http://www.pimcore.org/license GPLv3 and PCL
15+
*/
16+
17+
namespace Pimcore\Bundle\StudioBackendBundle\Translation\Repository;
18+
19+
use Pimcore\Bundle\StaticResolverBundle\Lib\Tools\AdminResolverInterface;
20+
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidLocaleException;
21+
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\NotFoundException;
22+
use Pimcore\Bundle\StudioBackendBundle\Translation\Schema\TranslationData;
23+
use Pimcore\Bundle\StudioBackendBundle\Translation\Service\TranslatorServiceInterface;
24+
use Pimcore\Model\Translation;
25+
use Pimcore\Model\Translation\Listing;
26+
use function in_array;
27+
28+
/**
29+
* @internal
30+
*/
31+
final readonly class TranslationRepository implements TranslationRepositoryInterface
32+
{
33+
public function __construct(
34+
private AdminResolverInterface $adminResolver,
35+
) {
36+
}
37+
38+
/**
39+
* {@inheritdoc}
40+
*/
41+
public function getAllTranslations(string $locale): array
42+
{
43+
$validLanguages = $this->adminResolver->getLanguages();
44+
$this->validateLocale($locale, $validLanguages);
45+
46+
$list = $this->getTranslationList();
47+
$list->setLanguages($validLanguages);
48+
$list->load();
49+
50+
return $list->getTranslations();
51+
}
52+
53+
/**
54+
* {@inheritdoc}
55+
*/
56+
public function createTranslations(array $translationData, string $locale): void
57+
{
58+
$languages = $this->adminResolver->getLanguages();
59+
$this->validateLocale($locale, $languages);
60+
61+
/** @var TranslationData $translation */
62+
foreach ($translationData as $translation) {
63+
$t = new Translation();
64+
$t->setDomain(TranslatorServiceInterface::DOMAIN);
65+
$t->setKey($translation->getKey());
66+
$t->setType($translation->getType());
67+
$t->addTranslation($locale, $translation->getTranslation());
68+
$t->setCreationDate(time());
69+
$t->setModificationDate(time());
70+
if ($this->getTranslationByKey($translation->getKey()) === null) {
71+
$this->setNewValues($t, $languages, $locale);
72+
}
73+
$t->save();
74+
}
75+
}
76+
77+
public function deleteTranslation(string $key): void
78+
{
79+
$translation = $this->getTranslationByKey($key);
80+
if ($translation === null) {
81+
throw new NotFoundException('translation', $key, 'key');
82+
}
83+
84+
$translation->delete();
85+
}
86+
87+
private function getTranslationList(): Listing
88+
{
89+
$list = new Translation\Listing();
90+
$list->setDomain(TranslatorServiceInterface::DOMAIN);
91+
$list->setOrder('asc');
92+
$list->setOrderKey('translations_' . TranslatorServiceInterface::DOMAIN . '.key', false);
93+
94+
return $list;
95+
}
96+
97+
private function setNewValues(Translation $translation, array $languages, string $locale): void
98+
{
99+
$translation->setCreationDate(time());
100+
101+
foreach ($languages as $language) {
102+
if ($language !== $locale) {
103+
$translation->addTranslation($language, '');
104+
}
105+
}
106+
}
107+
108+
private function getTranslationByKey(string $key): ?Translation
109+
{
110+
$list = $this->getTranslationList();
111+
$list->setLimit(1);
112+
$list->addConditionParam('`key` = ?', $key);
113+
114+
return !empty($list->load()) ? $list->current() : null;
115+
}
116+
117+
/**
118+
* @throws InvalidLocaleException
119+
*/
120+
private function validateLocale(string $locale, array $validLanguages): void
121+
{
122+
if (!in_array($locale, $validLanguages, true)) {
123+
throw new InvalidLocaleException($locale);
124+
}
125+
}
126+
}

0 commit comments

Comments
 (0)