diff --git a/Classes/Access/AccessItemInterface.php b/Classes/Access/AccessItemInterface.php new file mode 100644 index 00000000..498c04a2 --- /dev/null +++ b/Classes/Access/AccessItemInterface.php @@ -0,0 +1,36 @@ +getIdentifier() === $identifier) { + return $accessItem; + } + } + + return null; + } + + public function hasAccess(string $identifier): bool + { + $object = $this->getAccess($identifier); + return $object !== null; + } +} diff --git a/Classes/Access/AllowedGlossarySyncAccess.php b/Classes/Access/AllowedGlossarySyncAccess.php new file mode 100644 index 00000000..4e113591 --- /dev/null +++ b/Classes/Access/AllowedGlossarySyncAccess.php @@ -0,0 +1,30 @@ +buildParamsArrayForListView($id); + if (!$this->getBackendUserAuthentication()->check('custom_options', AllowedGlossarySyncAccess::ALLOWED_GLOSSARY_SYNC)) { + return; + } + + $parameters = $this->buildParamsArrayForListView((int)$id); $title = (string)LocalizationUtility::translate( 'glossary.sync.button.all', 'wv_deepltranslate' diff --git a/Classes/Event/Listener/UsageToolBarEventListener.php b/Classes/Event/Listener/UsageToolBarEventListener.php index ef0fc8c1..74c0104d 100644 --- a/Classes/Event/Listener/UsageToolBarEventListener.php +++ b/Classes/Event/Listener/UsageToolBarEventListener.php @@ -30,7 +30,7 @@ public function __invoke(SystemInformationToolbarCollectorEvent $systemInformati $usage = $this->usageService->getCurrentUsage(); // @todo Decide to handle empty UsageDetail later and add systeminformation with a default // (no limit retrieved) instead of simply omitting it here now. - if($usage === null || $usage->character === null) { + if ($usage === null || $usage->character === null) { return; } } catch (ApiKeyNotSetException $exception) { diff --git a/Classes/Hooks/ButtonBarHook.php b/Classes/Hooks/ButtonBarHook.php index f46ef808..16ec5167 100644 --- a/Classes/Hooks/ButtonBarHook.php +++ b/Classes/Hooks/ButtonBarHook.php @@ -9,10 +9,12 @@ use TYPO3\CMS\Backend\Template\Components\ButtonBar; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\Domain\Repository\PageRepository; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Utility\LocalizationUtility; +use WebVision\WvDeepltranslate\Access\AllowedGlossarySyncAccess; class ButtonBarHook { @@ -28,46 +30,53 @@ public function getButtons(array $params, ButtonBar $buttonBar): array $buttons = $params['buttons']; $queryParams = $GLOBALS['TYPO3_REQUEST']->getQueryParams(); - // we're inside a page - if (isset($queryParams['id'])) { - $page = BackendUtility::getRecord( - 'pages', - $queryParams['id'], - 'uid,module' - ); + if (!isset($queryParams['id']) || $queryParams['id'] === '0') { + return $buttons; + } - if ( - isset($page['module']) && $page['module'] === 'glossary' - && $this->getBackendUserAuthentication() - ->check('tables_modify', 'tx_wvdeepltranslate_glossaryentry') - ) { - $parameters = $this->buildParamsArrayForListView($page['uid']); - $title = (string)LocalizationUtility::translate( - 'glossary.sync.button.all', - 'wv_deepltranslate' - ); - // Style button - $iconFactory = GeneralUtility::makeInstance(IconFactory::class); - $button = $buttonBar->makeLinkButton(); - $button->setIcon($iconFactory->getIcon( - 'apps-pagetree-folder-contains-glossary', - Icon::SIZE_SMALL - )); - $button->setTitle($title); - $button->setShowLabelText(true); + /** @var array{uid: int, doktype: int, module: string} $page */ + $page = BackendUtility::getRecord( + 'pages', + $queryParams['id'], + 'uid,module,doktype' + ); - $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); - $uri = $uriBuilder->buildUriFromRoute( - 'glossaryupdate', - $parameters - ); - $button->setHref($uri); + if ( + (int)$page['doktype'] !== PageRepository::DOKTYPE_SYSFOLDER + || $page['module'] !== 'glossary' + ) { + return $buttons; + } - // Register Button and position it - $buttons[ButtonBar::BUTTON_POSITION_LEFT][5][] = $button; - } + if (!$this->getBackendUserAuthentication()->check('custom_options', AllowedGlossarySyncAccess::ALLOWED_GLOSSARY_SYNC)) { + return $buttons; } + $parameters = $this->buildParamsArrayForListView((int)$page['uid']); + $title = (string)LocalizationUtility::translate( + 'glossary.sync.button.all', + 'wv_deepltranslate' + ); + // Style button + $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + $button = $buttonBar->makeLinkButton(); + $button->setIcon($iconFactory->getIcon( + 'apps-pagetree-folder-contains-glossary', + Icon::SIZE_SMALL + )); + $button->setTitle($title); + $button->setShowLabelText(true); + + $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); + $uri = $uriBuilder->buildUriFromRoute( + 'glossaryupdate', + $parameters + ); + $button->setHref($uri); + + // Register Button and position it + $buttons[ButtonBar::BUTTON_POSITION_LEFT][5][] = $button; + return $buttons; } diff --git a/Classes/Hooks/TCEmainHook.php b/Classes/Hooks/TCEmainHook.php index c98a584f..ecc1ab84 100644 --- a/Classes/Hooks/TCEmainHook.php +++ b/Classes/Hooks/TCEmainHook.php @@ -7,6 +7,9 @@ use TYPO3\CMS\Core\DataHandling\DataHandler; use TYPO3\CMS\Core\DataHandling\DataHandlerCheckModifyAccessListHookInterface; +/** + * ToDo: Rename this class to "AllowedTableForCommandHandler" + */ class TCEmainHook implements DataHandlerCheckModifyAccessListHookInterface { /** diff --git a/Classes/Override/Core11/DatabaseRecordList.php b/Classes/Override/Core11/DatabaseRecordList.php index 0ee00d72..e5034a8b 100644 --- a/Classes/Override/Core11/DatabaseRecordList.php +++ b/Classes/Override/Core11/DatabaseRecordList.php @@ -4,6 +4,8 @@ namespace WebVision\WvDeepltranslate\Override\Core11; +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use WebVision\WvDeepltranslate\Access\AllowedTranslateAccess; use WebVision\WvDeepltranslate\Utility\DeeplBackendUtility; /** @@ -23,44 +25,51 @@ public function makeLocalizationPanel($table, $row, array $translations): string { $out = parent::makeLocalizationPanel($table, $row, $translations); - if ($out) { - if (!DeeplBackendUtility::isDeeplApiKeySet()) { - return $out; - } + if (!DeeplBackendUtility::isDeeplApiKeySet()) { + return $out; + } - // glossaries should not be auto translated by DeepL - if ($table === 'tx_wvdeepltranslate_glossaryentry') { - return $out; - } + // glossaries should not be auto translated by DeepL + if ($table === 'tx_wvdeepltranslate_glossaryentry') { + return $out; + } - $pageId = (int)($table === 'pages' ? $row['uid'] : $row['pid']); - // All records excluding pages - $possibleTranslations = $this->possibleTranslations; - if ($table === 'pages') { - // Calculate possible translations for pages - $possibleTranslations = array_map(static fn ($siteLanguage) => $siteLanguage->getLanguageId(), $this->languagesAllowedForUser); - $possibleTranslations = array_filter($possibleTranslations, static fn ($languageUid) => $languageUid > 0); - } - $languageInformation = $this->translateTools->getSystemLanguages($pageId); - foreach ($possibleTranslations as $lUid_OnPage) { - if ($this->isEditable($table) - && !$this->isRecordDeletePlaceholder($row) - && !isset($translations[$lUid_OnPage]) - && $this->getBackendUserAuthentication()->checkLanguageAccess($lUid_OnPage) - && DeeplBackendUtility::checkCanBeTranslated($pageId, $lUid_OnPage) - ) { - $out .= DeeplBackendUtility::buildTranslateButton( - $table, - $row['uid'], - $lUid_OnPage, - $this->listURL(), - $languageInformation[$lUid_OnPage]['title'], - $languageInformation[$lUid_OnPage]['flagIcon'] - ); - } + if (!$this->getBackendUserAuthentication()->check('custom_options', AllowedTranslateAccess::ALLOWED_TRANSLATE_OPTION_VALUE)) { + return $out; + } + + $pageId = (int)($table === 'pages' ? $row['uid'] : $row['pid']); + // All records excluding pages + $possibleTranslations = $this->possibleTranslations; + if ($table === 'pages') { + // Calculate possible translations for pages + $possibleTranslations = array_map(static fn ($siteLanguage) => $siteLanguage->getLanguageId(), $this->languagesAllowedForUser); + $possibleTranslations = array_filter($possibleTranslations, static fn ($languageUid) => $languageUid > 0); + } + $languageInformation = $this->translateTools->getSystemLanguages($pageId); + foreach ($possibleTranslations as $lUid_OnPage) { + if ($this->isEditable($table) + && !$this->isRecordDeletePlaceholder($row) + && !isset($translations[$lUid_OnPage]) + && $this->getBackendUserAuthentication()->checkLanguageAccess($lUid_OnPage) + && DeeplBackendUtility::checkCanBeTranslated($pageId, $lUid_OnPage) + ) { + $out .= DeeplBackendUtility::buildTranslateButton( + $table, + $row['uid'], + $lUid_OnPage, + $this->listURL(), + $languageInformation[$lUid_OnPage]['title'], + $languageInformation[$lUid_OnPage]['flagIcon'] + ); } } return $out; } + + protected function getBackendUserAuthentication(): BackendUserAuthentication + { + return $GLOBALS['BE_USER']; + } } diff --git a/Classes/Override/Core11/DeeplRecordListController.php b/Classes/Override/Core11/DeeplRecordListController.php index fcdb404b..c3917d49 100644 --- a/Classes/Override/Core11/DeeplRecordListController.php +++ b/Classes/Override/Core11/DeeplRecordListController.php @@ -16,6 +16,8 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Utility\LocalizationUtility; use TYPO3\CMS\Recordlist\Controller\RecordListController; +use WebVision\WvDeepltranslate\Access\AllowedGlossarySyncAccess; +use WebVision\WvDeepltranslate\Access\AllowedTranslateAccess; use WebVision\WvDeepltranslate\Service\DeeplGlossaryService; use WebVision\WvDeepltranslate\Utility\DeeplBackendUtility; @@ -40,6 +42,10 @@ protected function languageSelector(string $requestUri, $_forwardCore12CombatAnd return $originalOutput; } + if (!$this->getBackendUserAuthentication()->check('custom_options', AllowedTranslateAccess::ALLOWED_TRANSLATE_OPTION_VALUE)) { + return $originalOutput; + } + $options = DeeplBackendUtility::buildTranslateDropdown( $this->siteLanguages, $this->id, @@ -75,6 +81,10 @@ private function buildGlossaryTranslationOptionDropdown(string $requestUri): str return ''; } + if (!$this->getBackendUserAuthentication()->check('custom_options', AllowedGlossarySyncAccess::ALLOWED_GLOSSARY_SYNC)) { + return ''; + } + $glossaryService = GeneralUtility::makeInstance(DeeplGlossaryService::class); $possiblePairs = $glossaryService->getPossibleGlossaryLanguageConfig(); $site = GeneralUtility::makeInstance(SiteFinder::class) diff --git a/Classes/Override/Core12/DatabaseRecordList.php b/Classes/Override/Core12/DatabaseRecordList.php index c9884960..bf83352c 100644 --- a/Classes/Override/Core12/DatabaseRecordList.php +++ b/Classes/Override/Core12/DatabaseRecordList.php @@ -4,6 +4,7 @@ namespace WebVision\WvDeepltranslate\Override\Core12; +use WebVision\WvDeepltranslate\Access\AllowedTranslateAccess; use WebVision\WvDeepltranslate\Utility\DeeplBackendUtility; /** @@ -23,41 +24,43 @@ public function makeLocalizationPanel($table, $row, array $translations): string { $out = parent::makeLocalizationPanel($table, $row, $translations); - if ($out) { - if (!DeeplBackendUtility::isDeeplApiKeySet()) { - return $out; - } + if (!DeeplBackendUtility::isDeeplApiKeySet()) { + return $out; + } - // glossaries should not be auto translated by DeepL - if ($table === 'tx_wvdeepltranslate_glossaryentry') { - return $out; - } + // glossaries should not be auto translated by DeepL + if ($table === 'tx_wvdeepltranslate_glossaryentry') { + return $out; + } - $pageId = (int)($table === 'pages' ? $row['uid'] : $row['pid']); - // All records excluding pages - $possibleTranslations = $this->possibleTranslations; - if ($table === 'pages') { - // Calculate possible translations for pages - $possibleTranslations = array_map(static fn ($siteLanguage) => $siteLanguage->getLanguageId(), $this->languagesAllowedForUser); - $possibleTranslations = array_filter($possibleTranslations, static fn ($languageUid) => $languageUid > 0); - } - $languageInformation = $this->translateTools->getSystemLanguages($pageId); - foreach ($possibleTranslations as $lUid_OnPage) { - if ($this->isEditable($table) - && !$this->isRecordDeletePlaceholder($row) - && !isset($translations[$lUid_OnPage]) - && $this->getBackendUserAuthentication()->checkLanguageAccess($lUid_OnPage) - && DeeplBackendUtility::checkCanBeTranslated($pageId, $lUid_OnPage) - ) { - $out .= DeeplBackendUtility::buildTranslateButton( - $table, - $row['uid'], - $lUid_OnPage, - $this->listURL(), - $languageInformation[$lUid_OnPage]['title'], - $languageInformation[$lUid_OnPage]['flagIcon'] - ); - } + if (!$this->getBackendUserAuthentication()->check('custom_options', AllowedTranslateAccess::ALLOWED_TRANSLATE_OPTION_VALUE)) { + return $out; + } + + $pageId = (int)($table === 'pages' ? $row['uid'] : $row['pid']); + // All records excluding pages + $possibleTranslations = $this->possibleTranslations; + if ($table === 'pages') { + // Calculate possible translations for pages + $possibleTranslations = array_map(static fn ($siteLanguage) => $siteLanguage->getLanguageId(), $this->languagesAllowedForUser); + $possibleTranslations = array_filter($possibleTranslations, static fn ($languageUid) => $languageUid > 0); + } + $languageInformation = $this->translateTools->getSystemLanguages($pageId); + foreach ($possibleTranslations as $lUid_OnPage) { + if ($this->isEditable($table) + && !$this->isRecordDeletePlaceholder($row) + && !isset($translations[$lUid_OnPage]) + && $this->getBackendUserAuthentication()->checkLanguageAccess($lUid_OnPage) + && DeeplBackendUtility::checkCanBeTranslated($pageId, $lUid_OnPage) + ) { + $out .= DeeplBackendUtility::buildTranslateButton( + $table, + $row['uid'], + $lUid_OnPage, + $this->listURL(), + $languageInformation[$lUid_OnPage]['title'], + $languageInformation[$lUid_OnPage]['flagIcon'] + ); } } diff --git a/Classes/Override/Core12/DeeplRecordListController.php b/Classes/Override/Core12/DeeplRecordListController.php index f5f9c0db..91e50628 100644 --- a/Classes/Override/Core12/DeeplRecordListController.php +++ b/Classes/Override/Core12/DeeplRecordListController.php @@ -16,6 +16,8 @@ use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Utility\LocalizationUtility; +use WebVision\WvDeepltranslate\Access\AllowedGlossarySyncAccess; +use WebVision\WvDeepltranslate\Access\AllowedTranslateAccess; use WebVision\WvDeepltranslate\Service\DeeplGlossaryService; use WebVision\WvDeepltranslate\Utility\DeeplBackendUtility; @@ -44,6 +46,10 @@ protected function languageSelector(array $siteLanguages, string $requestUri): s return $originalOutput; } + if (!$this->getBackendUserAuthentication()->check('custom_options', AllowedTranslateAccess::ALLOWED_TRANSLATE_OPTION_VALUE)) { + return $originalOutput; + } + $options = DeeplBackendUtility::buildTranslateDropdown( $siteLanguages, $this->id, @@ -78,6 +84,10 @@ private function buildGlossaryTranslationOptionDropdown(array $siteLanguages, st return ''; } + if (!$this->getBackendUserAuthentication()->check('custom_options', AllowedGlossarySyncAccess::ALLOWED_GLOSSARY_SYNC)) { + return ''; + } + $glossaryService = GeneralUtility::makeInstance(DeeplGlossaryService::class); $possiblePairs = $glossaryService->getPossibleGlossaryLanguageConfig(); $site = GeneralUtility::makeInstance(SiteFinder::class) diff --git a/Classes/Override/LocalizationController.php b/Classes/Override/LocalizationController.php index 2c2a2bdc..afcfbef9 100644 --- a/Classes/Override/LocalizationController.php +++ b/Classes/Override/LocalizationController.php @@ -182,7 +182,7 @@ public function getRecordLocalizeSummary(ServerRequestInterface $request): Respo $payloadBody = $recordLocalizeSummaryModifier->rebuildPayload($payloadBody); } // Supported TYPO3 v12 - } elseif(class_exists(\TYPO3\CMS\Backend\Controller\Event\AfterRecordSummaryForLocalizationEvent::class)) { + } elseif (class_exists(\TYPO3\CMS\Backend\Controller\Event\AfterRecordSummaryForLocalizationEvent::class)) { $event = new AfterRecordSummaryForLocalizationEvent($payloadBody['records'], $payloadBody['columns']); $this->eventDispatcher->dispatch($event); diff --git a/Classes/ViewHelpers/Be/Access/DeeplTranslateAllowedViewHelper.php b/Classes/ViewHelpers/Be/Access/DeeplTranslateAllowedViewHelper.php new file mode 100644 index 00000000..6a9c9f77 --- /dev/null +++ b/Classes/ViewHelpers/Be/Access/DeeplTranslateAllowedViewHelper.php @@ -0,0 +1,25 @@ +check('custom_options', AllowedTranslateAccess::ALLOWED_TRANSLATE_OPTION_VALUE)) { + return true; + } + return false; + } +} diff --git a/Configuration/Icons.php b/Configuration/Icons.php index 5016f6f5..ea25b28d 100644 --- a/Configuration/Icons.php +++ b/Configuration/Icons.php @@ -17,4 +17,8 @@ 'provider' => SvgIconProvider::class, 'source' => 'EXT:wv_deepltranslate/Resources/Public/Icons/deepl-grey.svg', ], + 'deepl-logo' => [ + 'provider' => SvgIconProvider::class, + 'source' => 'EXT:wv_deepltranslate/Resources/Public/Icons/deepl.svg', + ], ]; diff --git a/Documentation/Administration/Access/Index.rst b/Documentation/Administration/Access/Index.rst new file mode 100644 index 00000000..2fabea88 --- /dev/null +++ b/Documentation/Administration/Access/Index.rst @@ -0,0 +1,27 @@ +.. include:: /Includes.rst.txt + +.. _administration-access: + +Access Configuration +==================================== + +Access to the automatic translation functions with Deepl-Translate in the TYPO3 backend +can be defined via the following options in the user group settings. + +.. figure:: /Images/Administration/BackendGroupAccess.png + :alt: Backend Gruppen Access Right - Custom module options + +.. note:: + + In order to be able to use the backend group authorisations, an update to the latest version of the `wv_deepltranslate` is required. + +.. confval:: Allowed Translate + + This setting controls the visibility of the general translation function in the + translation modal of the page module, in the translation options of data records in the list module + and in the translation selection in the page header of the page and list module. + +.. confval:: Allowed Glossary Sync + + This setting allows backend users of a backend user group with corresponding authorisation to + synchronise glossary entries of a glossary SysFolder (SysFolder with activated glossary module) towards Deepl. diff --git a/Documentation/Administration/Index.rst b/Documentation/Administration/Index.rst index fd2b91c4..11927c3b 100644 --- a/Documentation/Administration/Index.rst +++ b/Documentation/Administration/Index.rst @@ -12,5 +12,6 @@ Administration Installation/Index Configuration/Index + Access/Index AutoTranslatePrefix/Index Updates/Index diff --git a/Documentation/Images/Administration/BackendGroupAccess.png b/Documentation/Images/Administration/BackendGroupAccess.png new file mode 100644 index 00000000..a67bd398 Binary files /dev/null and b/Documentation/Images/Administration/BackendGroupAccess.png differ diff --git a/Resources/Private/Backend/Partials/PageLayout/LanguageColumns.html b/Resources/Private/Backend/Partials/PageLayout/LanguageColumns.html index 2da2c48c..c88ab2e5 100644 --- a/Resources/Private/Backend/Partials/PageLayout/LanguageColumns.html +++ b/Resources/Private/Backend/Partials/PageLayout/LanguageColumns.html @@ -49,6 +49,7 @@