-
{include file='footer'}
diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.3_styleAuthoredValues.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.3_styleAuthoredValues.php
new file mode 100644
index 00000000000..a53ed774552
--- /dev/null
+++ b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.3_styleAuthoredValues.php
@@ -0,0 +1,17 @@
+prepare($sql);
+$statement->execute(['com.woltlab.wcf']);
diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.3_styleProtection.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.3_styleProtection.php
new file mode 100644
index 00000000000..022acec7c16
--- /dev/null
+++ b/wcfsetup/install/files/acp/update_com.woltlab.wcf_6.3_styleProtection.php
@@ -0,0 +1,32 @@
+prepare($sql);
+$statement->execute([
+ "/* WCF_STYLE_CUSTOM_USER_MODIFICATIONS */\n",
+ 0,
+ '',
+ 'individualScss',
+ 'individualScssDarkMode',
+ 'overrideScss',
+]);
+
+$sql = "UPDATE wcf1_style
+ SET isTainted = ?
+ WHERE isTainted = ?
+ AND packageName = ?";
+$statement = WCF::getDB()->prepare($sql);
+$statement->execute([1, 0, '']);
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Style/Editor.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Style/Editor.js
index ef89d12ef64..99ff57fa542 100644
--- a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Style/Editor.js
+++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Style/Editor.js
@@ -5,14 +5,14 @@
* @copyright 2001-2019 WoltLab GmbH
* @license GNU Lesser General Public License
*/
-define(["require", "exports", "tslib", "../../../Ajax", "../../../Core", "../../../Dom/Util", "../../../Event/Handler", "../../../Ui/Screen"], function (require, exports, tslib_1, Ajax, Core, Util_1, EventHandler, UiScreen) {
+define(["require", "exports", "tslib", "../../../Core", "../../../ColorUtil", "../../../Dom/Util", "../../../Event/Handler", "../../../Ui/Screen"], function (require, exports, tslib_1, Core, ColorUtil, Util_1, EventHandler, UiScreen) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setup = setup;
exports.hideVisualEditor = hideVisualEditor;
exports.showVisualEditor = showVisualEditor;
- Ajax = tslib_1.__importStar(Ajax);
Core = tslib_1.__importStar(Core);
+ ColorUtil = tslib_1.__importStar(ColorUtil);
Util_1 = tslib_1.__importDefault(Util_1);
EventHandler = tslib_1.__importStar(EventHandler);
UiScreen = tslib_1.__importStar(UiScreen);
@@ -22,6 +22,20 @@ define(["require", "exports", "tslib", "../../../Ajax", "../../../Core", "../../
let _isVisible = true;
let _isSmartphone = false;
let _updateRegionMarker;
+ /**
+ * Compares two colors regardless of their notation, e.g. `.8` and `0.8` for the alpha channel.
+ */
+ function isSameColor(a, b) {
+ if (a === b) {
+ return true;
+ }
+ if (!ColorUtil.isValidColor(a) || !ColorUtil.isValidColor(b)) {
+ return false;
+ }
+ const colorA = ColorUtil.stringToRgba(a);
+ const colorB = ColorUtil.stringToRgba(b);
+ return colorA.r === colorB.r && colorA.g === colorB.g && colorA.b === colorB.b && colorA.a === colorB.a;
+ }
/**
* Handles the switch between static and fluid layout.
*/
@@ -74,25 +88,6 @@ define(["require", "exports", "tslib", "../../../Ajax", "../../../Core", "../../
});
}
}
- function handleProtection(styleId) {
- const button = document.getElementById("styleDisableProtectionSubmit");
- const checkbox = document.getElementById("styleDisableProtectionConfirm");
- checkbox.addEventListener("change", () => {
- button.disabled = !checkbox.checked;
- });
- button.addEventListener("click", () => {
- Ajax.apiOnce({
- data: {
- actionName: "markAsTainted",
- className: "wcf\\data\\style\\StyleAction",
- objectIDs: [styleId],
- },
- success: () => {
- window.location.reload();
- },
- });
- });
- }
/**
* Mirrors the selected style options in the preview, e.g. `data-page-header-layout`.
*/
@@ -183,10 +178,24 @@ define(["require", "exports", "tslib", "../../../Ajax", "../../../Core", "../../
const wrapper = document.getElementById("spVariablesWrapper");
wrapper.querySelectorAll(".styleVariableColor").forEach((colorField) => {
const variableName = colorField.dataset.store.replace(/_value$/, "");
+ const input = document.getElementById(colorField.dataset.store);
+ const restoreButton = wrapper.querySelector(`.spColorRestore[data-store="${colorField.dataset.store}"]`);
+ const updateRestoreButton = () => {
+ if (restoreButton !== null) {
+ restoreButton.hidden = isSameColor(input.value, restoreButton.dataset.authoredColor);
+ }
+ };
+ restoreButton?.addEventListener("click", () => {
+ const authoredColor = restoreButton.dataset.authoredColor;
+ input.value = authoredColor;
+ colorField.dataset.color = authoredColor;
+ colorField.style.setProperty("background-color", authoredColor);
+ });
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === "style") {
spWindow.style.setProperty(`--${variableName}`, colorField.style.getPropertyValue("background-color"));
+ updateRestoreButton();
}
});
});
@@ -194,6 +203,7 @@ define(["require", "exports", "tslib", "../../../Ajax", "../../../Core", "../../
attributes: true,
});
spWindow.style.setProperty(`--${variableName}`, colorField.style.getPropertyValue("background-color"));
+ updateRestoreButton();
});
// category selection by clicking on the area
const buttonToggleColorPalette = document.querySelector(".jsButtonToggleColorPalette");
@@ -242,9 +252,6 @@ define(["require", "exports", "tslib", "../../../Ajax", "../../../Core", "../../
function setup(options) {
handleLayoutWidth();
handleScss(options.isTainted);
- if (!options.isTainted) {
- handleProtection(options.styleId);
- }
initVisualEditor();
handleStyleOptions();
UiScreen.on("screen-sm-down", {
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Color/Picker.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Color/Picker.js
index 4aac61b166a..14d1ec3ad06 100644
--- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Color/Picker.js
+++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Color/Picker.js
@@ -143,6 +143,9 @@ define(["require", "exports", "tslib", "../../Core", "../Dialog", "../../Dom/Uti
this.opacityInput.addEventListener("input", (event) => {
this.setOpacity(event);
});
+ },
+ onShow: () => {
+ // The stored value can change while the dialog is closed.
if (ColorUtil.isValidColor(this.input.value)) {
this.setInitialColor(this.input.value);
}
@@ -152,8 +155,6 @@ define(["require", "exports", "tslib", "../../Core", "../Dialog", "../../Dom/Uti
else {
this.setInitialColor("#FFF0");
}
- },
- onShow: () => {
this.colorTextInput.focus();
this.colorTextInput.select();
},
diff --git a/wcfsetup/install/files/lib/acp/form/StyleEditForm.class.php b/wcfsetup/install/files/lib/acp/form/StyleEditForm.class.php
index 180c96c5a3a..79db9085d80 100644
--- a/wcfsetup/install/files/lib/acp/form/StyleEditForm.class.php
+++ b/wcfsetup/install/files/lib/acp/form/StyleEditForm.class.php
@@ -3,6 +3,8 @@
namespace wcf\acp\form;
use wcf\acp\page\StyleListPage;
+use wcf\data\package\Package;
+use wcf\data\package\PackageCache;
use wcf\data\style\Style;
use wcf\data\style\StyleAction;
use wcf\data\style\StyleEditor;
@@ -65,6 +67,23 @@ public function readParameters()
parent::readParameters();
}
+ #[\Override]
+ public function readFormParameters()
+ {
+ parent::readFormParameters();
+
+ // The fields are only marked as `readonly` for protected styles.
+ if ($this->style->isTainted === 0) {
+ $this->authorName = $this->style->authorName;
+ $this->authorURL = $this->style->authorURL;
+ $this->copyright = $this->style->copyright;
+ $this->license = $this->style->license;
+ $this->packageName = $this->style->packageName;
+ $this->styleDate = $this->style->styleDate;
+ $this->styleVersion = $this->style->styleVersion;
+ }
+ }
+
#[\Override]
public function validate()
{
@@ -435,6 +454,10 @@ public function assignVariables()
'style' => $this->style,
'styleID' => $this->styleID,
'isDarkMode' => $this->isDarkMode,
+ 'authoredVariables' => $this->getAuthoredVariables(),
+ 'stylePackage' => $this->getStylePackage(),
+ 'canViewStylePackage' => WCF::getSession()->hasPermission('admin.configuration.package.canInstallPackage')
+ && WCF::getSession()->hasPermission('admin.configuration.package.canUpdatePackage'),
'interactionContextMenu' => StandaloneInteractionContextMenuComponent::forContentHeaderButton(
new StyleInteractions(),
$this->style,
@@ -442,4 +465,51 @@ public function assignVariables()
),
]);
}
+
+ /**
+ * Returns the package that updates this style. Imported styles carry the
+ * Core's package id, but are only updated by importing them again.
+ */
+ private function getStylePackage(): ?Package
+ {
+ if ($this->style->isTainted !== 0) {
+ return null;
+ }
+
+ $package = PackageCache::getInstance()->getPackage($this->style->packageID);
+ if ($package === null || ($package->packageID === 1 && $this->style->packageName !== 'com.woltlab.wcf')) {
+ return null;
+ }
+
+ return $package;
+ }
+
+ /**
+ * Returns the authored values of the variables edited in the current mode.
+ * The dark mode edits the light value of variables without dark mode support.
+ *
+ * @return array
+ */
+ private function getAuthoredVariables(): array
+ {
+ $authoredVariables = $this->style->getAuthoredVariables();
+ $supportsDarkMode = $this->isDarkMode ? Style::getVariablesWithDarkModeSupport() : [];
+
+ $variables = [];
+ foreach ($authoredVariables as $variableName => $value) {
+ if (\str_starts_with($variableName, Style::DARK_MODE_PREFIX)) {
+ continue;
+ }
+
+ if (\in_array($variableName, $supportsDarkMode, true)) {
+ $value = $authoredVariables[Style::DARK_MODE_PREFIX . $variableName] ?? null;
+ }
+
+ if ($value !== null) {
+ $variables[$variableName] = $value;
+ }
+ }
+
+ return $variables;
+ }
}
diff --git a/wcfsetup/install/files/lib/command/style/CopyStyle.class.php b/wcfsetup/install/files/lib/command/style/CopyStyle.class.php
index e1eb20a5100..03f25223b34 100644
--- a/wcfsetup/install/files/lib/command/style/CopyStyle.class.php
+++ b/wcfsetup/install/files/lib/command/style/CopyStyle.class.php
@@ -55,6 +55,8 @@ private function copyStyle(Style $style): Style
'coverPhotoExtension' => $style->coverPhotoExtension,
'hasFavicon' => $style->hasFavicon,
'hasDarkMode' => $style->hasDarkMode,
+ // A copy is never updated, protecting it would only lock the preset declarations.
+ 'isTainted' => 1,
]);
}
@@ -89,14 +91,31 @@ private function handleI18n(Style $style): void
private function copyVariables(Style $source, Style $destination): void
{
$sql = "INSERT INTO wcf1_style_variable_value
- (styleID, variableID, variableValue, variableValueDarkMode)
- SELECT ?, variableID, variableValue, variableValueDarkMode
+ (styleID, variableID, variableValue, variableValueDarkMode, authoredValue, authoredValueDarkMode)
+ SELECT ?, variableID, variableValue, variableValueDarkMode, authoredValue, authoredValueDarkMode
FROM wcf1_style_variable_value
WHERE styleID = ?
ON DUPLICATE KEY UPDATE variableValue = VALUES(variableValue),
- variableValueDarkMode = VALUES(variableValueDarkMode)";
+ variableValueDarkMode = VALUES(variableValueDarkMode),
+ authoredValue = VALUES(authoredValue),
+ authoredValueDarkMode = VALUES(authoredValueDarkMode)";
$statement = WCF::getDB()->prepare($sql);
$statement->execute([$destination->styleID, $source->styleID]);
+
+ $sql = "UPDATE wcf1_style_variable_value style_variable_value
+ INNER JOIN wcf1_style_variable style_variable
+ ON style_variable.variableID = style_variable_value.variableID
+ SET style_variable_value.variableValue = REPLACE(style_variable_value.variableValue, ?, '')
+ WHERE style_variable_value.styleID = ?
+ AND style_variable.variableName IN (?, ?, ?)";
+ $statement = WCF::getDB()->prepare($sql);
+ $statement->execute([
+ "/* WCF_STYLE_CUSTOM_USER_MODIFICATIONS */\n",
+ $destination->styleID,
+ 'individualScss',
+ 'individualScssDarkMode',
+ 'overrideScss',
+ ]);
}
private function copyAssets(Style $source, Style $destination): void
diff --git a/wcfsetup/install/files/lib/data/style/Style.class.php b/wcfsetup/install/files/lib/data/style/Style.class.php
index bca3526e7f1..9659303495f 100644
--- a/wcfsetup/install/files/lib/data/style/Style.class.php
+++ b/wcfsetup/install/files/lib/data/style/Style.class.php
@@ -130,6 +130,37 @@ public function getVariables()
return $this->variables;
}
+ /**
+ * Returns the values shipped by the author of this style, the values for
+ * the dark mode are prefixed with `DARK_MODE_PREFIX`. Variables without a
+ * known value are omitted.
+ *
+ * @return array
+ * @since 6.3
+ */
+ public function getAuthoredVariables(): array
+ {
+ $sql = "SELECT variable.variableName, value.authoredValue, value.authoredValueDarkMode
+ FROM wcf1_style_variable_value value
+ INNER JOIN wcf1_style_variable variable
+ ON variable.variableID = value.variableID
+ WHERE value.styleID = ?";
+ $statement = WCF::getDB()->prepare($sql);
+ $statement->execute([$this->styleID]);
+
+ $variables = [];
+ while ($row = $statement->fetchArray()) {
+ if ($row['authoredValue'] !== null) {
+ $variables[$row['variableName']] = $row['authoredValue'];
+ }
+ if ($row['authoredValueDarkMode'] !== null) {
+ $variables[self::DARK_MODE_PREFIX . $row['variableName']] = $row['authoredValueDarkMode'];
+ }
+ }
+
+ return $variables;
+ }
+
/**
* Returns a specific style variable or null if not found.
* If $toHex is set to true the color defined by the variable
diff --git a/wcfsetup/install/files/lib/data/style/StyleAction.class.php b/wcfsetup/install/files/lib/data/style/StyleAction.class.php
index dbcb5f977fc..e5dd06a7556 100644
--- a/wcfsetup/install/files/lib/data/style/StyleAction.class.php
+++ b/wcfsetup/install/files/lib/data/style/StyleAction.class.php
@@ -586,54 +586,20 @@ public function getStyleChooser()
}
/**
- * Validates the mark as tainted action.
- *
* @return void
+ * @deprecated 6.3 The protection of a style can no longer be lifted.
*/
public function validateMarkAsTainted()
{
- if (!WCF::getSession()->hasPermission('admin.style.canManageStyle')) {
- throw new PermissionDeniedException();
- }
-
- $this->styleEditor = $this->getSingleObject();
+ throw new PermissionDeniedException();
}
/**
- * Marks a style as tainted.
+ * @deprecated 6.3 The protection of a style can no longer be lifted.
*/
public function markAsTainted(): void
{
- // merge definitions
- $variables = $this->styleEditor->getVariables();
- $variables['individualScss'] = \str_replace(
- "/* WCF_STYLE_CUSTOM_USER_MODIFICATIONS */\n",
- '',
- $variables['individualScss']
- );
- $variables['overrideScss'] = \str_replace(
- "/* WCF_STYLE_CUSTOM_USER_MODIFICATIONS */\n",
- '',
- $variables['overrideScss']
- );
-
- $variablesDarkMode = [];
- $variables = \array_filter($variables, static function ($value, $key) use (&$variablesDarkMode) {
- if (!\str_starts_with($key, Style::DARK_MODE_PREFIX)) {
- return true;
- }
-
- $variablesDarkMode[\str_replace(Style::DARK_MODE_PREFIX, '', $key)] = $value;
-
- return false;
- }, \ARRAY_FILTER_USE_BOTH);
-
- $this->styleEditor->setVariables($variables, $variablesDarkMode);
-
- $this->styleEditor->update([
- 'isTainted' => 1,
- 'packageName' => '',
- ]);
+ throw new PermissionDeniedException();
}
/**
diff --git a/wcfsetup/install/files/lib/data/style/StyleEditor.class.php b/wcfsetup/install/files/lib/data/style/StyleEditor.class.php
index 31b8afb30f4..18a36480015 100644
--- a/wcfsetup/install/files/lib/data/style/StyleEditor.class.php
+++ b/wcfsetup/install/files/lib/data/style/StyleEditor.class.php
@@ -645,14 +645,24 @@ public static function import(string $filename, int $packageID = 1, ?self $style
$importedOptions[$variableName] = $option::tryFromString($styleData['variables'][$variableName] ?? '');
}
+ $authoredVariables = $styleData['variables'];
+ foreach (Style::getAvailableOptions() as $option) {
+ $variableName = $option::getVariableName();
+ $authoredVariables[$variableName] = ($importedOptions[$variableName] ?? $option::getFallback())->toString();
+ }
+
+ $authoredVariablesDarkMode = null;
+ if ($styleData['hasDarkMode'] === 1) {
+ $authoredVariablesDarkMode = \is_array($styleData['variablesDarkMode']) ? $styleData['variablesDarkMode'] : [];
+ }
+
// save style
if ($style === null) {
- foreach (Style::getAvailableOptions() as $option) {
- $variableName = $option::getVariableName();
- $styleData['variables'][$variableName] = ($importedOptions[$variableName] ?? $option::getFallback())->toString();
- }
-
+ $styleData['variables'] = $authoredVariables;
$styleData['packageID'] = $packageID;
+ // A style without a package name is never updated, protecting it
+ // would only lock the preset declarations.
+ $styleData['isTainted'] = $styleData['packageName'] === '' ? 1 : 0;
$style = new self(self::create($styleData));
// handle descriptions
@@ -723,6 +733,9 @@ public static function import(string $filename, int $packageID = 1, ?self $style
$style->update($styleData);
}
+ // An update keeps the installed values, but records the ones it ships.
+ $style->setAuthoredVariables($authoredVariables, $authoredVariablesDarkMode);
+
// import images
if (!empty($data['images'])) {
$index = $tar->getIndexByFilename($data['images']);
@@ -1299,6 +1312,43 @@ public function setVariables(array $variables, array $variablesDarkMode): void
StyleHandler::getInstance()->resetStylesheet($this->getDecoratedObject());
}
+ /**
+ * Records the values the style author shipped. Variables missing from the
+ * archive store the current default, because a later change of the default
+ * must not alter what the author shipped.
+ *
+ * @param array $variables
+ * @param ?array $variablesDarkMode `null` if the archive has no dark mode
+ */
+ private function setAuthoredVariables(array $variables, ?array $variablesDarkMode): void
+ {
+ $sql = "SELECT variableID, variableName, defaultValue, defaultValueDarkMode
+ FROM wcf1_style_variable";
+ $statement = WCF::getDB()->prepare($sql);
+ $statement->execute();
+ $styleVariables = $statement->fetchAll(\PDO::FETCH_ASSOC);
+
+ $sql = "INSERT INTO wcf1_style_variable_value
+ (styleID, variableID, authoredValue, authoredValueDarkMode)
+ VALUES (?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE authoredValue = VALUES(authoredValue),
+ authoredValueDarkMode = VALUES(authoredValueDarkMode)";
+ $statement = WCF::getDB()->prepare($sql);
+
+ WCF::getDB()->beginTransaction();
+ foreach ($styleVariables as $row) {
+ $variableName = $row['variableName'];
+
+ $statement->execute([
+ $this->styleID,
+ $row['variableID'],
+ $variables[$variableName] ?? $row['defaultValue'],
+ $variablesDarkMode === null ? null : ($variablesDarkMode[$variableName] ?? $row['defaultValueDarkMode']),
+ ]);
+ }
+ WCF::getDB()->commitTransaction();
+ }
+
/**
* Writes the style-*.css file.
*/
diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml
index 8eef1973ee0..46cd0dbefd7 100644
--- a/wcfsetup/install/lang/de.xml
+++ b/wcfsetup/install/lang/de.xml
@@ -2898,6 +2898,7 @@ Abschnitte dürfen nicht leer sein und nur folgende Zeichen enthalten: [a-z
+
@@ -2957,11 +2958,8 @@ Abschnitte dürfen nicht leer sein und nur folgende Zeichen enthalten: [a-z
- Schutz aufheben.]]>
-
- Dieser Schritt wird nicht empfohlen und ist grundsätzlich nicht notwendig, Stile können weiterhin nach belieben angepasst werden, nur die Vorlage selbst kann nicht verändert werden.
Bitte {if LANGUAGE_USE_INFORMAL_VARIANT}speicher{else}speichern Sie{/if} eventuelle vorgenommene Änderungen bevor {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} diesen Schritt {if LANGUAGE_USE_INFORMAL_VARIANT}ausführst{else}ausführen{/if}.]]>
+ getLink()}">{$stylePackage->getTitle()}{else}„{$stylePackage->getTitle()}“{/if} bereitgestellt und mit diesem aktualisiert.{elseif $style->packageName !== ''}Dieser Stil wurde importiert und wird durch den Import einer neueren Version aktualisiert.{else}Dieser Stil ist geschützt und kann nur eingeschränkt verändert werden.{/if}]]>
-
@@ -5867,5 +5865,8 @@ Erlaubte Dateiendungen: {', '|implode:$allowedFileExtensions}]]>
+
+
+
diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml
index 2397762b774..616f00a23da 100644
--- a/wcfsetup/install/lang/en.xml
+++ b/wcfsetup/install/lang/en.xml
@@ -2827,6 +2827,7 @@ If you have already bought the licenses for the listed apps, th
+
@@ -2887,11 +2888,8 @@ If you have already bought the licenses for the listed apps, th
- disable this protection.]]>
-
- It is neither recommended nor necessary to remove the protection in most cases, styles can still be fully customized while preserving the preset declarations.
Please save any unsaved changes before proceeding.]]>
+ getLink()}">{$stylePackage->getTitle()}{else}“{$stylePackage->getTitle()}”{/if} and receives updates with it.{elseif $style->packageName !== ''}This style was imported and receives updates by importing a newer version of it.{else}This style is protected and editing is limited.{/if}]]>
-
@@ -5869,5 +5867,8 @@ Allowed extensions: {', '|implode:$allowedFileExtensions}]]>
+
+
+
diff --git a/wcfsetup/setup/db/install_com.woltlab.wcf.php b/wcfsetup/setup/db/install_com.woltlab.wcf.php
index 3f99455bd7d..99b787a0f41 100644
--- a/wcfsetup/setup/db/install_com.woltlab.wcf.php
+++ b/wcfsetup/setup/db/install_com.woltlab.wcf.php
@@ -3625,6 +3625,8 @@
NotNullInt10DatabaseTableColumn::create('variableID'),
MediumtextDatabaseTableColumn::create('variableValue'),
MediumtextDatabaseTableColumn::create('variableValueDarkMode'),
+ MediumtextDatabaseTableColumn::create('authoredValue'),
+ MediumtextDatabaseTableColumn::create('authoredValueDarkMode'),
])
->indices([
DatabaseTableIndex::create('styleID')