From c1f8a933fcdb746799e498bb2ecd0dfc77707f8d Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Wed, 16 Apr 2025 15:36:14 +0200 Subject: [PATCH 01/33] Add `SelectOptionsFormField` --- .../shared_selectOptionsFormField.tpl | 16 ++ .../Core/Form/Builder/Field/SelectOptions.ts | 138 ++++++++++++++++++ .../Core/Form/Builder/Field/SelectOptions.js | 101 +++++++++++++ .../field/SelectOptionsFormField.class.php | 83 +++++++++++ wcfsetup/install/files/style/layout/form.scss | 11 ++ wcfsetup/install/lang/de.xml | 2 + wcfsetup/install/lang/en.xml | 2 + 7 files changed, 353 insertions(+) create mode 100644 com.woltlab.wcf/templates/shared_selectOptionsFormField.tpl create mode 100644 ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js create mode 100644 wcfsetup/install/files/lib/system/form/builder/field/SelectOptionsFormField.class.php diff --git a/com.woltlab.wcf/templates/shared_selectOptionsFormField.tpl b/com.woltlab.wcf/templates/shared_selectOptionsFormField.tpl new file mode 100644 index 00000000000..bacfd991a7b --- /dev/null +++ b/com.woltlab.wcf/templates/shared_selectOptionsFormField.tpl @@ -0,0 +1,16 @@ + + + diff --git a/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts b/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts new file mode 100644 index 00000000000..222a6c9ee21 --- /dev/null +++ b/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts @@ -0,0 +1,138 @@ +import { identify } from "WoltLabSuite/Core/Dom/Util"; +import { getPhrase } from "WoltLabSuite/Core/Language"; +import { getValues, init as initI18n } from "WoltLabSuite/Core/Language/Input"; + +type Data = { + key: string; + value: Record; +}; + +type Languages = Record; + +let _languages: Languages; + +export function setup(formField: HTMLInputElement, languages: Languages): void { + _languages = languages; + + createUi(formField); + + formField.form?.addEventListener("submit", () => { + setHiddenValue(formField); + }); +} + +function createUi(formField: HTMLInputElement): void { + const ul = document.createElement("ul"); + ul.classList.add("selectOptionsList"); + formField.parentElement?.append(ul); + + if (formField.value) { + const data = JSON.parse(formField.value) as Data[]; + data.forEach((option) => { + createRow(ul, option); + }); + return; + } + + createRow(ul); +} + +function createRow(ul: HTMLUListElement, option?: Data): void { + const li = document.createElement("li"); + li.classList.add("selectOptionsListItem"); + ul.append(li); + + const addButton = getAddButton(); + addButton.addEventListener("click", () => { + createRow(ul); + }); + + const deleteButton = getDeleteButton(); + deleteButton.addEventListener("click", () => { + li.remove(); + + if (!ul.childElementCount) { + createRow(ul); + } + }); + + const keyInput = getKeyInput(); + keyInput.value = option ? option.key : ""; + + const equalsIcon = document.createElement("fa-icon"); + equalsIcon.setIcon("equals"); + + const valueInput = getValueInput(); + + li.append(addButton, deleteButton, keyInput, equalsIcon, valueInput); + + const hasI18nValues = option && !Object.hasOwn(option.value, 0); + + initI18n(identify(valueInput), hasI18nValues ? option.value : {}, _languages, false); + + if (!hasI18nValues) { + valueInput.value = option?.value[0] ?? ""; + } +} + +function getAddButton(): HTMLButtonElement { + const addIcon = document.createElement("fa-icon"); + addIcon.setIcon("plus"); + + const addButton = document.createElement("button"); + addButton.type = "button"; + addButton.append(addIcon); + addButton.classList.add("jsTooltip"); + addButton.title = getPhrase("wcf.global.button.add"); + + return addButton; +} + +function getDeleteButton(): HTMLButtonElement { + const deleteIcon = document.createElement("fa-icon"); + deleteIcon.setIcon("xmark"); + + const deleteButton = document.createElement("button"); + deleteButton.type = "button"; + deleteButton.append(deleteIcon); + deleteButton.classList.add("jsTooltip"); + deleteButton.title = getPhrase("wcf.global.button.delete"); + + return deleteButton; +} + +function getKeyInput(): HTMLInputElement { + const keyInput = document.createElement("input"); + keyInput.classList.add("selectOptionsListItem__key"); + keyInput.placeholder = getPhrase("wcf.form.selectOptions.key"); + keyInput.type = "text"; + keyInput.required = true; + + return keyInput; +} + +function getValueInput(): HTMLInputElement { + const valueInput = document.createElement("input"); + valueInput.classList.add("selectOptionsListItem__value"); + valueInput.placeholder = getPhrase("wcf.form.selectOptions.value"); + valueInput.type = "text"; + valueInput.required = true; + + return valueInput; +} + +function setHiddenValue(formField: HTMLInputElement): void { + const data: Data[] = []; + + formField.parentElement?.querySelectorAll(".selectOptionsListItem").forEach((li) => { + const key = li.querySelector(".selectOptionsListItem__key")!.value; + const valueInput = li.querySelector(".selectOptionsListItem__value")!; + + data.push({ + key, + value: Object.fromEntries(getValues(valueInput.id)), + }); + }); + + formField.value = JSON.stringify(data); +} diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js new file mode 100644 index 00000000000..7a989b0f89d --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js @@ -0,0 +1,101 @@ +define(["require", "exports", "WoltLabSuite/Core/Dom/Util", "WoltLabSuite/Core/Language", "WoltLabSuite/Core/Language/Input"], function (require, exports, Util_1, Language_1, Input_1) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.setup = setup; + let _languages; + function setup(formField, languages) { + _languages = languages; + createUi(formField); + formField.form?.addEventListener("submit", () => { + setHiddenValue(formField); + }); + } + function createUi(formField) { + const ul = document.createElement("ul"); + ul.classList.add("selectOptionsList"); + formField.parentElement?.append(ul); + if (formField.value) { + const data = JSON.parse(formField.value); + data.forEach((option) => { + createRow(ul, option); + }); + return; + } + createRow(ul); + } + function createRow(ul, option) { + const li = document.createElement("li"); + li.classList.add("selectOptionsListItem"); + ul.append(li); + const addButton = getAddButton(); + addButton.addEventListener("click", () => { + createRow(ul); + }); + const deleteButton = getDeleteButton(); + deleteButton.addEventListener("click", () => { + li.remove(); + if (!ul.childElementCount) { + createRow(ul); + } + }); + const keyInput = getKeyInput(); + keyInput.value = option ? option.key : ""; + const equalsIcon = document.createElement("fa-icon"); + equalsIcon.setIcon("equals"); + const valueInput = getValueInput(); + li.append(addButton, deleteButton, keyInput, equalsIcon, valueInput); + const hasI18nValues = option && !Object.hasOwn(option.value, 0); + (0, Input_1.init)((0, Util_1.identify)(valueInput), hasI18nValues ? option.value : {}, _languages, false); + if (!hasI18nValues) { + valueInput.value = option?.value[0] ?? ""; + } + } + function getAddButton() { + const addIcon = document.createElement("fa-icon"); + addIcon.setIcon("plus"); + const addButton = document.createElement("button"); + addButton.type = "button"; + addButton.append(addIcon); + addButton.classList.add("jsTooltip"); + addButton.title = (0, Language_1.getPhrase)("wcf.global.button.add"); + return addButton; + } + function getDeleteButton() { + const deleteIcon = document.createElement("fa-icon"); + deleteIcon.setIcon("xmark"); + const deleteButton = document.createElement("button"); + deleteButton.type = "button"; + deleteButton.append(deleteIcon); + deleteButton.classList.add("jsTooltip"); + deleteButton.title = (0, Language_1.getPhrase)("wcf.global.button.delete"); + return deleteButton; + } + function getKeyInput() { + const keyInput = document.createElement("input"); + keyInput.classList.add("selectOptionsListItem__key"); + keyInput.placeholder = (0, Language_1.getPhrase)("wcf.form.selectOptions.key"); + keyInput.type = "text"; + keyInput.required = true; + return keyInput; + } + function getValueInput() { + const valueInput = document.createElement("input"); + valueInput.classList.add("selectOptionsListItem__value"); + valueInput.placeholder = (0, Language_1.getPhrase)("wcf.form.selectOptions.value"); + valueInput.type = "text"; + valueInput.required = true; + return valueInput; + } + function setHiddenValue(formField) { + const data = []; + formField.parentElement?.querySelectorAll(".selectOptionsListItem").forEach((li) => { + const key = li.querySelector(".selectOptionsListItem__key").value; + const valueInput = li.querySelector(".selectOptionsListItem__value"); + data.push({ + key, + value: Object.fromEntries((0, Input_1.getValues)(valueInput.id)), + }); + }); + formField.value = JSON.stringify(data); + } +}); diff --git a/wcfsetup/install/files/lib/system/form/builder/field/SelectOptionsFormField.class.php b/wcfsetup/install/files/lib/system/form/builder/field/SelectOptionsFormField.class.php new file mode 100644 index 00000000000..f6651d6048b --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/builder/field/SelectOptionsFormField.class.php @@ -0,0 +1,83 @@ + + * @since 6.2 + */ +final class SelectOptionsFormField extends AbstractFormField implements + ICssClassFormField, + IImmutableFormField +{ + use TCssClassFormField; + use TImmutableFormField; + + /** + * @inheritDoc + */ + protected $javaScriptDataHandlerModule = 'WoltLabSuite/Core/Form/Builder/Field/Value'; + + /** + * @inheritDoc + */ + protected $templateName = 'shared_selectOptionsFormField'; + + #[\Override] + public function readValue() + { + if ($this->getDocument()->hasRequestData($this->getPrefixedId())) { + $value = $this->getDocument()->getRequestData($this->getPrefixedId()); + + if (\is_string($value)) { + $this->value = $value !== '' ? $value : null; + } + } + + return $this; + } + + #[\Override] + public function getHtmlVariables() + { + return [ + 'availableLanguages' => LanguageFactory::getInstance()->getLanguages(), + ]; + } + + #[\Override] + public function validate() + { + try { + $mapper = (new MapperBuilder())->mapper(); + $mapper->map( + <<<'EOT' + list, + }> + EOT, + Source::json($this->getValue()) + ); + } catch (MappingError) { + $this->addValidationError(new FormFieldValidationError('empty')); + } + + parent::validate(); + } +} diff --git a/wcfsetup/install/files/style/layout/form.scss b/wcfsetup/install/files/style/layout/form.scss index cc78a7fff84..f6cd3f56a32 100644 --- a/wcfsetup/install/files/style/layout/form.scss +++ b/wcfsetup/install/files/style/layout/form.scss @@ -358,3 +358,14 @@ html[data-color-scheme="dark"] .passwordStrengthScore { --score-3: #689f38; --score-4: #1b5e20; } + +.selectOptionsList { + display: flex; + flex-direction: column; + gap: 3px; +} + +.selectOptionsListItem { + display: flex; + align-items: center; +} diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index 00c7421d0f4..8b7ee42a8e5 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -4115,6 +4115,8 @@ Dateianhänge: 1}{#$minimum} Dateien{else}eine Datei{/if} hochladen.]]> 1}{#$maximum} Dateien{else}eine Datei{/if} hochladen.]]> + + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index 98961d44d1f..ed2ed74a69d 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -4061,6 +4061,8 @@ Attachments: 1}{#$minimum} files{else}one file{/if}.]]> 1}{#$maximum} files{else}one file{/if}.]]> + + From 697a60e96365e1e013a6379a6203378ee14969a4 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Wed, 16 Apr 2025 15:45:09 +0200 Subject: [PATCH 02/33] Add additional row on enter --- .../Core/Form/Builder/Field/SelectOptions.ts | 21 +++++++++++++++++-- .../Core/Form/Builder/Field/SelectOptions.js | 20 ++++++++++++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts b/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts index 222a6c9ee21..9aa2b002bf7 100644 --- a/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts +++ b/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts @@ -37,14 +37,14 @@ function createUi(formField: HTMLInputElement): void { createRow(ul); } -function createRow(ul: HTMLUListElement, option?: Data): void { +function createRow(ul: HTMLUListElement, option?: Data, autoFocus: boolean = false): void { const li = document.createElement("li"); li.classList.add("selectOptionsListItem"); ul.append(li); const addButton = getAddButton(); addButton.addEventListener("click", () => { - createRow(ul); + createRow(ul, undefined, true); }); const deleteButton = getDeleteButton(); @@ -57,12 +57,25 @@ function createRow(ul: HTMLUListElement, option?: Data): void { }); const keyInput = getKeyInput(); + keyInput.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + event.preventDefault(); + createRow(ul, undefined, true); + } + }); keyInput.value = option ? option.key : ""; const equalsIcon = document.createElement("fa-icon"); equalsIcon.setIcon("equals"); const valueInput = getValueInput(); + valueInput.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + event.preventDefault(); + createRow(ul); + createRow(ul, undefined, true); + } + }); li.append(addButton, deleteButton, keyInput, equalsIcon, valueInput); @@ -73,6 +86,10 @@ function createRow(ul: HTMLUListElement, option?: Data): void { if (!hasI18nValues) { valueInput.value = option?.value[0] ?? ""; } + + if (autoFocus) { + keyInput.focus(); + } } function getAddButton(): HTMLButtonElement { diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js index 7a989b0f89d..93198638b8e 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js @@ -23,13 +23,13 @@ define(["require", "exports", "WoltLabSuite/Core/Dom/Util", "WoltLabSuite/Core/L } createRow(ul); } - function createRow(ul, option) { + function createRow(ul, option, autoFocus = false) { const li = document.createElement("li"); li.classList.add("selectOptionsListItem"); ul.append(li); const addButton = getAddButton(); addButton.addEventListener("click", () => { - createRow(ul); + createRow(ul, undefined, true); }); const deleteButton = getDeleteButton(); deleteButton.addEventListener("click", () => { @@ -39,16 +39,32 @@ define(["require", "exports", "WoltLabSuite/Core/Dom/Util", "WoltLabSuite/Core/L } }); const keyInput = getKeyInput(); + keyInput.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + event.preventDefault(); + createRow(ul, undefined, true); + } + }); keyInput.value = option ? option.key : ""; const equalsIcon = document.createElement("fa-icon"); equalsIcon.setIcon("equals"); const valueInput = getValueInput(); + valueInput.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + event.preventDefault(); + createRow(ul); + createRow(ul, undefined, true); + } + }); li.append(addButton, deleteButton, keyInput, equalsIcon, valueInput); const hasI18nValues = option && !Object.hasOwn(option.value, 0); (0, Input_1.init)((0, Util_1.identify)(valueInput), hasI18nValues ? option.value : {}, _languages, false); if (!hasI18nValues) { valueInput.value = option?.value[0] ?? ""; } + if (autoFocus) { + keyInput.focus(); + } } function getAddButton() { const addIcon = document.createElement("fa-icon"); From c06763d1ae4349c6d909f11ac41c539e2b296af4 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Wed, 16 Apr 2025 16:26:33 +0200 Subject: [PATCH 03/33] Add drag and drop support --- .../Core/Form/Builder/Field/SelectOptions.ts | 30 +++++++++++++++---- .../Core/Form/Builder/Field/SelectOptions.js | 28 +++++++++++++---- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts b/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts index 9aa2b002bf7..093ad0ab72c 100644 --- a/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts +++ b/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts @@ -1,6 +1,7 @@ import { identify } from "WoltLabSuite/Core/Dom/Util"; import { getPhrase } from "WoltLabSuite/Core/Language"; import { getValues, init as initI18n } from "WoltLabSuite/Core/Language/Input"; +import Sortable from "sortablejs"; type Data = { key: string; @@ -14,14 +15,22 @@ let _languages: Languages; export function setup(formField: HTMLInputElement, languages: Languages): void { _languages = languages; - createUi(formField); + const ul = createUi(formField); formField.form?.addEventListener("submit", () => { setHiddenValue(formField); }); + + new Sortable(ul, { + direction: "vertical", + animation: 150, + fallbackOnBody: true, + draggable: "li", + handle: ".selectOptionsListItem__handle", + }); } -function createUi(formField: HTMLInputElement): void { +function createUi(formField: HTMLInputElement): HTMLUListElement { const ul = document.createElement("ul"); ul.classList.add("selectOptionsList"); formField.parentElement?.append(ul); @@ -31,10 +40,11 @@ function createUi(formField: HTMLInputElement): void { data.forEach((option) => { createRow(ul, option); }); - return; + } else { + createRow(ul); } - createRow(ul); + return ul; } function createRow(ul: HTMLUListElement, option?: Data, autoFocus: boolean = false): void { @@ -77,7 +87,7 @@ function createRow(ul: HTMLUListElement, option?: Data, autoFocus: boolean = fal } }); - li.append(addButton, deleteButton, keyInput, equalsIcon, valueInput); + li.append(getSortableHandle(), addButton, deleteButton, keyInput, equalsIcon, valueInput); const hasI18nValues = option && !Object.hasOwn(option.value, 0); @@ -138,6 +148,16 @@ function getValueInput(): HTMLInputElement { return valueInput; } +function getSortableHandle(): HTMLElement { + const icon = document.createElement("fa-icon"); + icon.setIcon("up-down"); + const handle = document.createElement("span"); + handle.append(icon); + handle.classList.add("selectOptionsListItem__handle"); + + return handle; +} + function setHiddenValue(formField: HTMLInputElement): void { const data: Data[] = []; diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js index 93198638b8e..556d952fd76 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js @@ -1,14 +1,22 @@ -define(["require", "exports", "WoltLabSuite/Core/Dom/Util", "WoltLabSuite/Core/Language", "WoltLabSuite/Core/Language/Input"], function (require, exports, Util_1, Language_1, Input_1) { +define(["require", "exports", "tslib", "WoltLabSuite/Core/Dom/Util", "WoltLabSuite/Core/Language", "WoltLabSuite/Core/Language/Input", "sortablejs"], function (require, exports, tslib_1, Util_1, Language_1, Input_1, sortablejs_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.setup = setup; + sortablejs_1 = tslib_1.__importDefault(sortablejs_1); let _languages; function setup(formField, languages) { _languages = languages; - createUi(formField); + const ul = createUi(formField); formField.form?.addEventListener("submit", () => { setHiddenValue(formField); }); + new sortablejs_1.default(ul, { + direction: "vertical", + animation: 150, + fallbackOnBody: true, + draggable: "li", + handle: ".selectOptionsListItem__handle", + }); } function createUi(formField) { const ul = document.createElement("ul"); @@ -19,9 +27,11 @@ define(["require", "exports", "WoltLabSuite/Core/Dom/Util", "WoltLabSuite/Core/L data.forEach((option) => { createRow(ul, option); }); - return; } - createRow(ul); + else { + createRow(ul); + } + return ul; } function createRow(ul, option, autoFocus = false) { const li = document.createElement("li"); @@ -56,7 +66,7 @@ define(["require", "exports", "WoltLabSuite/Core/Dom/Util", "WoltLabSuite/Core/L createRow(ul, undefined, true); } }); - li.append(addButton, deleteButton, keyInput, equalsIcon, valueInput); + li.append(getSortableHandle(), addButton, deleteButton, keyInput, equalsIcon, valueInput); const hasI18nValues = option && !Object.hasOwn(option.value, 0); (0, Input_1.init)((0, Util_1.identify)(valueInput), hasI18nValues ? option.value : {}, _languages, false); if (!hasI18nValues) { @@ -102,6 +112,14 @@ define(["require", "exports", "WoltLabSuite/Core/Dom/Util", "WoltLabSuite/Core/L valueInput.required = true; return valueInput; } + function getSortableHandle() { + const icon = document.createElement("fa-icon"); + icon.setIcon("up-down"); + const handle = document.createElement("span"); + handle.append(icon); + handle.classList.add("selectOptionsListItem__handle"); + return handle; + } function setHiddenValue(formField) { const data = []; formField.parentElement?.querySelectorAll(".selectOptionsListItem").forEach((li) => { From 24140e5474255eed0040975043e080d5a0255b2e Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Tue, 22 Apr 2025 14:45:02 +0200 Subject: [PATCH 04/33] Basic form option types --- .../option/FormOptionCollecting.class.php | 38 ++++++++++ ...ConfigurationFormFieldCollecting.class.php | 38 ++++++++++ .../form/option/AbstractFormOption.class.php | 20 +++++ .../form/option/FormOptionHandler.class.php | 73 +++++++++++++++++++ .../system/form/option/IFormOption.class.php | 22 ++++++ .../form/option/SelectFormOption.class.php | 58 +++++++++++++++ .../SharedConfigurationFormFields.class.php | 70 ++++++++++++++++++ .../form/option/TextFormOption.class.php | 40 ++++++++++ .../form/option/TextareaFormOption.class.php | 40 ++++++++++ 9 files changed, 399 insertions(+) create mode 100644 wcfsetup/install/files/lib/event/form/option/FormOptionCollecting.class.php create mode 100644 wcfsetup/install/files/lib/event/form/option/SharedConfigurationFormFieldCollecting.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/IFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php diff --git a/wcfsetup/install/files/lib/event/form/option/FormOptionCollecting.class.php b/wcfsetup/install/files/lib/event/form/option/FormOptionCollecting.class.php new file mode 100644 index 00000000000..17fd255def4 --- /dev/null +++ b/wcfsetup/install/files/lib/event/form/option/FormOptionCollecting.class.php @@ -0,0 +1,38 @@ + + * @since 6.2 + */ +final class FormOptionCollecting implements IPsr14Event +{ + /** + * @var array + */ + private array $options = []; + + /** + * Registers a new form option. + */ + public function register(IFormOption $option): void + { + $this->options[$option->getId()] = $option; + } + + /** + * @return array + */ + public function getOptions(): array + { + return $this->options; + } +} diff --git a/wcfsetup/install/files/lib/event/form/option/SharedConfigurationFormFieldCollecting.class.php b/wcfsetup/install/files/lib/event/form/option/SharedConfigurationFormFieldCollecting.class.php new file mode 100644 index 00000000000..8fa2fcc8f50 --- /dev/null +++ b/wcfsetup/install/files/lib/event/form/option/SharedConfigurationFormFieldCollecting.class.php @@ -0,0 +1,38 @@ + + * @since 6.2 + */ +final class SharedConfigurationFormFieldCollecting implements IPsr14Event +{ + /** + * @var array + */ + private array $formFields = []; + + /** + * Registers a new shared configuration form field. + */ + public function register(IFormField $formField): void + { + $this->formFields[$formField->getId()] = $formField; + } + + /** + * @return array + */ + public function getFormFields(): array + { + return $this->formFields; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php new file mode 100644 index 00000000000..a8cec62d4ae --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php @@ -0,0 +1,20 @@ + + * @since 6.2 + */ +abstract class AbstractFormOption implements IFormOption +{ + #[\Override] + public function getConfigurationFormFields(): array + { + return []; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php b/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php new file mode 100644 index 00000000000..e7ebf11e148 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php @@ -0,0 +1,73 @@ + + * @since 6.2 + */ +final class FormOptionHandler extends SingletonFactory +{ + /** + * @var array + */ + private array $options; + + #[\Override] + protected function init() + { + $this->options = \array_merge($this->getDefaultFormOptions(), $this->getEventFormOptions()); + } + + /** + * @return array + */ + private function getDefaultFormOptions(): array + { + $options = []; + + foreach ( + [ + new TextFormOption(), + new TextareaFormOption(), + new SelectFormOption() + ] as $defaultOption + ) { + $options[$defaultOption->getId()] = $defaultOption; + } + + return $options; + } + + /** + * @return array + */ + private function getEventFormOptions(): array + { + $event = new FormOptionCollecting(); + EventHandler::getInstance()->fire($event); + + return $event->getOptions(); + } + + /** + * @return array + */ + public function getOptions(): array + { + return $this->options; + } + + public function getOption(string $identifier): ?IFormOption + { + return $this->options[$identifier] ?? null; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php new file mode 100644 index 00000000000..73dec81ede6 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php @@ -0,0 +1,22 @@ + + * @since 6.2 + */ +interface IFormOption +{ + public function getId(): string; + + public function getFormField(string $id, array $configurationData = []): AbstractFormField; + + public function getConfigurationFormFields(): array; +} diff --git a/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php new file mode 100644 index 00000000000..fcf6d23009d --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php @@ -0,0 +1,58 @@ + + * @since 6.2 + */ +class SelectFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'select'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): AbstractFormField + { + $formField = SelectFormField::create($id); + + if (isset($configurationData['selectOptions'])) { + $selectOptions = []; + foreach (JSON::decode($configurationData['selectOptions']) as $selectOption) { + if (isset($selectOption['value'][0])) { + $value = $selectOption['value'][0]; + } else if (isset($selectOption['value'][WCF::getLanguage()->languageID])) { + $value = $selectOption['value'][WCF::getLanguage()->languageID]; + } else { + $value = reset($selectOption['value']); + } + + $selectOptions[$selectOption['key']] = $value; + } + + $formField->options($selectOptions); + } + + return $formField; + } + + #[\Override] + public function getConfigurationFormFields(): array + { + return [ + 'selectOptions' + ]; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php b/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php new file mode 100644 index 00000000000..923ca40a208 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php @@ -0,0 +1,70 @@ + + * @since 6.2 + */ +final class SharedConfigurationFormFields extends SingletonFactory +{ + /** + * @var array + */ + private array $formFields; + + #[\Override] + protected function init() + { + $this->formFields = \array_merge($this->getDefaultFormFields(), $this->getEventFormFields()); + } + + /** + * @return array + */ + private function getDefaultFormFields(): array + { + return [ + 'maxLength' => IntegerFormField::create('maxLength') + ->label('wcf.acp.customOption.maxLength'), + 'selectOptions' => SelectOptionsFormField::create('selectOptions') + ->label('wcf.acp.customOption.selectOptions') + ->required() + ]; + } + + /** + * @return array + */ + private function getEventFormFields(): array + { + $event = new SharedConfigurationFormFieldCollecting(); + EventHandler::getInstance()->fire($event); + + return $event->getFormFields(); + } + + /** + * @return array + */ + public function getFormFields(): array + { + return $this->formFields; + } + + public function getFormField(string $identifier): ?IFormField + { + return $this->formFields[$identifier] ?? null; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php new file mode 100644 index 00000000000..22fd10a6153 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php @@ -0,0 +1,40 @@ + + * @since 6.2 + */ +class TextFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'text'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): AbstractFormField + { + $formField = TextFormField::create($id); + if (isset($configurationData['maxLength'])) { + $formField->maximumLength($configurationData['maxLength']); + } + + return $formField; + } + + #[\Override] + public function getConfigurationFormFields(): array + { + return ['maxLength']; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php new file mode 100644 index 00000000000..ae1c6bc4fcb --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php @@ -0,0 +1,40 @@ + + * @since 6.2 + */ +class TextareaFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'textarea'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): AbstractFormField + { + $formField = MultilineTextFormField::create($id); + if (isset($configurationData['maxLength'])) { + $formField->maximumLength($configurationData['maxLength']); + } + + return $formField; + } + + #[\Override] + public function getConfigurationFormFields(): array + { + return ['maxLength']; + } +} From 9c75f50c303c309af1e88076b475207ce2774445 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Tue, 22 Apr 2025 14:49:46 +0200 Subject: [PATCH 05/33] Use new form options for the contact form --- com.woltlab.wcf/templates/contact.tpl | 93 +----- .../database/update_com.woltlab.wcf_6.2.php | 12 + .../files/acp/templates/contactOptionAdd.tpl | 19 +- .../acp/form/ContactOptionAddForm.class.php | 182 ++++++++--- .../acp/form/ContactOptionEditForm.class.php | 38 ++- .../contact/option/ContactOption.class.php | 49 ++- .../option/ContactOptionAction.class.php | 6 +- .../option/ContactOptionEditor.class.php | 8 +- .../option/ContactOptionList.class.php | 17 +- .../files/lib/form/ContactForm.class.php | 297 +++++------------- wcfsetup/setup/db/install.sql | 4 +- 11 files changed, 322 insertions(+), 403 deletions(-) diff --git a/com.woltlab.wcf/templates/contact.tpl b/com.woltlab.wcf/templates/contact.tpl index 028be4f70c7..37ce037f882 100644 --- a/com.woltlab.wcf/templates/contact.tpl +++ b/com.woltlab.wcf/templates/contact.tpl @@ -1,96 +1,5 @@ {include file='header'} -{include file='shared_formError'} - -
-
-

{lang}wcf.contact.sender.information{/lang}

- - -
*
-
- - {if $errorField == 'name'} - - {if $errorType == 'empty'} - {lang}wcf.global.form.error.empty{/lang} - {else} - {lang}wcf.contact.sender.error.{@$errorType}{/lang} - {/if} - - {/if} -
- - - -
*
-
- - {if $errorField == 'email'} - - {if $errorType == 'empty'} - {lang}wcf.global.form.error.empty{/lang} - {else} - {lang}wcf.user.email.error.{@$errorType}{/lang} - {/if} - - {/if} -
- - - {event name='informationFields'} -
- -
-

{lang}wcf.contact.data{/lang}

- - {if $recipientList|count > 1} - -
*
-
- - {if $errorField == 'recipientID'} - - {if $errorType == 'empty'} - {lang}wcf.global.form.error.empty{/lang} - {else} - {lang}wcf.contact.recipientID.error.{@$errorType}{/lang} - {/if} - - {/if} -
- - {/if} - - {include file='customOptionFieldList'} - - {event name='optionFields'} - - {if CONTACT_FORM_ENABLE_ATTACHMENTS && !$attachmentHandler|empty && $attachmentHandler->canUpload()} -
- {include file='shared_messageFormAttachments' wysiwygSelector=''} -
- {/if} -
- - {event name='sections'} - - {include file='shared_captcha'} - -
- - {csrfToken} -
-
- -

- * - {lang}wcf.global.form.required{/lang} -

+{unsafe:$form->getHtml()} {include file='footer'} diff --git a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php index 4a587ceebab..51ced97cf8a 100644 --- a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php +++ b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php @@ -9,6 +9,8 @@ */ use wcf\system\database\table\column\IntDatabaseTableColumn; +use wcf\system\database\table\column\MediumtextDatabaseTableColumn; +use wcf\system\database\table\column\TextDatabaseTableColumn; use wcf\system\database\table\index\DatabaseTableForeignKey; use wcf\system\database\table\PartialDatabaseTable; @@ -46,5 +48,15 @@ ->referencedTable('wcf1_file') ->referencedColumns(['fileID']) ->onDelete('SET NULL'), + ]), + PartialDatabaseTable::create('wcf1_contact_option') + ->columns([ + MediumtextDatabaseTableColumn::create('defaultValue') + ->drop(), + TextDatabaseTableColumn::create('validationPattern') + ->drop(), + MediumtextDatabaseTableColumn::create('selectOptions') + ->drop(), + MediumtextDatabaseTableColumn::create('configurationData'), ]) ]; diff --git a/wcfsetup/install/files/acp/templates/contactOptionAdd.tpl b/wcfsetup/install/files/acp/templates/contactOptionAdd.tpl index bb9eb7a4a2d..fe7ed672843 100644 --- a/wcfsetup/install/files/acp/templates/contactOptionAdd.tpl +++ b/wcfsetup/install/files/acp/templates/contactOptionAdd.tpl @@ -1,8 +1,10 @@ -{include file='header' pageTitle='wcf.acp.contact.option.'|concat:$action} +{assign var='pageTitle' value='wcf.acp.contact.option.'|concat:$action} + +{include file='header'}
-

