Skip to content

Commit 55266e0

Browse files
committed
Implement migration support for user condition types
1 parent d6282fe commit 55266e0

11 files changed

+287
-78
lines changed

wcfsetup/install/files/lib/system/condition/ConditionHandler.class.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@
99
use wcf\data\object\type\ObjectTypeCache;
1010
use wcf\system\cache\builder\ConditionCacheBuilder;
1111
use wcf\system\condition\provider\AbstractConditionProvider;
12+
use wcf\system\condition\provider\ConditionMigration;
1213
use wcf\system\condition\type\IConditionType;
14+
use wcf\system\condition\type\IMigrateConditionType;
15+
use wcf\system\database\util\PreparedStatementConditionBuilder;
1316
use wcf\system\exception\SystemException;
1417
use wcf\system\SingletonFactory;
18+
use wcf\system\WCF;
1519

1620
/**
1721
* Handles general condition-related matters.
@@ -172,4 +176,82 @@ public function getConditionsWithFilter(AbstractConditionProvider $provider, arr
172176

173177
return $result;
174178
}
179+
180+
/**
181+
* Exports the conditions for all objects that belong to the specified object type definition.
182+
*
183+
* @return array<int, array<string, mixed>>
184+
*/
185+
public function exportConditions(string $definitionName): array
186+
{
187+
$objectTypes = ObjectTypeCache::getInstance()->getObjectTypes($definitionName);
188+
if ($objectTypes === []) {
189+
return [];
190+
}
191+
192+
$conditionBuilder = new PreparedStatementConditionBuilder();
193+
$conditionBuilder->add('objectTypeID IN (?)', [
194+
\array_map(static fn (ObjectType $objectType): int => $objectType->objectTypeID, $objectTypes),
195+
]);
196+
197+
$sql = "SELECT *
198+
FROM wcf1_condition
199+
{$conditionBuilder}";
200+
$statement = WCF::getDB()->prepare($sql);
201+
$statement->execute($conditionBuilder->getParameters());
202+
203+
$result = [];
204+
while ($row = $statement->fetchArray()) {
205+
$result[$row['objectID']] ??= [];
206+
$result[$row['objectID']][ObjectTypeCache::getInstance()->getObjectType($row['objectTypeID'])->objectType] = \unserialize($row['conditionData']);
207+
}
208+
209+
return $result;
210+
}
211+
212+
/**
213+
* The stored data from the `wcf1_condition` table is migrated to the new format.
214+
* The key of `$conditionData` is the type of condition (objectType), the value is the content of the `wcf1_condition.conditionData` column, unserialize as an array.
215+
*
216+
* @template TCondition of IConditionType
217+
* @param AbstractConditionProvider<TCondition> $provider
218+
* @param array<string, array<string, mixed>> $conditionData
219+
*/
220+
public function migrateConditionData(AbstractConditionProvider $provider, array $conditionData): ConditionMigration
221+
{
222+
if ($conditionData === []) {
223+
return ConditionMigration::forNoConditions();
224+
}
225+
226+
$migratedData = [];
227+
/** @var IMigrateConditionType[] $conditionTypes */
228+
$conditionTypes = \array_filter(
229+
$provider->getConditionTypes(),
230+
static fn (IConditionType $condition): bool => $condition instanceof IMigrateConditionType
231+
);
232+
233+
if ($conditionTypes === []) {
234+
return ConditionMigration::forNoConditions();
235+
}
236+
237+
foreach ($conditionData as $objectType => &$condition) {
238+
foreach ($conditionTypes as $conditionType) {
239+
if (!$conditionType->canMigrateConditionData($objectType)) {
240+
continue;
241+
}
242+
243+
\array_push(
244+
$migratedData,
245+
...$conditionType->migrateConditionData($condition)
246+
);
247+
248+
if ($condition === []) {
249+
unset($conditionData[$objectType]);
250+
break;
251+
}
252+
}
253+
}
254+
255+
return ConditionMigration::for($conditionData, $migratedData);
256+
}
175257
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace wcf\system\condition\provider;
4+
5+
/**
6+
* @author Olaf Braun
7+
* @copyright 2001-2025 WoltLab GmbH
8+
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
9+
* @since 6.2
10+
*/
11+
final class ConditionMigration
12+
{
13+
private function __construct(
14+
public readonly bool $isFullMigrated,
15+
/** @var array{identifier: string, value: mixed}[] */
16+
public readonly array $conditions = [],
17+
) {
18+
}
19+
20+
/**
21+
* Creates a new ConditionMigration instance based on condition data and conditions.
22+
*
23+
* @param array{identifier: string, value: mixed}[] $previousConditionData
24+
* @param array{identifier: string, value: mixed}[] $migratedConditionData
25+
*/
26+
public static function for(array $previousConditionData, array $migratedConditionData): self
27+
{
28+
return new self($previousConditionData === [], $migratedConditionData);
29+
}
30+
31+
/**
32+
* Creates a new ConditionMigration instance for empty data.
33+
*/
34+
public static function forNoConditions(): self
35+
{
36+
return new self(true);
37+
}
38+
}

