Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
f8dbec9
Add `DatabaseObjectBuilder` with `TagBuilder` implementation
BurntimeX Jun 15, 2026
c1ec3a5
Migrate tag forms to `DatabaseObjectBuilder` with command and events
BurntimeX Jun 25, 2026
6632935
Simplify checks in `AbstractDatabaseObjectBuilderForm`
BurntimeX Jun 25, 2026
b90f591
Add `beforeDeleteAll()` hook and seal `DatabaseObjectBuilder` API
BurntimeX Jun 26, 2026
51cfd92
Use fully qualified `\array_merge()` calls
BurntimeX Jun 26, 2026
a73221e
Add `setID()` for explicit ID assignment on create
BurntimeX Jun 26, 2026
eb6cf2b
Add `loadValueCallback()` to `IFormField` for custom value loading
BurntimeX Jun 28, 2026
7f1c07a
Return void from save value callbacks
BurntimeX Jun 28, 2026
2056d4e
Add `updateCounters()` to `DatabaseObjectBuilder
BurntimeX Jun 30, 2026
3a8cb2f
Add `getHtmlInputProcessor()` to `WysiwygFormField`
BurntimeX Jun 30, 2026
73d38c5
Add `afterSave()` hook and use `DatabaseObject` for the form object
BurntimeX Jun 30, 2026
0a42c91
Drop `DatabaseObjectBuilder::fastSave()`
BurntimeX Jun 30, 2026
43b15ff
Add `getObject()` to `DatabaseObjectBuilder`
BurntimeX Jun 30, 2026
e81a351
Allow accessing wysiwyg and attachment fields before the form is built
BurntimeX Jun 30, 2026
ed0fadd
Use a dedicated template for the form field in save/load callback types
BurntimeX Jun 30, 2026
a5569f3
Honor `loadValueCallback` in `TI18nFormField::updatedObject()`
BurntimeX Jun 30, 2026
0aaf82d
Validate required properties in `DatabaseObjectBuilder::create()`
BurntimeX Jul 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 61 additions & 60 deletions wcfsetup/install/files/lib/acp/form/TagAddForm.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@

namespace wcf\acp\form;

use wcf\data\IStorableObject;
use wcf\command\tag\CreateTag;
use wcf\command\tag\UpdateTag;
use wcf\data\DatabaseObjectBuilder;
use wcf\data\tag\Tag;
use wcf\data\tag\TagAction;
use wcf\data\tag\TagBuilder;
use wcf\data\tag\TagList;
use wcf\form\AbstractFormBuilderForm;
use wcf\form\AbstractDatabaseObjectBuilderForm;
use wcf\system\form\builder\container\FormContainer;
use wcf\system\form\builder\data\processor\CustomFormDataProcessor;
use wcf\system\form\builder\field\IFormField;
use wcf\system\form\builder\field\SingleSelectionFormField;
use wcf\system\form\builder\field\tag\TagFormField;
use wcf\system\form\builder\field\TextFormField;
use wcf\system\form\builder\field\validation\FormFieldValidationError;
use wcf\system\form\builder\field\validation\FormFieldValidator;
use wcf\system\form\builder\IFormDocument;
use wcf\system\form\builder\TemplateFormNode;
use wcf\system\language\LanguageFactory;
use wcf\system\WCF;
Expand All @@ -23,13 +24,13 @@
/**
* Shows the tag add form.
*
* @author Olaf Braun, Tim Duesterhus
* @copyright 2001-2024 WoltLab GmbH
* @author Olaf Braun, Tim Duesterhus, Marcel Werk
* @copyright 2001-2026 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
*
* @extends AbstractFormBuilderForm<Tag>
* @extends AbstractDatabaseObjectBuilderForm<Tag, TagBuilder>
*/
class TagAddForm extends AbstractFormBuilderForm
class TagAddForm extends AbstractDatabaseObjectBuilderForm
{
/**
* @inheritDoc
Expand All @@ -49,18 +50,31 @@ class TagAddForm extends AbstractFormBuilderForm
/**
* @inheritDoc
*/
public $objectActionClass = TagAction::class;
public string $objectEditLinkController = TagEditForm::class;

/**
* @inheritDoc
*/
public $objectEditLinkController = TagEditForm::class;
#[\Override]
protected function getDatabaseObjectBuilder(): TagBuilder
{
if ($this->formObject !== null) {
return TagBuilder::forUpdate($this->formObject);
}

return TagBuilder::forCreate();
}

#[\Override]
protected function createForm()
protected function getCommand(DatabaseObjectBuilder $builder): callable
{
parent::createForm();
if ($this->formObject !== null) {
return new UpdateTag($builder);
}

return new CreateTag($builder);
}

#[\Override]
protected function createForm(): void
{
$contentLanguages = LanguageFactory::getInstance()->getContentLanguages();

$this->form->appendChildren([
Expand All @@ -70,12 +84,20 @@ protected function createForm()
->label('wcf.global.name')
->required()
->maximumLength(\TAGGING_MAX_TAG_LENGTH)
->saveValueCallback(static function (TagBuilder $builder, IFormField $field) {
$builder->setName(
\str_replace(',', '', StringUtil::trim($field->getSaveValue()))
);
})
->loadValueCallback(static function (Tag $object, IFormField $field) {
$field->value($object->name);
})
->addValidator(
new FormFieldValidator('duplicateTagValidator', function (TextFormField $field) {
$languageIDFormField = $field->getDocument()->getFormField('languageID');
$languageID = $languageIDFormField->getValue();

$tag = Tag::getTag($field->getValue(), $languageID);
$tag = Tag::getTag($field->getValue(), $languageID ?? 0);
if ($tag !== null && $tag->tagID !== $this->formObject?->tagID) {
$field->addValidationError(
new FormFieldValidationError(
Expand All @@ -92,10 +114,30 @@ protected function createForm()
->options($contentLanguages)
->value(isset($contentLanguages[WCF::getLanguage()->languageID]) ? WCF::getLanguage()->languageID : null)
->immutable($this->formAction !== 'create')
->required(),
->required()
->saveValueCallback(static function (TagBuilder $builder, IFormField $field) {
$builder->setLanguageID(
(int)$field->getSaveValue()
);
})->loadValueCallback(static function (Tag $object, IFormField $field) {
$field->value($object->languageID);
}),
TagFormField::create('synonyms')
->available($this->formObject?->synonymFor === null)
->label('wcf.acp.tag.synonyms'),
->label('wcf.acp.tag.synonyms')
->saveValueCallback(static function (TagBuilder $builder, IFormField $field) {
$builder->setSynonyms(
$field->getSaveValue() ?? []
);
})->loadValueCallback(static function (Tag $object, IFormField $field) {
$synonymList = new TagList();
$synonymList->getConditionBuilder()->add('synonymFor = ?', [$object->getObjectID()]);
$synonymList->readObjects();
$field->value(\array_map(
static fn($synonym) => $synonym->name,
$synonymList->getObjects()
));
}),
TemplateFormNode::create('tagSynonymFor')
->available($this->formObject?->synonymFor !== null)
->variables([
Expand All @@ -105,45 +147,4 @@ protected function createForm()
])
]);
}

#[\Override]
protected function finalizeForm()
{
parent::finalizeForm();

$this->form->getDataHandler()
->addProcessor(
new CustomFormDataProcessor(
'tagNameProcessor',
static function (IFormDocument $document, array $parameters) {
$parameters['data']['name'] = \str_replace(
',',
'',
StringUtil::trim($parameters['data']['name'])
);

return $parameters;
}
)
)
->addProcessor(
new CustomFormDataProcessor(
'synonymsProcessor',
null,
static function (IFormDocument $document, array $data, IStorableObject $tag) {
\assert($tag instanceof Tag);

$synonymList = new TagList();
$synonymList->getConditionBuilder()->add('synonymFor = ?', [$tag->tagID]);
$synonymList->readObjects();
$data['synonyms'] = [];
foreach ($synonymList as $synonym) {
$data['synonyms'][] = $synonym->name;
}

return $data;
}
)
);
}
}
2 changes: 1 addition & 1 deletion wcfsetup/install/files/lib/acp/form/TagEditForm.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class TagEditForm extends TagAddForm
/**
* @inheritDoc
*/
public $formAction = 'edit';
public string $formAction = 'edit';

#[\Override]
public function readParameters()
Expand Down
32 changes: 32 additions & 0 deletions wcfsetup/install/files/lib/command/tag/CreateTag.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace wcf\command\tag;

use wcf\data\tag\Tag;
use wcf\data\tag\TagBuilder;
use wcf\event\tag\TagCreated;
use wcf\system\event\EventHandler;

/**
* Creates a new tag.
*
* @author Marcel Werk
* @copyright 2001-2026 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.3
*/
final class CreateTag
{
public function __construct(
private readonly TagBuilder $builder,
) {}

public function __invoke(): Tag
{
$tag = $this->builder->save();

EventHandler::getInstance()->fire(new TagCreated($tag));

return $tag;
}
}
32 changes: 32 additions & 0 deletions wcfsetup/install/files/lib/command/tag/UpdateTag.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace wcf\command\tag;

use wcf\data\tag\Tag;
use wcf\data\tag\TagBuilder;
use wcf\event\tag\TagUpdated;
use wcf\system\event\EventHandler;

/**
* Updates a tag.
*
* @author Marcel Werk
* @copyright 2001-2026 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.3
*/
final class UpdateTag
{
public function __construct(
private readonly TagBuilder $builder,
) {}

public function __invoke(): Tag
{
$tag = $this->builder->save();

EventHandler::getInstance()->fire(new TagUpdated($tag));

return $tag;
}
}
Loading
Loading