Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ class Instructions {
event.preventDefault();
event.stopPropagation();

const instructionsId = ((event.currentTarget as HTMLElement).closest("li.section") as HTMLElement).dataset
.instructionsId!;
const section = (event.currentTarget as HTMLElement).closest("li.section") as HTMLElement;
const instructionsId = section.dataset.instructionsId!;

// note: data will be validated/filtered by the server

Expand All @@ -140,6 +140,10 @@ class Instructions {
return;
}

if (!this.validateVoidUsage(pipField, section, pipField.value, null)) {
return;
}

const valueField = document.getElementById(
`${this.formFieldId}_instructions${instructionsId}_value`,
) as HTMLInputElement;
Expand Down Expand Up @@ -367,8 +371,64 @@ class Instructions {
// toggle application selector
this.toggleApplicationFormField(instructionsId);

const value = document.getElementById(`${this.formFieldId}_instructions${instructionsId}_value`)!;
value.focus();
// hide value/runStandalone fields when the void instruction is selected
this.toggleValueAndRunStandaloneFormFields(instructionsId, pip !== "void");

if (pip !== "void") {
const value = document.getElementById(`${this.formFieldId}_instructions${instructionsId}_value`)!;
value.focus();
}
}

/**
* Toggles the visibility of the value and runStandalone form fields based on the selected pip.
*/
protected toggleValueAndRunStandaloneFormFields(instructionsId: InstructionsId, show: boolean): void {
const valueDl = document.getElementById(`${this.formFieldId}_instructions${instructionsId}_value`)!.closest("dl")!;
const runStandaloneDl = document
.getElementById(`${this.formFieldId}_instructions${instructionsId}_runStandalone`)!
.closest("dl")!;

if (show) {
DomUtil.show(valueDl);
DomUtil.show(runStandaloneDl);
} else {
DomUtil.hide(valueDl);
DomUtil.hide(runStandaloneDl);
}
}

/**
* Validates that the `void` instruction is only used inside `update` sections and that it is
* the only instruction within its section. Returns `false` and shows an inline error if the
* placement is invalid.
*/
protected validateVoidUsage(
errorTarget: HTMLElement,
section: HTMLElement,
pip: string,
excludedInstructionId: string | null,
): boolean {
const instructionList = section.querySelector(".sortableList") as HTMLElement;
const existingInstructions = Array.from(instructionList.children) as HTMLElement[];
const otherInstructions = existingInstructions.filter((li) => li.dataset.instructionId !== excludedInstructionId);

if (pip === "void") {
if (section.dataset.type !== "update") {
DomUtil.innerError(errorTarget, Language.get("wcf.acp.devtools.project.instruction.error.voidInInstall"), true);
return false;
}
if (otherInstructions.length > 0) {
DomUtil.innerError(errorTarget, Language.get("wcf.acp.devtools.project.instruction.error.voidNotAlone"), true);
return false;
}
} else if (otherInstructions.some((li) => li.dataset.pip === "void")) {
DomUtil.innerError(errorTarget, Language.get("wcf.acp.devtools.project.instruction.error.voidNotAlone"), true);
return false;
}

DomUtil.innerError(errorTarget, "");
return true;
}

/**
Expand Down Expand Up @@ -403,6 +463,11 @@ class Instructions {

const submit = () => {
const listItem = document.getElementById(`${this.formFieldId}_instruction${instructionId}`)!;
const section = listItem.closest("li.section") as HTMLElement;
if (!this.validateVoidUsage(pipSelect, section, pipSelect.value, instructionId)) {
return;
}

listItem.dataset.application =
Instructions.applicationPips.indexOf(pipSelect.value) !== -1 ? applicationSelect.value : "";
listItem.dataset.pip = pipSelect.value;
Expand Down Expand Up @@ -443,6 +508,14 @@ class Instructions {
DomUtil.hide(applicationSelect.closest("dl")!);
}

if (pip === "void") {
DomUtil.hide(valueInput.closest("dl")!);
DomUtil.hide(runStandaloneInput.closest("dl")!);
} else {
DomUtil.show(valueInput.closest("dl")!);
DomUtil.show(runStandaloneInput.closest("dl")!);
}

const description = DomTraverse.nextByTag(valueInput, "SMALL")!;
if (this.pipDefaultFilenames[pip] !== "") {
description.innerHTML = Language.get(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
{foreach from=$packageInstallationPlugins item=packageInstallationPlugin}
<option value="{$packageInstallationPlugin->pluginName}">{$packageInstallationPlugin->pluginName}</option>
{/foreach}
<option value="void">void</option>
</select>
</dd>
</dl>
Expand Down Expand Up @@ -131,6 +132,7 @@
{foreach from=$packageInstallationPlugins item=packageInstallationPlugin}
<option value="{$packageInstallationPlugin->pluginName}">{$packageInstallationPlugin->pluginName}</option>
{/foreach}
<option value="void">void</option>
</select>
</dd>
</dl>
Expand Down Expand Up @@ -188,6 +190,8 @@
) {
Language.addObject({
'wcf.acp.devtools.project.instruction.delete.confirmMessages': '{jslang}wcf.acp.devtools.project.instruction.delete.confirmMessages{/jslang}',
'wcf.acp.devtools.project.instruction.error.voidInInstall': '{jslang}wcf.acp.devtools.project.instruction.error.voidInInstall{/jslang}',
'wcf.acp.devtools.project.instruction.error.voidNotAlone': '{jslang}wcf.acp.devtools.project.instruction.error.voidNotAlone{/jslang}',
'wcf.acp.devtools.project.instruction.edit': '{jslang}wcf.acp.devtools.project.instruction.edit{/jslang}',
'wcf.acp.devtools.project.instruction.instruction': '{jslang __literal=true}wcf.acp.devtools.project.instruction.instruction{/jslang}',
'wcf.acp.devtools.project.instruction.value.description': '{jslang}wcf.acp.devtools.project.instruction.value.description{/jslang}',
Expand All @@ -214,6 +218,7 @@
{implode from=$packageInstallationPlugins item=packageInstallationPlugin}
'{$packageInstallationPlugin->pluginName}': '{$packageInstallationPlugin->getDefaultFilename()}'
{/implode}
, 'void': ''
},
[
{implode from=$field->getValue() key=instructionsKey item=instructions}
Expand Down

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

Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,10 @@ protected function getInstructionValuesValidator()
}

foreach ($instructions['instructions'] as $instructionKey => $instruction) {
if ($instruction['pip'] === 'void') {
continue;
}

$value = $instruction['value'];
$packageInstallationPlugin = $packageInstallationPlugins[$instruction['pip']];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use wcf\system\form\builder\field\devtools\project\DevtoolsProjectRequiredPackagesFormField;
use wcf\system\form\builder\field\TextFormField;
use wcf\system\language\LanguageFactory;
use wcf\system\package\PackageArchive;
use wcf\system\WCF;

/**
Expand Down Expand Up @@ -310,7 +311,7 @@ protected function setFormObjectData()
$versionUpdateInstructions[] = [
'application' => $instruction['attributes']['application'] ?? '',
'runStandalone' => isset($instruction['attributes']['run']) && $instruction['attributes']['run'] === 'standalone' ? 1 : 0,
'pip' => $instruction['pip'],
'pip' => $instruction['pip'] === PackageArchive::VOID_MARKER ? 'void' : $instruction['pip'],
'value' => $instruction['value'],
];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ protected function writeInstructions()
$this->xmlWriter->startElement('instructions', $attributes);

foreach ($instructions['instructions'] as $instruction) {
if ($instruction['pip'] === 'void') {
$this->xmlWriter->writeElement('void', '', [], false);
continue;
}

$attributes = ['type' => $instruction['pip']];
if (isset($instruction['runStandalone']) && $instruction['runStandalone'] !== "0") {
$attributes['run'] = 'standalone';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,11 @@ public function validate()
}

foreach ($instructions['instructions'] as $instruction) {
if (!isset($instruction['pip']) || !isset($this->getPackageInstallationPlugins()[$instruction['pip']])) {
if (!isset($instruction['pip'])) {
return false;
}

if ($instruction['pip'] !== 'void' && !isset($this->getPackageInstallationPlugins()[$instruction['pip']])) {
return false;
}

Expand All @@ -190,6 +194,24 @@ public function validate()
$instruction['value'] = $instruction['value'] ?? '';
}

// the `void` instruction is only allowed in update sections and
// must be the only instruction
$hasVoid = false;
foreach ($instructions['instructions'] as $instruction) {
if ($instruction['pip'] === 'void') {
$hasVoid = true;
break;
}
}
if ($hasVoid) {
if ($instructions['type'] !== 'update') {
return false;
}
if (\count($instructions['instructions']) !== 1) {
return false;
}
}

return true;
}));

Expand Down
2 changes: 2 additions & 0 deletions wcfsetup/install/lang/de.xml
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,8 @@
<item name="wcf.acp.devtools.project.instruction.language.error.missingFiles"><![CDATA[Das Verzeichnis <kbd>{$directory}</kbd> enthält keine XML-Dateien.]]></item>
<item name="wcf.acp.devtools.project.instruction.error.noXmlFile"><![CDATA[Die angegebene Datei ist keine XML-Datei.]]></item>
<item name="wcf.acp.devtools.project.instruction.delete.confirmMessages"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Willst du{else}Wollen Sie{/if} die Anweisung wirklich löschen?]]></item>
<item name="wcf.acp.devtools.project.instruction.error.voidInInstall"><![CDATA[Die <kbd>void</kbd>-Anweisung ist nur für Aktualisierungsanweisungen zulässig.]]></item>
<item name="wcf.acp.devtools.project.instruction.error.voidNotAlone"><![CDATA[Die <kbd>void</kbd>-Anweisung muss die einzige Anweisung in ihrem Anweisungssatz sein.]]></item>
<item name="wcf.acp.devtools.project.add.info"><![CDATA[Inkompatible Pakete, Installations- und Aktualisierungsanweisungen können erst beim Bearbeiten eines vorhandenen Projekts hinzugefügt werden.]]></item>
<item name="wcf.acp.devtools.project.optionalPackage.error.missingFiles"><![CDATA[Die folgenden Paketdateien fehlen: {implode from=$missingFiles item=missingFile}<kbd>{$missingFile}</kbd>{/implode}.]]></item>
<item name="wcf.acp.devtools.project.requiredPackage.error.missingFiles"><![CDATA[Die folgenden Paketdateien fehlen: {implode from=$missingFiles item=missingFile}<kbd>{$missingFile}</kbd>{/implode}.]]></item>
Expand Down
2 changes: 2 additions & 0 deletions wcfsetup/install/lang/en.xml
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,8 @@
<item name="wcf.acp.devtools.project.instruction.language.error.missingFiles"><![CDATA[The directory <kbd>{$directory}</kbd> contains no XML files.]]></item>
<item name="wcf.acp.devtools.project.instruction.error.noXmlFile"><![CDATA[The entered file is no XML file.]]></item>
<item name="wcf.acp.devtools.project.instruction.delete.confirmMessages"><![CDATA[Do you really want to delete this instruction?]]></item>
<item name="wcf.acp.devtools.project.instruction.error.voidInInstall"><![CDATA[The <kbd>void</kbd> instruction is only allowed for update instructions.]]></item>
<item name="wcf.acp.devtools.project.instruction.error.voidNotAlone"><![CDATA[The <kbd>void</kbd> instruction must be the only instruction in its set.]]></item>
<item name="wcf.acp.devtools.project.add.info"><![CDATA[Conflicting packages, installation instructions, and update instructions can only be added when editing an existing project.]]></item>
<item name="wcf.acp.devtools.project.optionalPackage.error.missingFiles"><![CDATA[The following package files are missing: {implode from=$missingFiles item=missingFile}<kbd>{$missingFile}</kbd>{/implode}.]]></item>
<item name="wcf.acp.devtools.project.requiredPackage.error.missingFiles"><![CDATA[The following package files are missing: {implode from=$missingFiles item=missingFile}<kbd>{$missingFile}</kbd>{/implode}.]]></item>
Expand Down
Loading