wcfsetup/install/files/lib/system/condition/provider/UserConditionProvider.class.php

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
use wcf\data\user\UserList;
77
use wcf\event\condition\provider\UserConditionProviderCollecting;
88
use wcf\system\condition\type\IDatabaseObjectListConditionType;
9-
use wcf\system\condition\type\IMigrateConditionType;
109
use wcf\system\condition\type\IObjectConditionType;
1110
use wcf\system\condition\type\user\AbstractUserBooleanConditionType;
1211
use wcf\system\condition\type\user\AbstractUserIntegerConditionType;
@@ -36,8 +35,8 @@ final class UserConditionProvider extends AbstractConditionProvider
3635
public function __construct()
3736
{
3837
$this->addConditions([
39-
new class("username", "username") extends AbstractUserStringConditionType {},
40-
new class("email", "email") extends AbstractUserStringConditionType {},
38+
new class("username", "username", "username", 'com.woltlab.wcf.username') extends AbstractUserStringConditionType {},
39+
new class("email", "email", "email", 'com.woltlab.wcf.email') extends AbstractUserStringConditionType {},
4140
new UserRegistrationDateConditionType(),
4241
new UserRegistrationDaysConditionType(),
4342
new UserInGroupConditionType(),
@@ -63,35 +62,4 @@ public function __construct()
6362
new UserConditionProviderCollecting($this)
6463
);
6564
}
66-
67-
/**
68-
* @param array<string, mixed> $conditionData
69-
*
70-
* @return array{identifier: string, value: mixed}[]
71-
*/
72-
public function migrateConditionData(array $conditionData): array
73-
{
74-
if ($conditionData === []) {
75-
return [];
76-
}
77-
78-
$result = [];
79-
80-
foreach ($this->conditionTypes as $conditionType) {
81-
if (!($conditionType instanceof IMigrateConditionType)) {
82-
continue;
83-
}
84-
85-
\array_push(
86-
$result,
87-
...$conditionType->migrateConditionData($conditionData)
88-
);
89-
90-
if ($conditionData === []) {
91-
break;
92-
}
93-
}
94-
95-
return $result;
96-
}
9765
}

wcfsetup/install/files/lib/system/condition/type/IMigrateConditionType.class.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,6 @@ interface IMigrateConditionType
1818
* @return array{identifier: string, value: mixed}[]
1919
*/
2020
public function migrateConditionData(array &$conditionData): array;
21+
22+
public function canMigrateConditionData(string $objectType): bool;
2123
}

wcfsetup/install/files/lib/system/condition/type/user/AbstractUserIntegerConditionType.class.php

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use wcf\data\user\UserList;
88
use wcf\system\condition\type\AbstractConditionType;
99
use wcf\system\condition\type\IDatabaseObjectListConditionType;
10+
use wcf\system\condition\type\IMigrateConditionType;
1011
use wcf\system\condition\type\IObjectConditionType;
1112
use wcf\system\form\builder\container\PrefixConditionFormFieldContainer;
1213
use wcf\system\form\builder\field\IntegerFormField;
@@ -23,11 +24,12 @@
2324
* @implements IObjectConditionType<User, Filter>
2425
* @extends AbstractConditionType<Filter>
2526
*/
26-
abstract class AbstractUserIntegerConditionType extends AbstractConditionType implements IDatabaseObjectListConditionType, IObjectConditionType
27+
abstract class AbstractUserIntegerConditionType extends AbstractConditionType implements IDatabaseObjectListConditionType, IObjectConditionType, IMigrateConditionType
2728
{
2829
public function __construct(
2930
public readonly string $identifier,
30-
public readonly string $columnName
31+
public readonly string $columnName,
32+
public readonly ?string $migrateConditionObjectType = null,
3133
) {
3234
}
3335

@@ -84,8 +86,39 @@ public function matches(object $object): bool
8486
/**
8587
* @return string[]
8688
*/
87-
private function getConditions(): array
89+
protected function getConditions(): array
8890
{
8991
return ["=", ">", "<", ">=", "<="];
9092
}
93+
94+
#[\Override]
95+
public function canMigrateConditionData(string $objectType): bool
96+
{
97+
return $this->migrateConditionObjectType !== null && $objectType === $this->migrateConditionObjectType;
98+
}
99+
100+
#[\Override]
101+
public function migrateConditionData(array &$conditionData): array
102+
{
103+
$lessThan = $conditionData['lessThan'] ?? null;
104+
$greaterThan = $conditionData['greaterThan'] ?? null;
105+
$conditions = [];
106+
107+
if ($lessThan !== null) {
108+
$conditions[] = [
109+
'identifier' => $this->getIdentifier(),
110+
'value' => ["value" => $lessThan, 'condition' => '<'],
111+
];
112+
}
113+
if ($greaterThan !== null) {
114+
$conditions[] = [
115+
'identifier' => $this->getIdentifier(),
116+
'value' => ["value" => $greaterThan, 'condition' => '>'],
117+
];
118+
}
119+
120+
unset($conditionData['lessThan'], $conditionData['greaterThan']);
121+
122+
return $conditions;
123+
}
91124
}

