Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5aa8b34
Add IMigrateConditionType interface and implement migration methods i…
Cyperghost Jun 5, 2025
b23c9da
Implement migration support for user condition types
Cyperghost Jun 10, 2025
f2e7e53
Add migration support for user group assignment conditions
Cyperghost Jun 10, 2025
d6d64ea
Use a unique structure for the object types for migration.
Cyperghost Jun 18, 2025
d341eeb
Rename 'needMigration' to 'isLegacy'
Cyperghost Jun 29, 2025
b580699
Apply suggestions from code review
Cyperghost Jun 29, 2025
470ccc9
Merge remote-tracking branch 'origin/6.3-condition-migrate' into 6.3-…
Cyperghost Jun 29, 2025
494f1b2
Refactor `ConditionMigration`
Cyperghost Jun 29, 2025
5daa363
Refactor migration condition checks for consistency
Cyperghost Jun 29, 2025
c897624
Refactor condition data migration logic to improve null handling
Cyperghost Jun 29, 2025
6aeb7e3
Migrate user condition types to use named parameters for improved rea…
Cyperghost Jun 29, 2025
c341d5b
Add legacy notice for automatic user group assignments and implement …
Cyperghost Jun 29, 2025
7716d1d
Rename `UserGroupAssignmentMigrateCondition` to `MigrateLegacyCondition`
Cyperghost Jun 29, 2025
7458aa7
Fix typo in `$field`
Cyperghost Jun 29, 2025
a72f4ee
Add error handling for JSON decoding in condition migration
Cyperghost Jun 29, 2025
0361a8a
Fix incorrect usage of unset function in ConditionHandler
Cyperghost Jun 29, 2025
ed94ef7
Use `DateTimeImmutable` instead of `\DateTime`
Cyperghost Jun 29, 2025
80ef76e
Expand the PHPDoc for methods in `IMigrateConditionType`
Cyperghost Jun 30, 2025
e238ed5
Apply suggestions from code review
Cyperghost Jul 7, 2025
9200137
Add a status box if content needs to be recalculated using Rebuild Data.
Cyperghost Jul 7, 2025
eb4e3d8
Invert check if migrations needed
Cyperghost Jul 10, 2025
57ff24f
Avoid inheriting from concrete implementations
dtdesign Jul 10, 2025
b160c1c
Fix the class naming to be consistent
dtdesign Jul 10, 2025
770801e
`\strtolower()` is not binary-safe
dtdesign Jul 10, 2025
ed0f57b
Fix the handling of condition types
dtdesign Jul 10, 2025
f99335a
Mark the abstract user conditions as non-abstract
dtdesign Jul 10, 2025
4acb7a3
Register the conditions sequentually
dtdesign Jul 10, 2025
388be41
Fix the direction of the comparison
dtdesign Jul 10, 2025
d1c8eb1
Simplify the condition names
dtdesign Jul 11, 2025
3c7a46c
Rename the class to the correct name
Cyperghost Jul 11, 2025
c54f1b5
Adding language variable
Cyperghost Jul 11, 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
7 changes: 3 additions & 4 deletions com.woltlab.wcf/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,8 @@
</instructions>

<!--
Required order of the following steps for the update to 6.2:
<instruction type="database" run="standalone">acp/database/update_com.woltlab.wcf_62_step1.php</instruction>
<instruction type="script">acp/update_com.woltlab.wcf_6.2_contactOptions.php</instruction>
<instruction type="database" run="standalone">acp/database/update_com.woltlab.wcf_62_step2.php</instruction>
Required order of the following steps for the update to 6.3:
<instruction type="database" run="standalone">acp/database/update_com.woltlab.wcf_6.3_step1.php</instruction>
<instruction type="script">acp/update_com.woltlab.wcf_6.3_userGroupAssignment.php</instruction>
-->
</package>
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
*/

