Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4251fea
Add database table `wcf1_user_rank_content`
Cyperghost Apr 28, 2025
8328c44
Create new container `MultilingualContainer` to handle multilingual a…
Cyperghost Apr 28, 2025
e1b8852
Create form data processor for processing multilingual and monolingua…
Cyperghost Apr 28, 2025
649158f
Add new column `isMultilingual` to `wcf1_user_rank`
Cyperghost Apr 28, 2025
9e9fdc3
Save multilingual values into `wcf1_user_rank_content`
Cyperghost Apr 28, 2025
f691627
Add todo for later
Cyperghost Apr 29, 2025
11c36de
Rename to `MultilingualFormContainer`
Cyperghost Apr 29, 2025
09e690a
Load the title when editing
Cyperghost Apr 29, 2025
d4fc68a
Implement a user rank list that loads the title from `wcf1_user_rank_…
Cyperghost Apr 29, 2025
7117f7f
Implement a cache for the ViewableUserRank
Cyperghost Apr 29, 2025
e163973
Use `ViewableUserRank` and `ViewableUserRankList` for the interactions
Cyperghost Apr 29, 2025
87a7adb
Load title from `wcf1_user_rank_content` instead of using `rankTitle`
Cyperghost Apr 29, 2025
023c76c
Change template type to `IFormChildNode` to allow also `IFormContainer`
Cyperghost Apr 29, 2025
0aeac84
Allow appending multilingual form fields into a container
Cyperghost Apr 29, 2025
0f50d58
Check whether it is an `IFormContainer` instead of a `FormContainer`.
Cyperghost Apr 29, 2025
c522871
Implement helper functions to insert new form fields into the `Multil…
Cyperghost Apr 29, 2025
390da2f
Extracting the code for loading multilingual content in `TMultilingua…
Cyperghost Apr 29, 2025
bd0e44d
Add `languageID` property to `ViewableUserRank`
Cyperghost Apr 29, 2025
7262978
Update wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2…
Cyperghost Apr 30, 2025
c15d5fb
Update database structure
Cyperghost Apr 30, 2025
796ea99
Revert "Use `ViewableUserRank` and `ViewableUserRankList` for the int…
Cyperghost Apr 30, 2025
577d6bd
Remove unnecessary `ViewableUserRank` and `ViewableUserRankList` clas…
Cyperghost Apr 30, 2025
ea91e7b
Remove the member `$languageID` from `UserRankCache`
Cyperghost Apr 30, 2025
e213f16
`isMultilingual` is not stored directly in the database and must ther…
Cyperghost Apr 30, 2025
eca01eb
For monolingual content, set the `languageID` to the default language
Cyperghost Apr 30, 2025
14b11b4
Use `AbstractLegacyCacheBuilder` for `UserRankCacheBuilder`
Cyperghost Apr 30, 2025
345e6ee
`ViewableUserRankList` no longer exists
Cyperghost Apr 30, 2025
774394a
Migration script for user ranks title
Cyperghost May 2, 2025
422d43c
Move the language data for the user rank titles into the installation…
Cyperghost May 2, 2025
739cce8
Allow multiple form fields to be used as text reference
Cyperghost May 2, 2025
eabbca4
Add bbcode content table
Cyperghost May 2, 2025
6e24a64
Add a data processor to handle the value `none` for the CSS class name
Cyperghost May 5, 2025
2de78e5
Enable saving of i18n values without `languageItemPattern` to save th…
Cyperghost May 5, 2025
be77483
Set empty string if no content table exists for this user rank
Cyperghost May 5, 2025
bfa8272
No longer use the I18nHandler to save `buttonLabel`.
Cyperghost May 5, 2025
1175f6b
Simplify the code
Cyperghost May 5, 2025
e3f2335
Remove code that is no longer required
Cyperghost May 5, 2025
89c5a2b
A field that is multilingual has no value that can be saved directly.
Cyperghost May 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions com.woltlab.wcf/templates/shared_badgeColorFormField.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@
</ul>

<script data-relocate="true">
{if $field->getTextReferenceNodeId()}
{if $field->getTextReferenceNodeIds() !== []}
require(["WoltLabSuite/Core/Form/Builder/Field/Controller/BadgeColor"], ({ BadgeColorPreview }) => {
new BadgeColorPreview(
'{unsafe:$field->getPrefixedId()|encodeJS}Container',
'{unsafe:$field->getTextReferenceNodeId()|encodeJS}',
[{implode from=$field->getTextReferenceNodeIds() item=nodeId glue=','}'{$nodeId|encodeJS}'{/implode}],
'{unsafe:$field->getDefaultLabelText()|encodeJS}',
);
});
Expand Down
33 changes: 25 additions & 8 deletions ts/WoltLabSuite/Core/Form/Builder/Field/Controller/BadgeColor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,44 @@

export class BadgeColorPreview {
readonly #container: HTMLElement | null;
readonly #referenceField: HTMLInputElement | null;
#activeReferenceField: HTMLInputElement | null;
readonly #defaultLabelText: string;

constructor(fieldId: string, referenceFieldId: string, defaultLabelText: string) {
constructor(fieldId: string, referenceFieldIds: string[], defaultLabelText: string) {
this.#defaultLabelText = defaultLabelText;
this.#container = document.getElementById(fieldId);

if (this.#container === null) {
throw new Error("Unknown field with id '" + fieldId + "'.");
}
this.#referenceField = document.getElementById(referenceFieldId) as HTMLInputElement | null;
if (this.#referenceField === null) {
throw new Error("Unknown reference element '" + referenceFieldId + "'.");
}

this.#referenceField.addEventListener("input", () => this.#updatePreview());
const observer = new IntersectionObserver((entries) => {
const entry = entries.filter((entry) => entry.isIntersecting).pop();

if (entry) {
this.#activeReferenceField = entry.target as HTMLInputElement;
this.#updatePreview();
}
});

referenceFieldIds.forEach((referenceFieldId: string) => {
const referenceField = document.getElementById(referenceFieldId) as HTMLInputElement | null;
if (referenceField === null) {
throw new Error("Unknown reference element '" + referenceFieldId + "'.");
}

referenceField.addEventListener("input", () => {
this.#updatePreview();
});

observer.observe(referenceField);
});

this.#updatePreview();
}

#updatePreview(): void {
const value = this.#referenceField!.value.trim() || this.#defaultLabelText;
const value = this.#activeReferenceField?.value.trim() || this.#defaultLabelText;
this.#container!.querySelectorAll(".labelSelection__span.badge").forEach((span: HTMLSpanElement) => {
span.textContent = value;
});
Expand Down
51 changes: 51 additions & 0 deletions wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.2.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
*/

use wcf\system\database\table\column\IntDatabaseTableColumn;
use wcf\system\database\table\column\NotNullVarchar255DatabaseTableColumn;
use wcf\system\database\table\DatabaseTable;
use wcf\system\database\table\index\DatabaseTableForeignKey;
use wcf\system\database\table\index\DatabaseTablePrimaryIndex;
use wcf\system\database\table\PartialDatabaseTable;

return [
Expand Down Expand Up @@ -46,5 +49,53 @@
->referencedTable('wcf1_file')
->referencedColumns(['fileID'])
->onDelete('SET NULL'),
]),
DatabaseTable::create('wcf1_user_rank_content')
->columns([
IntDatabaseTableColumn::create('rankID')
->notNull(),
IntDatabaseTableColumn::create('languageID')
->notNull(),
NotNullVarchar255DatabaseTableColumn::create('title'),
])
->indices([
DatabaseTablePrimaryIndex::create()
->columns(['rankID', 'languageID']),
])
->foreignKeys([
DatabaseTableForeignKey::create()
->columns(['rankID'])
->referencedTable('wcf1_user_rank')
->referencedColumns(['rankID'])
->onDelete('CASCADE'),
DatabaseTableForeignKey::create()
->columns(['languageID'])
->referencedTable('wcf1_language')
->referencedColumns(['languageID'])
->onDelete('CASCADE'),
]),
DatabaseTable::create('wcf1_bbcode_content')
->columns([
IntDatabaseTableColumn::create('bbcodeID')
->notNull(),
IntDatabaseTableColumn::create('languageID')
->notNull(),
NotNullVarchar255DatabaseTableColumn::create('buttonLabel'),
])
->indices([
DatabaseTablePrimaryIndex::create()
->columns(['bbcodeID', 'languageID']),
])
->foreignKeys([
DatabaseTableForeignKey::create()
->columns(['bbcodeID'])
->referencedTable('wcf1_bbcode')
->referencedColumns(['bbcodeID'])
->onDelete('CASCADE'),
DatabaseTableForeignKey::create()
->columns(['languageID'])
->referencedTable('wcf1_language')
->referencedColumns(['languageID'])
->onDelete('CASCADE'),
]),
];
40 changes: 29 additions & 11 deletions wcfsetup/install/files/acp/install_com.woltlab.wcf_step2.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use wcf\data\user\UserEditor;
use wcf\data\user\UserProfileAction;
use wcf\system\image\adapter\ImagickImageAdapter;
use wcf\system\language\LanguageFactory;
use wcf\system\WCF;

// set default landing page
Expand All @@ -24,22 +25,39 @@
]);

// install default user ranks
$sql = "INSERT INTO wcf1_user_rank_content
(rankID, languageID, title)
VALUES (?, ?, ?)";
$statement = WCF::getDB()->prepare($sql);

foreach ([
[4, 0, 'wcf.user.rank.administrator', 'blue'],
[5, 0, 'wcf.user.rank.moderator', 'blue'],
[3, 0, 'wcf.user.rank.user0', ''],
[3, 300, 'wcf.user.rank.user1', ''],
[3, 900, 'wcf.user.rank.user2', ''],
[3, 3000, 'wcf.user.rank.user3', ''],
[3, 9000, 'wcf.user.rank.user4', ''],
[3, 15000, 'wcf.user.rank.user5', ''],
] as [$groupID, $requiredPoints, $rankTitle, $cssClassName]) {
UserRankEditor::create([
[4, 0, ['de' => 'Administrator', 'en' => 'Administrator'], 'blue'],
[5, 0, ['de' => 'Moderator', 'en' => 'Moderator'], 'blue'],
[3, 0, ['de' => 'Anfänger', 'en' => 'Beginner'], ''],
[3, 300, ['de' => 'Schüler', 'en' => 'Student'], ''],
[3, 900, ['de' => 'Fortgeschrittener', 'en' => 'Intermediate'], ''],
[3, 3000, ['de' => 'Profi', 'en' => 'Professional'], ''],
[3, 9000, ['de' => 'Meister', 'en' => 'Master'], ''],
[3, 15000, ['de' => 'Erleuchteter', 'en' => 'Enlightened'], ''],
] as [$groupID, $requiredPoints, $rankTitles, $cssClassName]
) {
$userRank = UserRankEditor::create([
'groupID' => $groupID,
'requiredPoints' => $requiredPoints,
'rankTitle' => $rankTitle,
'cssClassName' => $cssClassName,
]);

foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
if (!isset($rankTitles[$language->languageCode])) {
continue;
}

$statement->execute([
$userRank->rankID,
$language->languageID,
$rankTitles[$language->languageCode],
]);
}
}

// update administrator user rank and user online marking
Expand Down
2 changes: 1 addition & 1 deletion wcfsetup/install/files/acp/templates/bbcodeAdd.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
<dl class="jsButtonSetting{if $errorField == 'buttonLabel'} formError{/if}">
<dt><label for="buttonLabel">{lang}wcf.acp.bbcode.buttonLabel{/lang}</label></dt>
<dd>
<input type="text" id="buttonLabel" name="buttonLabel" value="{$i18nPlainValues['buttonLabel']}" class="long">
<input type="text" id="buttonLabel" name="buttonLabel" value="{if !$buttonLabel|is_array}{$buttonLabel}{/if}" class="long">
{if $errorField == 'buttonLabel'}
<small class="innerError">
{if $errorType == 'empty'}
Expand Down
47 changes: 47 additions & 0 deletions wcfsetup/install/files/acp/update_com.woltlab.wcf_6.2_userRank.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

use wcf\system\database\util\PreparedStatementConditionBuilder;
use wcf\system\language\LanguageFactory;
use wcf\system\WCF;

$sql = "SELECT rankID, rankTitle
FROM wcf1_user_rank";
$statement = WCF::getDB()->prepare($sql);
$statement->execute();
$titles = $statement->fetchMap('rankID', 'rankTitle');

$sql = "INSERT INTO wcf1_user_rank_content
(rankID, languageID, title)
VALUES (?, ?, ?)";
$statement = WCF::getDB()->prepare($sql);

$languageItems = [];
foreach ($titles as $rankID => $title) {
if (\preg_match('~^wcf\.user\.rank\.\w+$~', $title, $matches)) {
$languageItems[] = $title;

foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
$statement->execute([
$rankID,
$language->languageID,
$language->get($title),
]);
}
} else {
$statement->execute([
$rankID,
LanguageFactory::getInstance()->getDefaultLanguageID(),
$title,
]);
}
}

if ($languageItems !== []) {
$conditionBuilder = new PreparedStatementConditionBuilder();
$conditionBuilder->add('languageItem IN (?)', [$languageItems]);

$sql = "DELETE FROM wcf1_language_item
{$conditionBuilder}";
$statement = WCF::getDB()->prepare($sql);
$statement->execute($conditionBuilder->getParameters());
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading