diff --git a/wcfsetup/install/files/acp/templates/__labelGroupObjectTypes.tpl b/wcfsetup/install/files/acp/templates/__labelGroupObjectTypes.tpl
new file mode 100644
index 0000000000..0956561d75
--- /dev/null
+++ b/wcfsetup/install/files/acp/templates/__labelGroupObjectTypes.tpl
@@ -0,0 +1,23 @@
+
+
+
+ {foreach from=$labelObjectTypeContainers item=container}
+
+ - {$container->getTitle()}
+ -
+
+
+
+ {/foreach}
+
diff --git a/wcfsetup/install/files/acp/templates/labelGroupAdd.tpl b/wcfsetup/install/files/acp/templates/labelGroupAdd.tpl
index 78779b2195..687f8c64e2 100644
--- a/wcfsetup/install/files/acp/templates/labelGroupAdd.tpl
+++ b/wcfsetup/install/files/acp/templates/labelGroupAdd.tpl
@@ -1,145 +1,36 @@
{include file='header' pageTitle='wcf.acp.label.group.'|concat:$action}
-{include file='aclPermissions'}
-
-
-{if !$groupID|isset}
- {include file='shared_aclPermissionJavaScript' containerID='groupPermissions'}
-{else}
- {include file='shared_aclPermissionJavaScript' containerID='groupPermissions' objectID=$groupID}
-{/if}
-
-{assign var=labelForceSelection value=$forceSelection}
-
-{include file='shared_formNotice'}
-
-
+{unsafe:$form->getHtml()}
-{if $action == 'edit' && !$labelGroup->sortAlphabetically}
+{if $action == 'edit' && !$formObject->sortAlphabetically}
diff --git a/wcfsetup/install/files/lib/acp/form/LabelGroupAddForm.class.php b/wcfsetup/install/files/lib/acp/form/LabelGroupAddForm.class.php
index 8038418c42..14ae1d6eb8 100644
--- a/wcfsetup/install/files/lib/acp/form/LabelGroupAddForm.class.php
+++ b/wcfsetup/install/files/lib/acp/form/LabelGroupAddForm.class.php
@@ -2,27 +2,37 @@
namespace wcf\acp\form;
+use wcf\data\label\group\LabelGroup;
use wcf\data\label\group\LabelGroupAction;
use wcf\data\label\group\LabelGroupEditor;
use wcf\data\object\type\ObjectTypeCache;
-use wcf\form\AbstractForm;
+use wcf\form\AbstractFormBuilderForm;
use wcf\system\acl\ACLHandler;
-use wcf\system\exception\UserInputException;
+use wcf\system\form\builder\container\FormContainer;
+use wcf\system\form\builder\container\TabFormContainer;
+use wcf\system\form\builder\container\TabMenuFormContainer;
+use wcf\system\form\builder\data\processor\CustomFormDataProcessor;
+use wcf\system\form\builder\field\acl\AclFormField;
+use wcf\system\form\builder\field\BooleanFormField;
+use wcf\system\form\builder\field\IntegerFormField;
+use wcf\system\form\builder\field\TextFormField;
+use wcf\system\form\builder\IFormDocument;
+use wcf\system\form\builder\TemplateFormNode;
use wcf\system\label\object\type\ILabelObjectTypeHandler;
use wcf\system\label\object\type\LabelObjectTypeContainer;
use wcf\system\language\I18nHandler;
-use wcf\system\request\LinkHandler;
use wcf\system\WCF;
-use wcf\util\StringUtil;
/**
* Shows the label group add form.
*
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License
+ * @author Alexander Ebert, Marcel Werk
+ * @copyright 2001-2026 WoltLab GmbH
+ * @license GNU Lesser General Public License
+ *
+ * @extends AbstractFormBuilderForm
*/
-class LabelGroupAddForm extends AbstractForm
+class LabelGroupAddForm extends AbstractFormBuilderForm
{
/**
* @inheritDoc
@@ -35,103 +45,41 @@ class LabelGroupAddForm extends AbstractForm
public $neededPermissions = ['admin.content.label.canManageLabel'];
/**
- * force users to select a label
- * @var bool
+ * @inheritDoc
*/
- public $forceSelection = false;
-
- public bool $sortAlphabetically = false;
+ public $objectActionClass = LabelGroupAction::class;
/**
- * group name
- * @var string
+ * @inheritDoc
*/
- public $groupName = '';
+ public $objectEditLinkController = LabelGroupEditForm::class;
/**
- * group description
- * @var string
+ * list of label group to object type relations
+ * @var array
*/
- public $groupDescription = '';
+ public array $objectTypes = [];
/**
* list of label object type handlers
* @var ILabelObjectTypeHandler[]
*/
- public $labelObjectTypes = [];
+ protected array $labelObjectTypes = [];
/**
* list of label object type containers
* @var LabelObjectTypeContainer[]
*/
- public $labelObjectTypeContainers = [];
-
- /**
- * list of label group to object type relations
- * @var array
- */
- public $objectTypes = [];
-
- /**
- * object type id
- * @var int
- */
- public $objectTypeID = 0;
-
- /**
- * show order
- * @var int
- */
- public $showOrder = 0;
+ protected array $labelObjectTypeContainers = [];
- /**
- * @inheritDoc
- */
+ #[\Override]
public function readParameters()
{
parent::readParameters();
- $this->objectTypeID = ACLHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.label');
-
- I18nHandler::getInstance()->register('groupName');
- }
-
- /**
- * @inheritDoc
- */
- public function readFormParameters()
- {
- parent::readFormParameters();
-
- I18nHandler::getInstance()->readValues();
-
- if (I18nHandler::getInstance()->isPlainValue('groupName')) {
- $this->groupName = I18nHandler::getInstance()->getValue('groupName');
- }
-
- if (isset($_POST['groupDescription'])) {
- $this->groupDescription = StringUtil::trim($_POST['groupDescription']);
- }
- if (isset($_POST['forceSelection'])) {
- $this->forceSelection = true;
- }
- if (isset($_POST['sortAlphabetically'])) {
- $this->sortAlphabetically = true;
- }
- if (isset($_POST['objectTypes']) && \is_array($_POST['objectTypes'])) {
- $this->objectTypes = $_POST['objectTypes'];
- }
- if (isset($_POST['showOrder'])) {
- $this->showOrder = \intval($_POST['showOrder']);
- }
- }
-
- /**
- * @inheritDoc
- */
- public function readData()
- {
- // get label object type handlers
+ // Initialize label object types and containers before form building
+ // (which happens in checkPermissions), since the TemplateFormNode
+ // in createForm() needs the containers.
$objectTypes = ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.label.objectType');
foreach ($objectTypes as $objectType) {
$handler = $objectType->getProcessor();
@@ -142,30 +90,99 @@ public function readData()
$this->labelObjectTypes[$objectType->objectTypeID] = $handler;
$this->labelObjectTypeContainers[$objectType->objectTypeID] = $container;
}
+ }
- parent::readData();
+ #[\Override]
+ protected function createForm()
+ {
+ parent::createForm();
+
+ $tabMenu = TabMenuFormContainer::create('tabMenu');
+ $tabMenu->appendChildren([
+ TabFormContainer::create('general')
+ ->label('wcf.global.form.data')
+ ->appendChildren([
+ FormContainer::create('generalContainer')
+ ->appendChildren([
+ TextFormField::create('groupName')
+ ->label('wcf.global.title')
+ ->required()
+ ->autoFocus()
+ ->maximumLength(80)
+ ->i18n()
+ ->languageItemPattern('wcf.acp.label.group\d+'),
+ TextFormField::create('groupDescription')
+ ->label('wcf.global.description')
+ ->description('wcf.acp.label.group.groupDescription.description')
+ ->maximumLength(255),
+ IntegerFormField::create('showOrder')
+ ->label('wcf.global.showOrder')
+ ->minimum(0)
+ ->value(0),
+ BooleanFormField::create('forceSelection')
+ ->label('wcf.acp.label.group.forceSelection'),
+ BooleanFormField::create('sortAlphabetically')
+ ->label('wcf.acp.label.group.sortAlphabetically'),
+ AclFormField::create('aclPermissions')
+ ->label('wcf.acl.permissions')
+ ->objectType('com.woltlab.wcf.label'),
+ ]),
+ ]),
+ TabFormContainer::create('connect')
+ ->label('wcf.acp.label.group.category.connect')
+ ->appendChildren([
+ FormContainer::create('connectElements')
+ ->appendChildren([
+ TemplateFormNode::create('labelObjectTypes')
+ ->templateName('__labelGroupObjectTypes')
+ ->variables([
+ 'labelObjectTypeContainers' => $this->labelObjectTypeContainers,
+ ])
+ ]),
+ ]),
+ ]);
- // assign new values for object relations
- $this->setObjectTypeRelations();
+ $this->form->appendChildren([$tabMenu]);
}
- /**
- * @inheritDoc
- */
- public function validate()
+ #[\Override]
+ protected function finalizeForm()
{
- parent::validate();
+ parent::finalizeForm();
+
+ // The groupName column is NOT NULL without a default. When i18n values
+ // are used, hasSaveValue() returns false and groupName would be missing
+ // from the data array. This processor ensures it's always present.
+ $this->form->getDataHandler()->addProcessor(
+ new CustomFormDataProcessor(
+ 'groupNameFallback',
+ function (IFormDocument $document, array $parameters) {
+ if (!isset($parameters['data']['groupName'])) {
+ $parameters['data']['groupName'] = '';
+ }
- // validate group name
- if (!I18nHandler::getInstance()->validateValue('groupName')) {
- if (I18nHandler::getInstance()->isPlainValue('groupName')) {
- throw new UserInputException('groupName');
- } else {
- throw new UserInputException('groupName', 'multilingual');
- }
+ return $parameters;
+ }
+ )
+ );
+ }
+
+ #[\Override]
+ public function readFormParameters()
+ {
+ parent::readFormParameters();
+
+ if (isset($_POST['objectTypes']) && \is_array($_POST['objectTypes'])) {
+ $this->objectTypes = $_POST['objectTypes'];
}
+ }
- // validate object type relations
+ #[\Override]
+ public function validate()
+ {
+ parent::validate();
+
+ // Sanitize object type relations.
foreach ($this->objectTypes as $objectTypeID => $data) {
if (!isset($this->labelObjectTypes[$objectTypeID])) {
unset($this->objectTypes[$objectTypeID]);
@@ -173,112 +190,78 @@ public function validate()
}
}
- /**
- * @inheritDoc
- */
- public function save()
+ #[\Override]
+ public function readData()
{
- parent::save();
-
- // save label
- $this->objectAction = new LabelGroupAction([], 'create', [
- 'data' => \array_merge($this->additionalFields, [
- 'forceSelection' => $this->forceSelection ? 1 : 0,
- 'sortAlphabetically' => $this->sortAlphabetically ? 1 : 0,
- 'groupName' => $this->groupName,
- 'groupDescription' => $this->groupDescription,
- 'showOrder' => $this->showOrder,
- ]),
- ]);
- $returnValues = $this->objectAction->executeAction();
+ parent::readData();
- if (!I18nHandler::getInstance()->isPlainValue('groupName')) {
+ $this->setObjectTypeRelations();
+ }
+
+ #[\Override]
+ public function saved()
+ {
+ $formData = $this->form->getData();
+
+ if ($this->formAction === 'create') {
+ $group = $this->objectAction->getReturnValues()['returnValues'];
+ \assert($group instanceof LabelGroup);
+ $groupID = $group->groupID;
+ } else {
+ $groupID = $this->formObject->groupID;
+ }
+
+ // Handle i18n groupName.
+ $languageItem = 'wcf.acp.label.group' . $groupID;
+ if (isset($formData['groupName_i18n'])) {
I18nHandler::getInstance()->save(
- 'groupName',
- 'wcf.acp.label.group' . $returnValues['returnValues']->groupID,
+ $formData['groupName_i18n'],
+ $languageItem,
'wcf.acp.label',
1
);
- // update group name
- $groupEditor = new LabelGroupEditor($returnValues['returnValues']);
- $groupEditor->update([
- 'groupName' => 'wcf.acp.label.group' . $returnValues['returnValues']->groupID,
- ]);
+ if ($this->formAction === 'create') {
+ (new LabelGroupEditor($group))->update(['groupName' => $languageItem]);
+ } else {
+ (new LabelGroupEditor($this->formObject))->update(['groupName' => $languageItem]);
+ }
+ } elseif ($this->formAction === 'edit') {
+ // Switched from i18n to plain value — remove old language items.
+ I18nHandler::getInstance()->remove($languageItem);
}
- // save acl
- ACLHandler::getInstance()->save($returnValues['returnValues']->groupID, $this->objectTypeID);
- ACLHandler::getInstance()->disableAssignVariables();
+ // Save ACL.
+ ACLHandler::getInstance()->save($groupID, $formData['aclPermissions_aclObjectTypeID']);
- // save object type relations
- $this->saveObjectTypeRelations($returnValues['returnValues']->groupID);
+ // Save object type relations.
+ $this->saveObjectTypeRelations($groupID);
foreach ($this->labelObjectTypes as $labelObjectType) {
$labelObjectType->save();
}
- $this->saved();
-
- // reset values
- $this->forceSelection = false;
- $this->sortAlphabetically = false;
- $this->groupName = $this->groupDescription = '';
- $this->objectTypes = [];
- $this->showOrder = 0;
- $this->setObjectTypeRelations();
-
- // show success message
- WCF::getTPL()->assign([
- 'success' => true,
- 'objectEditLink' => LinkHandler::getInstance()->getControllerLink(
- LabelGroupEditForm::class,
- ['id' => $returnValues['returnValues']->groupID]
- ),
- ]);
-
- I18nHandler::getInstance()->reset();
- }
+ // Reset object type selections for create form.
+ if ($this->formAction === 'create') {
+ $this->objectTypes = [];
+ $this->setObjectTypeRelations();
+ }
- /**
- * @inheritDoc
- */
- public function assignVariables()
- {
- parent::assignVariables();
-
- ACLHandler::getInstance()->assignVariables($this->objectTypeID);
- I18nHandler::getInstance()->assignVariables();
-
- WCF::getTPL()->assign([
- 'action' => 'add',
- 'forceSelection' => $this->forceSelection,
- 'sortAlphabetically' => $this->sortAlphabetically,
- 'groupName' => $this->groupName,
- 'groupDescription' => $this->groupDescription,
- 'labelObjectTypeContainers' => $this->labelObjectTypeContainers,
- 'objectTypeID' => $this->objectTypeID,
- 'showOrder' => $this->showOrder,
- ]);
+ parent::saved();
}
/**
* Saves label group to object relations.
- *
- * @param ?int $groupID
- * @return void
*/
- protected function saveObjectTypeRelations($groupID)
+ protected function saveObjectTypeRelations(int $groupID): void
{
WCF::getDB()->beginTransaction();
// remove old relations
- if ($groupID !== null) {
- $sql = "DELETE FROM wcf1_label_group_to_object
- WHERE groupID = ?";
- $statement = WCF::getDB()->prepare($sql);
- $statement->execute([$groupID]);
- }
+ $sql = "DELETE FROM wcf1_label_group_to_object
+ WHERE groupID = ?";
+ $statement = WCF::getDB()->prepare($sql);
+ $statement->execute([$groupID]);
// insert new relations
if (!empty($this->objectTypes)) {
@@ -310,9 +293,8 @@ protected function saveObjectTypeRelations($groupID)
* Sets object type relations.
*
* @param ?array $data
- * @return void
*/
- protected function setObjectTypeRelations($data = null)
+ protected function setObjectTypeRelations(?array $data = null): void
{
if (!empty($_POST)) {
// use POST data
diff --git a/wcfsetup/install/files/lib/acp/form/LabelGroupEditForm.class.php b/wcfsetup/install/files/lib/acp/form/LabelGroupEditForm.class.php
index 0390b286a4..480823eb01 100644
--- a/wcfsetup/install/files/lib/acp/form/LabelGroupEditForm.class.php
+++ b/wcfsetup/install/files/lib/acp/form/LabelGroupEditForm.class.php
@@ -2,24 +2,22 @@
namespace wcf\acp\form;
+use CuyZ\Valinor\Mapper\MappingError;
use wcf\acp\page\LabelGroupListPage;
use wcf\data\label\group\LabelGroup;
-use wcf\data\label\group\LabelGroupAction;
-use wcf\form\AbstractForm;
-use wcf\system\acl\ACLHandler;
+use wcf\http\Helper;
use wcf\system\exception\IllegalLinkException;
use wcf\system\interaction\admin\LabelGroupInteractions;
use wcf\system\interaction\StandaloneInteractionContextMenuComponent;
-use wcf\system\language\I18nHandler;
use wcf\system\request\LinkHandler;
use wcf\system\WCF;
/**
* Shows the label group edit form.
*
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License
+ * @author Alexander Ebert, Marcel Werk
+ * @copyright 2001-2026 WoltLab GmbH
+ * @license GNU Lesser General Public License
*/
class LabelGroupEditForm extends LabelGroupAddForm
{
@@ -31,127 +29,49 @@ class LabelGroupEditForm extends LabelGroupAddForm
/**
* @inheritDoc
*/
- public $neededPermissions = ['admin.content.label.canManageLabel'];
+ public $formAction = 'edit';
- /**
- * group id
- * @var int
- */
- public $groupID = 0;
-
- /**
- * label group object
- * @var LabelGroup
- */
- public $group;
-
- /**
- * @inheritDoc
- */
+ #[\Override]
public function readParameters()
{
parent::readParameters();
- if (isset($_REQUEST['id'])) {
- $this->groupID = \intval($_REQUEST['id']);
- }
- $this->group = new LabelGroup($this->groupID);
- if (!$this->group->groupID) {
+ try {
+ $queryParameters = Helper::mapQueryParameters(
+ $_GET,
+ <<<'EOT'
+ array {
+ id: positive-int
+ }
+ EOT
+ );
+ } catch (MappingError) {
throw new IllegalLinkException();
}
- }
-
- /**
- * @inheritDoc
- */
- public function save()
- {
- AbstractForm::save();
-
- $this->groupName = 'wcf.acp.label.group' . $this->group->groupID;
- if (I18nHandler::getInstance()->isPlainValue('groupName')) {
- I18nHandler::getInstance()->remove($this->groupName);
- $this->groupName = I18nHandler::getInstance()->getValue('groupName');
- } else {
- I18nHandler::getInstance()->save('groupName', $this->groupName, 'wcf.acp.label', 1);
- }
-
- // update label
- $this->objectAction = new LabelGroupAction(
- [$this->groupID],
- 'update',
- [
- 'data' => \array_merge($this->additionalFields, [
- 'forceSelection' => $this->forceSelection ? 1 : 0,
- 'sortAlphabetically' => $this->sortAlphabetically ? 1 : 0,
- 'groupName' => $this->groupName,
- 'groupDescription' => $this->groupDescription,
- 'showOrder' => $this->showOrder,
- ]),
- ]
- );
- $this->objectAction->executeAction();
-
- // update acl
- ACLHandler::getInstance()->save($this->groupID, $this->objectTypeID);
- ACLHandler::getInstance()->disableAssignVariables();
-
- // update object type relations
- $this->saveObjectTypeRelations($this->groupID);
-
- foreach ($this->labelObjectTypes as $labelObjectType) {
- $labelObjectType->save();
- }
-
- $this->saved();
-
- // show success message
- WCF::getTPL()->assign('success', true);
- }
-
- /**
- * @inheritDoc
- */
- public function readData()
- {
- parent::readData();
- if (empty($_POST)) {
- I18nHandler::getInstance()->setOptions('groupName', 1, $this->group->groupName, 'wcf.acp.label.group\d+');
+ $this->formObject = new LabelGroup($queryParameters['id']);
- $this->forceSelection = ($this->group->forceSelection ? true : false);
- $this->sortAlphabetically = ($this->group->sortAlphabetically ? true : false);
- $this->groupName = $this->group->groupName;
- $this->groupDescription = $this->group->groupDescription;
- $this->showOrder = $this->group->showOrder;
+ if (!$this->formObject->getObjectID()) {
+ throw new IllegalLinkException();
}
}
- /**
- * @inheritDoc
- */
+ #[\Override]
public function assignVariables()
{
parent::assignVariables();
- I18nHandler::getInstance()->assignVariables(!empty($_POST));
-
WCF::getTPL()->assign([
- 'action' => 'edit',
- 'groupID' => $this->groupID,
- 'labelGroup' => $this->group,
'interactionContextMenu' => StandaloneInteractionContextMenuComponent::forContentHeaderButton(
new LabelGroupInteractions(),
- $this->group,
+ $this->formObject,
LinkHandler::getInstance()->getControllerLink(LabelGroupListPage::class)
),
]);
}
- /**
- * @inheritDoc
- */
- protected function setObjectTypeRelations($data = null)
+ #[\Override]
+ protected function setObjectTypeRelations(?array $data = null): void
{
if (empty($_POST)) {
// read database values
@@ -159,7 +79,7 @@ protected function setObjectTypeRelations($data = null)
FROM wcf1_label_group_to_object
WHERE groupID = ?";
$statement = WCF::getDB()->prepare($sql);
- $statement->execute([$this->groupID]);
+ $statement->execute([$this->formObject->groupID]);
$data = [];
while ($row = $statement->fetchArray()) {