use wcf\system\database\table\column\DefaultFalseBooleanDatabaseTableColumn;
use wcf\system\database\table\column\MediumtextDatabaseTableColumn;
use wcf\system\database\table\PartialDatabaseTable;

return [
PartialDatabaseTable::create('wcf1_user_group_assignment')
->columns([
MediumtextDatabaseTableColumn::create('conditions'),
DefaultFalseBooleanDatabaseTableColumn::create('isLegacy'),
]),
];
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
</nav>
</header>

{if $hasLegacyObjects}
<woltlab-core-notice type="warning">
{lang}wcf.acp.group.assignment.legacyNotice{/lang}
</woltlab-core-notice>
{/if}

<div class="section">
{unsafe:$gridView->render()}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

use wcf\system\condition\ConditionHandler;
use wcf\system\WCF;
use wcf\util\JSON;

$exportedConditions = ConditionHandler::getInstance()->exportConditions("com.woltlab.wcf.condition.userGroupAssignment");
if ($exportedConditions === []) {
return;
}

$sql = "UPDATE wcf1_user_group_assignment
SET conditions = ?,
isLegacy = ?
WHERE assignmentID = ?";
$statement = WCF::getDB()->prepare($sql);
foreach ($exportedConditions as $assignmentID => $conditionData) {
renameObjectTypes($conditionData);

$statement->execute([
JSON::encode($conditionData),
1,
$assignmentID,
]);
}