{lang}wcf.acp.contact.option.{@$action}{/lang}

+

{lang}{$pageTitle}{/lang}

-{include file='shared_formNotice'} - -
- {include file='customOptionAdd'} - - {event name='sections'} - -
- - {csrfToken} -
-
+{unsafe:$form->getHtml()} {include file='footer'} diff --git a/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php b/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php index d8e82e73f17..f43a5d455d1 100644 --- a/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php @@ -4,25 +4,34 @@ use wcf\data\contact\option\ContactOption; use wcf\data\contact\option\ContactOptionAction; -use wcf\data\contact\option\ContactOptionEditor; -use wcf\system\request\LinkHandler; -use wcf\system\WCF; +use wcf\data\contact\option\ContactOptionList; +use wcf\data\IStorableObject; +use wcf\form\AbstractFormBuilderForm; +use wcf\system\form\builder\data\processor\CustomFormDataProcessor; +use wcf\system\form\builder\field\BooleanFormField; +use wcf\system\form\builder\field\dependency\ValueFormFieldDependency; +use wcf\system\form\builder\field\IFormField; +use wcf\system\form\builder\field\MultilineTextFormField; +use wcf\system\form\builder\field\SelectFormField; +use wcf\system\form\builder\field\ShowOrderFormField; +use wcf\system\form\builder\field\TextFormField; +use wcf\system\form\builder\IFormDocument; +use wcf\system\form\option\FormOptionHandler; +use wcf\system\form\option\SharedConfigurationFormFields; +use wcf\util\JSON; /** * Shows the contact option add form. * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License + * @author Alexander Ebert + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License * @since 3.1 + * + * @extends AbstractFormBuilderForm */ -class ContactOptionAddForm extends AbstractCustomOptionForm +class ContactOptionAddForm extends AbstractFormBuilderForm { - /** - * @inheritDoc - */ - public $action = 'add'; - /** * @inheritDoc */ @@ -39,50 +48,145 @@ class ContactOptionAddForm extends AbstractCustomOptionForm public $neededPermissions = ['admin.contact.canManageContactForm']; /** - * action class name - * @var string + * @inheritDoc */ - public $actionClass = ContactOptionAction::class; + public $objectActionClass = ContactOptionAction::class; + + #[\Override] + protected function createForm() + { + parent::createForm(); + + $this->form->appendChildren([ + TextFormField::create('optionTitle') + ->label('wcf.global.name') + ->maximumLength(255) + ->i18n() + ->languageItemPattern('wcf.contact.option\d+') + ->required(), + MultilineTextFormField::create('optionDescription') + ->label('wcf.global.description') + ->maximumLength(5000) + ->rows(5) + ->i18n() + ->languageItemPattern('wcf.contact.optionDescription\d+'), + ShowOrderFormField::create('showOrder') + ->options($this->getContactOptions()), + SelectFormField::create('optionType') + ->label('wcf.acp.customOption.optionType') + ->immutable($this->formAction != 'create') + ->options($this->getAvailableOptionTypes()) + ->required(), + ...$this->getSharedConfigurationFormFields(), + BooleanFormField::create('required') + ->label('wcf.acp.customOption.required'), + BooleanFormField::create('isDisabled') + ->label('wcf.acp.customOption.isDisabled'), + ]); + } + + #[\Override] + public function finalizeForm() + { + parent::finalizeForm(); + + $this->form->getDataHandler()->addProcessor( + new CustomFormDataProcessor( + 'saveOptionProcessor', + function (IFormDocument $document, array $parameters) { + $configurationData = []; + + foreach ($this->getConfigurationFormFieldIds() as $parameter) { + if (isset($parameters['data'][$parameter])) { + $configurationData[$parameter] = $parameters['data'][$parameter]; + unset($parameters['data'][$parameter]); + } + } + + if ($configurationData !== []) { + $parameters['data']['configurationData'] = JSON::encode($configurationData); + } + + return $parameters; + }, + function (IFormDocument $document, array $data, IStorableObject $object) { + \assert($object instanceof ContactOption); + + if ($object->configurationData) { + $data = \array_merge($data, JSON::decode($object->configurationData)); + } + + return $data; + } + ) + ); + } /** - * base class name - * @var string + * @return array */ - public $baseClass = ContactOption::class; + private function getContactOptions(): array + { + $optionList = new ContactOptionList(); + $optionList->sqlOrderBy = 'showOrder ASC'; + $optionList->readObjects(); + + return \array_map(static fn($option) => $option->getTitle(), $optionList->getObjects()); + } /** - * editor class name - * @var string + * @return array */ - public $editorClass = ContactOptionEditor::class; + private function getAvailableOptionTypes(): array + { + return \array_map(fn($option) => $option->getId(), FormOptionHandler::getInstance()->getOptions()); + } /** - * @inheritDoc + * @return IFormField[] */ - public function readParameters() + private function getSharedConfigurationFormFields(): array { - parent::readParameters(); + $matrix = []; - $this->getI18nValue('optionTitle')->setLanguageItem('wcf.contact.option', 'wcf.contact', 'com.woltlab.wcf'); - $this->getI18nValue('optionDescription')->setLanguageItem( - 'wcf.contact.optionDescription', - 'wcf.contact', - 'com.woltlab.wcf' - ); + foreach (FormOptionHandler::getInstance()->getOptions() as $option) { + foreach ($option->getConfigurationFormFields() as $formFieldId) { + if (!isset($matrix[$formFieldId])) { + $matrix[$formFieldId] = []; + } + + $matrix[$formFieldId][] = $option->getId(); + } + } + + $formFields = []; + + foreach ($matrix as $formFieldId => $dependencies) { + $formField = SharedConfigurationFormFields::getInstance()->getFormField($formFieldId); + $formField->addDependency( + ValueFormFieldDependency::create($formFieldId . 'OptionTypeDependency') + ->fieldId('optionType') + ->values($dependencies) + ); + $formFields[] = $formField; + } + + return $formFields; } /** - * @inheritDoc + * @return string[] */ - public function save() + private function getConfigurationFormFieldIds(): array { - parent::save(); + $ids = []; - WCF::getTPL()->assign([ - 'objectEditLink' => LinkHandler::getInstance()->getControllerLink( - ContactOptionEditForm::class, - ['id' => $this->objectAction->getReturnValues()['returnValues']->getObjectID()] - ), - ]); + foreach (FormOptionHandler::getInstance()->getOptions() as $option) { + foreach ($option->getConfigurationFormFields() as $formFieldId) { + $ids[] = $formFieldId; + } + } + + return \array_unique($ids); } } diff --git a/wcfsetup/install/files/lib/acp/form/ContactOptionEditForm.class.php b/wcfsetup/install/files/lib/acp/form/ContactOptionEditForm.class.php index ebf85862f93..5181668449d 100644 --- a/wcfsetup/install/files/lib/acp/form/ContactOptionEditForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/ContactOptionEditForm.class.php @@ -2,12 +2,17 @@ namespace wcf\acp\form; +use CuyZ\Valinor\Mapper\MappingError; +use wcf\data\contact\option\ContactOption; +use wcf\http\Helper; +use wcf\system\exception\IllegalLinkException; + /** * Shows the contact option edit form. * - * @author Alexander Ebert + * @author Alexander Ebert * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License + * @license GNU Lesser General Public License * @since 3.1 */ class ContactOptionEditForm extends ContactOptionAddForm @@ -15,13 +20,30 @@ class ContactOptionEditForm extends ContactOptionAddForm /** * @inheritDoc */ - public $action = 'edit'; + public $formAction = 'edit'; - /** - * @inheritDoc - */ - public function save() + #[\Override] + public function readParameters() { - AbstractCustomOptionForm::save(); + parent::readParameters(); + + try { + $queryParameters = Helper::mapQueryParameters( + $_GET, + <<<'EOT' + array { + id: positive-int + } + EOT + ); + } catch (MappingError) { + throw new IllegalLinkException(); + } + + $this->formObject = new ContactOption($queryParameters['id']); + + if (!$this->formObject->getObjectID()) { + throw new IllegalLinkException(); + } } } diff --git a/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php b/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php index b9f871beca0..c4cbc49f8f5 100644 --- a/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php +++ b/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php @@ -2,23 +2,54 @@ namespace wcf\data\contact\option; -use wcf\data\custom\option\CustomOption; +use wcf\data\DatabaseObject; +use wcf\data\ITitledObject; +use wcf\system\WCF; /** * Represents a contact option. * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License - * @since 3.1 + * @author Alexander Ebert + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 3.1 + * + * @property-read int $optionID unique id of the option + * @property-read string $optionTitle title of the option or name of language item which contains the title + * @property-read string $optionDescription description of the option or name of language item which contains the description + * @property-read string $optionType type of the option which determines its input and output + * @property-read string $configurationData JSON-encoded configuration information depending on the option type + * @property-read int $required is `1` if the option has to be filled out, otherwise `0` + * @property-read int $showOrder position of the option in relation to the other options + * @property-read int $isDisabled is `1` if the option is disabled, otherwise `0` + * @property-read int $originIsSystem is `1` if the option has been delivered by a package, otherwise `0` (i.e. the option has been created in the ACP) */ -class ContactOption extends CustomOption +class ContactOption extends DatabaseObject implements ITitledObject { - /** - * @inheritDoc - */ + #[\Override] public static function getDatabaseTableAlias() { return 'contact_option'; } + + #[\Override] + public function getTitle(): string + { + return WCF::getLanguage()->get($this->optionTitle); + } + + /** + * Returns the option description in the active user's language. + * + * @since 5.2 + */ + public function getDescription(): string + { + return WCF::getLanguage()->get($this->optionDescription); + } + + public function canDelete(): bool + { + return !$this->originIsSystem; + } } diff --git a/wcfsetup/install/files/lib/data/contact/option/ContactOptionAction.class.php b/wcfsetup/install/files/lib/data/contact/option/ContactOptionAction.class.php index af03147be92..38ace7653bc 100644 --- a/wcfsetup/install/files/lib/data/contact/option/ContactOptionAction.class.php +++ b/wcfsetup/install/files/lib/data/contact/option/ContactOptionAction.class.php @@ -2,11 +2,11 @@ namespace wcf\data\contact\option; +use wcf\data\AbstractDatabaseObjectAction; use wcf\data\attachment\AttachmentEditor; use wcf\data\contact\attachment\ContactAttachment; use wcf\data\contact\attachment\ContactAttachmentEditor; use wcf\data\contact\recipient\ContactRecipient; -use wcf\data\custom\option\CustomOptionAction; use wcf\data\ISortableAction; use wcf\system\attachment\AttachmentHandler; use wcf\system\email\Email; @@ -25,8 +25,10 @@ * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License * @since 3.1 + * + * @extends AbstractDatabaseObjectAction */ -class ContactOptionAction extends CustomOptionAction implements ISortableAction +class ContactOptionAction extends AbstractDatabaseObjectAction implements ISortableAction { /** * @inheritDoc diff --git a/wcfsetup/install/files/lib/data/contact/option/ContactOptionEditor.class.php b/wcfsetup/install/files/lib/data/contact/option/ContactOptionEditor.class.php index c7a4b70aa2d..70276260def 100644 --- a/wcfsetup/install/files/lib/data/contact/option/ContactOptionEditor.class.php +++ b/wcfsetup/install/files/lib/data/contact/option/ContactOptionEditor.class.php @@ -2,7 +2,7 @@ namespace wcf\data\contact\option; -use wcf\data\custom\option\CustomOptionEditor; +use wcf\data\DatabaseObjectEditor; use wcf\data\IEditableCachedObject; use wcf\system\cache\builder\ContactOptionCacheBuilder; @@ -14,11 +14,11 @@ * @license GNU Lesser General Public License * @since 3.1 * - * @mixin ContactOption - * @extends CustomOptionEditor + * @mixin ContactOption + * @extends DatabaseObjectEditor * @implements IEditableCachedObject */ -class ContactOptionEditor extends CustomOptionEditor implements IEditableCachedObject +class ContactOptionEditor extends DatabaseObjectEditor implements IEditableCachedObject { /** * @inheritDoc diff --git a/wcfsetup/install/files/lib/data/contact/option/ContactOptionList.class.php b/wcfsetup/install/files/lib/data/contact/option/ContactOptionList.class.php index eace172674e..6cfaee88e2f 100644 --- a/wcfsetup/install/files/lib/data/contact/option/ContactOptionList.class.php +++ b/wcfsetup/install/files/lib/data/contact/option/ContactOptionList.class.php @@ -2,26 +2,27 @@ namespace wcf\data\contact\option; -use wcf\data\custom\option\CustomOptionList; +use wcf\data\DatabaseObjectList; /** - * Represents a list of contact recipients. + * Represents a list of contact options. * * @author Alexander Ebert * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License * @since 3.1 * - * @method ContactOption current() - * @method ContactOption[] getObjects() - * @method ContactOption|null getSingleObject() - * @method ContactOption|null search($objectID) - * @property ContactOption[] $objects + * @extends DatabaseObjectList */ -class ContactOptionList extends CustomOptionList +class ContactOptionList extends DatabaseObjectList { /** * @inheritDoc */ public $className = ContactOption::class; + + /** + * @inheritDoc + */ + public $sqlOrderBy = 'showOrder'; } diff --git a/wcfsetup/install/files/lib/form/ContactForm.class.php b/wcfsetup/install/files/lib/form/ContactForm.class.php index 6f7e89dc845..930f3bb18ee 100644 --- a/wcfsetup/install/files/lib/form/ContactForm.class.php +++ b/wcfsetup/install/files/lib/form/ContactForm.class.php @@ -3,267 +3,116 @@ namespace wcf\form; use wcf\data\contact\option\ContactOption; -use wcf\data\contact\option\ContactOptionAction; +use wcf\data\contact\option\ContactOptionList; +use wcf\data\contact\recipient\ContactRecipient; use wcf\data\contact\recipient\ContactRecipientList; -use wcf\event\page\ContactFormSpamChecking; -use wcf\system\attachment\AttachmentHandler; -use wcf\system\email\Mailbox; -use wcf\system\event\EventHandler; -use wcf\system\exception\IllegalLinkException; -use wcf\system\exception\PermissionDeniedException; -use wcf\system\exception\UserInputException; -use wcf\system\option\ContactOptionHandler; -use wcf\system\request\LinkHandler; +use wcf\system\form\builder\container\FormContainer; +use wcf\system\form\builder\field\CaptchaFormField; +use wcf\system\form\builder\field\EmailFormField; +use wcf\system\form\builder\field\IFormField; +use wcf\system\form\builder\field\SelectFormField; +use wcf\system\form\builder\field\TextFormField; +use wcf\system\form\option\FormOptionHandler; use wcf\system\WCF; -use wcf\util\HeaderUtil; -use wcf\util\StringUtil; -use wcf\util\UserUtil; +use wcf\util\JSON; -/** - * Customizable contact form with selectable recipients. - * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License - */ -class ContactForm extends AbstractCaptchaForm +class ContactForm extends AbstractFormBuilderForm { - /** - * @var AttachmentHandler - */ - public $attachmentHandler; - - /** - * @var string - */ - public $attachmentObjectType = 'com.woltlab.wcf.contact'; - - /** - * sender email address - * @var string - */ - public $email = ''; - - /** - * sender name - * @var string - */ - public $name = ''; - /** * @inheritDoc */ public $neededModules = ['MODULE_CONTACT_FORM']; - /** - * @var ContactOptionHandler - */ - public $optionHandler; - - /** - * recipient id - * @var int - */ - public $recipientID = 0; - - /** - * @var ContactRecipientList - */ - public $recipientList; - - /** - * @var string - */ - public $tmpHash = ''; - - /** - * @inheritDoc - */ - public function readParameters() + #[\Override] + protected function createForm() { - parent::readParameters(); - - $this->optionHandler = new ContactOptionHandler(false); - $this->optionHandler->init(); - - $this->recipientList = new ContactRecipientList(); - $this->recipientList->getConditionBuilder()->add("contact_recipient.isDisabled = ?", [0]); - $this->recipientList->readObjects(); - - if (!\count($this->recipientList)) { - throw new IllegalLinkException(); - } + parent::createForm(); + + $this->form->appendChildren([ + TextFormField::create('name') + ->label('wcf.contact.sender') + ->required() + ->value(WCF::getUser()->username), + EmailFormField::create('email') + ->label('wcf.user.email') + ->required() + ->value(WCF::getUser()->email), + $this->getRecipientFormField(), + ...$this->getOptionFormFields() + ]); - if (isset($_REQUEST['tmpHash'])) { - $this->tmpHash = $_REQUEST['tmpHash']; - } - if (empty($this->tmpHash)) { - $this->tmpHash = StringUtil::getRandomID(); + if (!WCF::getUser()->userID) { + $captchaContainer = FormContainer::create('captchaContainer') + ->appendChildren([ + CaptchaFormField::create() + ->objectType(\CAPTCHA_TYPE), + ]); + $this->form->appendChild($captchaContainer); } } - /** - * @inheritDoc - */ - public function readFormParameters() + protected function getRecipientFormField(): SelectFormField { - parent::readFormParameters(); + $recipients = $this->getAvailableRecipients(); - $this->optionHandler->readUserInput($_POST); - - if (isset($_POST['email'])) { - $this->email = StringUtil::trim($_POST['email']); - } - if (isset($_POST['name'])) { - $this->name = StringUtil::trim($_POST['name']); - } - if (isset($_POST['recipientID'])) { - $this->recipientID = \intval($_POST['recipientID']); - } + return SelectFormField::create('recipientID') + ->label('wcf.contact.recipientID') + ->required() + ->options($recipients) + ->available(\count($recipients) > 1); } /** - * @inheritDoc + * @return array */ - public function validate() + protected function getAvailableRecipients(): array { - // validate file options - $optionHandlerErrors = $this->optionHandler->validate(); + $recipientList = new ContactRecipientList(); + $recipientList->getConditionBuilder()->add("contact_recipient.isDisabled = ?", [0]); + $recipientList->readObjects(); - parent::validate(); - - if (!empty($optionHandlerErrors)) { - throw new UserInputException('options', $optionHandlerErrors); - } - - if (empty($this->email)) { - throw new UserInputException('email'); - } else { - try { - new Mailbox($this->email); - } catch (\DomainException $e) { - throw new UserInputException('email', 'invalid'); - } - } - - if (empty($this->name)) { - throw new UserInputException('name'); - } - - $recipients = $this->recipientList->getObjects(); - if (\count($recipients) === 1) { - $this->recipientID = \reset($recipients)->recipientID; - } else { - if (!$this->recipientID) { - throw new UserInputException('recipientID'); - } - - $isValid = false; - foreach ($recipients as $recipient) { - if ($this->recipientID == $recipient->recipientID) { - $isValid = true; - break; - } - } - - if (!$isValid) { - throw new UserInputException('recipientID', 'invalid'); - } - } - - $this->handleSpamCheck(); - } - - private function handleSpamCheck(): void - { - $messages = []; - foreach ($this->optionHandler->getOptions() as $option) { - $object = $option['object']; - \assert($object instanceof ContactOption); - if (!$object->isMessage || !$object->getOptionValue()) { - continue; - } - - $messages[] = $object->getOptionValue(); - } - - $spamCheckEvent = new ContactFormSpamChecking( - $this->email, - UserUtil::getIpAddress(), - $messages, - ); - EventHandler::getInstance()->fire($spamCheckEvent); - if ($spamCheckEvent->defaultPrevented()) { - throw new PermissionDeniedException(); - } + return $recipientList->getObjects(); } /** - * @inheritDoc + * @return array */ - public function readData() + protected function getAvailableOptions(): array { - if (CONTACT_FORM_ENABLE_ATTACHMENTS && $this->attachmentObjectType) { - $this->attachmentHandler = new AttachmentHandler($this->attachmentObjectType, 0, $this->tmpHash, 0); - } - - parent::readData(); + $optionList = new ContactOptionList(); + $optionList->getConditionBuilder()->add("contact_option.isDisabled = ?", [0]); + $optionList->readObjects(); - if (empty($_POST)) { - if (WCF::getUser()->userID) { - $this->email = WCF::getUser()->email; - $this->name = WCF::getUser()->username; - } - - $this->optionHandler->readData(); - } + return $optionList->getObjects(); } /** - * @inheritDoc + * @return IFormField[] */ - public function save() + protected function getOptionFormFields(): array { - parent::save(); - - $this->objectAction = new ContactOptionAction([], 'send', [ - 'attachmentHandler' => $this->attachmentHandler, - 'email' => $this->email, - 'name' => $this->name, - 'optionHandler' => $this->optionHandler, - 'recipientID' => $this->recipientID, - ]); - $this->objectAction->executeAction(); + $formFields = []; - // call saved event - $this->saved(); + foreach ($this->getAvailableOptions() as $option) { + $formOption = FormOptionHandler::getInstance()->getOption($option->optionType); + if ($formOption === null) { + throw new \BadMethodCallException("unknown form option type '{$option->optionType}'"); + } - HeaderUtil::delayedRedirect( - LinkHandler::getInstance()->getLink(), - WCF::getLanguage()->getDynamicVariable('wcf.contact.success') - ); + $formField = $formOption->getFormField( + 'option' . $option->optionID, + $option->configurationData ? JSON::decode($option->configurationData) : [] + ); + $formField->label($option->optionTitle); + $formField->description($option->optionDescription); - exit; - } + if ($option->required) { + $formField->required(); + } - /** - * @inheritDoc - */ - public function assignVariables() - { - parent::assignVariables(); + $formFields[] = $formField; + } - WCF::getTPL()->assign([ - 'email' => $this->email, - 'name' => $this->name, - 'options' => $this->optionHandler->getOptions(), - 'recipientList' => $this->recipientList, - 'recipientID' => $this->recipientID, - 'attachmentHandler' => $this->attachmentHandler, - 'attachmentObjectID' => 0, - 'attachmentObjectType' => $this->attachmentObjectType, - 'attachmentParentObjectID' => 0, - 'tmpHash' => $this->tmpHash, - ]); + return $formFields; } } diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index e273b4af331..fd49cab4a5d 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -466,9 +466,7 @@ CREATE TABLE wcf1_contact_option ( optionTitle VARCHAR(255) NOT NULL DEFAULT '', optionDescription TEXT, optionType VARCHAR(255) NOT NULL DEFAULT '', - defaultValue MEDIUMTEXT, - validationPattern TEXT, - selectOptions MEDIUMTEXT, + configurationData MEDIUMTEXT, required TINYINT(1) NOT NULL DEFAULT 0, showOrder INT(10) NOT NULL DEFAULT 0, isDisabled TINYINT(1) NOT NULL DEFAULT 0, From 0f2c108af78fd0c26e2c50cb75776ee8bc92cba9 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Tue, 22 Apr 2025 16:23:30 +0200 Subject: [PATCH 06/33] Add form option formatters --- .../system/form/option/IFormOption.class.php | 5 ++ .../form/option/SelectFormOption.class.php | 13 +++++ .../form/option/TextareaFormOption.class.php | 7 +++ .../formatter/BooleanFormatter.class.php | 24 +++++++++ .../formatter/DefaultFormatter.class.php | 22 ++++++++ .../DefaultPlainTextFormatter.class.php | 20 ++++++++ .../option/formatter/EmailFormatter.class.php | 24 +++++++++ .../option/formatter/FloatFormatter.class.php | 22 ++++++++ .../formatter/IFormOptionFormatter.class.php | 16 ++++++ .../option/formatter/IconFormatter.class.php | 22 ++++++++ .../formatter/IntegerFormatter.class.php | 22 ++++++++ .../MultilineTextFormatter.class.php | 22 ++++++++ .../MultipleSelectionFormatter.class.php | 51 +++++++++++++++++++ .../formatter/RatingFormatter.class.php | 35 +++++++++++++ .../formatter/SelectFormatter.class.php | 43 ++++++++++++++++ .../formatter/SourceCodeFormatter.class.php | 22 ++++++++ .../option/formatter/UrlFormatter.class.php | 22 ++++++++ 17 files changed, 392 insertions(+) create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/BooleanFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/DefaultFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/DefaultPlainTextFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/EmailFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/FloatFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/IFormOptionFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/IconFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/IntegerFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/MultilineTextFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/MultipleSelectionFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/RatingFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/SelectFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/SourceCodeFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/UrlFormatter.class.php diff --git a/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php index 73dec81ede6..e0d61a914d7 100644 --- a/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php @@ -3,6 +3,7 @@ namespace wcf\system\form\option; use wcf\system\form\builder\field\AbstractFormField; +use wcf\system\form\option\formatter\IFormOptionFormatter; /** * Represents a form option type. @@ -19,4 +20,8 @@ public function getId(): string; public function getFormField(string $id, array $configurationData = []): AbstractFormField; public function getConfigurationFormFields(): array; + + public function getFormatter(): IFormOptionFormatter; + + public function getPlainTextFormatter(): IFormOptionFormatter; } diff --git a/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php index fcf6d23009d..0d80773b7d0 100644 --- a/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php @@ -4,6 +4,7 @@ use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\SelectFormField; +use wcf\system\form\option\formatter\SelectFormatter; use wcf\system\WCF; use wcf\util\JSON; @@ -55,4 +56,16 @@ public function getConfigurationFormFields(): array 'selectOptions' ]; } + + #[\Override] + public function getFormatter(): SelectFormatter + { + return new SelectFormatter(); + } + + #[\Override] + public function getPlainTextFormatter(): SelectFormatter + { + return new SelectFormatter(false); + } } diff --git a/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php index ae1c6bc4fcb..f67ff2425cf 100644 --- a/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php @@ -4,6 +4,7 @@ use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\MultilineTextFormField; +use wcf\system\form\option\formatter\MultilineTextFormatter; /** * Implementation of a form field for multi-line text values. @@ -37,4 +38,10 @@ public function getConfigurationFormFields(): array { return ['maxLength']; } + + #[\Override] + public function getFormatter(): MultilineTextFormatter + { + return new MultilineTextFormatter(); + } } diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/BooleanFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/BooleanFormatter.class.php new file mode 100644 index 00000000000..6c8f75281cb --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/BooleanFormatter.class.php @@ -0,0 +1,24 @@ + + * @since 6.2 + */ +final class BooleanFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + if (!$value) { + return ''; + } + + return '✔️'; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/DefaultFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/DefaultFormatter.class.php new file mode 100644 index 00000000000..d6343342215 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/DefaultFormatter.class.php @@ -0,0 +1,22 @@ + + * @since 6.2 + */ +final class DefaultFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + return StringUtil::encodeHTML($value); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/DefaultPlainTextFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/DefaultPlainTextFormatter.class.php new file mode 100644 index 00000000000..b30242c0f79 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/DefaultPlainTextFormatter.class.php @@ -0,0 +1,20 @@ + + * @since 6.2 + */ +final class DefaultPlainTextFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + return $value; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/EmailFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/EmailFormatter.class.php new file mode 100644 index 00000000000..34626abe47a --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/EmailFormatter.class.php @@ -0,0 +1,24 @@ + + * @since 6.2 + */ +final class EmailFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + $encodedValue = StringUtil::encodeHTML($value); + + return '' . $encodedValue . ''; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/FloatFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/FloatFormatter.class.php new file mode 100644 index 00000000000..5498e005e8a --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/FloatFormatter.class.php @@ -0,0 +1,22 @@ + + * @since 6.2 + */ +final class FloatFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + return StringUtil::formatNumeric(\floatval($value)); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/IFormOptionFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/IFormOptionFormatter.class.php new file mode 100644 index 00000000000..5da81e30521 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/IFormOptionFormatter.class.php @@ -0,0 +1,16 @@ + + * @since 6.2 + */ +interface IFormOptionFormatter +{ + public function format(string $value, int $languageID, array $configurationData): string; +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/IconFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/IconFormatter.class.php new file mode 100644 index 00000000000..d2f58b0b551 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/IconFormatter.class.php @@ -0,0 +1,22 @@ + + * @since 6.2 + */ +final class IconFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + return FontAwesomeIcon::fromString($value)->toHtml(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/IntegerFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/IntegerFormatter.class.php new file mode 100644 index 00000000000..48f01d6c8e4 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/IntegerFormatter.class.php @@ -0,0 +1,22 @@ + + * @since 6.2 + */ +final class IntegerFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + return StringUtil::formatNumeric(\intval($value)); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/MultilineTextFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/MultilineTextFormatter.class.php new file mode 100644 index 00000000000..006e5136ca2 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/MultilineTextFormatter.class.php @@ -0,0 +1,22 @@ + + * @since 6.2 + */ +final class MultilineTextFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + return \nl2br(StringUtil::encodeHTML($value), false); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/MultipleSelectionFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/MultipleSelectionFormatter.class.php new file mode 100644 index 00000000000..51f4b2dcde0 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/MultipleSelectionFormatter.class.php @@ -0,0 +1,51 @@ + + * @since 6.2 + */ +final class MultipleSelectionFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + if (!$value) { + return ''; + }; + + $keys = \explode("\n", $value); + $selectOptions = JSON::decode($configurationData['selectOptions']); + $html = ''; + + foreach ($keys as $key) { + foreach ($selectOptions as $selectOption) { + if ($selectOption['key'] == $key) { + if (isset($selectOption['value'][0])) { + $value = $selectOption['value'][0]; + } else if (isset($selectOption['value'][$languageID])) { + $value = $selectOption['value'][$languageID]; + } else { + $value = reset($selectOption['value']); + } + + if ($html !== '') { + $html .= ', '; + } + + $html .= StringUtil::encodeHTML($value); + } + } + } + + return $html; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/RatingFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/RatingFormatter.class.php new file mode 100644 index 00000000000..3f83dc81742 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/RatingFormatter.class.php @@ -0,0 +1,35 @@ + + * @since 6.2 + */ +final class RatingFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + $rating = \floatval($value); + $html = ''; + + for ($i = 1; $i <= 5; $i++) { + if ($rating >= $i) { + $html .= FontAwesomeIcon::fromString('star;true')->toHtml(); + } else if ($rating + 0.5 >= $i) { + $html .= FontAwesomeIcon::fromString('star-half-stroke;false')->toHtml(); + } else { + $html .= FontAwesomeIcon::fromString('star;false')->toHtml(); + } + } + + return $html; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/SelectFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/SelectFormatter.class.php new file mode 100644 index 00000000000..e07c743a986 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/SelectFormatter.class.php @@ -0,0 +1,43 @@ + + * @since 6.2 + */ +final class SelectFormatter implements IFormOptionFormatter +{ + public function __construct(private readonly bool $encode = true) {} + + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + foreach (JSON::decode($configurationData['selectOptions']) as $selectOption) { + if ($selectOption['key'] == $value) { + if (isset($selectOption['value'][0])) { + $value = $selectOption['value'][0]; + } else if (isset($selectOption['value'][$languageID])) { + $value = $selectOption['value'][$languageID]; + } else { + $value = reset($selectOption['value']); + } + + if ($this->encode) { + return StringUtil::encodeHTML($value); + } + + return $value; + } + } + + return ''; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/SourceCodeFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/SourceCodeFormatter.class.php new file mode 100644 index 00000000000..f4d2d38f112 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/SourceCodeFormatter.class.php @@ -0,0 +1,22 @@ + + * @since 6.2 + */ +final class SourceCodeFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + return '
' . StringUtil::encodeHTML($value) . '
'; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/UrlFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/UrlFormatter.class.php new file mode 100644 index 00000000000..32a479a6ee6 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/UrlFormatter.class.php @@ -0,0 +1,22 @@ + + * @since 6.2 + */ +final class UrlFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + return StringUtil::getAnchorTag($value, $value, true, true); + } +} From 621c7b1bc7437fbf577e8668257d532fe91b7feb Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Tue, 22 Apr 2025 16:24:17 +0200 Subject: [PATCH 07/33] Migrate contact form submit to command --- .../contact/option/ContactOption.class.php | 26 +++++ .../files/lib/form/ContactForm.class.php | 58 ++++++++-- .../contact/form/SubmitContactForm.class.php | 100 ++++++++++++++++++ .../form/option/AbstractFormOption.class.php | 16 +++ 4 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/contact/form/SubmitContactForm.class.php diff --git a/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php b/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php index c4cbc49f8f5..3847cf7dce9 100644 --- a/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php +++ b/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php @@ -4,7 +4,11 @@ use wcf\data\DatabaseObject; use wcf\data\ITitledObject; +use wcf\data\language\Language; +use wcf\system\form\option\FormOptionHandler; +use wcf\system\form\option\IFormOption; use wcf\system\WCF; +use wcf\util\JSON; /** * Represents a contact option. @@ -52,4 +56,26 @@ public function canDelete(): bool { return !$this->originIsSystem; } + + /** + * @since 6.2 + */ + public function getFormOption(): IFormOption + { + $formOption = FormOptionHandler::getInstance()->getOption($this->optionType); + if ($formOption === null) { + throw new \BadMethodCallException("unknown form option type '{$this->optionType}'"); + } + + return $formOption; + } + + /** + * @return array + * @since 6.2 + */ + public function getConfigurationData(): array + { + return $this->configurationData ? JSON::decode($this->configurationData) : []; + } } diff --git a/wcfsetup/install/files/lib/form/ContactForm.class.php b/wcfsetup/install/files/lib/form/ContactForm.class.php index 930f3bb18ee..56d4334ad11 100644 --- a/wcfsetup/install/files/lib/form/ContactForm.class.php +++ b/wcfsetup/install/files/lib/form/ContactForm.class.php @@ -6,6 +6,7 @@ use wcf\data\contact\option\ContactOptionList; use wcf\data\contact\recipient\ContactRecipient; use wcf\data\contact\recipient\ContactRecipientList; +use wcf\system\contact\form\SubmitContactForm; use wcf\system\form\builder\container\FormContainer; use wcf\system\form\builder\field\CaptchaFormField; use wcf\system\form\builder\field\EmailFormField; @@ -13,9 +14,18 @@ use wcf\system\form\builder\field\SelectFormField; use wcf\system\form\builder\field\TextFormField; use wcf\system\form\option\FormOptionHandler; +use wcf\system\request\LinkHandler; use wcf\system\WCF; +use wcf\util\HeaderUtil; use wcf\util\JSON; +/** + * Customizable contact form with selectable recipients. + * + * @author Marcel Werk + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + */ class ContactForm extends AbstractFormBuilderForm { /** @@ -51,6 +61,45 @@ protected function createForm() } } + #[\Override] + public function save() + { + AbstractForm::save(); + + $formData = $this->form->getData()['data']; + + $availableRecipients = $this->getAvailableRecipients(); + if (\count($availableRecipients) > 1) { + $recipient = $availableRecipients[$formData['recipientID']]; + } else { + $recipient = \reset($availableRecipients); + } + + $optionValues = []; + foreach ($formData as $key => $value) { + if (\str_starts_with($key, 'option')) { + $optionValues[\substr($key, 6)] = $value; + } + } + + $command = new SubmitContactForm( + $recipient, + $formData['name'], + $formData['email'], + $optionValues + ); + $command(); + + $this->saved(); + + HeaderUtil::delayedRedirect( + LinkHandler::getInstance()->getLink(), + WCF::getLanguage()->getDynamicVariable('wcf.contact.success') + ); + + exit; + } + protected function getRecipientFormField(): SelectFormField { $recipients = $this->getAvailableRecipients(); @@ -94,14 +143,9 @@ protected function getOptionFormFields(): array $formFields = []; foreach ($this->getAvailableOptions() as $option) { - $formOption = FormOptionHandler::getInstance()->getOption($option->optionType); - if ($formOption === null) { - throw new \BadMethodCallException("unknown form option type '{$option->optionType}'"); - } - - $formField = $formOption->getFormField( + $formField = $option->getFormOption()->getFormField( 'option' . $option->optionID, - $option->configurationData ? JSON::decode($option->configurationData) : [] + $option->getConfigurationData() ); $formField->label($option->optionTitle); $formField->description($option->optionDescription); diff --git a/wcfsetup/install/files/lib/system/contact/form/SubmitContactForm.class.php b/wcfsetup/install/files/lib/system/contact/form/SubmitContactForm.class.php new file mode 100644 index 00000000000..7bd2157acab --- /dev/null +++ b/wcfsetup/install/files/lib/system/contact/form/SubmitContactForm.class.php @@ -0,0 +1,100 @@ + + * @since 6.2 + */ +final class SubmitContactForm +{ + /** + * @param array $optionValues + */ + public function __construct( + private readonly ContactRecipient $recipient, + private readonly string $senderName, + private readonly string $senderEmail, + private readonly array $optionValues = [] + ) {} + + public function __invoke() + { + $messageData = [ + 'options' => $this->getFormattedOptionValues($this->optionValues), + 'recipient' => $this->recipient, + 'name' => $this->senderName, + 'emailAddress' => $this->senderEmail, + 'attachments' => [], + ]; + + $email = new Email(); + $email->addRecipient($this->recipient->getMailbox()); + $email->setSubject(LanguageFactory::getInstance()->getDefaultLanguage()->get('wcf.contact.mail.subject')); + $email->setBody(new MimePartFacade([ + new RecipientAwareTextMimePart('text/html', 'email_contact', 'wcf', $messageData), + new RecipientAwareTextMimePart('text/plain', 'email_contact', 'wcf', $messageData), + ])); + $email->setReplyTo(new Mailbox($this->senderEmail, $this->senderName)); + $email->send(); + } + + /** + * @param array $optionValues + */ + private function getFormattedOptionValues(array $optionValues): array + { + $options = []; + foreach ($this->getAvailableOptions() as $availableOption) { + if (empty($optionValues[$availableOption->optionID])) { + continue; + } + + $value = $optionValues[$availableOption->optionID]; + $configurationData = $availableOption->getConfigurationData(); + $formOption = $availableOption->getFormOption(); + + $options[] = [ + 'isMessage' => false, // Unused, but is here for backward compatibility in third-party translations. + 'title' => LanguageFactory::getInstance()->getDefaultLanguage()->get($availableOption->optionTitle), + 'value' => $formOption->getPlainTextFormatter()->format( + $value, + LanguageFactory::getInstance()->getDefaultLanguage()->languageID, + $configurationData + ), + 'htmlValue' => $formOption->getFormatter()->format( + $value, + LanguageFactory::getInstance()->getDefaultLanguage()->languageID, + $configurationData + ), + ]; + } + + return $options; + } + + /** + * @return array + */ + private function getAvailableOptions(): array + { + $optionList = new ContactOptionList(); + $optionList->getConditionBuilder()->add("contact_option.isDisabled = ?", [0]); + $optionList->readObjects(); + + return $optionList->getObjects(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php index a8cec62d4ae..441096fb4a0 100644 --- a/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php @@ -2,6 +2,10 @@ namespace wcf\system\form\option; +use wcf\system\form\option\formatter\DefaultFormatter; +use wcf\system\form\option\formatter\DefaultPlainTextFormatter; +use wcf\system\form\option\formatter\IFormOptionFormatter; + /** * Provides abstract implementations for form option types. * @@ -17,4 +21,16 @@ public function getConfigurationFormFields(): array { return []; } + + #[\Override] + public function getFormatter(): IFormOptionFormatter + { + return new DefaultFormatter(); + } + + #[\Override] + public function getPlainTextFormatter(): IFormOptionFormatter + { + return new DefaultPlainTextFormatter(); + } } From ef7d4b1a58864b1111c9c1ae54e417ff55c3974c Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Fri, 25 Apr 2025 16:27:26 +0200 Subject: [PATCH 08/33] Migrate contact form attachments to a pure file processor --- com.woltlab.wcf/objectType.xml | 5 ++ .../files/lib/data/file/File.class.php | 10 ++- .../files/lib/form/ContactForm.class.php | 36 ++++++-- .../contact/form/SubmitContactForm.class.php | 25 +++++- .../ContactFormFileProcessor.class.php | 88 +++++++++++++++++++ wcfsetup/install/lang/de.xml | 9 +- wcfsetup/install/lang/en.xml | 9 +- 7 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/file/processor/ContactFormFileProcessor.class.php diff --git a/com.woltlab.wcf/objectType.xml b/com.woltlab.wcf/objectType.xml index d6f816dfe49..d5423df277e 100644 --- a/com.woltlab.wcf/objectType.xml +++ b/com.woltlab.wcf/objectType.xml @@ -223,6 +223,11 @@ com.woltlab.wcf.attachment.objectType wcf\system\attachment\ContactAttachmentObjectType + + com.woltlab.wcf.contact.form + com.woltlab.wcf.file + wcf\system\file\processor\ContactFormFileProcessor + com.woltlab.wcf.page.recentActivityEvent diff --git a/wcfsetup/install/files/lib/data/file/File.class.php b/wcfsetup/install/files/lib/data/file/File.class.php index b72e116f27c..dfcf60337d4 100644 --- a/wcfsetup/install/files/lib/data/file/File.class.php +++ b/wcfsetup/install/files/lib/data/file/File.class.php @@ -5,7 +5,7 @@ use wcf\action\FileDownloadAction; use wcf\data\DatabaseObject; use wcf\data\file\thumbnail\FileThumbnail; -use wcf\data\ILinkableObject; +use wcf\data\ITitledLinkObject; use wcf\system\application\ApplicationHandler; use wcf\system\file\processor\FileProcessor; use wcf\system\file\processor\IFileProcessor; @@ -32,7 +32,7 @@ * @property-read int|null $height * @property-read string|null $fileHashWebp */ -class File extends DatabaseObject implements ILinkableObject, IImageDataProvider +class File extends DatabaseObject implements ITitledLinkObject, IImageDataProvider { /** * List of common file extensions that are always safe to be served directly @@ -278,4 +278,10 @@ public function getImageData(?int $minWidth = null, ?int $minHeight = null): ?Im return new ImageData($this->getLink(), $this->width, $this->height); } + + #[\Override] + public function getTitle(): string + { + return $this->filename; + } } diff --git a/wcfsetup/install/files/lib/form/ContactForm.class.php b/wcfsetup/install/files/lib/form/ContactForm.class.php index 56d4334ad11..66ee6cc67e9 100644 --- a/wcfsetup/install/files/lib/form/ContactForm.class.php +++ b/wcfsetup/install/files/lib/form/ContactForm.class.php @@ -10,14 +10,13 @@ use wcf\system\form\builder\container\FormContainer; use wcf\system\form\builder\field\CaptchaFormField; use wcf\system\form\builder\field\EmailFormField; +use wcf\system\form\builder\field\FileProcessorFormField; use wcf\system\form\builder\field\IFormField; use wcf\system\form\builder\field\SelectFormField; use wcf\system\form\builder\field\TextFormField; -use wcf\system\form\option\FormOptionHandler; use wcf\system\request\LinkHandler; use wcf\system\WCF; use wcf\util\HeaderUtil; -use wcf\util\JSON; /** * Customizable contact form with selectable recipients. @@ -51,6 +50,10 @@ protected function createForm() ...$this->getOptionFormFields() ]); + if (\CONTACT_FORM_ENABLE_ATTACHMENTS) { + $this->form->appendChild($this->getFileUploadFormField()); + } + if (!WCF::getUser()->userID) { $captchaContainer = FormContainer::create('captchaContainer') ->appendChildren([ @@ -66,17 +69,18 @@ public function save() { AbstractForm::save(); - $formData = $this->form->getData()['data']; + $formData = $this->form->getData(); + $data = $formData['data']; $availableRecipients = $this->getAvailableRecipients(); if (\count($availableRecipients) > 1) { - $recipient = $availableRecipients[$formData['recipientID']]; + $recipient = $availableRecipients[$data['recipientID']]; } else { $recipient = \reset($availableRecipients); } $optionValues = []; - foreach ($formData as $key => $value) { + foreach ($data as $key => $value) { if (\str_starts_with($key, 'option')) { $optionValues[\substr($key, 6)] = $value; } @@ -84,9 +88,10 @@ public function save() $command = new SubmitContactForm( $recipient, - $formData['name'], - $formData['email'], - $optionValues + $data['name'], + $data['email'], + $optionValues, + $formData['attachments'] ?? [] ); $command(); @@ -159,4 +164,19 @@ protected function getOptionFormFields(): array return $formFields; } + + protected function getFileUploadFormField(): FileProcessorFormField + { + return FileProcessorFormField::create('attachments') + ->objectType('com.woltlab.wcf.contact.form') + ->label('wcf.contact.attachments') + ->description('wcf.upload.multiple.limits', [ + 'maximumCount' => WCF::getSession()->getPermission('user.contactForm.attachment.maxCount'), + 'maximumSize' => WCF::getSession()->getPermission('user.contactForm.attachment.maxSize'), + 'allowedFileExtensions' => \explode( + "\n", + WCF::getSession()->getPermission('user.contactForm.attachment.allowedExtensions') + ), + ]); + } } diff --git a/wcfsetup/install/files/lib/system/contact/form/SubmitContactForm.class.php b/wcfsetup/install/files/lib/system/contact/form/SubmitContactForm.class.php index 7bd2157acab..0e60c566b11 100644 --- a/wcfsetup/install/files/lib/system/contact/form/SubmitContactForm.class.php +++ b/wcfsetup/install/files/lib/system/contact/form/SubmitContactForm.class.php @@ -5,6 +5,8 @@ use wcf\data\contact\option\ContactOption; use wcf\data\contact\option\ContactOptionList; use wcf\data\contact\recipient\ContactRecipient; +use wcf\data\file\File; +use wcf\data\file\FileList; use wcf\system\email\Email; use wcf\system\email\Mailbox; use wcf\system\email\mime\MimePartFacade; @@ -23,12 +25,14 @@ final class SubmitContactForm { /** * @param array $optionValues + * @param int[] $fileIDs */ public function __construct( private readonly ContactRecipient $recipient, private readonly string $senderName, private readonly string $senderEmail, - private readonly array $optionValues = [] + private readonly array $optionValues = [], + private readonly array $fileIDs = [] ) {} public function __invoke() @@ -38,7 +42,7 @@ public function __invoke() 'recipient' => $this->recipient, 'name' => $this->senderName, 'emailAddress' => $this->senderEmail, - 'attachments' => [], + 'attachments' => $this->getFiles($this->fileIDs), ]; $email = new Email(); @@ -97,4 +101,21 @@ private function getAvailableOptions(): array return $optionList->getObjects(); } + + /** + * @param int[] $fileIDs + * @return array + */ + private function getFiles(array $fileIDs): array + { + if ($fileIDs === []) { + return []; + } + + $list = new FileList(); + $list->setObjectIDs($fileIDs); + $list->readObjects(); + + return $list->getObjects(); + } } diff --git a/wcfsetup/install/files/lib/system/file/processor/ContactFormFileProcessor.class.php b/wcfsetup/install/files/lib/system/file/processor/ContactFormFileProcessor.class.php new file mode 100644 index 00000000000..16e506d4d73 --- /dev/null +++ b/wcfsetup/install/files/lib/system/file/processor/ContactFormFileProcessor.class.php @@ -0,0 +1,88 @@ + + * @since 6.2 + */ +final class ContactFormFileProcessor extends AbstractFileProcessor +{ + private const SESSION_VARIABLE = 'contact_form_file_processor_%d'; + + #[\Override] + public function acceptUpload(string $filename, int $fileSize, array $context): FileProcessorPreflightResult + { + if (!\CONTACT_FORM_ENABLE_ATTACHMENTS) { + return FileProcessorPreflightResult::InsufficientPermissions; + } + + return FileProcessorPreflightResult::Passed; + } + + #[\Override] + public function canAdopt(File $file, array $context): bool + { + return true; + } + + #[\Override] + public function adopt(File $file, array $context): void + { + // Save the `fileID` in the session variable so that the current user can download or delete it. + WCF::getSession()->register(\sprintf(self::SESSION_VARIABLE, $file->fileID), TIME_NOW); + WCF::getSession()->update(); + } + + #[\Override] + public function getMaximumCount(array $context): ?int + { + return WCF::getSession()->getPermission('user.contactForm.attachment.maxCount'); + } + + #[\Override] + public function getAllowedFileExtensions(array $context): array + { + return \explode("\n", WCF::getSession()->getPermission('user.contactForm.attachment.allowedExtensions')); + } + + #[\Override] + public function getMaximumSize(array $context): ?int + { + return WCF::getSession()->getPermission('user.contactForm.attachment.maxSize'); + } + + #[\Override] + public function canDelete(File $file): bool + { + return WCF::getSession()->getVar( + \sprintf(self::SESSION_VARIABLE, $file->fileID) + ) !== null; + } + + #[\Override] + public function canDownload(File $file): bool + { + if (WCF::getSession()->getPermission('admin.contact.canManageContactForm')) { + return true; + } + + return WCF::getSession()->getVar( + \sprintf(self::SESSION_VARIABLE, $file->fileID) + ) !== null; + } + + #[\Override] + public function delete(array $fileIDs, array $thumbnailIDs): void {} + + #[\Override] + public function getObjectTypeName(): string + { + return 'com.woltlab.wcf.contact.form'; + } +} diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index 8b7ee42a8e5..4c86b044d43 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -3529,7 +3529,6 @@ Erlaubte Dateiendungen: {', '|implode:$attachmentHandler->getFormattedAllowedExt
- - + - *]]> @@ -5585,6 +5583,11 @@ Benachrichtigungen auf {PAGE_TITLE|phra Ersetzen, um die Datei zu ersetzen.]]> + +Erlaubte Dateiendungen: {', '|implode:$allowedFileExtensions}]]> + +Maximale Dateigröße: {$maximumSize|filesize}
+Erlaubte Dateiendungen: {', '|implode:$allowedFileExtensions}]]>
diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index ed2ed74a69d..012b3272b3d 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -3454,7 +3454,6 @@ Allowed extensions: {', '|implode:$attachmentHandler->getFormattedAllowedExtensi - - + - *]]> @@ -5587,6 +5585,11 @@ your notifications on {PAGE_TITLE|phras Replace instead to swap out the file.]]> + +Allowed extensions: {', '|implode:$allowedFileExtensions}]]> + +Maximum file size: {$maximumSize|filesize}
+Allowed extensions: {', '|implode:$allowedFileExtensions}]]>
From d85d11305a62ef3aef19b2783a9f562fabd54689 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Fri, 25 Apr 2025 16:28:34 +0200 Subject: [PATCH 09/33] Remove obsolete code --- .../option/ContactOptionAction.class.php | 85 ------------------- 1 file changed, 85 deletions(-) diff --git a/wcfsetup/install/files/lib/data/contact/option/ContactOptionAction.class.php b/wcfsetup/install/files/lib/data/contact/option/ContactOptionAction.class.php index 38ace7653bc..1d860f06740 100644 --- a/wcfsetup/install/files/lib/data/contact/option/ContactOptionAction.class.php +++ b/wcfsetup/install/files/lib/data/contact/option/ContactOptionAction.class.php @@ -3,19 +3,8 @@ namespace wcf\data\contact\option; use wcf\data\AbstractDatabaseObjectAction; -use wcf\data\attachment\AttachmentEditor; -use wcf\data\contact\attachment\ContactAttachment; -use wcf\data\contact\attachment\ContactAttachmentEditor; -use wcf\data\contact\recipient\ContactRecipient; use wcf\data\ISortableAction; -use wcf\system\attachment\AttachmentHandler; -use wcf\system\email\Email; -use wcf\system\email\Mailbox; -use wcf\system\email\mime\MimePartFacade; -use wcf\system\email\mime\RecipientAwareTextMimePart; use wcf\system\exception\UserInputException; -use wcf\system\language\LanguageFactory; -use wcf\system\option\ContactOptionHandler; use wcf\system\WCF; /** @@ -55,80 +44,6 @@ class ContactOptionAction extends AbstractDatabaseObjectAction implements ISorta */ protected $requireACP = ['create', 'delete', 'update', 'updatePosition']; - /** - * Sends an email to the selected recipient. - * - * @return void - */ - public function send() - { - $defaultLanguage = LanguageFactory::getInstance()->getDefaultLanguage(); - - $recipient = new ContactRecipient($this->parameters['recipientID']); - /** @var ContactOptionHandler $optionHandler */ - $optionHandler = $this->parameters['optionHandler']; - - /** @var ?AttachmentHandler $attachmentHandler */ - $attachmentHandler = (!empty($this->parameters['attachmentHandler'])) ? $this->parameters['attachmentHandler'] : null; - - /** @var ContactAttachment[] $attachments */ - $attachments = []; - if ($attachmentHandler !== null) { - foreach ($attachmentHandler->getAttachmentList() as $attachment) { - $attachments[] = ContactAttachmentEditor::create([ - 'attachmentID' => $attachment->attachmentID, - 'accessKey' => ContactAttachment::generateKey(), - ]); - - (new AttachmentEditor($attachment))->update([ - 'objectID' => $attachment->attachmentID, - 'tmpHash' => '', - ]); - } - } - - $options = []; - foreach ($optionHandler->getOptions() as $option) { - /** @var ContactOption $object */ - $object = $option['object']; - if ($object->optionType === 'date' && !$object->getOptionValue()) { - // skip empty dates - continue; - } - - $options[] = [ - 'isMessage' => $object->isMessage(), - 'title' => $object->getLocalizedName($defaultLanguage), - 'value' => $object->getFormattedOptionValue(true), - 'htmlValue' => $object->getFormattedOptionValue(), - ]; - } - - // build message data - $messageData = [ - 'options' => $options, - 'recipient' => $recipient, - 'name' => $this->parameters['name'], - 'emailAddress' => $this->parameters['email'], - 'attachments' => $attachments, - ]; - - // build mail - $email = new Email(); - $email->addRecipient($recipient->getMailbox()); - $email->setSubject($defaultLanguage->get('wcf.contact.mail.subject')); - $email->setBody(new MimePartFacade([ - new RecipientAwareTextMimePart('text/html', 'email_contact', 'wcf', $messageData), - new RecipientAwareTextMimePart('text/plain', 'email_contact', 'wcf', $messageData), - ])); - - // add reply-to tag - $email->setReplyTo(new Mailbox($this->parameters['email'], $this->parameters['name'])); - - // send mail - $email->send(); - } - /** * @inheritDoc */ From 996cdd32b315a8405916925b0809455760cf1202 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Fri, 25 Apr 2025 16:35:21 +0200 Subject: [PATCH 10/33] Deprecate `ContactAttachmentObjectType` --- .../files/lib/acp/page/ContactAttachmentPage.class.php | 5 ++--- .../lib/data/contact/attachment/ContactAttachment.class.php | 1 + .../contact/attachment/ContactAttachmentAction.class.php | 1 + .../contact/attachment/ContactAttachmentEditor.class.php | 1 + .../data/contact/attachment/ContactAttachmentList.class.php | 1 + .../install/files/lib/page/ContactAttachmentPage.class.php | 1 + .../system/attachment/ContactAttachmentObjectType.class.php | 1 + 7 files changed, 8 insertions(+), 3 deletions(-) diff --git a/wcfsetup/install/files/lib/acp/page/ContactAttachmentPage.class.php b/wcfsetup/install/files/lib/acp/page/ContactAttachmentPage.class.php index cd98ba8795c..988b0edeb20 100644 --- a/wcfsetup/install/files/lib/acp/page/ContactAttachmentPage.class.php +++ b/wcfsetup/install/files/lib/acp/page/ContactAttachmentPage.class.php @@ -8,7 +8,6 @@ * @author Alexander Ebert * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License + * @deprecated 6.2 Contact form attachments are using `ContactFormFileProcessor` instead. */ -class ContactAttachmentPage extends \wcf\page\ContactAttachmentPage -{ -} +class ContactAttachmentPage extends \wcf\page\ContactAttachmentPage {} diff --git a/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachment.class.php b/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachment.class.php index 3f0f96d683b..2520fbf4f14 100644 --- a/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachment.class.php +++ b/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachment.class.php @@ -15,6 +15,7 @@ * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License * @since 5.2 + * @deprecated 6.2 Contact form attachments are using `ContactFormFileProcessor` instead. * * @property-read int $attachmentID * @property-read string $accessKey diff --git a/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachmentAction.class.php b/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachmentAction.class.php index e566df2a534..cb86fc5c837 100644 --- a/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachmentAction.class.php +++ b/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachmentAction.class.php @@ -11,6 +11,7 @@ * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License * @since 5.2 + * @deprecated 6.2 Contact form attachments are using `ContactFormFileProcessor` instead. * * @extends AbstractDatabaseObjectAction */ diff --git a/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachmentEditor.class.php b/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachmentEditor.class.php index d84f0e43c93..f9d7bcb0dd6 100644 --- a/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachmentEditor.class.php +++ b/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachmentEditor.class.php @@ -11,6 +11,7 @@ * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License * @since 5.2 + * @deprecated 6.2 Contact form attachments are using `ContactFormFileProcessor` instead. * * @mixin ContactAttachment * @extends DatabaseObjectEditor diff --git a/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachmentList.class.php b/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachmentList.class.php index 6d2c36963fa..c6db8819c29 100644 --- a/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachmentList.class.php +++ b/wcfsetup/install/files/lib/data/contact/attachment/ContactAttachmentList.class.php @@ -11,6 +11,7 @@ * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License * @since 5.2 + * @deprecated 6.2 Contact form attachments are using `ContactFormFileProcessor` instead. * * @extends DatabaseObjectList */ diff --git a/wcfsetup/install/files/lib/page/ContactAttachmentPage.class.php b/wcfsetup/install/files/lib/page/ContactAttachmentPage.class.php index 7732ac020a9..bad98cca722 100644 --- a/wcfsetup/install/files/lib/page/ContactAttachmentPage.class.php +++ b/wcfsetup/install/files/lib/page/ContactAttachmentPage.class.php @@ -12,6 +12,7 @@ * @author Alexander Ebert * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License + * @deprecated 6.2 Contact form attachments are using `ContactFormFileProcessor` instead. */ class ContactAttachmentPage extends AttachmentPage { diff --git a/wcfsetup/install/files/lib/system/attachment/ContactAttachmentObjectType.class.php b/wcfsetup/install/files/lib/system/attachment/ContactAttachmentObjectType.class.php index 67e047c5e35..01edf79c77f 100644 --- a/wcfsetup/install/files/lib/system/attachment/ContactAttachmentObjectType.class.php +++ b/wcfsetup/install/files/lib/system/attachment/ContactAttachmentObjectType.class.php @@ -14,6 +14,7 @@ * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License * @since 5.2 + * @deprecated 6.2 Contact form attachments are using `ContactFormFileProcessor` instead. * * @extends AbstractAttachmentObjectType */ From 8c531f080cf56184edefead6d77c808ef985bd56 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Fri, 25 Apr 2025 16:55:23 +0200 Subject: [PATCH 11/33] Save the upload time of file uploads --- .../files/acp/database/update_com.woltlab.wcf_6.2.php | 7 ++++++- wcfsetup/install/files/lib/data/file/File.class.php | 1 + wcfsetup/install/files/lib/data/file/FileEditor.class.php | 7 +++++-- wcfsetup/setup/db/install.sql | 3 ++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php index 51ced97cf8a..b7d1fe52ef5 100644 --- a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php +++ b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php @@ -58,5 +58,10 @@ MediumtextDatabaseTableColumn::create('selectOptions') ->drop(), MediumtextDatabaseTableColumn::create('configurationData'), - ]) + ]), + PartialDatabaseTable::create('wcf1_file') + ->columns([ + IntDatabaseTableColumn::create('uploadTime') + ->length(10), + ]), ]; diff --git a/wcfsetup/install/files/lib/data/file/File.class.php b/wcfsetup/install/files/lib/data/file/File.class.php index dfcf60337d4..2a80dc1a9ed 100644 --- a/wcfsetup/install/files/lib/data/file/File.class.php +++ b/wcfsetup/install/files/lib/data/file/File.class.php @@ -31,6 +31,7 @@ * @property-read int|null $width * @property-read int|null $height * @property-read string|null $fileHashWebp + * @property-read int $uploadTime */ class File extends DatabaseObject implements ITitledLinkObject, IImageDataProvider { diff --git a/wcfsetup/install/files/lib/data/file/FileEditor.class.php b/wcfsetup/install/files/lib/data/file/FileEditor.class.php index d7cd358fb32..ff7840ea923 100644 --- a/wcfsetup/install/files/lib/data/file/FileEditor.class.php +++ b/wcfsetup/install/files/lib/data/file/FileEditor.class.php @@ -96,6 +96,7 @@ public static function createFromTemporary(FileTemporary $fileTemporary): File 'mimeType' => $mimeType, 'width' => $width, 'height' => $height, + 'uploadTime' => \TIME_NOW, ]]); $file = $fileAction->executeAction()['returnValues']; \assert($file instanceof File); @@ -117,7 +118,8 @@ public static function createFromExistingFile( string $pathname, string $originalFilename, string $objectTypeName, - bool $copy = false + bool $copy = false, + ?int $uploadTime = null ): ?File { if (!\is_readable($pathname)) { return null; @@ -165,6 +167,7 @@ public static function createFromExistingFile( 'mimeType' => $mimeType, 'width' => $width, 'height' => $height, + 'uploadTime' => $uploadTime, ]]); $file = $fileAction->executeAction()['returnValues']; \assert($file instanceof File); @@ -217,7 +220,7 @@ private static function normalizeImageRotation( ExifUtil::ORIENTATION_180_ROTATE => 180, ExifUtil::ORIENTATION_90_ROTATE => 90, ExifUtil::ORIENTATION_270_ROTATE => 270, - // Any other rotation is unsupported. + // Any other rotation is unsupported. default => null, }; diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index fd49cab4a5d..b30274aff7b 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -607,7 +607,8 @@ CREATE TABLE wcf1_file ( mimeType VARCHAR(255) NOT NULL, width INT, height INT, - fileHashWebp CHAR(64) + fileHashWebp CHAR(64), + uploadTime INT(10), ); DROP TABLE IF EXISTS wcf1_file_temporary; From 7eb608c53f25b67f8e85237f3d7513207922e345 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Fri, 25 Apr 2025 16:58:32 +0200 Subject: [PATCH 12/33] Add cleanup for old contact form attachments --- .../cronjob/FileCleanUpCronjob.class.php | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/wcfsetup/install/files/lib/system/cronjob/FileCleanUpCronjob.class.php b/wcfsetup/install/files/lib/system/cronjob/FileCleanUpCronjob.class.php index 3df780e0dfb..5a4dbc9c6e7 100644 --- a/wcfsetup/install/files/lib/system/cronjob/FileCleanUpCronjob.class.php +++ b/wcfsetup/install/files/lib/system/cronjob/FileCleanUpCronjob.class.php @@ -4,6 +4,7 @@ use wcf\data\cronjob\Cronjob; use wcf\data\file\FileEditor; +use wcf\data\object\type\ObjectTypeCache; use wcf\system\WCF; /** @@ -28,10 +29,34 @@ public function execute(Cronjob $cronjob) $statement->execute(); $fileIDs = $statement->fetchAll(\PDO::FETCH_COLUMN); + if (\MODULE_CONTACT_FORM && \CONTACT_FORM_PRUNE_ATTACHMENTS > 0) { + $fileIDs = \array_merge($fileIDs, $this->getOldContactFileIDs()); + } + if ($fileIDs === []) { return; } FileEditor::deleteAll($fileIDs); } + + /** + * @return int[] + */ + private function getOldContactFileIDs(): array + { + $sql = "SELECT fileID + FROM wcf1_file + WHERE objectTypeID = ? + AND uploadTime IS NOT NULL + AND uploadTime < ?"; + $statement = WCF::getDB()->prepare($sql); + $statement->execute([ + ObjectTypeCache::getInstance() + ->getObjectTypeIDByName('com.woltlab.wcf.file', 'com.woltlab.wcf.contact.form'), + \TIME_NOW - (\CONTACT_FORM_PRUNE_ATTACHMENTS * 86_400), + ]); + + return $statement->fetchAll(\PDO::FETCH_COLUMN); + } } From 027a14e11972207f584971f8587915500e0a2bdf Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Mon, 28 Apr 2025 12:43:33 +0200 Subject: [PATCH 13/33] Re-add spam check --- .../files/lib/form/ContactForm.class.php | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/wcfsetup/install/files/lib/form/ContactForm.class.php b/wcfsetup/install/files/lib/form/ContactForm.class.php index 66ee6cc67e9..738a2319761 100644 --- a/wcfsetup/install/files/lib/form/ContactForm.class.php +++ b/wcfsetup/install/files/lib/form/ContactForm.class.php @@ -6,7 +6,10 @@ use wcf\data\contact\option\ContactOptionList; use wcf\data\contact\recipient\ContactRecipient; use wcf\data\contact\recipient\ContactRecipientList; +use wcf\event\page\ContactFormSpamChecking; use wcf\system\contact\form\SubmitContactForm; +use wcf\system\event\EventHandler; +use wcf\system\exception\PermissionDeniedException; use wcf\system\form\builder\container\FormContainer; use wcf\system\form\builder\field\CaptchaFormField; use wcf\system\form\builder\field\EmailFormField; @@ -17,6 +20,7 @@ use wcf\system\request\LinkHandler; use wcf\system\WCF; use wcf\util\HeaderUtil; +use wcf\util\UserUtil; /** * Customizable contact form with selectable recipients. @@ -86,6 +90,11 @@ public function save() } } + $this->handleSpamCheck( + $data['email'], + $optionValues + ); + $command = new SubmitContactForm( $recipient, $data['name'], @@ -105,6 +114,38 @@ public function save() exit; } + /** + * @param array $optionValues + */ + private function handleSpamCheck(string $email, array $optionValues): void + { + $messages = []; + foreach ($this->getAvailableOptions() as $option) { + if (empty($optionValues[$option->optionID])) { + continue; + } + + if (!\in_array($option->optionType, [ + 'text', + 'textarea' + ])) { + continue; + } + + $messages[] = $optionValues[$option->optionID]; + } + + $spamCheckEvent = new ContactFormSpamChecking( + $email, + UserUtil::getIpAddress(), + $messages, + ); + EventHandler::getInstance()->fire($spamCheckEvent); + if ($spamCheckEvent->defaultPrevented()) { + throw new PermissionDeniedException(); + } + } + protected function getRecipientFormField(): SelectFormField { $recipients = $this->getAvailableRecipients(); From 92af395acf9ffc8230079507fa0919c951b92ceb Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Mon, 28 Apr 2025 12:43:54 +0200 Subject: [PATCH 14/33] Simplify contact form phrases --- wcfsetup/install/lang/de.xml | 11 +++++------ wcfsetup/install/lang/en.xml | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index 4c86b044d43..24649e01710 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -3532,18 +3532,17 @@ Erlaubte Dateiendungen: {', '|implode:$attachmentHandler->getFormattedAllowedExt getTitle()} [URL: {@$attachment->getLink()} ] +- {unsafe:$attachment->getTitle()} [URL: {unsafe:$attachment->getLink()} ] {/foreach} {if CONTACT_FORM_PRUNE_ATTACHMENTS}(Dateianhänge werden nach {if CONTACT_FORM_PRUNE_ATTACHMENTS == 1}einem Tag{else}{CONTACT_FORM_PRUNE_ATTACHMENTS} Tagen{/if} gelöscht.){/if} {* this line ends with a space *} {/if}]]> @@ -3553,7 +3552,7 @@ Dateianhänge:


E-Mail-Adresse: {$emailAddress}

{foreach from=$options item=option} -

{@$option['title']}: {@$option['htmlValue']}

+

{unsafe:$option['title']}: {unsafe:$option['htmlValue']}

{/foreach} {if !$attachments|empty}


diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index 012b3272b3d..55c4afebef8 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -3457,18 +3457,17 @@ Allowed extensions: {', '|implode:$attachmentHandler->getFormattedAllowedExtensi getTitle()} [URL: {@$attachment->getLink()} ] +- {unsafe:$attachment->getTitle()} [URL: {unsafe:$attachment->getLink()} ] {/foreach} {if CONTACT_FORM_PRUNE_ATTACHMENTS}(Attachments are removed after {if CONTACT_FORM_PRUNE_ATTACHMENTS == 1}one day{else}{CONTACT_FORM_PRUNE_ATTACHMENTS} days{/if}.){/if} {* this line ends with a space *} {/if}]]> @@ -3478,7 +3477,7 @@ Attachments:


Email: {$emailAddress}

{foreach from=$options item=option} -

{@$option['title']}: {@$option['htmlValue']}

+

{unsafe:$option['title']}: {unsafe:$option['htmlValue']}

{/foreach} {if !$attachments|empty}


From 75a9df8008077b72ca842acbb52de5a0b3aab6e4 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Mon, 28 Apr 2025 12:45:56 +0200 Subject: [PATCH 15/33] Deprecate custom options --- .../files/lib/acp/form/AbstractCustomOptionForm.class.php | 1 + .../install/files/lib/data/custom/option/CustomOption.class.php | 1 + .../files/lib/data/custom/option/CustomOptionAction.class.php | 1 + .../files/lib/data/custom/option/CustomOptionEditor.class.php | 1 + .../files/lib/data/custom/option/CustomOptionList.class.php | 1 + .../files/lib/system/option/CustomOptionHandler.class.php | 1 + 6 files changed, 6 insertions(+) diff --git a/wcfsetup/install/files/lib/acp/form/AbstractCustomOptionForm.class.php b/wcfsetup/install/files/lib/acp/form/AbstractCustomOptionForm.class.php index 1784a8eb19c..7a3aa06e8b0 100644 --- a/wcfsetup/install/files/lib/acp/form/AbstractCustomOptionForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/AbstractCustomOptionForm.class.php @@ -15,6 +15,7 @@ * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License * @since 3.1 + * @deprecated 6.2 Use `IFormOption` instead */ abstract class AbstractCustomOptionForm extends AbstractAcpForm { diff --git a/wcfsetup/install/files/lib/data/custom/option/CustomOption.class.php b/wcfsetup/install/files/lib/data/custom/option/CustomOption.class.php index 3b7328f7aeb..0bf4695a521 100644 --- a/wcfsetup/install/files/lib/data/custom/option/CustomOption.class.php +++ b/wcfsetup/install/files/lib/data/custom/option/CustomOption.class.php @@ -31,6 +31,7 @@ * @property-read int $showOrder position of the option in relation to the other options * @property-read int $isDisabled is `1` if the option is disabled, otherwise `0` * @property-read int $originIsSystem is `1` if the option has been delivered by a package, otherwise `0` (i.e. the option has been created in the ACP) + * @deprecated 6.2 Use `IFormOption` instead */ abstract class CustomOption extends Option implements ITitledObject { diff --git a/wcfsetup/install/files/lib/data/custom/option/CustomOptionAction.class.php b/wcfsetup/install/files/lib/data/custom/option/CustomOptionAction.class.php index b13c7eee1a0..59e3baf005f 100644 --- a/wcfsetup/install/files/lib/data/custom/option/CustomOptionAction.class.php +++ b/wcfsetup/install/files/lib/data/custom/option/CustomOptionAction.class.php @@ -19,6 +19,7 @@ * @template TCustomOptionEditor of CustomOptionEditor|DatabaseObjectDecorator = CustomOptionEditor * @extends AbstractDatabaseObjectAction * @phpstan-ignore generics.notSubtype + * @deprecated 6.2 Use `IFormOption` instead */ abstract class CustomOptionAction extends AbstractDatabaseObjectAction implements IToggleAction { diff --git a/wcfsetup/install/files/lib/data/custom/option/CustomOptionEditor.class.php b/wcfsetup/install/files/lib/data/custom/option/CustomOptionEditor.class.php index 46d7f91221f..d5d7252ae29 100644 --- a/wcfsetup/install/files/lib/data/custom/option/CustomOptionEditor.class.php +++ b/wcfsetup/install/files/lib/data/custom/option/CustomOptionEditor.class.php @@ -15,6 +15,7 @@ * @mixin CustomOption * @template TCustomOption of CustomOption = CustomOption * @extends DatabaseObjectEditor + * @deprecated 6.2 Use `IFormOption` instead */ abstract class CustomOptionEditor extends DatabaseObjectEditor { diff --git a/wcfsetup/install/files/lib/data/custom/option/CustomOptionList.class.php b/wcfsetup/install/files/lib/data/custom/option/CustomOptionList.class.php index 30345774e72..97aa57cf00b 100644 --- a/wcfsetup/install/files/lib/data/custom/option/CustomOptionList.class.php +++ b/wcfsetup/install/files/lib/data/custom/option/CustomOptionList.class.php @@ -13,6 +13,7 @@ * @since 3.1 * * @extends DatabaseObjectList + * @deprecated 6.2 Use `IFormOption` instead */ abstract class CustomOptionList extends DatabaseObjectList { diff --git a/wcfsetup/install/files/lib/system/option/CustomOptionHandler.class.php b/wcfsetup/install/files/lib/system/option/CustomOptionHandler.class.php index fa1e2311174..f1dec15540e 100644 --- a/wcfsetup/install/files/lib/system/option/CustomOptionHandler.class.php +++ b/wcfsetup/install/files/lib/system/option/CustomOptionHandler.class.php @@ -18,6 +18,7 @@ * @template TOption of CustomOption = CustomOption * @extends OptionHandler * @phpstan-import-type ParsedOption from OptionHandler + * @deprecated 6.2 Use `IFormOption` instead */ abstract class CustomOptionHandler extends OptionHandler { From a8017f789bbb93f3e9eb11de929d8f69a2d895dc Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Mon, 28 Apr 2025 12:53:33 +0200 Subject: [PATCH 16/33] Improve guest version of the contact form --- .../install/files/lib/form/ContactForm.class.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/wcfsetup/install/files/lib/form/ContactForm.class.php b/wcfsetup/install/files/lib/form/ContactForm.class.php index 738a2319761..951d15677f0 100644 --- a/wcfsetup/install/files/lib/form/ContactForm.class.php +++ b/wcfsetup/install/files/lib/form/ContactForm.class.php @@ -45,11 +45,11 @@ protected function createForm() TextFormField::create('name') ->label('wcf.contact.sender') ->required() - ->value(WCF::getUser()->username), + ->value(WCF::getUser()->username ?: ''), EmailFormField::create('email') ->label('wcf.user.email') ->required() - ->value(WCF::getUser()->email), + ->value(WCF::getUser()->email ?: ''), $this->getRecipientFormField(), ...$this->getOptionFormFields() ]); @@ -59,12 +59,10 @@ protected function createForm() } if (!WCF::getUser()->userID) { - $captchaContainer = FormContainer::create('captchaContainer') - ->appendChildren([ - CaptchaFormField::create() - ->objectType(\CAPTCHA_TYPE), - ]); - $this->form->appendChild($captchaContainer); + $this->form->appendChild( + CaptchaFormField::create() + ->objectType(\CAPTCHA_TYPE) + ); } } From b3a5f36838f37de03e0afd5d64d2f1e1836a10c1 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Mon, 28 Apr 2025 12:59:52 +0200 Subject: [PATCH 17/33] Add flood control for the contact form --- com.woltlab.wcf/objectType.xml | 4 ++++ .../files/lib/form/ContactForm.class.php | 21 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/com.woltlab.wcf/objectType.xml b/com.woltlab.wcf/objectType.xml index d5423df277e..de876a31dd4 100644 --- a/com.woltlab.wcf/objectType.xml +++ b/com.woltlab.wcf/objectType.xml @@ -1727,6 +1727,10 @@ com.woltlab.wcf.lostPasswordForm com.woltlab.wcf.floodControl + + com.woltlab.wcf.contactForm + com.woltlab.wcf.floodControl + com.woltlab.wcf.search com.woltlab.wcf.floodControl diff --git a/wcfsetup/install/files/lib/form/ContactForm.class.php b/wcfsetup/install/files/lib/form/ContactForm.class.php index 951d15677f0..8ce0d1bf6e4 100644 --- a/wcfsetup/install/files/lib/form/ContactForm.class.php +++ b/wcfsetup/install/files/lib/form/ContactForm.class.php @@ -9,8 +9,9 @@ use wcf\event\page\ContactFormSpamChecking; use wcf\system\contact\form\SubmitContactForm; use wcf\system\event\EventHandler; +use wcf\system\exception\NamedUserException; use wcf\system\exception\PermissionDeniedException; -use wcf\system\form\builder\container\FormContainer; +use wcf\system\flood\FloodControl; use wcf\system\form\builder\field\CaptchaFormField; use wcf\system\form\builder\field\EmailFormField; use wcf\system\form\builder\field\FileProcessorFormField; @@ -31,6 +32,8 @@ */ class ContactForm extends AbstractFormBuilderForm { + private const ALLOWED_MAILS_PER_10M = 2; + /** * @inheritDoc */ @@ -66,6 +69,20 @@ protected function createForm() } } + #[\Override] + public function validate() + { + $requests = FloodControl::getInstance()->countContent( + 'com.woltlab.wcf.contactForm', + new \DateInterval('PT10M') + ); + if ($requests['count'] >= self::ALLOWED_MAILS_PER_10M) { + throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.page.error.flood')); + } + + parent::validate(); + } + #[\Override] public function save() { @@ -104,6 +121,8 @@ public function save() $this->saved(); + FloodControl::getInstance()->registerContent('com.woltlab.wcf.contactForm'); + HeaderUtil::delayedRedirect( LinkHandler::getInstance()->getLink(), WCF::getLanguage()->getDynamicVariable('wcf.contact.success') From 0cd905b73a0980d9432f85c7de0d1fda4583bf9f Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Mon, 28 Apr 2025 18:01:23 +0200 Subject: [PATCH 18/33] Add additional form option types --- .../acp/form/ContactOptionAddForm.class.php | 3 +- .../form/option/BooleanFormOption.class.php | 42 +++++++++++ .../option/CheckboxesFormOption.class.php | 72 +++++++++++++++++++ .../form/option/CurrencyFormOption.class.php | 59 +++++++++++++++ .../form/option/DateFormOption.class.php | 44 ++++++++++++ .../form/option/EmailFormOption.class.php | 42 +++++++++++ .../form/option/FloatFormOption.class.php | 56 +++++++++++++++ .../form/option/FormOptionHandler.class.php | 14 +++- .../system/form/option/IFormOption.class.php | 6 ++ .../form/option/IconFormOption.class.php | 35 +++++++++ .../form/option/IntegerFormOption.class.php | 56 +++++++++++++++ .../option/RadioButtonFormOption.class.php | 20 ++++++ .../form/option/RatingFormOption.class.php | 35 +++++++++ .../SharedConfigurationFormFields.class.php | 24 +++++-- .../option/SourceCodeFormOption.class.php | 35 +++++++++ .../form/option/TextFormOption.class.php | 7 +- .../form/option/TextareaFormOption.class.php | 2 +- .../form/option/UrlFormOption.class.php | 35 +++++++++ .../formatter/CurrencyFormatter.class.php | 36 ++++++++++ .../option/formatter/DateFormatter.class.php | 32 +++++++++ 20 files changed, 645 insertions(+), 10 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/form/option/BooleanFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/DateFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/IconFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/RadioButtonFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/RatingFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/DateFormatter.class.php diff --git a/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php b/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php index f43a5d455d1..17667c89ef4 100644 --- a/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php @@ -147,6 +147,7 @@ private function getAvailableOptionTypes(): array */ private function getSharedConfigurationFormFields(): array { + $sharedConfigurationFormFields = new SharedConfigurationFormFields(); $matrix = []; foreach (FormOptionHandler::getInstance()->getOptions() as $option) { @@ -162,7 +163,7 @@ private function getSharedConfigurationFormFields(): array $formFields = []; foreach ($matrix as $formFieldId => $dependencies) { - $formField = SharedConfigurationFormFields::getInstance()->getFormField($formFieldId); + $formField = $sharedConfigurationFormFields->getFormField($formFieldId); $formField->addDependency( ValueFormFieldDependency::create($formFieldId . 'OptionTypeDependency') ->fieldId('optionType') diff --git a/wcfsetup/install/files/lib/system/form/option/BooleanFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/BooleanFormOption.class.php new file mode 100644 index 00000000000..55567d6c0f7 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/BooleanFormOption.class.php @@ -0,0 +1,42 @@ + + * @since 6.2 + */ +class BooleanFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'boolean'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): AbstractFormField + { + return BooleanFormField::create($id); + } + + #[\Override] + public function getFormatter(): BooleanFormatter + { + return new BooleanFormatter(); + } + + #[\Override] + public function getPlainTextFormatter(): BooleanFormatter + { + return $this->getFormatter(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php new file mode 100644 index 00000000000..9645664b79d --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php @@ -0,0 +1,72 @@ + + * @since 6.2 + */ +class CheckboxesFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'checkboxes'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): MultipleSelectionFormField + { + $formField = MultipleSelectionFormField::create($id); + + if (isset($configurationData['selectOptions'])) { + $selectOptions = []; + foreach (JSON::decode($configurationData['selectOptions']) as $selectOption) { + if (isset($selectOption['value'][0])) { + $value = $selectOption['value'][0]; + } else if (isset($selectOption['value'][WCF::getLanguage()->languageID])) { + $value = $selectOption['value'][WCF::getLanguage()->languageID]; + } else { + $value = reset($selectOption['value']); + } + + $selectOptions[$selectOption['key']] = $value; + } + + $formField->options($selectOptions); + } + + return $formField; + } + + #[\Override] + public function getConfigurationFormFields(): array + { + return [ + 'selectOptions' + ]; + } + + #[\Override] + public function getFormatter(): MultipleSelectionFormatter + { + return new MultipleSelectionFormatter(); + } + + #[\Override] + public function getPlainTextFormatter(): MultipleSelectionFormatter + { + return $this->getFormatter(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php new file mode 100644 index 00000000000..56bccf81066 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php @@ -0,0 +1,59 @@ + + * @since 6.2 + */ +class CurrencyFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'currency'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): AbstractFormField + { + $formField = CurrencyFormField::create($id); + if (isset($configurationData['currency'])) { + $formField->currency($configurationData['currency']); + } + if (isset($configurationData['minFloatValue'])) { + $formField->minimum($configurationData['minFloatValue']); + } + if (isset($configurationData['maxFloatValue'])) { + $formField->maximum($configurationData['maxFloatValue']); + } + + return $formField; + } + + #[\Override] + public function getConfigurationFormFields(): array + { + return ['currency', 'minFloatValue', 'maxFloatValue']; + } + + #[\Override] + public function getFormatter(): CurrencyFormatter + { + return new CurrencyFormatter(); + } + + #[\Override] + public function getPlainTextFormatter(): CurrencyFormatter + { + return $this->getFormatter(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/DateFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/DateFormOption.class.php new file mode 100644 index 00000000000..b40fbe54588 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/DateFormOption.class.php @@ -0,0 +1,44 @@ + + * @since 6.2 + */ +class DateFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'date'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): DateFormField + { + $formField = DateFormField::create($id) + ->saveValueFormat('Y-m-d'); + + return $formField; + } + + #[\Override] + public function getFormatter(): DateFormatter + { + return new DateFormatter(); + } + + #[\Override] + public function getPlainTextFormatter(): DateFormatter + { + return $this->getFormatter(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php new file mode 100644 index 00000000000..6f51364807d --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php @@ -0,0 +1,42 @@ + + * @since 6.2 + */ +class EmailFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'email'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): AbstractFormField + { + return EmailFormField::create($id); + } + + #[\Override] + public function getFormatter(): EmailFormatter + { + return new EmailFormatter(); + } + + #[\Override] + public function getPlainTextFormatter(): EmailFormatter + { + return $this->getFormatter(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php new file mode 100644 index 00000000000..da74268db6b --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php @@ -0,0 +1,56 @@ + + * @since 6.2 + */ +class FloatFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'float'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): AbstractFormField + { + $formField = FloatFormField::create($id); + if (isset($configurationData['minValue'])) { + $formField->minimum($configurationData['minValue']); + } + if (isset($configurationData['maxValue'])) { + $formField->maximum($configurationData['maxValue']); + } + + return $formField; + } + + #[\Override] + public function getConfigurationFormFields(): array + { + return ['minFloatValue', 'maxFloatValue']; + } + + #[\Override] + public function getFormatter(): FloatFormatter + { + return new FloatFormatter(); + } + + #[\Override] + public function getPlainTextFormatter(): FloatFormatter + { + return $this->getFormatter(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php b/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php index e7ebf11e148..967275b4789 100644 --- a/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php +++ b/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php @@ -36,9 +36,21 @@ private function getDefaultFormOptions(): array foreach ( [ + new BooleanFormOption(), + new CheckboxesFormOption(), + new CurrencyFormOption(), + new DateFormOption(), + new EmailFormOption(), + new FloatFormOption(), + new IconFormOption(), + new IntegerFormOption(), + new RadioButtonFormOption(), + new RatingFormOption(), + new SelectFormOption(), + new SourceCodeFormOption(), new TextFormOption(), new TextareaFormOption(), - new SelectFormOption() + new UrlFormOption(), ] as $defaultOption ) { $options[$defaultOption->getId()] = $defaultOption; diff --git a/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php index e0d61a914d7..fc235d9a425 100644 --- a/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php @@ -17,8 +17,14 @@ interface IFormOption { public function getId(): string; + /** + * @param array $configurationData + */ public function getFormField(string $id, array $configurationData = []): AbstractFormField; + /** + * @return string[] + */ public function getConfigurationFormFields(): array; public function getFormatter(): IFormOptionFormatter; diff --git a/wcfsetup/install/files/lib/system/form/option/IconFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/IconFormOption.class.php new file mode 100644 index 00000000000..58dd4aeaa9c --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/IconFormOption.class.php @@ -0,0 +1,35 @@ + + * @since 6.2 + */ +class IconFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'icon'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): IconFormField + { + return IconFormField::create($id); + } + + #[\Override] + public function getFormatter(): IconFormatter + { + return new IconFormatter(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php new file mode 100644 index 00000000000..f3c3970a7bb --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php @@ -0,0 +1,56 @@ + + * @since 6.2 + */ +class IntegerFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'integer'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): AbstractFormField + { + $formField = IntegerFormField::create($id); + if (isset($configurationData['minIntegerValue'])) { + $formField->minimum($configurationData['minIntegerValue']); + } + if (isset($configurationData['maxIntegerValue'])) { + $formField->maximum($configurationData['maxIntegerValue']); + } + + return $formField; + } + + #[\Override] + public function getConfigurationFormFields(): array + { + return ['minIntegerValue', 'maxIntegerValue']; + } + + #[\Override] + public function getFormatter(): IntegerFormatter + { + return new IntegerFormatter(); + } + + #[\Override] + public function getPlainTextFormatter(): IntegerFormatter + { + return $this->getFormatter(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/RadioButtonFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/RadioButtonFormOption.class.php new file mode 100644 index 00000000000..7bb0a3d5da4 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/RadioButtonFormOption.class.php @@ -0,0 +1,20 @@ + + * @since 6.2 + */ +class RadioButtonFormOption extends SelectFormOption +{ + #[\Override] + public function getId(): string + { + return 'radioButton'; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/RatingFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/RatingFormOption.class.php new file mode 100644 index 00000000000..41ba8ada9e9 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/RatingFormOption.class.php @@ -0,0 +1,35 @@ + + * @since 6.2 + */ +class RatingFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'rating'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): RatingFormField + { + return RatingFormField::create($id); + } + + #[\Override] + public function getFormatter(): RatingFormatter + { + return new RatingFormatter(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php b/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php index 923ca40a208..74aacc64d93 100644 --- a/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php +++ b/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php @@ -4,10 +4,11 @@ use wcf\event\form\option\SharedConfigurationFormFieldCollecting; use wcf\system\event\EventHandler; +use wcf\system\form\builder\field\FloatFormField; use wcf\system\form\builder\field\IFormField; use wcf\system\form\builder\field\IntegerFormField; use wcf\system\form\builder\field\SelectOptionsFormField; -use wcf\system\SingletonFactory; +use wcf\system\form\builder\field\TextFormField; /** * Provides the available shared configuration form fields. @@ -17,15 +18,14 @@ * @license GNU Lesser General Public License * @since 6.2 */ -final class SharedConfigurationFormFields extends SingletonFactory +final class SharedConfigurationFormFields { /** * @var array */ private array $formFields; - #[\Override] - protected function init() + public function __construct() { $this->formFields = \array_merge($this->getDefaultFormFields(), $this->getEventFormFields()); } @@ -36,11 +36,25 @@ protected function init() private function getDefaultFormFields(): array { return [ + 'currency' => TextFormField::create('currency') + ->label('wcf.acp.customOption.currency') + ->value('EUR') + ->required(), + 'defaultTextValue' => TextFormField::create('defaultTextValue') + ->label('wcf.acp.customOption.defaultValue'), 'maxLength' => IntegerFormField::create('maxLength') ->label('wcf.acp.customOption.maxLength'), + 'minIntegerValue' => IntegerFormField::create('minIntegerValue') + ->label('wcf.acp.customOption.minValue'), + 'maxIntegerValue' => IntegerFormField::create('maxIntegerValue') + ->label('wcf.acp.customOption.maxValue'), + 'minFloatValue' => FloatFormField::create('minFloatValue') + ->label('wcf.acp.customOption.minValue'), + 'maxFloatValue' => FloatFormField::create('maxFloatValue') + ->label('wcf.acp.customOption.maxValue'), 'selectOptions' => SelectOptionsFormField::create('selectOptions') ->label('wcf.acp.customOption.selectOptions') - ->required() + ->required(), ]; } diff --git a/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php new file mode 100644 index 00000000000..c147cff1aa9 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php @@ -0,0 +1,35 @@ + + * @since 6.2 + */ +class SourceCodeFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'sourceCode'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): SourceCodeFormField + { + return SourceCodeFormField::create($id); + } + + #[\Override] + public function getFormatter(): SourceCodeFormatter + { + return new SourceCodeFormatter(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php index 22fd10a6153..1ee89db0b76 100644 --- a/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php @@ -25,9 +25,12 @@ public function getId(): string public function getFormField(string $id, array $configurationData = []): AbstractFormField { $formField = TextFormField::create($id); - if (isset($configurationData['maxLength'])) { + if (!empty($configurationData['maxLength'])) { $formField->maximumLength($configurationData['maxLength']); } + if (isset($configurationData['defaultTextValue'])) { + $formField->value($configurationData['defaultTextValue']); + } return $formField; } @@ -35,6 +38,6 @@ public function getFormField(string $id, array $configurationData = []): Abstrac #[\Override] public function getConfigurationFormFields(): array { - return ['maxLength']; + return ['maxLength', 'defaultTextValue']; } } diff --git a/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php index f67ff2425cf..0775101e274 100644 --- a/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php @@ -26,7 +26,7 @@ public function getId(): string public function getFormField(string $id, array $configurationData = []): AbstractFormField { $formField = MultilineTextFormField::create($id); - if (isset($configurationData['maxLength'])) { + if (!empty($configurationData['maxLength'])) { $formField->maximumLength($configurationData['maxLength']); } diff --git a/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php new file mode 100644 index 00000000000..bc9d9b00722 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php @@ -0,0 +1,35 @@ + + * @since 6.2 + */ +class UrlFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'url'; + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): UrlFormField + { + return UrlFormField::create($id); + } + + #[\Override] + public function getFormatter(): UrlFormatter + { + return new UrlFormatter(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php new file mode 100644 index 00000000000..b2007e001f2 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php @@ -0,0 +1,36 @@ + + * @since 6.2 + */ +class CurrencyFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + $showDecimals = $value % 100 != 0; + $value /= 100; + $language = LanguageFactory::getInstance()->getLanguage($languageID); + $suffix = ''; + if (!empty($configurationData['currency'])) { + $suffix = ' ' . StringUtil::encodeHTML($configurationData['currency']); + } + + return \number_format( + \round($value, 2), + $showDecimals ? 2 : 0, + $language->get('wcf.global.decimalPoint'), + $language->get('wcf.global.thousandsSeparator') + ) . $suffix; + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/DateFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/DateFormatter.class.php new file mode 100644 index 00000000000..09d8393ed28 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/DateFormatter.class.php @@ -0,0 +1,32 @@ + + * @since 6.2 + */ +final class DateFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + $dateTime = new \DateTimeImmutable($value); + $locale = LanguageFactory::getInstance()->getLanguage($languageID)->getLocale(); + + return \IntlDateFormatter::formatObject( + $dateTime, + [ + \IntlDateFormatter::LONG, + \IntlDateFormatter::NONE, + ], + $locale + ); + } +} From 2107cfa6f94c7197357cceafda219fc6bb4ffe92 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Mon, 28 Apr 2025 18:04:08 +0200 Subject: [PATCH 19/33] Remove duplicate code --- ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts | 1 - .../js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js | 1 - 2 files changed, 2 deletions(-) diff --git a/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts b/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts index 093ad0ab72c..b1ebb8eea60 100644 --- a/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts +++ b/ts/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.ts @@ -82,7 +82,6 @@ function createRow(ul: HTMLUListElement, option?: Data, autoFocus: boolean = fal valueInput.addEventListener("keydown", (event) => { if (event.key === "Enter") { event.preventDefault(); - createRow(ul); createRow(ul, undefined, true); } }); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js index 556d952fd76..e7a018f1b89 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/SelectOptions.js @@ -62,7 +62,6 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Dom/Util", "WoltLabSui valueInput.addEventListener("keydown", (event) => { if (event.key === "Enter") { event.preventDefault(); - createRow(ul); createRow(ul, undefined, true); } }); From 4046975cd3fea5bd50436bf8c605aa797110ebbc Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Tue, 29 Apr 2025 12:40:47 +0200 Subject: [PATCH 20/33] Add missing language variables --- .../acp/form/ContactOptionAddForm.class.php | 11 +++++++++- .../form/option/AbstractFormOption.class.php | 7 +++++++ .../system/form/option/IFormOption.class.php | 2 ++ .../SharedConfigurationFormFields.class.php | 18 +++++++++------- wcfsetup/install/lang/de.xml | 21 +++++++++++++++++++ wcfsetup/install/lang/en.xml | 21 +++++++++++++++++++ 6 files changed, 71 insertions(+), 9 deletions(-) diff --git a/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php b/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php index 17667c89ef4..3029495e08d 100644 --- a/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php @@ -18,6 +18,7 @@ use wcf\system\form\builder\IFormDocument; use wcf\system\form\option\FormOptionHandler; use wcf\system\form\option\SharedConfigurationFormFields; +use wcf\system\WCF; use wcf\util\JSON; /** @@ -139,7 +140,15 @@ private function getContactOptions(): array */ private function getAvailableOptionTypes(): array { - return \array_map(fn($option) => $option->getId(), FormOptionHandler::getInstance()->getOptions()); + $optionTypes = \array_map(fn($option) => $option->getTitle(), FormOptionHandler::getInstance()->getOptions()); + + $collator = new \Collator(WCF::getLanguage()->getLocale()); + \uasort( + $optionTypes, + static fn(string $a, string $b) => $collator->compare($a, $b) + ); + + return $optionTypes; } /** diff --git a/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php index 441096fb4a0..a74d95f3b1b 100644 --- a/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php @@ -5,6 +5,7 @@ use wcf\system\form\option\formatter\DefaultFormatter; use wcf\system\form\option\formatter\DefaultPlainTextFormatter; use wcf\system\form\option\formatter\IFormOptionFormatter; +use wcf\system\WCF; /** * Provides abstract implementations for form option types. @@ -22,6 +23,12 @@ public function getConfigurationFormFields(): array return []; } + #[\Override] + public function getTitle(): string + { + return WCF::getLanguage()->get('wcf.form.option.' . $this->getId()); + } + #[\Override] public function getFormatter(): IFormOptionFormatter { diff --git a/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php index fc235d9a425..78c32521b46 100644 --- a/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php @@ -17,6 +17,8 @@ interface IFormOption { public function getId(): string; + public function getTitle(): string; + /** * @param array $configurationData */ diff --git a/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php b/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php index 74aacc64d93..de403f0686a 100644 --- a/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php +++ b/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php @@ -37,23 +37,25 @@ private function getDefaultFormFields(): array { return [ 'currency' => TextFormField::create('currency') - ->label('wcf.acp.customOption.currency') + ->label('wcf.form.option.shared.currency') ->value('EUR') + ->addFieldClass('short') ->required(), 'defaultTextValue' => TextFormField::create('defaultTextValue') - ->label('wcf.acp.customOption.defaultValue'), + ->label('wcf.form.option.shared.defaultValue') + ->addFieldClass('medium'), 'maxLength' => IntegerFormField::create('maxLength') - ->label('wcf.acp.customOption.maxLength'), + ->label('wcf.form.option.shared.maxLength'), 'minIntegerValue' => IntegerFormField::create('minIntegerValue') - ->label('wcf.acp.customOption.minValue'), + ->label('wcf.form.option.shared.minValue'), 'maxIntegerValue' => IntegerFormField::create('maxIntegerValue') - ->label('wcf.acp.customOption.maxValue'), + ->label('wcf.form.option.shared.maxValue'), 'minFloatValue' => FloatFormField::create('minFloatValue') - ->label('wcf.acp.customOption.minValue'), + ->label('wcf.form.option.shared.minValue'), 'maxFloatValue' => FloatFormField::create('maxFloatValue') - ->label('wcf.acp.customOption.maxValue'), + ->label('wcf.form.option.shared.maxValue'), 'selectOptions' => SelectOptionsFormField::create('selectOptions') - ->label('wcf.acp.customOption.selectOptions') + ->label('wcf.form.option.shared.selectOptions') ->required(), ]; } diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index 24649e01710..cbd0355780a 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -4114,6 +4114,27 @@ Dateianhänge: + + + + + + + + + + + + + + + + + + + + + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index 55c4afebef8..9a6368c1611 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -4060,6 +4060,27 @@ Attachments: + + + + + + + + + + + + + + + + + + + + + From 5789cb4cf2dfa2adfb1219fdd7f210247f5ff448 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Wed, 30 Apr 2025 16:46:23 +0200 Subject: [PATCH 21/33] Add `AbstractFormOptionAddForm` --- .../database/update_com.woltlab.wcf_6.2.php | 3 + .../files/acp/templates/contactSettings.tpl | 2 +- .../form/AbstractCustomOptionForm.class.php | 2 +- .../form/AbstractFormOptionAddForm.class.php | 137 ++++++++++++++++++ .../acp/form/ContactOptionAddForm.class.php | 122 +--------------- .../contact/option/ContactOption.class.php | 1 - .../files/lib/form/ContactForm.class.php | 2 +- .../form/option/AbstractFormOption.class.php | 2 +- .../option/CheckboxesFormOption.class.php | 4 +- .../form/option/CurrencyFormOption.class.php | 2 +- .../form/option/FloatFormOption.class.php | 2 +- .../form/option/IntegerFormOption.class.php | 2 +- .../form/option/SelectFormOption.class.php | 4 +- .../SharedConfigurationFormFields.class.php | 4 + .../form/option/TextFormOption.class.php | 2 +- .../form/option/TextareaFormOption.class.php | 2 +- wcfsetup/install/lang/de.xml | 2 + wcfsetup/install/lang/en.xml | 2 + wcfsetup/setup/db/install.sql | 1 - 19 files changed, 161 insertions(+), 137 deletions(-) create mode 100644 wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php diff --git a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php index b7d1fe52ef5..cdcf8ef0000 100644 --- a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php +++ b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php @@ -11,6 +11,7 @@ use wcf\system\database\table\column\IntDatabaseTableColumn; use wcf\system\database\table\column\MediumtextDatabaseTableColumn; use wcf\system\database\table\column\TextDatabaseTableColumn; +use wcf\system\database\table\column\TinyintDatabaseTableColumn; use wcf\system\database\table\index\DatabaseTableForeignKey; use wcf\system\database\table\PartialDatabaseTable; @@ -57,6 +58,8 @@ ->drop(), MediumtextDatabaseTableColumn::create('selectOptions') ->drop(), + TinyintDatabaseTableColumn::create('required') + ->drop(), MediumtextDatabaseTableColumn::create('configurationData'), ]), PartialDatabaseTable::create('wcf1_file') diff --git a/wcfsetup/install/files/acp/templates/contactSettings.tpl b/wcfsetup/install/files/acp/templates/contactSettings.tpl index 915d9adf2d9..e42632d97dd 100644 --- a/wcfsetup/install/files/acp/templates/contactSettings.tpl +++ b/wcfsetup/install/files/acp/templates/contactSettings.tpl @@ -64,7 +64,7 @@ {@$option->optionID} {$option->getTitle()} - {lang}wcf.acp.customOption.optionType.{$option->optionType}{/lang} + {lang}wcf.form.option.{$option->optionType}{/lang} {#$option->showOrder} {event name='columns'} diff --git a/wcfsetup/install/files/lib/acp/form/AbstractCustomOptionForm.class.php b/wcfsetup/install/files/lib/acp/form/AbstractCustomOptionForm.class.php index 7a3aa06e8b0..5d263bdba15 100644 --- a/wcfsetup/install/files/lib/acp/form/AbstractCustomOptionForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/AbstractCustomOptionForm.class.php @@ -15,7 +15,7 @@ * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License * @since 3.1 - * @deprecated 6.2 Use `IFormOption` instead + * @deprecated 6.2 Use `AbstractFormOptionAddForm` instead */ abstract class AbstractCustomOptionForm extends AbstractAcpForm { diff --git a/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php b/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php new file mode 100644 index 00000000000..a2830d799ae --- /dev/null +++ b/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php @@ -0,0 +1,137 @@ + + * @since 6.2 + */ +abstract class AbstractFormOptionAddForm extends AbstractFormBuilderForm +{ + #[\Override] + public function finalizeForm() + { + parent::finalizeForm(); + + $this->form->getDataHandler()->addProcessor( + new CustomFormDataProcessor( + 'saveOptionProcessor', + function (IFormDocument $document, array $parameters) { + $configurationData = []; + + foreach ($this->getConfigurationFormFieldIds() as $parameter) { + if (isset($parameters['data'][$parameter])) { + $configurationData[$parameter] = $parameters['data'][$parameter]; + unset($parameters['data'][$parameter]); + } + } + + if ($configurationData !== []) { + $parameters['data']['configurationData'] = JSON::encode($configurationData); + } + + return $parameters; + }, + function (IFormDocument $document, array $data, IStorableObject $object) { + \assert($object instanceof DatabaseObject); + + if ($object->configurationData) { + $data = \array_merge($data, JSON::decode($object->configurationData)); + } + + return $data; + } + ) + ); + } + + /** + * @return string[] + */ + protected function getConfigurationFormFieldIds(): array + { + $ids = []; + + foreach (FormOptionHandler::getInstance()->getOptions() as $option) { + foreach ($option->getConfigurationFormFields() as $formFieldId) { + $ids[] = $formFieldId; + } + } + + return \array_unique($ids); + } + + /** + * @return IFormField[] + */ + protected function getSharedConfigurationFormFields(): array + { + $sharedConfigurationFormFields = new SharedConfigurationFormFields(); + $matrix = []; + + foreach (FormOptionHandler::getInstance()->getOptions() as $option) { + foreach ($option->getConfigurationFormFields() as $formFieldId) { + if (!isset($matrix[$formFieldId])) { + $matrix[$formFieldId] = []; + } + + $matrix[$formFieldId][] = $option->getId(); + } + } + + $formFields = []; + + foreach ($matrix as $formFieldId => $dependencies) { + $formField = $sharedConfigurationFormFields->getFormField($formFieldId); + $formField->addDependency( + ValueFormFieldDependency::create($formFieldId . 'OptionTypeDependency') + ->fieldId('optionType') + ->values($dependencies) + ); + $formFields[] = $formField; + } + + return $formFields; + } + + /** + * @return array + */ + protected function getAvailableOptionTypes(): array + { + $optionTypes = \array_map(fn($option) => $option->getTitle(), FormOptionHandler::getInstance()->getOptions()); + + $collator = new \Collator(WCF::getLanguage()->getLocale()); + \uasort( + $optionTypes, + static fn(string $a, string $b) => $collator->compare($a, $b) + ); + + return $optionTypes; + } + + protected function getOptionTypeFormField(): SelectFormField + { + return SelectFormField::create('optionType') + ->label('wcf.form.option.optionType') + ->immutable($this->formAction != 'create') + ->options($this->getAvailableOptionTypes()) + ->required(); + } +} diff --git a/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php b/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php index 3029495e08d..a342d2caf42 100644 --- a/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/ContactOptionAddForm.class.php @@ -5,21 +5,11 @@ use wcf\data\contact\option\ContactOption; use wcf\data\contact\option\ContactOptionAction; use wcf\data\contact\option\ContactOptionList; -use wcf\data\IStorableObject; use wcf\form\AbstractFormBuilderForm; -use wcf\system\form\builder\data\processor\CustomFormDataProcessor; use wcf\system\form\builder\field\BooleanFormField; -use wcf\system\form\builder\field\dependency\ValueFormFieldDependency; -use wcf\system\form\builder\field\IFormField; use wcf\system\form\builder\field\MultilineTextFormField; -use wcf\system\form\builder\field\SelectFormField; use wcf\system\form\builder\field\ShowOrderFormField; use wcf\system\form\builder\field\TextFormField; -use wcf\system\form\builder\IFormDocument; -use wcf\system\form\option\FormOptionHandler; -use wcf\system\form\option\SharedConfigurationFormFields; -use wcf\system\WCF; -use wcf\util\JSON; /** * Shows the contact option add form. @@ -31,7 +21,7 @@ * * @extends AbstractFormBuilderForm */ -class ContactOptionAddForm extends AbstractFormBuilderForm +class ContactOptionAddForm extends AbstractFormOptionAddForm { /** * @inheritDoc @@ -73,56 +63,13 @@ protected function createForm() ->languageItemPattern('wcf.contact.optionDescription\d+'), ShowOrderFormField::create('showOrder') ->options($this->getContactOptions()), - SelectFormField::create('optionType') - ->label('wcf.acp.customOption.optionType') - ->immutable($this->formAction != 'create') - ->options($this->getAvailableOptionTypes()) - ->required(), + $this->getOptionTypeFormField(), ...$this->getSharedConfigurationFormFields(), - BooleanFormField::create('required') - ->label('wcf.acp.customOption.required'), BooleanFormField::create('isDisabled') ->label('wcf.acp.customOption.isDisabled'), ]); } - #[\Override] - public function finalizeForm() - { - parent::finalizeForm(); - - $this->form->getDataHandler()->addProcessor( - new CustomFormDataProcessor( - 'saveOptionProcessor', - function (IFormDocument $document, array $parameters) { - $configurationData = []; - - foreach ($this->getConfigurationFormFieldIds() as $parameter) { - if (isset($parameters['data'][$parameter])) { - $configurationData[$parameter] = $parameters['data'][$parameter]; - unset($parameters['data'][$parameter]); - } - } - - if ($configurationData !== []) { - $parameters['data']['configurationData'] = JSON::encode($configurationData); - } - - return $parameters; - }, - function (IFormDocument $document, array $data, IStorableObject $object) { - \assert($object instanceof ContactOption); - - if ($object->configurationData) { - $data = \array_merge($data, JSON::decode($object->configurationData)); - } - - return $data; - } - ) - ); - } - /** * @return array */ @@ -134,69 +81,4 @@ private function getContactOptions(): array return \array_map(static fn($option) => $option->getTitle(), $optionList->getObjects()); } - - /** - * @return array - */ - private function getAvailableOptionTypes(): array - { - $optionTypes = \array_map(fn($option) => $option->getTitle(), FormOptionHandler::getInstance()->getOptions()); - - $collator = new \Collator(WCF::getLanguage()->getLocale()); - \uasort( - $optionTypes, - static fn(string $a, string $b) => $collator->compare($a, $b) - ); - - return $optionTypes; - } - - /** - * @return IFormField[] - */ - private function getSharedConfigurationFormFields(): array - { - $sharedConfigurationFormFields = new SharedConfigurationFormFields(); - $matrix = []; - - foreach (FormOptionHandler::getInstance()->getOptions() as $option) { - foreach ($option->getConfigurationFormFields() as $formFieldId) { - if (!isset($matrix[$formFieldId])) { - $matrix[$formFieldId] = []; - } - - $matrix[$formFieldId][] = $option->getId(); - } - } - - $formFields = []; - - foreach ($matrix as $formFieldId => $dependencies) { - $formField = $sharedConfigurationFormFields->getFormField($formFieldId); - $formField->addDependency( - ValueFormFieldDependency::create($formFieldId . 'OptionTypeDependency') - ->fieldId('optionType') - ->values($dependencies) - ); - $formFields[] = $formField; - } - - return $formFields; - } - - /** - * @return string[] - */ - private function getConfigurationFormFieldIds(): array - { - $ids = []; - - foreach (FormOptionHandler::getInstance()->getOptions() as $option) { - foreach ($option->getConfigurationFormFields() as $formFieldId) { - $ids[] = $formFieldId; - } - } - - return \array_unique($ids); - } } diff --git a/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php b/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php index 3847cf7dce9..314754d81cf 100644 --- a/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php +++ b/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php @@ -23,7 +23,6 @@ * @property-read string $optionDescription description of the option or name of language item which contains the description * @property-read string $optionType type of the option which determines its input and output * @property-read string $configurationData JSON-encoded configuration information depending on the option type - * @property-read int $required is `1` if the option has to be filled out, otherwise `0` * @property-read int $showOrder position of the option in relation to the other options * @property-read int $isDisabled is `1` if the option is disabled, otherwise `0` * @property-read int $originIsSystem is `1` if the option has been delivered by a package, otherwise `0` (i.e. the option has been created in the ACP) diff --git a/wcfsetup/install/files/lib/form/ContactForm.class.php b/wcfsetup/install/files/lib/form/ContactForm.class.php index 8ce0d1bf6e4..d3713575e67 100644 --- a/wcfsetup/install/files/lib/form/ContactForm.class.php +++ b/wcfsetup/install/files/lib/form/ContactForm.class.php @@ -213,7 +213,7 @@ protected function getOptionFormFields(): array $formField->label($option->optionTitle); $formField->description($option->optionDescription); - if ($option->required) { + if (!empty($option->getConfigurationData()['required'])) { $formField->required(); } diff --git a/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php index a74d95f3b1b..8909c7a11ca 100644 --- a/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php @@ -20,7 +20,7 @@ abstract class AbstractFormOption implements IFormOption #[\Override] public function getConfigurationFormFields(): array { - return []; + return ['required']; } #[\Override] diff --git a/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php index 9645664b79d..ed2c0c5185c 100644 --- a/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php @@ -53,9 +53,7 @@ public function getFormField(string $id, array $configurationData = []): Multipl #[\Override] public function getConfigurationFormFields(): array { - return [ - 'selectOptions' - ]; + return ['selectOptions', 'required']; } #[\Override] diff --git a/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php index 56bccf81066..0505614c9c4 100644 --- a/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php @@ -42,7 +42,7 @@ public function getFormField(string $id, array $configurationData = []): Abstrac #[\Override] public function getConfigurationFormFields(): array { - return ['currency', 'minFloatValue', 'maxFloatValue']; + return ['currency', 'minFloatValue', 'maxFloatValue', 'required']; } #[\Override] diff --git a/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php index da74268db6b..093e2997ed7 100644 --- a/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php @@ -39,7 +39,7 @@ public function getFormField(string $id, array $configurationData = []): Abstrac #[\Override] public function getConfigurationFormFields(): array { - return ['minFloatValue', 'maxFloatValue']; + return ['minFloatValue', 'maxFloatValue', 'required']; } #[\Override] diff --git a/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php index f3c3970a7bb..ea92f9dad70 100644 --- a/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php @@ -39,7 +39,7 @@ public function getFormField(string $id, array $configurationData = []): Abstrac #[\Override] public function getConfigurationFormFields(): array { - return ['minIntegerValue', 'maxIntegerValue']; + return ['minIntegerValue', 'maxIntegerValue', 'required']; } #[\Override] diff --git a/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php index 0d80773b7d0..25c7da76fb3 100644 --- a/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php @@ -52,9 +52,7 @@ public function getFormField(string $id, array $configurationData = []): Abstrac #[\Override] public function getConfigurationFormFields(): array { - return [ - 'selectOptions' - ]; + return ['selectOptions', 'required']; } #[\Override] diff --git a/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php b/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php index de403f0686a..ad9f0711dcf 100644 --- a/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php +++ b/wcfsetup/install/files/lib/system/form/option/SharedConfigurationFormFields.class.php @@ -4,6 +4,7 @@ use wcf\event\form\option\SharedConfigurationFormFieldCollecting; use wcf\system\event\EventHandler; +use wcf\system\form\builder\field\BooleanFormField; use wcf\system\form\builder\field\FloatFormField; use wcf\system\form\builder\field\IFormField; use wcf\system\form\builder\field\IntegerFormField; @@ -57,6 +58,9 @@ private function getDefaultFormFields(): array 'selectOptions' => SelectOptionsFormField::create('selectOptions') ->label('wcf.form.option.shared.selectOptions') ->required(), + 'required' => BooleanFormField::create('required') + ->label('wcf.form.option.shared.required') + ->value(false), ]; } diff --git a/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php index 1ee89db0b76..ad0465d6b8f 100644 --- a/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php @@ -38,6 +38,6 @@ public function getFormField(string $id, array $configurationData = []): Abstrac #[\Override] public function getConfigurationFormFields(): array { - return ['maxLength', 'defaultTextValue']; + return ['maxLength', 'defaultTextValue', 'required']; } } diff --git a/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php index 0775101e274..03328d10a64 100644 --- a/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php @@ -36,7 +36,7 @@ public function getFormField(string $id, array $configurationData = []): Abstrac #[\Override] public function getConfigurationFormFields(): array { - return ['maxLength']; + return ['maxLength', 'required']; } #[\Override] diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index cbd0355780a..abd613fa3b0 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -4135,6 +4135,8 @@ Dateianhänge: + + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index 9a6368c1611..da645e8887f 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -4081,6 +4081,8 @@ Attachments: + + diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index b30274aff7b..8b6bd7f3bec 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -467,7 +467,6 @@ CREATE TABLE wcf1_contact_option ( optionDescription TEXT, optionType VARCHAR(255) NOT NULL DEFAULT '', configurationData MEDIUMTEXT, - required TINYINT(1) NOT NULL DEFAULT 0, showOrder INT(10) NOT NULL DEFAULT 0, isDisabled TINYINT(1) NOT NULL DEFAULT 0, originIsSystem TINYINT(1) NOT NULL DEFAULT 0 From b230517caad319cb8b09be61ef98fd4a2d91db7b Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Wed, 30 Apr 2025 18:09:07 +0200 Subject: [PATCH 22/33] Add methods for list view filtering --- .../form/AbstractFormOptionAddForm.class.php | 18 +----- .../form/option/AbstractFormOption.class.php | 14 +++++ .../form/option/EmailFormOption.class.php | 15 +++++ .../form/option/FormOptionHandler.class.php | 17 ++++++ .../system/form/option/IFormOption.class.php | 8 +++ .../option/SourceCodeFormOption.class.php | 16 ++++++ .../form/option/TextFormOption.class.php | 14 +++++ .../form/option/TextareaFormOption.class.php | 15 +++++ .../form/option/UrlFormOption.class.php | 16 ++++++ .../filter/FormOptionFilter.class.php | 55 +++++++++++++++++++ 10 files changed, 171 insertions(+), 17 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/listView/filter/FormOptionFilter.class.php diff --git a/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php b/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php index a2830d799ae..7402e9f2631 100644 --- a/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php @@ -110,28 +110,12 @@ protected function getSharedConfigurationFormFields(): array return $formFields; } - /** - * @return array - */ - protected function getAvailableOptionTypes(): array - { - $optionTypes = \array_map(fn($option) => $option->getTitle(), FormOptionHandler::getInstance()->getOptions()); - - $collator = new \Collator(WCF::getLanguage()->getLocale()); - \uasort( - $optionTypes, - static fn(string $a, string $b) => $collator->compare($a, $b) - ); - - return $optionTypes; - } - protected function getOptionTypeFormField(): SelectFormField { return SelectFormField::create('optionType') ->label('wcf.form.option.optionType') ->immutable($this->formAction != 'create') - ->options($this->getAvailableOptionTypes()) + ->options(FormOptionHandler::getInstance()->getSortedOptionTypes()) ->required(); } } diff --git a/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php index 8909c7a11ca..78d29923f03 100644 --- a/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php @@ -2,6 +2,8 @@ namespace wcf\system\form\option; +use wcf\data\DatabaseObjectList; +use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\option\formatter\DefaultFormatter; use wcf\system\form\option\formatter\DefaultPlainTextFormatter; use wcf\system\form\option\formatter\IFormOptionFormatter; @@ -40,4 +42,16 @@ public function getPlainTextFormatter(): IFormOptionFormatter { return new DefaultPlainTextFormatter(); } + + #[\Override] + public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField + { + return $this->getFormField($id, $configurationData); + } + + #[\Override] + public function applyFilter(DatabaseObjectList $list, string $columnName, mixed $value): void + { + $list->getConditionBuilder()->add("{$columnName} = ?", [$value]); + } } diff --git a/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php index 6f51364807d..1fb1cc0c64c 100644 --- a/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php @@ -2,9 +2,12 @@ namespace wcf\system\form\option; +use wcf\data\DatabaseObjectList; use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\EmailFormField; +use wcf\system\form\builder\field\TextFormField; use wcf\system\form\option\formatter\EmailFormatter; +use wcf\system\WCF; /** * Implementation of a form field for email addresses. @@ -39,4 +42,16 @@ public function getPlainTextFormatter(): EmailFormatter { return $this->getFormatter(); } + + #[\Override] + public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField + { + return TextFormField::create($id); + } + + #[\Override] + public function applyFilter(DatabaseObjectList $list, string $columnName, mixed $value): void + { + $list->getConditionBuilder()->add("{$columnName} LIKE ?", ['%' . WCF::getDB()->escapeLikeValue($value) . '%']); + } } diff --git a/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php b/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php index 967275b4789..273767d82b0 100644 --- a/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php +++ b/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php @@ -5,6 +5,7 @@ use wcf\event\form\option\FormOptionCollecting; use wcf\system\event\EventHandler; use wcf\system\SingletonFactory; +use wcf\system\WCF; /** * Provides the available form options. @@ -82,4 +83,20 @@ public function getOption(string $identifier): ?IFormOption { return $this->options[$identifier] ?? null; } + + /** + * @return array + */ + public function getSortedOptionTypes(): array + { + $optionTypes = \array_map(fn($option) => $option->getTitle(), $this->getOptions()); + + $collator = new \Collator(WCF::getLanguage()->getLocale()); + \uasort( + $optionTypes, + static fn(string $a, string $b) => $collator->compare($a, $b) + ); + + return $optionTypes; + } } diff --git a/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php index 78c32521b46..3fa7417f44f 100644 --- a/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php @@ -2,6 +2,7 @@ namespace wcf\system\form\option; +use wcf\data\DatabaseObjectList; use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\option\formatter\IFormOptionFormatter; @@ -24,6 +25,11 @@ public function getTitle(): string; */ public function getFormField(string $id, array $configurationData = []): AbstractFormField; + /** + * @param array $configurationData + */ + public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField; + /** * @return string[] */ @@ -32,4 +38,6 @@ public function getConfigurationFormFields(): array; public function getFormatter(): IFormOptionFormatter; public function getPlainTextFormatter(): IFormOptionFormatter; + + public function applyFilter(DatabaseObjectList $list, string $columnName, mixed $value): void; } diff --git a/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php index c147cff1aa9..ba692ea7e72 100644 --- a/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php @@ -2,8 +2,12 @@ namespace wcf\system\form\option; +use wcf\data\DatabaseObjectList; +use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\SourceCodeFormField; +use wcf\system\form\builder\field\TextFormField; use wcf\system\form\option\formatter\SourceCodeFormatter; +use wcf\system\WCF; /** * Implementation of a form field for source code values. @@ -32,4 +36,16 @@ public function getFormatter(): SourceCodeFormatter { return new SourceCodeFormatter(); } + + #[\Override] + public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField + { + return TextFormField::create($id); + } + + #[\Override] + public function applyFilter(DatabaseObjectList $list, string $columnName, mixed $value): void + { + $list->getConditionBuilder()->add("{$columnName} LIKE ?", ['%' . WCF::getDB()->escapeLikeValue($value) . '%']); + } } diff --git a/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php index ad0465d6b8f..acb351f1646 100644 --- a/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php @@ -2,8 +2,10 @@ namespace wcf\system\form\option; +use wcf\data\DatabaseObjectList; use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\TextFormField; +use wcf\system\WCF; /** * Implementation of a form field for single-line text values. @@ -21,6 +23,12 @@ public function getId(): string return 'text'; } + #[\Override] + public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField + { + return TextFormField::create($id); + } + #[\Override] public function getFormField(string $id, array $configurationData = []): AbstractFormField { @@ -40,4 +48,10 @@ public function getConfigurationFormFields(): array { return ['maxLength', 'defaultTextValue', 'required']; } + + #[\Override] + public function applyFilter(DatabaseObjectList $list, string $columnName, mixed $value): void + { + $list->getConditionBuilder()->add("{$columnName} LIKE ?", ['%' . WCF::getDB()->escapeLikeValue($value) . '%']); + } } diff --git a/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php index 03328d10a64..4adebda6cd7 100644 --- a/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php @@ -2,9 +2,12 @@ namespace wcf\system\form\option; +use wcf\data\DatabaseObjectList; use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\MultilineTextFormField; +use wcf\system\form\builder\field\TextFormField; use wcf\system\form\option\formatter\MultilineTextFormatter; +use wcf\system\WCF; /** * Implementation of a form field for multi-line text values. @@ -44,4 +47,16 @@ public function getFormatter(): MultilineTextFormatter { return new MultilineTextFormatter(); } + + #[\Override] + public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField + { + return TextFormField::create($id); + } + + #[\Override] + public function applyFilter(DatabaseObjectList $list, string $columnName, mixed $value): void + { + $list->getConditionBuilder()->add("{$columnName} LIKE ?", ['%' . WCF::getDB()->escapeLikeValue($value) . '%']); + } } diff --git a/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php index bc9d9b00722..2b5cbc0c53f 100644 --- a/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php @@ -2,8 +2,12 @@ namespace wcf\system\form\option; +use wcf\data\DatabaseObjectList; +use wcf\system\form\builder\field\AbstractFormField; +use wcf\system\form\builder\field\TextFormField; use wcf\system\form\builder\field\UrlFormField; use wcf\system\form\option\formatter\UrlFormatter; +use wcf\system\WCF; /** * Implementation of a form field for URLs. @@ -32,4 +36,16 @@ public function getFormatter(): UrlFormatter { return new UrlFormatter(); } + + #[\Override] + public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField + { + return TextFormField::create($id); + } + + #[\Override] + public function applyFilter(DatabaseObjectList $list, string $columnName, mixed $value): void + { + $list->getConditionBuilder()->add("{$columnName} LIKE ?", ['%' . WCF::getDB()->escapeLikeValue($value) . '%']); + } } diff --git a/wcfsetup/install/files/lib/system/listView/filter/FormOptionFilter.class.php b/wcfsetup/install/files/lib/system/listView/filter/FormOptionFilter.class.php new file mode 100644 index 00000000000..6e1278d7977 --- /dev/null +++ b/wcfsetup/install/files/lib/system/listView/filter/FormOptionFilter.class.php @@ -0,0 +1,55 @@ + + * @since 6.2 + */ +final class FormOptionFilter extends AbstractFilter +{ + /** + * @param arrayoption->getFilterFormField($this->id, $this->configurationData)->label($this->languageItem); + } + + #[\Override] + public function applyFilter(DatabaseObjectList $list, string $value): void + { + $this->option->applyFilter($list, $this->getDatabaseColumnName($list), $value); + } + + #[\Override] + public function renderValue(string $value): string + { + return $this->option->getPlainTextFormatter()->format( + $value, + WCF::getLanguage()->languageID, + $this->configurationData + ); + } +} From 78614b82b34dc9cb7300c850d15b1af99c80c363 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Wed, 30 Apr 2025 18:36:16 +0200 Subject: [PATCH 23/33] Fix sql syntax error --- wcfsetup/setup/db/install.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index 8b6bd7f3bec..170bf5aa266 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -607,7 +607,7 @@ CREATE TABLE wcf1_file ( width INT, height INT, fileHashWebp CHAR(64), - uploadTime INT(10), + uploadTime INT(10) ); DROP TABLE IF EXISTS wcf1_file_temporary; From 6e788ba84a0380bca04c5b8153b8d8810bdd72a4 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Thu, 1 May 2025 14:17:59 +0200 Subject: [PATCH 24/33] Fix SQL queries for creating the default options --- wcfsetup/setup/db/install.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index 170bf5aa266..5f5c10e8ac9 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -2572,8 +2572,8 @@ INSERT INTO wcf1_template_group (parentTemplateGroupID, templateGroupName, templ INSERT INTO wcf1_template_group (parentTemplateGroupID, templateGroupName, templateGroupFolderName) VALUES (NULL, 'wcf.acp.template.group.shared', '_wcf_shared/'); -- default options: subject and message -INSERT INTO wcf1_contact_option (optionID, optionTitle, optionDescription, optionType, required, showOrder, originIsSystem) VALUES (1, 'wcf.contact.option1', 'wcf.contact.optionDescription1', 'text', 1, 1, 1); -INSERT INTO wcf1_contact_option (optionID, optionTitle, optionDescription, optionType, required, showOrder, originIsSystem) VALUES (2, 'wcf.contact.option2', '', 'textarea', 1, 1, 1); +INSERT INTO wcf1_contact_option (optionID, optionTitle, optionDescription, optionType, configurationData, showOrder, originIsSystem) VALUES (1, 'wcf.contact.option1', 'wcf.contact.optionDescription1', 'text', '{\"required\":1}', 1, 1); +INSERT INTO wcf1_contact_option (optionID, optionTitle, optionDescription, optionType, configurationData, showOrder, originIsSystem) VALUES (2, 'wcf.contact.option2', '', 'textarea', '{\"required\":1}', 1, 1); -- default recipient: site administrator INSERT INTO wcf1_contact_recipient (recipientID, name, email, isAdministrator, originIsSystem) VALUES (1, 'wcf.contact.recipient.name1', '', 1, 1); From 047b331c627d59a7cfd84224f3d41ce0e9facf84 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Fri, 2 May 2025 15:45:22 +0200 Subject: [PATCH 25/33] Fix loading of a simple ckeditor The mention plugin is required by the emoji plugin. --- ts/WoltLabSuite/Core/Component/Ckeditor/Configuration.ts | 2 +- .../js/WoltLabSuite/Core/Component/Ckeditor/Configuration.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/WoltLabSuite/Core/Component/Ckeditor/Configuration.ts b/ts/WoltLabSuite/Core/Component/Ckeditor/Configuration.ts index 76507159fce..be6615f857c 100644 --- a/ts/WoltLabSuite/Core/Component/Ckeditor/Configuration.ts +++ b/ts/WoltLabSuite/Core/Component/Ckeditor/Configuration.ts @@ -215,7 +215,7 @@ class ConfigurationBuilder { #setupMention(): void { if (!this.#features.mention) { - this.#removePlugins.push("Mention", "WoltlabMention"); + this.#removePlugins.push("WoltlabMention"); } } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor/Configuration.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor/Configuration.js index eccdc6ebe03..7d0aae6c8fa 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor/Configuration.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor/Configuration.js @@ -197,7 +197,7 @@ define(["require", "exports", "../../Language", "WoltLabSuite/Core/Component/Emo } #setupMention() { if (!this.#features.mention) { - this.#removePlugins.push("Mention", "WoltlabMention"); + this.#removePlugins.push("WoltlabMention"); } } #getToolbar() { From 58f9721ab5c3766f9f5903c2b8fc0a39e777facf Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Fri, 2 May 2025 15:45:43 +0200 Subject: [PATCH 26/33] Add "wysiwyg" form option type --- com.woltlab.wcf/objectType.xml | 4 ++ .../form/option/FormOptionHandler.class.php | 1 + .../form/option/WysiwygFormOption.class.php | 64 +++++++++++++++++++ .../formatter/WysiwygFormatter.class.php | 25 ++++++++ .../WysiwygPlainTextFormatter.class.php | 26 ++++++++ wcfsetup/install/lang/de.xml | 1 + wcfsetup/install/lang/en.xml | 1 + 7 files changed, 122 insertions(+) create mode 100644 wcfsetup/install/files/lib/system/form/option/WysiwygFormOption.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/WysiwygFormatter.class.php create mode 100644 wcfsetup/install/files/lib/system/form/option/formatter/WysiwygPlainTextFormatter.class.php diff --git a/com.woltlab.wcf/objectType.xml b/com.woltlab.wcf/objectType.xml index de876a31dd4..e6cf1f372f7 100644 --- a/com.woltlab.wcf/objectType.xml +++ b/com.woltlab.wcf/objectType.xml @@ -157,6 +157,10 @@ com.woltlab.wcf.message user.signature.disallowedBBCodes + + com.woltlab.wcf.genericFormOption + com.woltlab.wcf.message + com.woltlab.wcf.user.signature com.woltlab.wcf.attachment.objectType diff --git a/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php b/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php index 273767d82b0..0f13266d8e4 100644 --- a/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php +++ b/wcfsetup/install/files/lib/system/form/option/FormOptionHandler.class.php @@ -52,6 +52,7 @@ private function getDefaultFormOptions(): array new TextFormOption(), new TextareaFormOption(), new UrlFormOption(), + new WysiwygFormOption(), ] as $defaultOption ) { $options[$defaultOption->getId()] = $defaultOption; diff --git a/wcfsetup/install/files/lib/system/form/option/WysiwygFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/WysiwygFormOption.class.php new file mode 100644 index 00000000000..60c4a77ff9a --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/WysiwygFormOption.class.php @@ -0,0 +1,64 @@ + + * @since 6.2 + */ +class WysiwygFormOption extends AbstractFormOption +{ + #[\Override] + public function getId(): string + { + return 'wysiwyg'; + } + + #[\Override] + public function getFilterFormField(string $id, array $configurationData = []): TextFormField + { + return TextFormField::create($id); + } + + #[\Override] + public function getFormField(string $id, array $configurationData = []): WysiwygFormField + { + return WysiwygFormField::create($id) + ->objectType('com.woltlab.wcf.genericFormOption'); + } + + #[\Override] + public function getConfigurationFormFields(): array + { + return ['required']; + } + + #[\Override] + public function applyFilter(DatabaseObjectList $list, string $columnName, mixed $value): void + { + $list->getConditionBuilder()->add("{$columnName} LIKE ?", ['%' . WCF::getDB()->escapeLikeValue($value) . '%']); + } + + #[\Override] + public function getFormatter(): WysiwygFormatter + { + return new WysiwygFormatter(); + } + + #[\Override] + public function getPlainTextFormatter(): WysiwygPlainTextFormatter + { + return new WysiwygPlainTextFormatter(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygFormatter.class.php new file mode 100644 index 00000000000..325b20510e0 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygFormatter.class.php @@ -0,0 +1,25 @@ + + * @since 6.2 + */ +final class WysiwygFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + $processor = new HtmlOutputProcessor(); + $processor->process($value, 'com.woltlab.wcf.genericFormOption', 0, true, $languageID); + + return $processor->getHtml(); + } +} diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygPlainTextFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygPlainTextFormatter.class.php new file mode 100644 index 00000000000..1103d8d0a12 --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygPlainTextFormatter.class.php @@ -0,0 +1,26 @@ + + * @since 6.2 + */ +final class WysiwygPlainTextFormatter implements IFormOptionFormatter +{ + #[\Override] + public function format(string $value, int $languageID, array $configurationData): string + { + $processor = new HtmlOutputProcessor(); + $processor->setOutputType('text/plain'); + $processor->process($value, 'com.woltlab.wcf.genericFormOption', 0, true, $languageID); + + return $processor->getHtml(); + } +} diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index abd613fa3b0..72edadeb1cc 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -4129,6 +4129,7 @@ Dateianhänge: + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index da645e8887f..931c376811e 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -4075,6 +4075,7 @@ Attachments: + From 7621048181453a64939b74bc06417fc26c0ea1b5 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Fri, 2 May 2025 16:14:13 +0200 Subject: [PATCH 27/33] Add script for the migration of existing contact options --- com.woltlab.wcf/package.xml | 7 +++ ...p => update_com.woltlab.wcf_6.2_step1.php} | 8 --- .../update_com.woltlab.wcf_6.2_step2.php | 28 +++++++++ ...ate_com.woltlab.wcf_6.2_contactOptions.php | 57 +++++++++++++++++++ 4 files changed, 92 insertions(+), 8 deletions(-) rename wcfsetup/install/files/acp/database/{update_com.woltlab.wcf_6.2.php => update_com.woltlab.wcf_6.2_step1.php} (86%) create mode 100644 wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step2.php create mode 100644 wcfsetup/install/files/acp/update_com.woltlab.wcf_6.2_contactOptions.php diff --git a/com.woltlab.wcf/package.xml b/com.woltlab.wcf/package.xml index b986154f945..bbf5922e535 100644 --- a/com.woltlab.wcf/package.xml +++ b/com.woltlab.wcf/package.xml @@ -49,4 +49,11 @@ acp/install_com.woltlab.wcf_step2.php + + diff --git a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step1.php similarity index 86% rename from wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php rename to wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step1.php index cdcf8ef0000..f196aa19541 100644 --- a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php +++ b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step1.php @@ -52,14 +52,6 @@ ]), PartialDatabaseTable::create('wcf1_contact_option') ->columns([ - MediumtextDatabaseTableColumn::create('defaultValue') - ->drop(), - TextDatabaseTableColumn::create('validationPattern') - ->drop(), - MediumtextDatabaseTableColumn::create('selectOptions') - ->drop(), - TinyintDatabaseTableColumn::create('required') - ->drop(), MediumtextDatabaseTableColumn::create('configurationData'), ]), PartialDatabaseTable::create('wcf1_file') diff --git a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step2.php b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step2.php new file mode 100644 index 00000000000..f10a241c93d --- /dev/null +++ b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step2.php @@ -0,0 +1,28 @@ + + */ + +use wcf\system\database\table\column\MediumtextDatabaseTableColumn; +use wcf\system\database\table\column\TextDatabaseTableColumn; +use wcf\system\database\table\column\TinyintDatabaseTableColumn; +use wcf\system\database\table\PartialDatabaseTable; + +return [ + PartialDatabaseTable::create('wcf1_contact_option') + ->columns([ + MediumtextDatabaseTableColumn::create('defaultValue') + ->drop(), + TextDatabaseTableColumn::create('validationPattern') + ->drop(), + MediumtextDatabaseTableColumn::create('selectOptions') + ->drop(), + TinyintDatabaseTableColumn::create('required') + ->drop(), + ]), +]; diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.2_contactOptions.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.2_contactOptions.php new file mode 100644 index 00000000000..4aec768acc6 --- /dev/null +++ b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.2_contactOptions.php @@ -0,0 +1,57 @@ +readObjects(); + +foreach ($contactOptionList as $contactOption) { + $configurationData = []; + $optionType = ''; + $optionType = match ($contactOption->optionType) { + 'multiSelect' => 'checkboxes', + 'message' => 'wysiwyg', + 'URL' => 'url', + default => $contactOption->optionType, + }; + + if ($contactOption->required) { + $configurationData['required'] = 1; + } + if ($contactOption->defaultValue && $contactOption->optionType == 'text') { + $configurationData['defaultValue'] = $contactOption->defaultValue; + } + if ($contactOption->selectOptions) { + $configurationData['required'] = convertSelectOptions($contactOption->selectOptions); + } + + $editor = new ContactOptionEditor($contactOption); + $editor->update([ + 'optionType' => $optionType, + 'configurationData' => JSON::encode($configurationData), + ]); +} + +function convertSelectOptions(string $selectOptions): string +{ + $options = []; + + $parsedSelectOptions = OptionUtil::parseSelectOptions($selectOptions); + foreach ($parsedSelectOptions as $key => $value) { + $options[] = [ + 'key' => $key, + 'value' => [ + 0 => $value + ] + ]; + } + + return JSON::encode($options); +} From 3fd3594b04b7e22c206330630860b6ee0fb6478b Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Tue, 6 May 2025 12:04:29 +0200 Subject: [PATCH 28/33] Apply suggestions from code review Co-authored-by: Alexander Ebert --- .../files/lib/acp/form/AbstractFormOptionAddForm.class.php | 2 +- .../system/form/option/formatter/CurrencyFormatter.class.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php b/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php index 7402e9f2631..93ab5f51886 100644 --- a/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php @@ -114,7 +114,7 @@ protected function getOptionTypeFormField(): SelectFormField { return SelectFormField::create('optionType') ->label('wcf.form.option.optionType') - ->immutable($this->formAction != 'create') + ->immutable($this->formAction !== 'create') ->options(FormOptionHandler::getInstance()->getSortedOptionTypes()) ->required(); } diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php index b2007e001f2..c6db3bb37fc 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php @@ -18,7 +18,7 @@ class CurrencyFormatter implements IFormOptionFormatter #[\Override] public function format(string $value, int $languageID, array $configurationData): string { - $showDecimals = $value % 100 != 0; + $showDecimals = $value % 100 !== 0; $value /= 100; $language = LanguageFactory::getInstance()->getLanguage($languageID); $suffix = ''; From 78c8dc1fdba261ecf0ab3098f33d6ad931d39205 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Tue, 6 May 2025 12:04:43 +0200 Subject: [PATCH 29/33] Apply suggestions from code review --- .../files/acp/database/update_com.woltlab.wcf_6.2_step1.php | 3 +-- .../files/acp/update_com.woltlab.wcf_6.2_contactOptions.php | 1 + wcfsetup/setup/db/install.sql | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step1.php b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step1.php index f196aa19541..7430ba94584 100644 --- a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step1.php +++ b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step1.php @@ -56,7 +56,6 @@ ]), PartialDatabaseTable::create('wcf1_file') ->columns([ - IntDatabaseTableColumn::create('uploadTime') - ->length(10), + IntDatabaseTableColumn::create('uploadTime'), ]), ]; diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.2_contactOptions.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.2_contactOptions.php index 4aec768acc6..c443b88c9d0 100644 --- a/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.2_contactOptions.php +++ b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.2_contactOptions.php @@ -11,6 +11,7 @@ $contactOptionList = new ContactOptionList(); $contactOptionList->readObjects(); +$contactOptionList->getConditionBuilder()->add('configurationData IS NULL'); foreach ($contactOptionList as $contactOption) { $configurationData = []; diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index 5f5c10e8ac9..b55b1ae322f 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -607,7 +607,7 @@ CREATE TABLE wcf1_file ( width INT, height INT, fileHashWebp CHAR(64), - uploadTime INT(10) + uploadTime INT ); DROP TABLE IF EXISTS wcf1_file_temporary; From 407b30e85e3356c6359e473d038286e23617b139 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Tue, 6 May 2025 12:09:53 +0200 Subject: [PATCH 30/33] Rename `configurationData` to `configuration` --- .../database/update_com.woltlab.wcf_6.2_step1.php | 2 +- .../update_com.woltlab.wcf_6.2_contactOptions.php | 12 ++++++------ .../acp/form/AbstractFormOptionAddForm.class.php | 12 ++++++------ .../data/contact/option/ContactOption.class.php | 6 +++--- .../install/files/lib/form/ContactForm.class.php | 4 ++-- .../contact/form/SubmitContactForm.class.php | 6 +++--- .../form/option/AbstractFormOption.class.php | 4 ++-- .../system/form/option/BooleanFormOption.class.php | 2 +- .../form/option/CheckboxesFormOption.class.php | 6 +++--- .../form/option/CurrencyFormOption.class.php | 14 +++++++------- .../system/form/option/DateFormOption.class.php | 2 +- .../system/form/option/EmailFormOption.class.php | 4 ++-- .../system/form/option/FloatFormOption.class.php | 10 +++++----- .../lib/system/form/option/IFormOption.class.php | 8 ++++---- .../system/form/option/IconFormOption.class.php | 2 +- .../system/form/option/IntegerFormOption.class.php | 10 +++++----- .../system/form/option/RatingFormOption.class.php | 2 +- .../system/form/option/SelectFormOption.class.php | 6 +++--- .../form/option/SourceCodeFormOption.class.php | 4 ++-- .../system/form/option/TextFormOption.class.php | 12 ++++++------ .../form/option/TextareaFormOption.class.php | 8 ++++---- .../lib/system/form/option/UrlFormOption.class.php | 4 ++-- .../system/form/option/WysiwygFormOption.class.php | 4 ++-- .../option/formatter/BooleanFormatter.class.php | 2 +- .../option/formatter/CurrencyFormatter.class.php | 6 +++--- .../form/option/formatter/DateFormatter.class.php | 2 +- .../option/formatter/DefaultFormatter.class.php | 2 +- .../formatter/DefaultPlainTextFormatter.class.php | 2 +- .../form/option/formatter/EmailFormatter.class.php | 2 +- .../form/option/formatter/FloatFormatter.class.php | 2 +- .../formatter/IFormOptionFormatter.class.php | 2 +- .../form/option/formatter/IconFormatter.class.php | 2 +- .../option/formatter/IntegerFormatter.class.php | 2 +- .../formatter/MultilineTextFormatter.class.php | 2 +- .../formatter/MultipleSelectionFormatter.class.php | 4 ++-- .../option/formatter/RatingFormatter.class.php | 2 +- .../option/formatter/SelectFormatter.class.php | 4 ++-- .../option/formatter/SourceCodeFormatter.class.php | 2 +- .../form/option/formatter/UrlFormatter.class.php | 2 +- .../option/formatter/WysiwygFormatter.class.php | 2 +- .../formatter/WysiwygPlainTextFormatter.class.php | 2 +- .../listView/filter/FormOptionFilter.class.php | 8 ++++---- wcfsetup/setup/db/install.sql | 6 +++--- 43 files changed, 101 insertions(+), 101 deletions(-) diff --git a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step1.php b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step1.php index 7430ba94584..144efaffb35 100644 --- a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step1.php +++ b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2_step1.php @@ -52,7 +52,7 @@ ]), PartialDatabaseTable::create('wcf1_contact_option') ->columns([ - MediumtextDatabaseTableColumn::create('configurationData'), + MediumtextDatabaseTableColumn::create('configuration'), ]), PartialDatabaseTable::create('wcf1_file') ->columns([ diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.2_contactOptions.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.2_contactOptions.php index c443b88c9d0..c443158e1e1 100644 --- a/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.2_contactOptions.php +++ b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.2_contactOptions.php @@ -11,10 +11,10 @@ $contactOptionList = new ContactOptionList(); $contactOptionList->readObjects(); -$contactOptionList->getConditionBuilder()->add('configurationData IS NULL'); +$contactOptionList->getConditionBuilder()->add('configuration IS NULL'); foreach ($contactOptionList as $contactOption) { - $configurationData = []; + $configuration = []; $optionType = ''; $optionType = match ($contactOption->optionType) { 'multiSelect' => 'checkboxes', @@ -24,19 +24,19 @@ }; if ($contactOption->required) { - $configurationData['required'] = 1; + $configuration['required'] = 1; } if ($contactOption->defaultValue && $contactOption->optionType == 'text') { - $configurationData['defaultValue'] = $contactOption->defaultValue; + $configuration['defaultValue'] = $contactOption->defaultValue; } if ($contactOption->selectOptions) { - $configurationData['required'] = convertSelectOptions($contactOption->selectOptions); + $configuration['required'] = convertSelectOptions($contactOption->selectOptions); } $editor = new ContactOptionEditor($contactOption); $editor->update([ 'optionType' => $optionType, - 'configurationData' => JSON::encode($configurationData), + 'configuration' => JSON::encode($configuration), ]); } diff --git a/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php b/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php index 7402e9f2631..598eda5ecc5 100644 --- a/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/AbstractFormOptionAddForm.class.php @@ -33,17 +33,17 @@ public function finalizeForm() new CustomFormDataProcessor( 'saveOptionProcessor', function (IFormDocument $document, array $parameters) { - $configurationData = []; + $configuration = []; foreach ($this->getConfigurationFormFieldIds() as $parameter) { if (isset($parameters['data'][$parameter])) { - $configurationData[$parameter] = $parameters['data'][$parameter]; + $configuration[$parameter] = $parameters['data'][$parameter]; unset($parameters['data'][$parameter]); } } - if ($configurationData !== []) { - $parameters['data']['configurationData'] = JSON::encode($configurationData); + if ($configuration !== []) { + $parameters['data']['configuration'] = JSON::encode($configuration); } return $parameters; @@ -51,8 +51,8 @@ function (IFormDocument $document, array $parameters) { function (IFormDocument $document, array $data, IStorableObject $object) { \assert($object instanceof DatabaseObject); - if ($object->configurationData) { - $data = \array_merge($data, JSON::decode($object->configurationData)); + if ($object->configuration) { + $data = \array_merge($data, JSON::decode($object->configuration)); } return $data; diff --git a/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php b/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php index 314754d81cf..75ea37865a8 100644 --- a/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php +++ b/wcfsetup/install/files/lib/data/contact/option/ContactOption.class.php @@ -22,7 +22,7 @@ * @property-read string $optionTitle title of the option or name of language item which contains the title * @property-read string $optionDescription description of the option or name of language item which contains the description * @property-read string $optionType type of the option which determines its input and output - * @property-read string $configurationData JSON-encoded configuration information depending on the option type + * @property-read string $configuration JSON-encoded configuration information depending on the option type * @property-read int $showOrder position of the option in relation to the other options * @property-read int $isDisabled is `1` if the option is disabled, otherwise `0` * @property-read int $originIsSystem is `1` if the option has been delivered by a package, otherwise `0` (i.e. the option has been created in the ACP) @@ -73,8 +73,8 @@ public function getFormOption(): IFormOption * @return array * @since 6.2 */ - public function getConfigurationData(): array + public function getConfiguration(): array { - return $this->configurationData ? JSON::decode($this->configurationData) : []; + return $this->configuration ? JSON::decode($this->configuration) : []; } } diff --git a/wcfsetup/install/files/lib/form/ContactForm.class.php b/wcfsetup/install/files/lib/form/ContactForm.class.php index d3713575e67..67209cf96ea 100644 --- a/wcfsetup/install/files/lib/form/ContactForm.class.php +++ b/wcfsetup/install/files/lib/form/ContactForm.class.php @@ -208,12 +208,12 @@ protected function getOptionFormFields(): array foreach ($this->getAvailableOptions() as $option) { $formField = $option->getFormOption()->getFormField( 'option' . $option->optionID, - $option->getConfigurationData() + $option->getConfiguration() ); $formField->label($option->optionTitle); $formField->description($option->optionDescription); - if (!empty($option->getConfigurationData()['required'])) { + if (!empty($option->getConfiguration()['required'])) { $formField->required(); } diff --git a/wcfsetup/install/files/lib/system/contact/form/SubmitContactForm.class.php b/wcfsetup/install/files/lib/system/contact/form/SubmitContactForm.class.php index 0e60c566b11..0f68cecd7e8 100644 --- a/wcfsetup/install/files/lib/system/contact/form/SubmitContactForm.class.php +++ b/wcfsetup/install/files/lib/system/contact/form/SubmitContactForm.class.php @@ -68,7 +68,7 @@ private function getFormattedOptionValues(array $optionValues): array } $value = $optionValues[$availableOption->optionID]; - $configurationData = $availableOption->getConfigurationData(); + $configuration = $availableOption->getConfiguration(); $formOption = $availableOption->getFormOption(); $options[] = [ @@ -77,12 +77,12 @@ private function getFormattedOptionValues(array $optionValues): array 'value' => $formOption->getPlainTextFormatter()->format( $value, LanguageFactory::getInstance()->getDefaultLanguage()->languageID, - $configurationData + $configuration ), 'htmlValue' => $formOption->getFormatter()->format( $value, LanguageFactory::getInstance()->getDefaultLanguage()->languageID, - $configurationData + $configuration ), ]; } diff --git a/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php index 78d29923f03..7fafda27115 100644 --- a/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/AbstractFormOption.class.php @@ -44,9 +44,9 @@ public function getPlainTextFormatter(): IFormOptionFormatter } #[\Override] - public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField + public function getFilterFormField(string $id, array $configuration = []): AbstractFormField { - return $this->getFormField($id, $configurationData); + return $this->getFormField($id, $configuration); } #[\Override] diff --git a/wcfsetup/install/files/lib/system/form/option/BooleanFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/BooleanFormOption.class.php index 55567d6c0f7..86582f29cf8 100644 --- a/wcfsetup/install/files/lib/system/form/option/BooleanFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/BooleanFormOption.class.php @@ -23,7 +23,7 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configurationData = []): AbstractFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { return BooleanFormField::create($id); } diff --git a/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php index ed2c0c5185c..ee788bd1917 100644 --- a/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php @@ -26,13 +26,13 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configurationData = []): MultipleSelectionFormField + public function getFormField(string $id, array $configuration = []): MultipleSelectionFormField { $formField = MultipleSelectionFormField::create($id); - if (isset($configurationData['selectOptions'])) { + if (isset($configuration['selectOptions'])) { $selectOptions = []; - foreach (JSON::decode($configurationData['selectOptions']) as $selectOption) { + foreach (JSON::decode($configuration['selectOptions']) as $selectOption) { if (isset($selectOption['value'][0])) { $value = $selectOption['value'][0]; } else if (isset($selectOption['value'][WCF::getLanguage()->languageID])) { diff --git a/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php index 0505614c9c4..1f78a999e9a 100644 --- a/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php @@ -23,17 +23,17 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configurationData = []): AbstractFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { $formField = CurrencyFormField::create($id); - if (isset($configurationData['currency'])) { - $formField->currency($configurationData['currency']); + if (isset($configuration['currency'])) { + $formField->currency($configuration['currency']); } - if (isset($configurationData['minFloatValue'])) { - $formField->minimum($configurationData['minFloatValue']); + if (isset($configuration['minFloatValue'])) { + $formField->minimum($configuration['minFloatValue']); } - if (isset($configurationData['maxFloatValue'])) { - $formField->maximum($configurationData['maxFloatValue']); + if (isset($configuration['maxFloatValue'])) { + $formField->maximum($configuration['maxFloatValue']); } return $formField; diff --git a/wcfsetup/install/files/lib/system/form/option/DateFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/DateFormOption.class.php index b40fbe54588..b2a3aaadaa6 100644 --- a/wcfsetup/install/files/lib/system/form/option/DateFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/DateFormOption.class.php @@ -22,7 +22,7 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configurationData = []): DateFormField + public function getFormField(string $id, array $configuration = []): DateFormField { $formField = DateFormField::create($id) ->saveValueFormat('Y-m-d'); diff --git a/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php index 1fb1cc0c64c..509f7d8ff06 100644 --- a/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php @@ -26,7 +26,7 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configurationData = []): AbstractFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { return EmailFormField::create($id); } @@ -44,7 +44,7 @@ public function getPlainTextFormatter(): EmailFormatter } #[\Override] - public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField + public function getFilterFormField(string $id, array $configuration = []): AbstractFormField { return TextFormField::create($id); } diff --git a/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php index 093e2997ed7..102d62e2ccb 100644 --- a/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php @@ -23,14 +23,14 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configurationData = []): AbstractFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { $formField = FloatFormField::create($id); - if (isset($configurationData['minValue'])) { - $formField->minimum($configurationData['minValue']); + if (isset($configuration['minValue'])) { + $formField->minimum($configuration['minValue']); } - if (isset($configurationData['maxValue'])) { - $formField->maximum($configurationData['maxValue']); + if (isset($configuration['maxValue'])) { + $formField->maximum($configuration['maxValue']); } return $formField; diff --git a/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php index 3fa7417f44f..ae79d3f20ca 100644 --- a/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/IFormOption.class.php @@ -21,14 +21,14 @@ public function getId(): string; public function getTitle(): string; /** - * @param array $configurationData + * @param array $configuration */ - public function getFormField(string $id, array $configurationData = []): AbstractFormField; + public function getFormField(string $id, array $configuration = []): AbstractFormField; /** - * @param array $configurationData + * @param array $configuration */ - public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField; + public function getFilterFormField(string $id, array $configuration = []): AbstractFormField; /** * @return string[] diff --git a/wcfsetup/install/files/lib/system/form/option/IconFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/IconFormOption.class.php index 58dd4aeaa9c..6cfeac0c748 100644 --- a/wcfsetup/install/files/lib/system/form/option/IconFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/IconFormOption.class.php @@ -22,7 +22,7 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configurationData = []): IconFormField + public function getFormField(string $id, array $configuration = []): IconFormField { return IconFormField::create($id); } diff --git a/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php index ea92f9dad70..e26b9ccd48d 100644 --- a/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php @@ -23,14 +23,14 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configurationData = []): AbstractFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { $formField = IntegerFormField::create($id); - if (isset($configurationData['minIntegerValue'])) { - $formField->minimum($configurationData['minIntegerValue']); + if (isset($configuration['minIntegerValue'])) { + $formField->minimum($configuration['minIntegerValue']); } - if (isset($configurationData['maxIntegerValue'])) { - $formField->maximum($configurationData['maxIntegerValue']); + if (isset($configuration['maxIntegerValue'])) { + $formField->maximum($configuration['maxIntegerValue']); } return $formField; diff --git a/wcfsetup/install/files/lib/system/form/option/RatingFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/RatingFormOption.class.php index 41ba8ada9e9..4a11b065b93 100644 --- a/wcfsetup/install/files/lib/system/form/option/RatingFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/RatingFormOption.class.php @@ -22,7 +22,7 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configurationData = []): RatingFormField + public function getFormField(string $id, array $configuration = []): RatingFormField { return RatingFormField::create($id); } diff --git a/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php index 25c7da76fb3..4261b53f6b9 100644 --- a/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php @@ -25,13 +25,13 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configurationData = []): AbstractFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { $formField = SelectFormField::create($id); - if (isset($configurationData['selectOptions'])) { + if (isset($configuration['selectOptions'])) { $selectOptions = []; - foreach (JSON::decode($configurationData['selectOptions']) as $selectOption) { + foreach (JSON::decode($configuration['selectOptions']) as $selectOption) { if (isset($selectOption['value'][0])) { $value = $selectOption['value'][0]; } else if (isset($selectOption['value'][WCF::getLanguage()->languageID])) { diff --git a/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php index ba692ea7e72..2275fac5096 100644 --- a/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php @@ -26,7 +26,7 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configurationData = []): SourceCodeFormField + public function getFormField(string $id, array $configuration = []): SourceCodeFormField { return SourceCodeFormField::create($id); } @@ -38,7 +38,7 @@ public function getFormatter(): SourceCodeFormatter } #[\Override] - public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField + public function getFilterFormField(string $id, array $configuration = []): AbstractFormField { return TextFormField::create($id); } diff --git a/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php index acb351f1646..158891d1c4a 100644 --- a/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/TextFormOption.class.php @@ -24,20 +24,20 @@ public function getId(): string } #[\Override] - public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField + public function getFilterFormField(string $id, array $configuration = []): AbstractFormField { return TextFormField::create($id); } #[\Override] - public function getFormField(string $id, array $configurationData = []): AbstractFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { $formField = TextFormField::create($id); - if (!empty($configurationData['maxLength'])) { - $formField->maximumLength($configurationData['maxLength']); + if (!empty($configuration['maxLength'])) { + $formField->maximumLength($configuration['maxLength']); } - if (isset($configurationData['defaultTextValue'])) { - $formField->value($configurationData['defaultTextValue']); + if (isset($configuration['defaultTextValue'])) { + $formField->value($configuration['defaultTextValue']); } return $formField; diff --git a/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php index 4adebda6cd7..50d5e8b9530 100644 --- a/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php @@ -26,11 +26,11 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configurationData = []): AbstractFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { $formField = MultilineTextFormField::create($id); - if (!empty($configurationData['maxLength'])) { - $formField->maximumLength($configurationData['maxLength']); + if (!empty($configuration['maxLength'])) { + $formField->maximumLength($configuration['maxLength']); } return $formField; @@ -49,7 +49,7 @@ public function getFormatter(): MultilineTextFormatter } #[\Override] - public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField + public function getFilterFormField(string $id, array $configuration = []): AbstractFormField { return TextFormField::create($id); } diff --git a/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php index 2b5cbc0c53f..bdd8eff60f6 100644 --- a/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php @@ -26,7 +26,7 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configurationData = []): UrlFormField + public function getFormField(string $id, array $configuration = []): UrlFormField { return UrlFormField::create($id); } @@ -38,7 +38,7 @@ public function getFormatter(): UrlFormatter } #[\Override] - public function getFilterFormField(string $id, array $configurationData = []): AbstractFormField + public function getFilterFormField(string $id, array $configuration = []): AbstractFormField { return TextFormField::create($id); } diff --git a/wcfsetup/install/files/lib/system/form/option/WysiwygFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/WysiwygFormOption.class.php index 60c4a77ff9a..6a6b1b05b4d 100644 --- a/wcfsetup/install/files/lib/system/form/option/WysiwygFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/WysiwygFormOption.class.php @@ -26,13 +26,13 @@ public function getId(): string } #[\Override] - public function getFilterFormField(string $id, array $configurationData = []): TextFormField + public function getFilterFormField(string $id, array $configuration = []): TextFormField { return TextFormField::create($id); } #[\Override] - public function getFormField(string $id, array $configurationData = []): WysiwygFormField + public function getFormField(string $id, array $configuration = []): WysiwygFormField { return WysiwygFormField::create($id) ->objectType('com.woltlab.wcf.genericFormOption'); diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/BooleanFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/BooleanFormatter.class.php index 6c8f75281cb..05ff91dc0d9 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/BooleanFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/BooleanFormatter.class.php @@ -13,7 +13,7 @@ final class BooleanFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { if (!$value) { return ''; diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php index b2007e001f2..9367c7bb4d6 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php @@ -16,14 +16,14 @@ class CurrencyFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { $showDecimals = $value % 100 != 0; $value /= 100; $language = LanguageFactory::getInstance()->getLanguage($languageID); $suffix = ''; - if (!empty($configurationData['currency'])) { - $suffix = ' ' . StringUtil::encodeHTML($configurationData['currency']); + if (!empty($configuration['currency'])) { + $suffix = ' ' . StringUtil::encodeHTML($configuration['currency']); } return \number_format( diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/DateFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/DateFormatter.class.php index 09d8393ed28..f6b2113af87 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/DateFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/DateFormatter.class.php @@ -15,7 +15,7 @@ final class DateFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { $dateTime = new \DateTimeImmutable($value); $locale = LanguageFactory::getInstance()->getLanguage($languageID)->getLocale(); diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/DefaultFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/DefaultFormatter.class.php index d6343342215..429cede31bd 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/DefaultFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/DefaultFormatter.class.php @@ -15,7 +15,7 @@ final class DefaultFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { return StringUtil::encodeHTML($value); } diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/DefaultPlainTextFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/DefaultPlainTextFormatter.class.php index b30242c0f79..576c9fe2b2a 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/DefaultPlainTextFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/DefaultPlainTextFormatter.class.php @@ -13,7 +13,7 @@ final class DefaultPlainTextFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { return $value; } diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/EmailFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/EmailFormatter.class.php index 34626abe47a..d8d2b12456f 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/EmailFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/EmailFormatter.class.php @@ -15,7 +15,7 @@ final class EmailFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { $encodedValue = StringUtil::encodeHTML($value); diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/FloatFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/FloatFormatter.class.php index 5498e005e8a..a5b0d81af1e 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/FloatFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/FloatFormatter.class.php @@ -15,7 +15,7 @@ final class FloatFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { return StringUtil::formatNumeric(\floatval($value)); } diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/IFormOptionFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/IFormOptionFormatter.class.php index 5da81e30521..20fbe29b9fa 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/IFormOptionFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/IFormOptionFormatter.class.php @@ -12,5 +12,5 @@ */ interface IFormOptionFormatter { - public function format(string $value, int $languageID, array $configurationData): string; + public function format(string $value, int $languageID, array $configuration): string; } diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/IconFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/IconFormatter.class.php index d2f58b0b551..2f8607300cf 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/IconFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/IconFormatter.class.php @@ -15,7 +15,7 @@ final class IconFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { return FontAwesomeIcon::fromString($value)->toHtml(); } diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/IntegerFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/IntegerFormatter.class.php index 48f01d6c8e4..9a6a9d54741 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/IntegerFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/IntegerFormatter.class.php @@ -15,7 +15,7 @@ final class IntegerFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { return StringUtil::formatNumeric(\intval($value)); } diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/MultilineTextFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/MultilineTextFormatter.class.php index 006e5136ca2..2d16a6af3b9 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/MultilineTextFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/MultilineTextFormatter.class.php @@ -15,7 +15,7 @@ final class MultilineTextFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { return \nl2br(StringUtil::encodeHTML($value), false); } diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/MultipleSelectionFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/MultipleSelectionFormatter.class.php index 51f4b2dcde0..c61aad2caf7 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/MultipleSelectionFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/MultipleSelectionFormatter.class.php @@ -16,14 +16,14 @@ final class MultipleSelectionFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { if (!$value) { return ''; }; $keys = \explode("\n", $value); - $selectOptions = JSON::decode($configurationData['selectOptions']); + $selectOptions = JSON::decode($configuration['selectOptions']); $html = ''; foreach ($keys as $key) { diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/RatingFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/RatingFormatter.class.php index 3f83dc81742..cb06ebf10eb 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/RatingFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/RatingFormatter.class.php @@ -15,7 +15,7 @@ final class RatingFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { $rating = \floatval($value); $html = ''; diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/SelectFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/SelectFormatter.class.php index e07c743a986..989d72e0028 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/SelectFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/SelectFormatter.class.php @@ -18,9 +18,9 @@ final class SelectFormatter implements IFormOptionFormatter public function __construct(private readonly bool $encode = true) {} #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { - foreach (JSON::decode($configurationData['selectOptions']) as $selectOption) { + foreach (JSON::decode($configuration['selectOptions']) as $selectOption) { if ($selectOption['key'] == $value) { if (isset($selectOption['value'][0])) { $value = $selectOption['value'][0]; diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/SourceCodeFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/SourceCodeFormatter.class.php index f4d2d38f112..f45786fb5fa 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/SourceCodeFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/SourceCodeFormatter.class.php @@ -15,7 +15,7 @@ final class SourceCodeFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { return '
' . StringUtil::encodeHTML($value) . '
'; } diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/UrlFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/UrlFormatter.class.php index 32a479a6ee6..11c7c032da5 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/UrlFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/UrlFormatter.class.php @@ -15,7 +15,7 @@ final class UrlFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { return StringUtil::getAnchorTag($value, $value, true, true); } diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygFormatter.class.php index 325b20510e0..0803b2db328 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygFormatter.class.php @@ -15,7 +15,7 @@ final class WysiwygFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { $processor = new HtmlOutputProcessor(); $processor->process($value, 'com.woltlab.wcf.genericFormOption', 0, true, $languageID); diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygPlainTextFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygPlainTextFormatter.class.php index 1103d8d0a12..11945644321 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygPlainTextFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/WysiwygPlainTextFormatter.class.php @@ -15,7 +15,7 @@ final class WysiwygPlainTextFormatter implements IFormOptionFormatter { #[\Override] - public function format(string $value, int $languageID, array $configurationData): string + public function format(string $value, int $languageID, array $configuration): string { $processor = new HtmlOutputProcessor(); $processor->setOutputType('text/plain'); diff --git a/wcfsetup/install/files/lib/system/listView/filter/FormOptionFilter.class.php b/wcfsetup/install/files/lib/system/listView/filter/FormOptionFilter.class.php index 6e1278d7977..0bf1c433371 100644 --- a/wcfsetup/install/files/lib/system/listView/filter/FormOptionFilter.class.php +++ b/wcfsetup/install/files/lib/system/listView/filter/FormOptionFilter.class.php @@ -19,11 +19,11 @@ final class FormOptionFilter extends AbstractFilter { /** - * @param arrayoption->getFilterFormField($this->id, $this->configurationData)->label($this->languageItem); + return $this->option->getFilterFormField($this->id, $this->configuration)->label($this->languageItem); } #[\Override] @@ -49,7 +49,7 @@ public function renderValue(string $value): string return $this->option->getPlainTextFormatter()->format( $value, WCF::getLanguage()->languageID, - $this->configurationData + $this->configuration ); } } diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index b55b1ae322f..185ebbf105a 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -466,7 +466,7 @@ CREATE TABLE wcf1_contact_option ( optionTitle VARCHAR(255) NOT NULL DEFAULT '', optionDescription TEXT, optionType VARCHAR(255) NOT NULL DEFAULT '', - configurationData MEDIUMTEXT, + configuration MEDIUMTEXT, showOrder INT(10) NOT NULL DEFAULT 0, isDisabled TINYINT(1) NOT NULL DEFAULT 0, originIsSystem TINYINT(1) NOT NULL DEFAULT 0 @@ -2572,8 +2572,8 @@ INSERT INTO wcf1_template_group (parentTemplateGroupID, templateGroupName, templ INSERT INTO wcf1_template_group (parentTemplateGroupID, templateGroupName, templateGroupFolderName) VALUES (NULL, 'wcf.acp.template.group.shared', '_wcf_shared/'); -- default options: subject and message -INSERT INTO wcf1_contact_option (optionID, optionTitle, optionDescription, optionType, configurationData, showOrder, originIsSystem) VALUES (1, 'wcf.contact.option1', 'wcf.contact.optionDescription1', 'text', '{\"required\":1}', 1, 1); -INSERT INTO wcf1_contact_option (optionID, optionTitle, optionDescription, optionType, configurationData, showOrder, originIsSystem) VALUES (2, 'wcf.contact.option2', '', 'textarea', '{\"required\":1}', 1, 1); +INSERT INTO wcf1_contact_option (optionID, optionTitle, optionDescription, optionType, configuration, showOrder, originIsSystem) VALUES (1, 'wcf.contact.option1', 'wcf.contact.optionDescription1', 'text', '{\"required\":1}', 1, 1); +INSERT INTO wcf1_contact_option (optionID, optionTitle, optionDescription, optionType, configuration, showOrder, originIsSystem) VALUES (2, 'wcf.contact.option2', '', 'textarea', '{\"required\":1}', 1, 1); -- default recipient: site administrator INSERT INTO wcf1_contact_recipient (recipientID, name, email, isAdministrator, originIsSystem) VALUES (1, 'wcf.contact.recipient.name1', '', 1, 1); From 60aa0e1c5857230648bff62835f2b0b8e7a2a207 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Tue, 6 May 2025 12:11:00 +0200 Subject: [PATCH 31/33] Make `CurrencyFormatter` final --- .../system/form/option/formatter/CurrencyFormatter.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php b/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php index c5fa78e3abe..f1a1ad4c9b8 100644 --- a/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php +++ b/wcfsetup/install/files/lib/system/form/option/formatter/CurrencyFormatter.class.php @@ -13,7 +13,7 @@ * @license GNU Lesser General Public License * @since 6.2 */ -class CurrencyFormatter implements IFormOptionFormatter +final class CurrencyFormatter implements IFormOptionFormatter { #[\Override] public function format(string $value, int $languageID, array $configuration): string From 3508aacf4f2da186cc23df791c9b09cd15944073 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Tue, 6 May 2025 12:17:44 +0200 Subject: [PATCH 32/33] Apply suggestions from code review --- .../lib/system/form/option/BooleanFormOption.class.php | 5 +++-- .../system/form/option/CheckboxesFormOption.class.php | 10 +++++----- .../system/form/option/CurrencyFormOption.class.php | 5 +++-- .../lib/system/form/option/DateFormOption.class.php | 8 +++++--- .../lib/system/form/option/EmailFormOption.class.php | 5 +++-- .../lib/system/form/option/FloatFormOption.class.php | 5 +++-- .../lib/system/form/option/IconFormOption.class.php | 6 ++++-- .../lib/system/form/option/IntegerFormOption.class.php | 5 +++-- .../lib/system/form/option/RatingFormOption.class.php | 6 ++++-- .../lib/system/form/option/SelectFormOption.class.php | 5 +++-- .../system/form/option/SourceCodeFormOption.class.php | 5 +++-- .../system/form/option/TextareaFormOption.class.php | 3 ++- .../lib/system/form/option/UrlFormOption.class.php | 5 +++-- .../lib/system/form/option/WysiwygFormOption.class.php | 10 ++++++---- 14 files changed, 50 insertions(+), 33 deletions(-) diff --git a/wcfsetup/install/files/lib/system/form/option/BooleanFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/BooleanFormOption.class.php index 86582f29cf8..bfca00989a7 100644 --- a/wcfsetup/install/files/lib/system/form/option/BooleanFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/BooleanFormOption.class.php @@ -5,6 +5,7 @@ use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\BooleanFormField; use wcf\system\form\option\formatter\BooleanFormatter; +use wcf\system\form\option\formatter\IFormOptionFormatter; /** * Implementation of a form field for boolean-type values. @@ -29,13 +30,13 @@ public function getFormField(string $id, array $configuration = []): AbstractFor } #[\Override] - public function getFormatter(): BooleanFormatter + public function getFormatter(): IFormOptionFormatter { return new BooleanFormatter(); } #[\Override] - public function getPlainTextFormatter(): BooleanFormatter + public function getPlainTextFormatter(): IFormOptionFormatter { return $this->getFormatter(); } diff --git a/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php index ee788bd1917..798f42e7564 100644 --- a/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php @@ -2,9 +2,9 @@ namespace wcf\system\form\option; -use wcf\system\form\builder\field\DateFormField; +use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\MultipleSelectionFormField; -use wcf\system\form\option\formatter\DateFormatter; +use wcf\system\form\option\formatter\IFormOptionFormatter; use wcf\system\form\option\formatter\MultipleSelectionFormatter; use wcf\system\WCF; use wcf\util\JSON; @@ -26,7 +26,7 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configuration = []): MultipleSelectionFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { $formField = MultipleSelectionFormField::create($id); @@ -57,13 +57,13 @@ public function getConfigurationFormFields(): array } #[\Override] - public function getFormatter(): MultipleSelectionFormatter + public function getFormatter(): IFormOptionFormatter { return new MultipleSelectionFormatter(); } #[\Override] - public function getPlainTextFormatter(): MultipleSelectionFormatter + public function getPlainTextFormatter(): IFormOptionFormatter { return $this->getFormatter(); } diff --git a/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php index 1f78a999e9a..f23b074db50 100644 --- a/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/CurrencyFormOption.class.php @@ -5,6 +5,7 @@ use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\CurrencyFormField; use wcf\system\form\option\formatter\CurrencyFormatter; +use wcf\system\form\option\formatter\IFormOptionFormatter; /** * Implementation of a form field for currency values. @@ -46,13 +47,13 @@ public function getConfigurationFormFields(): array } #[\Override] - public function getFormatter(): CurrencyFormatter + public function getFormatter(): IFormOptionFormatter { return new CurrencyFormatter(); } #[\Override] - public function getPlainTextFormatter(): CurrencyFormatter + public function getPlainTextFormatter(): IFormOptionFormatter { return $this->getFormatter(); } diff --git a/wcfsetup/install/files/lib/system/form/option/DateFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/DateFormOption.class.php index b2a3aaadaa6..de3a22e7780 100644 --- a/wcfsetup/install/files/lib/system/form/option/DateFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/DateFormOption.class.php @@ -2,8 +2,10 @@ namespace wcf\system\form\option; +use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\DateFormField; use wcf\system\form\option\formatter\DateFormatter; +use wcf\system\form\option\formatter\IFormOptionFormatter; /** * Implementation of a form field for date values. @@ -22,7 +24,7 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configuration = []): DateFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { $formField = DateFormField::create($id) ->saveValueFormat('Y-m-d'); @@ -31,13 +33,13 @@ public function getFormField(string $id, array $configuration = []): DateFormFie } #[\Override] - public function getFormatter(): DateFormatter + public function getFormatter(): IFormOptionFormatter { return new DateFormatter(); } #[\Override] - public function getPlainTextFormatter(): DateFormatter + public function getPlainTextFormatter(): IFormOptionFormatter { return $this->getFormatter(); } diff --git a/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php index 509f7d8ff06..e23e3ef0d96 100644 --- a/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/EmailFormOption.class.php @@ -7,6 +7,7 @@ use wcf\system\form\builder\field\EmailFormField; use wcf\system\form\builder\field\TextFormField; use wcf\system\form\option\formatter\EmailFormatter; +use wcf\system\form\option\formatter\IFormOptionFormatter; use wcf\system\WCF; /** @@ -32,13 +33,13 @@ public function getFormField(string $id, array $configuration = []): AbstractFor } #[\Override] - public function getFormatter(): EmailFormatter + public function getFormatter(): IFormOptionFormatter { return new EmailFormatter(); } #[\Override] - public function getPlainTextFormatter(): EmailFormatter + public function getPlainTextFormatter(): IFormOptionFormatter { return $this->getFormatter(); } diff --git a/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php index 102d62e2ccb..2bfbe762097 100644 --- a/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/FloatFormOption.class.php @@ -5,6 +5,7 @@ use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\FloatFormField; use wcf\system\form\option\formatter\FloatFormatter; +use wcf\system\form\option\formatter\IFormOptionFormatter; /** * Implementation of a form field for float values. @@ -43,13 +44,13 @@ public function getConfigurationFormFields(): array } #[\Override] - public function getFormatter(): FloatFormatter + public function getFormatter(): IFormOptionFormatter { return new FloatFormatter(); } #[\Override] - public function getPlainTextFormatter(): FloatFormatter + public function getPlainTextFormatter(): IFormOptionFormatter { return $this->getFormatter(); } diff --git a/wcfsetup/install/files/lib/system/form/option/IconFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/IconFormOption.class.php index 6cfeac0c748..672e989641c 100644 --- a/wcfsetup/install/files/lib/system/form/option/IconFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/IconFormOption.class.php @@ -2,8 +2,10 @@ namespace wcf\system\form\option; +use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\IconFormField; use wcf\system\form\option\formatter\IconFormatter; +use wcf\system\form\option\formatter\IFormOptionFormatter; /** * Implementation of a form field for icon values. @@ -22,13 +24,13 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configuration = []): IconFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { return IconFormField::create($id); } #[\Override] - public function getFormatter(): IconFormatter + public function getFormatter(): IFormOptionFormatter { return new IconFormatter(); } diff --git a/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php index e26b9ccd48d..32e0f6350b7 100644 --- a/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/IntegerFormOption.class.php @@ -4,6 +4,7 @@ use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\IntegerFormField; +use wcf\system\form\option\formatter\IFormOptionFormatter; use wcf\system\form\option\formatter\IntegerFormatter; /** @@ -43,13 +44,13 @@ public function getConfigurationFormFields(): array } #[\Override] - public function getFormatter(): IntegerFormatter + public function getFormatter(): IFormOptionFormatter { return new IntegerFormatter(); } #[\Override] - public function getPlainTextFormatter(): IntegerFormatter + public function getPlainTextFormatter(): IFormOptionFormatter { return $this->getFormatter(); } diff --git a/wcfsetup/install/files/lib/system/form/option/RatingFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/RatingFormOption.class.php index 4a11b065b93..318b9a02187 100644 --- a/wcfsetup/install/files/lib/system/form/option/RatingFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/RatingFormOption.class.php @@ -2,7 +2,9 @@ namespace wcf\system\form\option; +use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\RatingFormField; +use wcf\system\form\option\formatter\IFormOptionFormatter; use wcf\system\form\option\formatter\RatingFormatter; /** @@ -22,13 +24,13 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configuration = []): RatingFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { return RatingFormField::create($id); } #[\Override] - public function getFormatter(): RatingFormatter + public function getFormatter(): IFormOptionFormatter { return new RatingFormatter(); } diff --git a/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php index 4261b53f6b9..2661604f1c3 100644 --- a/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php @@ -4,6 +4,7 @@ use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\SelectFormField; +use wcf\system\form\option\formatter\IFormOptionFormatter; use wcf\system\form\option\formatter\SelectFormatter; use wcf\system\WCF; use wcf\util\JSON; @@ -56,13 +57,13 @@ public function getConfigurationFormFields(): array } #[\Override] - public function getFormatter(): SelectFormatter + public function getFormatter(): IFormOptionFormatter { return new SelectFormatter(); } #[\Override] - public function getPlainTextFormatter(): SelectFormatter + public function getPlainTextFormatter(): IFormOptionFormatter { return new SelectFormatter(false); } diff --git a/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php index 2275fac5096..ec1e5e74bf5 100644 --- a/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/SourceCodeFormOption.class.php @@ -6,6 +6,7 @@ use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\SourceCodeFormField; use wcf\system\form\builder\field\TextFormField; +use wcf\system\form\option\formatter\IFormOptionFormatter; use wcf\system\form\option\formatter\SourceCodeFormatter; use wcf\system\WCF; @@ -26,13 +27,13 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configuration = []): SourceCodeFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { return SourceCodeFormField::create($id); } #[\Override] - public function getFormatter(): SourceCodeFormatter + public function getFormatter(): IFormOptionFormatter { return new SourceCodeFormatter(); } diff --git a/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php index 50d5e8b9530..104bbca3362 100644 --- a/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/TextareaFormOption.class.php @@ -6,6 +6,7 @@ use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\MultilineTextFormField; use wcf\system\form\builder\field\TextFormField; +use wcf\system\form\option\formatter\IFormOptionFormatter; use wcf\system\form\option\formatter\MultilineTextFormatter; use wcf\system\WCF; @@ -43,7 +44,7 @@ public function getConfigurationFormFields(): array } #[\Override] - public function getFormatter(): MultilineTextFormatter + public function getFormatter(): IFormOptionFormatter { return new MultilineTextFormatter(); } diff --git a/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php index bdd8eff60f6..69666ea5c74 100644 --- a/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/UrlFormOption.class.php @@ -6,6 +6,7 @@ use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\TextFormField; use wcf\system\form\builder\field\UrlFormField; +use wcf\system\form\option\formatter\IFormOptionFormatter; use wcf\system\form\option\formatter\UrlFormatter; use wcf\system\WCF; @@ -26,13 +27,13 @@ public function getId(): string } #[\Override] - public function getFormField(string $id, array $configuration = []): UrlFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { return UrlFormField::create($id); } #[\Override] - public function getFormatter(): UrlFormatter + public function getFormatter(): IFormOptionFormatter { return new UrlFormatter(); } diff --git a/wcfsetup/install/files/lib/system/form/option/WysiwygFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/WysiwygFormOption.class.php index 6a6b1b05b4d..02b03018e88 100644 --- a/wcfsetup/install/files/lib/system/form/option/WysiwygFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/WysiwygFormOption.class.php @@ -3,8 +3,10 @@ namespace wcf\system\form\option; use wcf\data\DatabaseObjectList; +use wcf\system\form\builder\field\AbstractFormField; use wcf\system\form\builder\field\TextFormField; use wcf\system\form\builder\field\wysiwyg\WysiwygFormField; +use wcf\system\form\option\formatter\IFormOptionFormatter; use wcf\system\form\option\formatter\WysiwygFormatter; use wcf\system\form\option\formatter\WysiwygPlainTextFormatter; use wcf\system\WCF; @@ -26,13 +28,13 @@ public function getId(): string } #[\Override] - public function getFilterFormField(string $id, array $configuration = []): TextFormField + public function getFilterFormField(string $id, array $configuration = []): AbstractFormField { return TextFormField::create($id); } #[\Override] - public function getFormField(string $id, array $configuration = []): WysiwygFormField + public function getFormField(string $id, array $configuration = []): AbstractFormField { return WysiwygFormField::create($id) ->objectType('com.woltlab.wcf.genericFormOption'); @@ -51,13 +53,13 @@ public function applyFilter(DatabaseObjectList $list, string $columnName, mixed } #[\Override] - public function getFormatter(): WysiwygFormatter + public function getFormatter(): IFormOptionFormatter { return new WysiwygFormatter(); } #[\Override] - public function getPlainTextFormatter(): WysiwygPlainTextFormatter + public function getPlainTextFormatter(): IFormOptionFormatter { return new WysiwygPlainTextFormatter(); } From ece77b987b6b306ad05c0f4e89082d916a70b7ac Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Tue, 6 May 2025 15:59:59 +0200 Subject: [PATCH 33/33] Add abstract implementation for select options --- .../option/CheckboxesFormOption.class.php | 22 ++-------- .../option/RadioButtonFormOption.class.php | 14 ++++++ .../form/option/SelectFormOption.class.php | 22 ++-------- .../option/TSelectOptionsFormOption.class.php | 43 +++++++++++++++++++ 4 files changed, 63 insertions(+), 38 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/form/option/TSelectOptionsFormOption.class.php diff --git a/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php index 798f42e7564..a6d95198cb2 100644 --- a/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/CheckboxesFormOption.class.php @@ -6,8 +6,6 @@ use wcf\system\form\builder\field\MultipleSelectionFormField; use wcf\system\form\option\formatter\IFormOptionFormatter; use wcf\system\form\option\formatter\MultipleSelectionFormatter; -use wcf\system\WCF; -use wcf\util\JSON; /** * Implementation of a form field for selecting multiple values. @@ -19,6 +17,8 @@ */ class CheckboxesFormOption extends AbstractFormOption { + use TSelectOptionsFormOption; + #[\Override] public function getId(): string { @@ -29,23 +29,7 @@ public function getId(): string public function getFormField(string $id, array $configuration = []): AbstractFormField { $formField = MultipleSelectionFormField::create($id); - - if (isset($configuration['selectOptions'])) { - $selectOptions = []; - foreach (JSON::decode($configuration['selectOptions']) as $selectOption) { - if (isset($selectOption['value'][0])) { - $value = $selectOption['value'][0]; - } else if (isset($selectOption['value'][WCF::getLanguage()->languageID])) { - $value = $selectOption['value'][WCF::getLanguage()->languageID]; - } else { - $value = reset($selectOption['value']); - } - - $selectOptions[$selectOption['key']] = $value; - } - - $formField->options($selectOptions); - } + $this->setSelectOptions($formField, $configuration); return $formField; } diff --git a/wcfsetup/install/files/lib/system/form/option/RadioButtonFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/RadioButtonFormOption.class.php index 7bb0a3d5da4..9f06399f14c 100644 --- a/wcfsetup/install/files/lib/system/form/option/RadioButtonFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/RadioButtonFormOption.class.php @@ -2,6 +2,9 @@ namespace wcf\system\form\option; +use wcf\system\form\builder\field\AbstractFormField; +use wcf\system\form\builder\field\RadioButtonFormField; + /** * Implementation of a form option for selecting a single value using radio buttons. * @@ -12,9 +15,20 @@ */ class RadioButtonFormOption extends SelectFormOption { + use TSelectOptionsFormOption; + #[\Override] public function getId(): string { return 'radioButton'; } + + #[\Override] + public function getFormField(string $id, array $configuration = []): AbstractFormField + { + $formField = RadioButtonFormField::create($id); + $this->setSelectOptions($formField, $configuration); + + return $formField; + } } diff --git a/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php index 2661604f1c3..9a520906d6a 100644 --- a/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php +++ b/wcfsetup/install/files/lib/system/form/option/SelectFormOption.class.php @@ -6,8 +6,6 @@ use wcf\system\form\builder\field\SelectFormField; use wcf\system\form\option\formatter\IFormOptionFormatter; use wcf\system\form\option\formatter\SelectFormatter; -use wcf\system\WCF; -use wcf\util\JSON; /** * Implementation of a form option for selecting a single value. @@ -19,6 +17,8 @@ */ class SelectFormOption extends AbstractFormOption { + use TSelectOptionsFormOption; + #[\Override] public function getId(): string { @@ -29,23 +29,7 @@ public function getId(): string public function getFormField(string $id, array $configuration = []): AbstractFormField { $formField = SelectFormField::create($id); - - if (isset($configuration['selectOptions'])) { - $selectOptions = []; - foreach (JSON::decode($configuration['selectOptions']) as $selectOption) { - if (isset($selectOption['value'][0])) { - $value = $selectOption['value'][0]; - } else if (isset($selectOption['value'][WCF::getLanguage()->languageID])) { - $value = $selectOption['value'][WCF::getLanguage()->languageID]; - } else { - $value = reset($selectOption['value']); - } - - $selectOptions[$selectOption['key']] = $value; - } - - $formField->options($selectOptions); - } + $this->setSelectOptions($formField, $configuration); return $formField; } diff --git a/wcfsetup/install/files/lib/system/form/option/TSelectOptionsFormOption.class.php b/wcfsetup/install/files/lib/system/form/option/TSelectOptionsFormOption.class.php new file mode 100644 index 00000000000..5e0acc9a18b --- /dev/null +++ b/wcfsetup/install/files/lib/system/form/option/TSelectOptionsFormOption.class.php @@ -0,0 +1,43 @@ + + * @since 6.2 + */ +trait TSelectOptionsFormOption +{ + /** + * @param array $configuration + */ + protected function setSelectOptions(ISelectionFormField $formField, array $configuration): void + { + if (!isset($configuration['selectOptions'])) { + return; + } + + $selectOptions = []; + foreach (JSON::decode($configuration['selectOptions']) as $selectOption) { + if (isset($selectOption['value'][0])) { + $value = $selectOption['value'][0]; + } else if (isset($selectOption['value'][WCF::getLanguage()->languageID])) { + $value = $selectOption['value'][WCF::getLanguage()->languageID]; + } else { + $value = reset($selectOption['value']); + } + + $selectOptions[$selectOption['key']] = $value; + } + + $formField->options($selectOptions); + } +}