diff --git a/wcfsetup/install/files/acp/templates/languageList.tpl b/wcfsetup/install/files/acp/templates/languageList.tpl index 953d77a3792..fe0164128e1 100644 --- a/wcfsetup/install/files/acp/templates/languageList.tpl +++ b/wcfsetup/install/files/acp/templates/languageList.tpl @@ -2,7 +2,7 @@
-

{lang}wcf.acp.language.list{/lang} {#$items}

+

{lang}wcf.acp.language.list{/lang} {#$gridView->countRows()}

-{hascontent} -
- {content}{pages print=true assign=pagesLinks controller='LanguageList' link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder"}{/content} -
-{/hascontent} - -{if $objects|count} -
- - - - - - - - - - {event name='columnHeads'} - - - - - {foreach from=$objects item=language} - - - - - - - - - {event name='columns'} - - {/foreach} - -
{lang}wcf.global.objectID{/lang}{lang}wcf.global.name{/lang}{lang}wcf.acp.language.users{/lang}{lang}wcf.acp.language.variables{/lang}{lang}wcf.acp.language.customVariables{/lang}
- {icon name='download'} - - {if !$language->isDefault} - {objectAction action="toggle" isDisabled=$language->isDisabled} - - {else} - - {if $language->isDisabled} - {icon name='square'} - {else} - {icon name='square-check'} - {/if} - - - {icon name='circle-check'} - - {/if} - - {icon name='pencil'} - - {if $language->isDeletable()} - {objectAction action="delete" objectTitle=$language->languageName} - {else} - - {icon name='xmark'} - - {/if} - - {event name='rowButtons'} - {@$language->languageID}{$language->languageName} ({@$language->languageCode}){#$language->users}{#$language->variables}{if $language->customVariables > 0}{#$language->customVariables}{else}{#$language->customVariables}{/if}
-
- - -{/if} +
+ {unsafe:$gridView->render()} +
{include file='footer'} diff --git a/wcfsetup/install/files/lib/acp/page/LanguageListPage.class.php b/wcfsetup/install/files/lib/acp/page/LanguageListPage.class.php index 53f1f72fca8..ae899a34105 100644 --- a/wcfsetup/install/files/lib/acp/page/LanguageListPage.class.php +++ b/wcfsetup/install/files/lib/acp/page/LanguageListPage.class.php @@ -2,78 +2,34 @@ namespace wcf\acp\page; -use wcf\data\language\LanguageList; -use wcf\page\SortablePage; -use wcf\system\WCF; +use wcf\page\AbstractGridViewPage; +use wcf\system\gridView\AbstractGridView; +use wcf\system\gridView\admin\LanguageGridView; /** * Shows a list of all installed languages. * - * @author Marcel Werk - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License + * @author Olaf Brau, Marcel Werk + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License * - * @property LanguageList $objectList + * @property LanguageGridView $gridView */ -class LanguageListPage extends SortablePage +class LanguageListPage extends AbstractGridViewPage { /** * @inheritDoc */ public $activeMenuItem = 'wcf.acp.menu.link.language.list'; - /** - * @inheritDoc - */ - public $defaultSortField = 'languageName'; - /** * @inheritDoc */ public $neededPermissions = ['admin.language.canManageLanguage']; - /** - * @inheritDoc - */ - public $objectListClassName = LanguageList::class; - - /** - * @inheritDoc - */ - public $validSortFields = ['languageID', 'languageCode', 'languageName', 'users', 'variables', 'customVariables']; - - /** - * @inheritDoc - */ - public function initObjectList() + #[\Override] + protected function createGridViewController(): AbstractGridView { - parent::initObjectList(); - - $this->objectList->sqlSelects = "( - SELECT COUNT(*) - FROM wcf1_user user - WHERE languageID = language.languageID - ) AS users, ( - SELECT COUNT(*) - FROM wcf1_language_item - WHERE languageID = language.languageID - ) AS variables, ( - SELECT COUNT(*) - FROM wcf1_language_item - WHERE languageID = language.languageID - AND languageCustomItemValue IS NOT NULL - ) AS customVariables"; - } - - /** - * @inheritDoc - */ - public function assignVariables() - { - parent::assignVariables(); - - WCF::getTPL()->assign([ - 'languages' => $this->objectList->getObjects(), - ]); + return new LanguageGridView(); } } diff --git a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php index b48015eeb5f..3c08fa42719 100644 --- a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php +++ b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php @@ -180,6 +180,10 @@ static function (\wcf\event\endpoint\ControllerCollecting $event) { $event->register(new \wcf\system\endpoint\controller\core\bbcodes\media\providers\DisableProvider()); $event->register(new \wcf\system\endpoint\controller\core\bbcodes\media\providers\EnableProvider()); $event->register(new \wcf\system\endpoint\controller\core\bbCodes\DeleteBBCodes()); + $event->register(new \wcf\system\endpoint\controller\core\languages\DisableLanguage()); + $event->register(new \wcf\system\endpoint\controller\core\languages\DeleteLanguage()); + $event->register(new \wcf\system\endpoint\controller\core\languages\EnableLanguage()); + $event->register(new \wcf\system\endpoint\controller\core\languages\SetAsDefaultLanguage()); $event->register(new \wcf\system\endpoint\controller\core\languages\items\DeleteItem()); $event->register(new \wcf\system\endpoint\controller\core\labels\groups\DeleteGroup()); } diff --git a/wcfsetup/install/files/lib/event/gridView/admin/LanguageGridViewInitialized.class.php b/wcfsetup/install/files/lib/event/gridView/admin/LanguageGridViewInitialized.class.php new file mode 100644 index 00000000000..2f0cb1ac33c --- /dev/null +++ b/wcfsetup/install/files/lib/event/gridView/admin/LanguageGridViewInitialized.class.php @@ -0,0 +1,21 @@ + + * @since 6.2 + */ +final class LanguageGridViewInitialized implements IPsr14Event +{ + public function __construct(public readonly LanguageGridView $gridView) + { + } +} diff --git a/wcfsetup/install/files/lib/event/interaction/admin/LanguageInteractionCollecting.class.php b/wcfsetup/install/files/lib/event/interaction/admin/LanguageInteractionCollecting.class.php new file mode 100644 index 00000000000..90d7402d477 --- /dev/null +++ b/wcfsetup/install/files/lib/event/interaction/admin/LanguageInteractionCollecting.class.php @@ -0,0 +1,21 @@ + + * @since 6.2 + */ +final class LanguageInteractionCollecting implements IPsr14Event +{ + public function __construct(public readonly LanguageInteractions $provider) + { + } +} diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/languages/DeleteLanguage.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/languages/DeleteLanguage.class.php new file mode 100644 index 00000000000..294fb2d281b --- /dev/null +++ b/wcfsetup/install/files/lib/system/endpoint/controller/core/languages/DeleteLanguage.class.php @@ -0,0 +1,51 @@ + + * @since 6.2 + */ +#[DeleteRequest('/core/languages/{id:\d+}')] +final class DeleteLanguage implements IController +{ + #[\Override] + public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface + { + $language = Helper::fetchObjectFromRequestParameter($variables['id'], Language::class); + + $this->assertLanguageCanBeDeleted($language); + + (new LanguageAction([$language], 'delete'))->executeAction(); + + LanguageFactory::getInstance()->clearCache(); + LanguageFactory::getInstance()->deleteLanguageCache(); + + return new JsonResponse([]); + } + + private function assertLanguageCanBeDeleted(Language $language): void + { + WCF::getSession()->checkPermissions(['admin.language.canManageLanguage']); + + if (!$language->isDeletable()) { + throw new PermissionDeniedException(); + } + } +} diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/languages/DisableLanguage.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/languages/DisableLanguage.class.php new file mode 100644 index 00000000000..410357f2a41 --- /dev/null +++ b/wcfsetup/install/files/lib/system/endpoint/controller/core/languages/DisableLanguage.class.php @@ -0,0 +1,51 @@ + + * @since 6.2 + */ +#[PostRequest('/core/languages/{id:\d+}/disable')] +final class DisableLanguage implements IController +{ + #[\Override] + public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface + { + $language = Helper::fetchObjectFromRequestParameter($variables['id'], Language::class); + + $this->assertLanguageCanBeDisabled($language); + + (new LanguageAction([$language], 'toggle'))->executeAction(); + + return new JsonResponse([]); + } + + private function assertLanguageCanBeDisabled(Language $language): void + { + WCF::getSession()->checkPermissions(['admin.language.canManageLanguage']); + + if ($language->isDefault) { + throw new PermissionDeniedException(); + } + + if ($language->isDisabled) { + throw new PermissionDeniedException(); + } + } +} diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/languages/EnableLanguage.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/languages/EnableLanguage.class.php new file mode 100644 index 00000000000..a5d2381f44b --- /dev/null +++ b/wcfsetup/install/files/lib/system/endpoint/controller/core/languages/EnableLanguage.class.php @@ -0,0 +1,51 @@ + + * @since 6.2 + */ +#[PostRequest('/core/languages/{id:\d+}/enable')] +final class EnableLanguage implements IController +{ + #[\Override] + public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface + { + $language = Helper::fetchObjectFromRequestParameter($variables['id'], Language::class); + + $this->assertLanguageCanBeEnabled($language); + + (new LanguageAction([$language], 'toggle'))->executeAction(); + + return new JsonResponse([]); + } + + private function assertLanguageCanBeEnabled(Language $language): void + { + WCF::getSession()->checkPermissions(['admin.language.canManageLanguage']); + + if ($language->isDefault) { + throw new PermissionDeniedException(); + } + + if (!$language->isDisabled) { + throw new PermissionDeniedException(); + } + } +} diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/languages/SetAsDefaultLanguage.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/languages/SetAsDefaultLanguage.class.php new file mode 100644 index 00000000000..79b6c1fca86 --- /dev/null +++ b/wcfsetup/install/files/lib/system/endpoint/controller/core/languages/SetAsDefaultLanguage.class.php @@ -0,0 +1,52 @@ + + * @since 6.2 + */ +#[PostRequest('/core/languages/{id:\d+}/default')] +final class SetAsDefaultLanguage implements IController +{ + #[\Override] + public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface + { + $language = Helper::fetchObjectFromRequestParameter($variables['id'], Language::class); + + $this->assertLanguageCanBeSetAsDefault($language); + + $languageEditor = new LanguageEditor($language); + $languageEditor->setAsDefault(); + + if ($languageEditor->isDisabled) { + $languageEditor->update(['isDisabled' => 0]); + } + + return new JsonResponse([]); + } + + private function assertLanguageCanBeSetAsDefault(Language $language): void + { + WCF::getSession()->checkPermissions(['admin.language.canManageLanguage']); + + if ($language->isDefault) { + throw new PermissionDeniedException(); + } + } +} diff --git a/wcfsetup/install/files/lib/system/gridView/admin/LanguageGridView.class.php b/wcfsetup/install/files/lib/system/gridView/admin/LanguageGridView.class.php new file mode 100644 index 00000000000..f1d72c66c80 --- /dev/null +++ b/wcfsetup/install/files/lib/system/gridView/admin/LanguageGridView.class.php @@ -0,0 +1,180 @@ + + * @since 6.2 + */ +final class LanguageGridView extends AbstractGridView +{ + public function __construct() + { + $this->addColumns([ + GridViewColumn::for('languageID') + ->label('wcf.global.objectID') + ->filter(new ObjectIdFilter()) + ->renderer(new ObjectIdColumnRenderer()) + ->sortable(), + GridViewColumn::for('languageCode') + ->label('wcf.acp.language.code') + ->filter(new TextFilter()) + ->sortable(), + GridViewColumn::for('languageName') + ->label('wcf.global.name') + ->filter(new TextFilter()) + ->titleColumn() + ->sortable(), + GridViewColumn::for('users') + ->label('wcf.acp.language.users') + ->filter(new NumericFilter($this->subSelectUsers())) + ->renderer(new NumberColumnRenderer()) + ->sortable(sortByDatabaseColumn: $this->subSelectUsers()), + GridViewColumn::for('variables') + ->label('wcf.acp.language.variables') + ->filter(new NumericFilter($this->subSelectVariables())) + ->renderer( + new class extends DefaultColumnRenderer { + #[\Override] + public function render(mixed $value, DatabaseObject $row): string + { + \assert($row instanceof Language); + + $href = LinkHandler::getInstance()->getControllerLink( + LanguageItemListPage::class, + ["filters" => ["languageID" => $row->languageID]] + ); + + return '' + . StringUtil::formatNumeric($value) + . ''; + } + } + ) + ->sortable(sortByDatabaseColumn: $this->subSelectVariables()), + GridViewColumn::for('customVariables') + ->label('wcf.acp.language.customVariables') + ->filter(new NumericFilter($this->subSelectCustomVariables())) + ->renderer( + new class extends DefaultColumnRenderer { + #[\Override] + public function render(mixed $value, DatabaseObject $row): string + { + \assert($row instanceof Language); + + $href = LinkHandler::getInstance()->getControllerLink( + LanguageItemListPage::class, + ["filters" => ["languageID" => $row->languageID, "languageUseCustomValue" => 1]] + ); + + return '' + . StringUtil::formatNumeric($value) + . ''; + } + } + ) + ->sortable(sortByDatabaseColumn: $this->subSelectCustomVariables()) + ]); + + $provider = new LanguageInteractions(); + $provider->addInteractions([ + new Divider(), + new EditInteraction(LanguageEditForm::class) + ]); + $this->setInteractionProvider($provider); + $this->addQuickInteraction( + new ToggleInteraction( + "enable", + "core/languages/%s/enable", + "core/languages/%s/disable", + isAvailableCallback: static function (Language $language) { + return !$language->isDefault; + } + ) + ); + + $this->setSortField('languageName'); + } + + private function subSelectUsers(): string + { + return "( + SELECT COUNT(*) + FROM wcf1_user user + WHERE languageID = language.languageID + )"; + } + + private function subSelectVariables(): string + { + return "( + SELECT COUNT(*) + FROM wcf1_language_item + WHERE languageID = language.languageID + )"; + } + + private function subSelectCustomVariables(): string + { + return "( + SELECT COUNT(*) + FROM wcf1_language_item + WHERE languageID = language.languageID + AND languageCustomItemValue IS NOT NULL + )"; + } + + #[\Override] + public function isAccessible(): bool + { + return WCF::getSession()->getPermission('admin.language.canManageLanguage'); + } + + #[\Override] + protected function createObjectList(): DatabaseObjectList + { + $list = new LanguageList(); + $list->sqlSelects = \sprintf( + "%s as users, %s as variables, %s as customVariables", + $this->subSelectUsers(), + $this->subSelectVariables(), + $this->subSelectCustomVariables() + ); + + return $list; + } + + #[\Override] + protected function getInitializedEvent(): ?IPsr14Event + { + return new LanguageGridViewInitialized($this); + } +} diff --git a/wcfsetup/install/files/lib/system/interaction/admin/LanguageInteractions.class.php b/wcfsetup/install/files/lib/system/interaction/admin/LanguageInteractions.class.php new file mode 100644 index 00000000000..c410d6f0621 --- /dev/null +++ b/wcfsetup/install/files/lib/system/interaction/admin/LanguageInteractions.class.php @@ -0,0 +1,51 @@ + + * @since 6.2 + */ +final class LanguageInteractions extends AbstractInteractionProvider +{ + public function __construct() + { + $this->addInteractions([ + new LinkInteraction("export", LanguageExportForm::class, "wcf.acp.language.export"), + new RpcInteraction( + "setAsDefault", + "core/languages/%s/default", + "wcf.acp.language.setAsDefault", + isAvailableCallback: static function (Language $language) { + return !$language->isDefault; + } + ), + new DeleteInteraction("core/languages/%s", static function (Language $language) { + return $language->isDeletable(); + }) + ]); + + EventHandler::getInstance()->fire( + new LanguageInteractionCollecting($this) + ); + } + + #[\Override] + public function getObjectClassName(): string + { + return Language::class; + } +}