wcfsetup/install/files/lib/system/condition/type/user/AbstractUserStringConditionType.class.php

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use wcf\data\user\UserList;
88
use wcf\system\condition\type\AbstractConditionType;
99
use wcf\system\condition\type\IDatabaseObjectListConditionType;
10+
use wcf\system\condition\type\IMigrateConditionType;
1011
use wcf\system\condition\type\IObjectConditionType;
1112
use wcf\system\form\builder\container\PrefixConditionFormFieldContainer;
1213
use wcf\system\form\builder\field\SingleSelectionFormField;
@@ -23,11 +24,13 @@
2324
* @implements IObjectConditionType<User, Filter>
2425
* @extends AbstractConditionType<Filter>
2526
*/
26-
abstract class AbstractUserStringConditionType extends AbstractConditionType implements IDatabaseObjectListConditionType, IObjectConditionType
27+
abstract class AbstractUserStringConditionType extends AbstractConditionType implements IDatabaseObjectListConditionType, IObjectConditionType, IMigrateConditionType
2728
{
2829
public function __construct(
2930
public readonly string $identifier,
30-
public readonly string $columnName
31+
public readonly string $columnName,
32+
public readonly ?string $migrateKeyName = null,
33+
public readonly ?string $migrateConditionObjectType = null,
3134
) {
3235
}
3336

@@ -101,4 +104,31 @@ private function getConditions(): array
101104
"%_" => "wcf.condition.endsWith",
102105
];
103106
}
107+
108+
#[\Override]
109+
public function canMigrateConditionData(string $objectType): bool
110+
{
111+
return $this->migrateConditionObjectType !== null && $objectType === $this->migrateConditionObjectType;
112+
}
113+
114+
#[\Override]
115+
public function migrateConditionData(array &$conditionData): array
116+
{
117+
if ($this->migrateKeyName === null || !isset($conditionData[$this->migrateKeyName])) {
118+
return [];
119+
}
120+
121+
$value = $conditionData[$this->migrateKeyName];
122+
unset($conditionData[$this->migrateKeyName]);
123+
124+
return [
125+
[
126+
'identifier' => $this->identifier,
127+
'value' => [
128+
'condition' => "%_%",
129+
'value' => $value,
130+
],
131+
],
132+
];
133+
}
104134
}

wcfsetup/install/files/lib/system/condition/type/user/UserInGroupConditionType.class.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ public function matches(object $object): bool
6969
return \in_array($this->filter, $object->getGroupIDs(), true);
7070
}
7171

72+
#[\Override]
73+
public function canMigrateConditionData(string $objectType): bool
74+
{
75+
return $objectType === 'com.woltlab.wcf.userGroup';
76+
}
77+
7278
#[\Override]
7379
public function migrateConditionData(array &$conditionData): array
7480
{

wcfsetup/install/files/lib/system/condition/type/user/UserNotInGroupConditionType.class.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ public function matches(object $object): bool
6969
return !\in_array($this->filter, $object->getGroupIDs(), true);
7070
}
7171

72+
#[\Override]
73+
public function canMigrateConditionData(string $objectType): bool
74+
{
75+
return $objectType === 'com.woltlab.wcf.userGroup';
76+
}
77+
7278
#[\Override]
7379
public function migrateConditionData(array &$conditionData): array
7480
{

0 commit comments

Comments
 (0)