From f50c0ac08c62b5ee340fd0fff64da2b8df126c3d Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Thu, 15 Oct 2026 17:44:30 +0200 Subject: [PATCH] Add configurable border color to badge trophies Closes #6137 --- .../templates/shared_trophyBadge.tpl | 2 +- .../templates/shared_trophyBadgeFormField.tpl | 4 +++- .../Builder/Field/Controller/TrophyBadge.ts | 12 +++++++++--- .../Core/Form/Builder/Field/TrophyBadge.ts | 1 + .../update_com.woltlab.wcf_6.3_step1.php | 2 ++ .../Builder/Field/Controller/TrophyBadge.js | 5 +++-- .../Core/Form/Builder/Field/TrophyBadge.js | 1 + .../files/lib/acp/form/TrophyAddForm.class.php | 8 +++++++- .../files/lib/data/trophy/Trophy.class.php | 1 + .../lib/data/trophy/TrophyBuilder.class.php | 4 +++- .../trophy/TrophyBadgeFormField.class.php | 18 +++++++++++++++--- ...erTrophyReceivedNotificationEvent.class.php | 3 ++- wcfsetup/install/files/style/ui/trophy.scss | 1 + wcfsetup/install/lang/de.xml | 1 + wcfsetup/install/lang/en.xml | 1 + wcfsetup/setup/db/install_com.woltlab.wcf.php | 2 ++ 16 files changed, 53 insertions(+), 13 deletions(-) diff --git a/com.woltlab.wcf/templates/shared_trophyBadge.tpl b/com.woltlab.wcf/templates/shared_trophyBadge.tpl index fc77fb751dc..a0326d2f0d1 100644 --- a/com.woltlab.wcf/templates/shared_trophyBadge.tpl +++ b/com.woltlab.wcf/templates/shared_trophyBadge.tpl @@ -1,6 +1,6 @@ diff --git a/com.woltlab.wcf/templates/shared_trophyBadgeFormField.tpl b/com.woltlab.wcf/templates/shared_trophyBadgeFormField.tpl index ddb1766efd7..d2edcfc1bd9 100644 --- a/com.woltlab.wcf/templates/shared_trophyBadgeFormField.tpl +++ b/com.woltlab.wcf/templates/shared_trophyBadgeFormField.tpl @@ -1,5 +1,5 @@
- + {unsafe:$field->getIcon()->toHtml(64)} {if !$field->isImmutable()} @@ -7,6 +7,7 @@ +
{/if} @@ -14,6 +15,7 @@ + {if !$field->isImmutable()} {if $__trophyBadgeFormFieldIncludeJavaScript} diff --git a/ts/WoltLabSuite/Core/Form/Builder/Field/Controller/TrophyBadge.ts b/ts/WoltLabSuite/Core/Form/Builder/Field/Controller/TrophyBadge.ts index dbe7810fab6..e763427f1b1 100644 --- a/ts/WoltLabSuite/Core/Form/Builder/Field/Controller/TrophyBadge.ts +++ b/ts/WoltLabSuite/Core/Form/Builder/Field/Controller/TrophyBadge.ts @@ -1,6 +1,6 @@ /** - * Enables editing of the icon, the icon color and the background color of a - * badge trophy. + * Enables editing of the icon, the icon color, the background color and the + * border color of a badge trophy. * * @author Alexander Ebert, Marcel Werk * @copyright 2001-2026 WoltLab GmbH @@ -56,7 +56,7 @@ function setupColorPicker( button: HTMLButtonElement, preview: HTMLElement, input: HTMLInputElement, - property: "color" | "background-color", + property: "color" | "background-color" | "--trophy-border-color", ): void { button.dataset.store = input.id; @@ -83,4 +83,10 @@ export function setup(fieldId: string): void { getInput(fieldId, "badgeColor"), "background-color", ); + setupColorPicker( + getButton(container, "border-color"), + preview, + getInput(fieldId, "borderColor"), + "--trophy-border-color", + ); } diff --git a/ts/WoltLabSuite/Core/Form/Builder/Field/TrophyBadge.ts b/ts/WoltLabSuite/Core/Form/Builder/Field/TrophyBadge.ts index 4aa15b22d33..c03a3b14253 100644 --- a/ts/WoltLabSuite/Core/Form/Builder/Field/TrophyBadge.ts +++ b/ts/WoltLabSuite/Core/Form/Builder/Field/TrophyBadge.ts @@ -16,6 +16,7 @@ class TrophyBadge extends Field { iconName: this.#getValue("iconName"), iconColor: this.#getValue("iconColor"), badgeColor: this.#getValue("badgeColor"), + borderColor: this.#getValue("borderColor"), }, }; } diff --git a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.3_step1.php b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.3_step1.php index 20b7cadf780..00e0820f46d 100644 --- a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.3_step1.php +++ b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.3_step1.php @@ -53,6 +53,8 @@ ->notNull() ->defaultValue(1), IntDatabaseTableColumn::create('iconFileID'), + VarcharDatabaseTableColumn::create('borderColor') + ->length(255), JsonDatabaseTableColumn::create('conditions'), ]) ->foreignKeys([ diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Controller/TrophyBadge.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Controller/TrophyBadge.js index decdc2c292e..147e2eb37ab 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Controller/TrophyBadge.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Controller/TrophyBadge.js @@ -1,6 +1,6 @@ /** - * Enables editing of the icon, the icon color and the background color of a - * badge trophy. + * Enables editing of the icon, the icon color, the background color and the + * border color of a badge trophy. * * @author Alexander Ebert, Marcel Werk * @copyright 2001-2026 WoltLab GmbH @@ -63,5 +63,6 @@ define(["require", "exports", "tslib", "../../../../Ui/Style/FontAwesome", "../. setupChangeIcon(container, preview, getInput(fieldId, "iconName")); setupColorPicker(getButton(container, "color"), preview, getInput(fieldId, "iconColor"), "color"); setupColorPicker(getButton(container, "background-color"), preview, getInput(fieldId, "badgeColor"), "background-color"); + setupColorPicker(getButton(container, "border-color"), preview, getInput(fieldId, "borderColor"), "--trophy-border-color"); } }); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/TrophyBadge.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/TrophyBadge.js index 2054ae7fa90..9fb1830ea44 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/TrophyBadge.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/TrophyBadge.js @@ -8,6 +8,7 @@ define(["require", "exports", "tslib", "./Field"], function (require, exports, t iconName: this.#getValue("iconName"), iconColor: this.#getValue("iconColor"), badgeColor: this.#getValue("badgeColor"), + borderColor: this.#getValue("borderColor"), }, }; } diff --git a/wcfsetup/install/files/lib/acp/form/TrophyAddForm.class.php b/wcfsetup/install/files/lib/acp/form/TrophyAddForm.class.php index a92b41eabfc..03924a2db76 100644 --- a/wcfsetup/install/files/lib/acp/form/TrophyAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/TrophyAddForm.class.php @@ -205,7 +205,12 @@ protected function createForm(): void ->label('wcf.acp.trophy.type.badge') ->required() ->saveValueCallback(static function (TrophyBuilder $builder, TrophyBadgeFormField $field): void { - $builder->setBadge($field->getIconName(), $field->getIconColor(), $field->getBadgeColor()); + $builder->setBadge( + $field->getIconName(), + $field->getIconColor(), + $field->getBadgeColor(), + $field->getBorderColor() + ); }) ->loadValueCallback(static function (Trophy $object, IFormField $field): void { // Image trophies keep the default badge. @@ -214,6 +219,7 @@ protected function createForm(): void 'iconName' => $object->iconName, 'iconColor' => $object->iconColor, 'badgeColor' => $object->badgeColor, + 'borderColor' => $object->borderColor, ]); } }) diff --git a/wcfsetup/install/files/lib/data/trophy/Trophy.class.php b/wcfsetup/install/files/lib/data/trophy/Trophy.class.php index 1e0d6f40f98..4ce420c7ff6 100644 --- a/wcfsetup/install/files/lib/data/trophy/Trophy.class.php +++ b/wcfsetup/install/files/lib/data/trophy/Trophy.class.php @@ -36,6 +36,7 @@ * @property-read ?string $iconName * @property-read ?string $iconColor * @property-read ?string $badgeColor + * @property-read ?string $borderColor * @property-read 0|1 $isDisabled `1` if the trophy is disabled, otherwise `0` * @property-read 0|1 $awardAutomatically `1` if the trophy is awarded automatically, otherwise `0` * @property-read 0|1 $revokeAutomatically `1` if the trophy should be automatically revoked once the conditions are no longer met, otherwise `0` diff --git a/wcfsetup/install/files/lib/data/trophy/TrophyBuilder.class.php b/wcfsetup/install/files/lib/data/trophy/TrophyBuilder.class.php index 2c13ae55ef2..827889fb67e 100644 --- a/wcfsetup/install/files/lib/data/trophy/TrophyBuilder.class.php +++ b/wcfsetup/install/files/lib/data/trophy/TrophyBuilder.class.php @@ -80,6 +80,7 @@ public function setType(int $type): static $this->properties['iconName'] = ''; $this->properties['iconColor'] = ''; $this->properties['badgeColor'] = ''; + $this->properties['borderColor'] = ''; } elseif ($type === Trophy::TYPE_BADGE) { $this->properties['iconFileID'] = null; } @@ -100,11 +101,12 @@ public function setIconFileID(?int $iconFileID): static /** * Sets the icon and the colors of a badge trophy. */ - public function setBadge(string $iconName, string $iconColor, string $badgeColor): static + public function setBadge(string $iconName, string $iconColor, string $badgeColor, string $borderColor): static { $this->properties['iconName'] = $iconName; $this->properties['iconColor'] = $iconColor; $this->properties['badgeColor'] = $badgeColor; + $this->properties['borderColor'] = $borderColor; return $this; } diff --git a/wcfsetup/install/files/lib/system/form/builder/field/trophy/TrophyBadgeFormField.class.php b/wcfsetup/install/files/lib/system/form/builder/field/trophy/TrophyBadgeFormField.class.php index 36aa6aa2598..4954f1c7ea5 100644 --- a/wcfsetup/install/files/lib/system/form/builder/field/trophy/TrophyBadgeFormField.class.php +++ b/wcfsetup/install/files/lib/system/form/builder/field/trophy/TrophyBadgeFormField.class.php @@ -9,10 +9,11 @@ use wcf\system\style\FontAwesomeIcon; /** - * Form field to configure the icon, the icon color and the background color of - * a badge trophy. + * Form field to configure the icon, the icon color, the background color and the + * border color of a badge trophy. * - * The value is an array with the keys `iconName`, `iconColor` and `badgeColor`. + * The value is an array with the keys `iconName`, `iconColor`, `badgeColor` and + * `borderColor`. * * @author Marcel Werk * @copyright 2001-2026 WoltLab GmbH @@ -29,6 +30,8 @@ final class TrophyBadgeFormField extends AbstractFormField implements IImmutable public const DEFAULT_BADGE_COLOR = 'rgba(50, 92, 132, 1)'; + public const DEFAULT_BORDER_COLOR = 'rgba(0, 0, 0, 0)'; + private const COLOR_PATTERN = '~^rgba\(\d{1,3}, ?\d{1,3}, ?\d{1,3}, ?(1|1\.00?|0|0?\.[0-9]{1,2})\)$~'; /** @@ -48,6 +51,7 @@ final class TrophyBadgeFormField extends AbstractFormField implements IImmutable 'iconName' => self::DEFAULT_ICON_NAME, 'iconColor' => self::DEFAULT_ICON_COLOR, 'badgeColor' => self::DEFAULT_BADGE_COLOR, + 'borderColor' => self::DEFAULT_BORDER_COLOR, ]; /** @@ -76,6 +80,7 @@ public function readValue(): TrophyBadgeFormField 'iconName' => (string)($value['iconName'] ?? ''), 'iconColor' => (string)($value['iconColor'] ?? ''), 'badgeColor' => (string)($value['badgeColor'] ?? ''), + 'borderColor' => (string)($value['borderColor'] ?? ''), ]; } } @@ -96,6 +101,7 @@ public function value(mixed $value): TrophyBadgeFormField 'iconName' => (string)($value['iconName'] ?? self::DEFAULT_ICON_NAME), 'iconColor' => (string)($value['iconColor'] ?? self::DEFAULT_ICON_COLOR), 'badgeColor' => (string)($value['badgeColor'] ?? self::DEFAULT_BADGE_COLOR), + 'borderColor' => (string)($value['borderColor'] ?? self::DEFAULT_BORDER_COLOR), ]; return $this; @@ -126,6 +132,7 @@ public function validate(): void if ( \preg_match(self::COLOR_PATTERN, $this->getIconColor()) !== 1 || \preg_match(self::COLOR_PATTERN, $this->getBadgeColor()) !== 1 + || \preg_match(self::COLOR_PATTERN, $this->getBorderColor()) !== 1 ) { $this->addValidationError(new FormFieldValidationError( 'invalidValue', @@ -149,6 +156,11 @@ public function getBadgeColor(): string return $this->value['badgeColor']; } + public function getBorderColor(): string + { + return $this->value['borderColor']; + } + /** * Returns the selected icon or the default icon if the selection is invalid. */ diff --git a/wcfsetup/install/files/lib/system/user/notification/event/UserTrophyReceivedNotificationEvent.class.php b/wcfsetup/install/files/lib/system/user/notification/event/UserTrophyReceivedNotificationEvent.class.php index 461d76381e3..d3c238d15b0 100644 --- a/wcfsetup/install/files/lib/system/user/notification/event/UserTrophyReceivedNotificationEvent.class.php +++ b/wcfsetup/install/files/lib/system/user/notification/event/UserTrophyReceivedNotificationEvent.class.php @@ -80,7 +80,8 @@ public static function getTestObjects(UserProfile $recipient, UserProfile $autho ->setBadge( FontAwesomeIcon::fromString('trophy;false')->__toString(), 'rgba(255, 255, 255, 1)', - 'rgba(50, 92, 132, 1)' + 'rgba(50, 92, 132, 1)', + 'rgba(0, 0, 0, 0)' ) ->create(); diff --git a/wcfsetup/install/files/style/ui/trophy.scss b/wcfsetup/install/files/style/ui/trophy.scss index 1b3e3908657..112a923fe6d 100644 --- a/wcfsetup/install/files/style/ui/trophy.scss +++ b/wcfsetup/install/files/style/ui/trophy.scss @@ -26,6 +26,7 @@ align-self: flex-start; display: inline-block; border-radius: 50%; + box-shadow: inset 0 0 0 2px var(--trophy-border-color, transparent); fa-icon { transform: scale(0.5625); diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index f9951c4d2e1..02e9bfd6b3a 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -4514,6 +4514,7 @@ Erlaubte Dateiendungen: gif, jpg, jpeg, png, webp]]> + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index ec5e8847677..90885495ae1 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -4513,6 +4513,7 @@ Allowed extensions: gif, jpg, jpeg, png, webp]]> + diff --git a/wcfsetup/setup/db/install_com.woltlab.wcf.php b/wcfsetup/setup/db/install_com.woltlab.wcf.php index 268c9ced0d8..81821112298 100644 --- a/wcfsetup/setup/db/install_com.woltlab.wcf.php +++ b/wcfsetup/setup/db/install_com.woltlab.wcf.php @@ -3868,6 +3868,8 @@ ->length(255), VarcharDatabaseTableColumn::create('badgeColor') ->length(255), + VarcharDatabaseTableColumn::create('borderColor') + ->length(255), DefaultFalseBooleanDatabaseTableColumn::create('isDisabled'), DefaultFalseBooleanDatabaseTableColumn::create('awardAutomatically'), DefaultFalseBooleanDatabaseTableColumn::create('revokeAutomatically'),