/**
* Rename the object types so that the migration functions can handle them.
* @see \wcf\system\condition\provider\UserConditionProvider
*
* @param array<string, mixed> $conditionData
*/
function renameObjectTypes(array &$conditionData): void
{
$objectTypeMap = [
'com.woltlab.wcf.username' => 'com.woltlab.wcf.user.username',
'com.woltlab.wcf.email' => 'com.woltlab.wcf.user.email',
'com.woltlab.wcf.userGroup' => 'com.woltlab.wcf.user.userGroup',
'com.woltlab.wcf.languages' => 'com.woltlab.wcf.user.languages',
'com.woltlab.wcf.registrationDate' => 'com.woltlab.wcf.user.registrationDate',
'com.woltlab.wcf.registrationDateInterval' => 'com.woltlab.wcf.user.registrationDateInterval',
'com.woltlab.wcf.avatar' => 'com.woltlab.wcf.user.avatar',
'com.woltlab.wcf.signature' => 'com.woltlab.wcf.user.signature',
'com.woltlab.wcf.coverPhoto' => 'com.woltlab.wcf.user.coverPhoto',
'com.woltlab.wcf.state' => 'com.woltlab.wcf.user.state',
'com.woltlab.wcf.activityPoints' => 'com.woltlab.wcf.user.activityPoints',
'com.woltlab.wcf.likesReceived' => 'com.woltlab.wcf.user.likesReceived',
// TODO 'com.woltlab.wcf.userOptions'
'com.woltlab.wcf.userTrophyCondition' => 'com.woltlab.wcf.user.trophyCondition',
'com.woltlab.wcf.trophyPoints' => 'com.woltlab.wcf.user.trophyPoints',
];

foreach ($objectTypeMap as $currentName => $newName) {
if (isset($conditionData[$currentName])) {
$conditionData[$newName] = $conditionData[$currentName];
unset($conditionData[$currentName]);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

use wcf\data\user\group\assignment\UserGroupAssignment;
use wcf\system\exception\IllegalLinkException;
use wcf\system\exception\NamedUserException;
use wcf\system\WCF;
use wcf\util\HtmlString;

/**
* Shows the form to edit an existing automatic user group assignment.
Expand Down Expand Up @@ -39,5 +42,11 @@ public function readParameters()
if (!$this->formObject->assignmentID) {
throw new IllegalLinkException();
}

if ($this->formObject->isLegacy) {
throw new NamedUserException(
HtmlString::fromSafeHtml(WCF::getLanguage()->getDynamicVariable('wcf.acp.group.assignment.legacyNotice'))
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use wcf\page\AbstractGridViewPage;
use wcf\system\gridView\admin\UserGroupAssignmentGridView;
use wcf\system\WCF;

/**
* Lists the available automatic user group assignments.
Expand Down Expand Up @@ -31,4 +32,25 @@ protected function createGridView(): UserGroupAssignmentGridView
{
return new UserGroupAssignmentGridView();
}

#[\Override]
public function assignVariables()
{
parent::assignVariables();

WCF::getTPL()->assign([
'hasLegacyObjects' => $this->hasLegacyObjects(),
]);
}

private function hasLegacyObjects(): bool
{
$sql = "SELECT COUNT(*) AS count
FROM wcf1_user_group_assignment
WHERE isLegacy = ?";
$statement = WCF::getDB()->prepare($sql);
$statement->execute([1]);

return $statement->fetchColumn() > 0;
}
}
1 change: 1 addition & 0 deletions wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ static function (\wcf\event\worker\RebuildWorkerCollecting $event) {
$event->register(\wcf\system\worker\UnfurlUrlRebuildDataWorker::class, 450);
$event->register(\wcf\system\worker\FileRebuildDataWorker::class, 475);
$event->register(\wcf\system\worker\SitemapRebuildWorker::class, 500);
$event->register(\wcf\system\worker\UserGroupAssignmentRebuildDataWorker::class, 600);
$event->register(\wcf\system\worker\StatDailyRebuildDataWorker::class, 800);
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

use wcf\data\DatabaseObjectEditor;
use wcf\data\IEditableCachedObject;
use wcf\system\cache\builder\UserGroupAssignmentCacheBuilder;
use wcf\system\cache\builder\UserGroupCacheBuilder;
use wcf\system\cache\builder\UserGroupPermissionCacheBuilder;
use wcf\system\cache\eager\UserGroupAssignmentCache;
use wcf\system\exception\SystemException;
use wcf\system\user\storage\UserStorageHandler;
use wcf\system\WCF;
Expand Down Expand Up @@ -208,7 +208,7 @@ public static function resetCache()
UserGroupPermissionCacheBuilder::getInstance()->reset();

// https://github.com/WoltLab/WCF/issues/4045
UserGroupAssignmentCacheBuilder::getInstance()->reset();
(new UserGroupAssignmentCache())->rebuild();

// Clear cached group assignments.
UserStorageHandler::getInstance()->resetAll('groupIDs');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* @property-read string $title title of the automatic user group assignment
* @property-read int $isDisabled is `1` if the user group assignment is disabled and thus not checked for automatic assignments, otherwise `0`
* @property-read string $conditions JSON-encoded string containing the conditions of the automatic user group assignment
* @property-read bool $isLegacy indicates whether the conditions need to be migrated to the new format
*/
class UserGroupAssignment extends DatabaseObject implements IRouteController
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use wcf\data\DatabaseObjectEditor;
use wcf\data\IEditableCachedObject;
use wcf\system\cache\builder\UserGroupAssignmentCacheBuilder;
use wcf\system\cache\eager\UserGroupAssignmentCache;

/**
* Executes user group assignment-related actions.
Expand All @@ -29,6 +29,6 @@ class UserGroupAssignmentEditor extends DatabaseObjectEditor implements IEditabl
*/
public static function resetCache()
{
UserGroupAssignmentCacheBuilder::getInstance()->reset();
(new UserGroupAssignmentCache())->rebuild();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace wcf\event\acp\dashboard\box;

use wcf\event\IPsr14Event;

/**
* Requests the collection of objects that still need to be migrated
*
* @author Olaf Braun
* @copyright 2001-2025 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.2
*/
final class MigrationCollecting implements IPsr14Event
{
/**
* @var string[]
*/
private array $needsMigration = [];

/**
* Adds the name of objects that still need to be migrated on the `RebuildDataPage`
*/
public function migrationNeeded(string $title): void
{
$this->needsMigration[] = $title;
}

/**
* @return string[]
*/
public function needsMigration(): array
{
return $this->needsMigration;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace wcf\system\acp\dashboard\box;

use wcf\data\devtools\missing\language\item\DevtoolsMissingLanguageItemList;
use wcf\event\acp\dashboard\box\MigrationCollecting;
use wcf\event\acp\dashboard\box\PHPExtensionCollecting;
use wcf\event\acp\dashboard\box\StatusMessageCollecting;
use wcf\system\application\ApplicationHandler;
Expand Down Expand Up @@ -60,6 +61,7 @@ private function getMessages(): array
$this->getPHPExtensionMessage(),
$this->getEvaluationMessages(),
$this->getBasicMessages(),
$this->getMigrationMessage(),
$this->getCustomMessages()
);
}
Expand Down Expand Up @@ -271,4 +273,42 @@ private function getPHPExtensionMessage(): array

return [];
}

/**
* @return StatusMessage[]
*
* @since 6.2
*/
private function getMigrationMessage(): array
{
$event = new MigrationCollecting();
EventHandler::getInstance()->fire($event);
if ($this->userGroupAssignmentHasLegacyObjects()) {
$event->migrationNeeded(WCF::getLanguage()->get('wcf.acp.group.assignment'));
}

if ($event->needsMigration() === []) {
return [];
}

return [
new StatusMessage(
StatusMessageType::Warning,
WCF::getLanguage()->getDynamicVariable('wcf.acp.dashboard.box.migrationNeeded', [
'titles' => $event->needsMigration(),
])
),
];
}

private function userGroupAssignmentHasLegacyObjects(): bool
{
$sql = "SELECT COUNT(*) AS count
FROM wcf1_user_group_assignment
WHERE isLegacy = ?";
$statement = WCF::getDB()->prepare($sql);
$statement->execute([1]);

return $statement->fetchColumn() > 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,28 @@

namespace wcf\system\cache\builder;

use wcf\data\user\group\assignment\UserGroupAssignmentList;
use wcf\system\cache\eager\UserGroupAssignmentCache;

/**
* Caches the enabled automatic user group assignments.
*
* @author Matthias Schmidt
* @copyright 2001-2019 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
*
* @deprecated 6.2 use `UserGroupAssignmentCache` instead
*/
class UserGroupAssignmentCacheBuilder extends AbstractCacheBuilder
final class UserGroupAssignmentCacheBuilder extends AbstractLegacyCacheBuilder
{
/**
* @inheritDoc
*/
protected function rebuild(array $parameters)
#[\Override]
protected function rebuild(array $parameters): array
{
$assignmentList = new UserGroupAssignmentList();
$assignmentList->getConditionBuilder()->add('isDisabled = ?', [0]);
$assignmentList->readObjects();
return (new UserGroupAssignmentCache())->getCache();
}

return $assignmentList->getObjects();
#[\Override]
public function reset(array $parameters = [])
{
(new UserGroupAssignmentCache())->rebuild();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace wcf\system\cache\eager;

use wcf\data\user\group\assignment\UserGroupAssignment;
use wcf\data\user\group\assignment\UserGroupAssignmentList;

/**
* Caches the enabled automatic user group assignments.
*
* @author Olaf Braun
* @copyright 2001-2025 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.3
*
* @extends AbstractEagerCache<array<int, UserGroupAssignment>>
*/
final class UserGroupAssignmentCache extends AbstractEagerCache
{
#[\Override]
protected function getCacheData(): array
{
$assignmentList = new UserGroupAssignmentList();
$assignmentList->getConditionBuilder()->add('isDisabled = ?', [0]);
$assignmentList->getConditionBuilder()->add('isLegacy = ?', [0]);
$assignmentList->readObjects();

return $assignmentList->getObjects();
}
}
Loading
Loading