From: Matthias Schmidt Date: Sun, 15 Apr 2018 15:39:33 +0000 (+0200) Subject: Add GUI for pip package installation plugin X-Git-Tag: 5.2.0_Alpha_1~773 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=dab5ab299c1cde227aae1524126323ca2d9db0dc;p=GitHub%2FWoltLab%2FWCF.git Add GUI for pip package installation plugin See #2545 --- diff --git a/wcfsetup/install/files/lib/system/devtools/pip/TXmlGuiPackageInstallationPlugin.class.php b/wcfsetup/install/files/lib/system/devtools/pip/TXmlGuiPackageInstallationPlugin.class.php index 18752d8ce0..a63e1bcb3a 100644 --- a/wcfsetup/install/files/lib/system/devtools/pip/TXmlGuiPackageInstallationPlugin.class.php +++ b/wcfsetup/install/files/lib/system/devtools/pip/TXmlGuiPackageInstallationPlugin.class.php @@ -5,6 +5,7 @@ use wcf\data\devtools\project\DevtoolsProject; use wcf\data\IEditableCachedObject; use wcf\system\form\builder\field\IFormField; use wcf\system\form\builder\IFormDocument; +use wcf\system\form\builder\IFormNode; use wcf\system\WCF; use wcf\util\DOMUtil; use wcf\util\StringUtil; @@ -255,8 +256,17 @@ XML; } $data = []; + /** @var \DOMNode $attribute */ + foreach ($element->attributes as $attribute) { + $data[$attribute->nodeName] = $attribute->nodeValue; + } foreach ($element->childNodes as $childNode) { - $data[$childNode->nodeName] = $childNode->nodeValue; + if ($childNode instanceof \DOMText) { + $data['__value'] = $childNode->nodeValue; + } + else { + $data[$childNode->nodeName] = $childNode->nodeValue; + } } /** @var IFormNode $node */ diff --git a/wcfsetup/install/files/lib/system/package/plugin/PIPPackageInstallationPlugin.class.php b/wcfsetup/install/files/lib/system/package/plugin/PIPPackageInstallationPlugin.class.php index 121ff5d1e0..7342e05113 100644 --- a/wcfsetup/install/files/lib/system/package/plugin/PIPPackageInstallationPlugin.class.php +++ b/wcfsetup/install/files/lib/system/package/plugin/PIPPackageInstallationPlugin.class.php @@ -2,7 +2,16 @@ declare(strict_types=1); namespace wcf\system\package\plugin; use wcf\data\package\installation\plugin\PackageInstallationPluginEditor; -use wcf\system\devtools\pip\IIdempotentPackageInstallationPlugin; +use wcf\data\package\installation\plugin\PackageInstallationPluginList; +use wcf\system\devtools\pip\DevtoolsPipEntryList; +use wcf\system\devtools\pip\IDevtoolsPipEntryList; +use wcf\system\devtools\pip\IGuiPackageInstallationPlugin; +use wcf\system\devtools\pip\TXmlGuiPackageInstallationPlugin; +use wcf\system\form\builder\container\FormContainer; +use wcf\system\form\builder\field\validation\FormFieldValidationError; +use wcf\system\form\builder\field\validation\FormFieldValidator; +use wcf\system\form\builder\field\TextFormField; +use wcf\system\form\builder\IFormDocument; use wcf\system\WCF; /** @@ -13,7 +22,9 @@ use wcf\system\WCF; * @license GNU Lesser General Public License * @package WoltLabSuite\Core\System\Package\Plugin */ -class PIPPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IIdempotentPackageInstallationPlugin { +class PIPPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IGuiPackageInstallationPlugin { + use TXmlGuiPackageInstallationPlugin; + /** * @inheritDoc */ @@ -84,4 +95,169 @@ class PIPPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin public static function getSyncDependencies() { return []; } + + /** + * @inheritDoc + * @since 3.2 + */ + public function addFormFields(IFormDocument $form) { + /** @var FormContainer $dataContainer */ + $dataContainer = $form->getNodeById('data'); + + $dataContainer->appendChildren([ + TextFormField::create('pluginName') + ->attribute('data-tag', 'name') + ->label('wcf.acp.pip.pip.pluginName') + ->description('wcf.acp.pip.pip.pluginName.description') + ->required() + ->addValidator(new FormFieldValidator('format', function(TextFormField $formField) { + if (preg_match('~^[a-z][A-z]+$~', $formField->getValue()) !== 1) { + $formField->addValidationError( + new FormFieldValidationError( + 'format', + 'wcf.acp.pip.pip.pluginName.error.format' + ) + ); + } + })) + ->addValidator(new FormFieldValidator('uniqueness', function(TextFormField $formField) { + $pipList = new PackageInstallationPluginList(); + $pipList->getConditionBuilder()->add('pluginName = ?', [$formField->getValue()]); + + if ($pipList->countObjects()) { + $formField->addValidationError( + new FormFieldValidationError( + 'format', + 'wcf.acp.pip.pip.pluginName.error.notUnique' + ) + ); + } + })), + + TextFormField::create('className') + ->attribute('data-tag', '__value') + ->label('wcf.acp.pip.pip.className') + ->description('wcf.acp.pip.pip.className.description') + ->required() + ->addValidator(new FormFieldValidator('noLeadingBackslash', function(TextFormField $formField) { + if (substr($formField->getValue(), 0, 1) === '\\') { + $formField->addValidationError( + new FormFieldValidationError( + 'leadingBackslash', + 'wcf.acp.pip.pip.className.error.leadingBackslash' + ) + ); + } + })) + ->addValidator(new FormFieldValidator('classExists', function(TextFormField $formField) { + if (!class_exists($formField->getValue())) { + $formField->addValidationError( + new FormFieldValidationError( + 'nonExistent', + 'wcf.acp.pip.pip.className.error.nonExistent' + ) + ); + } + })) + ->addValidator(new FormFieldValidator('implementsInterface', function(TextFormField $formField) { + if (!is_subclass_of($formField->getValue(), IPackageInstallationPlugin::class)) { + $formField->addValidationError( + new FormFieldValidationError( + 'interface', + 'wcf.acp.pip.pip.className.error.interface' + ) + ); + } + })) + ->addValidator(new FormFieldValidator('isInstantiable', function(TextFormField $formField) { + $reflection = new \ReflectionClass($formField->getValue()); + if (!$reflection->isInstantiable()) { + $formField->addValidationError( + new FormFieldValidationError( + 'interface', + 'wcf.acp.pip.pip.className.error.isInstantiable' + ) + ); + } + })) + ]); + } + + /** + * @inheritDoc + * @since 3.2 + */ + protected function getElementData(\DOMElement $element): array { + return [ + 'className' => $element->nodeValue, + 'pluginName' => $element->getAttribute('name'), + 'priority' => $this->installation->getPackage()->package == 'com.woltlab.wcf' ? 1 : 0 + ]; + } + + /** + * @inheritDoc + * @since 3.2 + */ + public function getElementIdentifier(\DOMElement $element): string { + return $element->getAttribute('name'); + } + + /** + * @inheritDoc + * @since 3.2 + */ + public function getEntryList(): IDevtoolsPipEntryList { + $xml = $this->getProjectXml(); + $xpath = $xml->xpath(); + + $entryList = new DevtoolsPipEntryList(); + $entryList->setKeys([ + 'pluginName' => 'wcf.acp.pip.pip.pluginName', + 'className' => 'wcf.acp.pip.pip.className' + ]); + + /** @var \DOMElement $languageItem */ + foreach ($this->getImportElements($xpath) as $element) { + $entryList->addEntry($this->getElementIdentifier($element), [ + 'className' => $element->nodeValue, + 'pluginName' => $element->getAttribute('name') + ]); + } + + return $entryList; + } + + /** + * @inheritDoc + * @since 3.2 + */ + protected function sortDocument(\DOMDocument $document) { + $this->sortImportDelete($document); + + $compareFunction = function(\DOMElement $element1, \DOMElement $element2) { + return strcmp($element1->getAttribute('name'), $element2->getAttribute('name')); + }; + + $this->sortChildNodes($document->getElementsByTagName('import'), $compareFunction); + $this->sortChildNodes($document->getElementsByTagName('delete'), $compareFunction); + } + + /** + * @inheritDoc + * @since 3.2 + */ + protected function writeEntry(\DOMDocument $document, IFormDocument $form): \DOMElement { + /** @var TextFormField $className */ + $className = $form->getNodeById('className'); + /** @var TextFormField $pluginName */ + $pluginName = $form->getNodeById('pluginName'); + + $pip = $document->createElement('pip', $className->getSaveValue()); + $pip->setAttribute('name', $pluginName->getSaveValue()); + + $document->getElementsByTagName('import')->item(0)->appendChild($pip); + + return $pip; + } } diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index fe59843935..aca176f829 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -1698,7 +1698,7 @@ When prompted for the notification URL for the instant payment notifications, pl [A-z0-9-_].]]> {$segment}{else}(empty){/if} (segment {#$segmentNumber + 1}){/implode}.]]> - 1}s{/if}.]]> + 1}s{/if}.]]> {$invalidClass}{/implode}.]]> @@ -1710,7 +1710,7 @@ When prompted for the notification URL for the instant payment notifications, pl - + @@ -1747,12 +1747,12 @@ When prompted for the notification URL for the instant payment notifications, pl [A-z0-9-_].]]> {$segment}{else}(empty){/if} (segment {#$segmentNumber + 1}){/implode}.]]> - 1}s{/if}.]]> + 1}s{/if}.]]> - + @@ -1767,9 +1767,9 @@ When prompted for the notification URL for the instant payment notifications, pl {$segment}{else}(empty){/if} (segment {#$segmentNumber + 1}){/implode}.]]> - 1}s{/if}.]]> + 1}s{/if}.]]> - + @@ -1838,16 +1838,26 @@ When prompted for the notification URL for the instant payment notifications, pl [A-z0-9-_].]]> {$segment}{else}(empty){/if} (segment {#$segmentNumber + 1}){/implode}.]]> - - 1}s{/if}.]]> + + 1}s{/if}.]]> {$project->getPackage()->package}.type]]> {$segment}{else}(empty){/if} (segment {#$segmentNumber + 1}){/implode}.]]> - - 1}s{/if}.]]> + + 1}s{/if}.]]> - + + + type attribute of an instruction element in a package.xml file. The name may only consist of letters and must begin with a lowercase letter.]]> + + + + wcf\system\package\plugin\IPackageInstallationPlugin.]]> + + + wcf\system\package\plugin\IPackageInstallationPlugin.]]> +