Add GUI for ACP search provider package installation plugin
authorMatthias Schmidt <gravatronics@live.com>
Thu, 24 May 2018 16:35:32 +0000 (18:35 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Thu, 24 May 2018 16:35:32 +0000 (18:35 +0200)
See #2545

wcfsetup/install/files/lib/system/package/plugin/ACPSearchProviderPackageInstallationPlugin.class.php
wcfsetup/install/files/lib/system/package/plugin/ObjectTypePackageInstallationPlugin.class.php
wcfsetup/install/lang/en.xml

index e6ed2335a0db9cf0a3aff32172570766a6b8432e..04b60581557fb9d35c52a89075a8f01eea098df5 100644 (file)
@@ -2,8 +2,20 @@
 declare(strict_types=1);
 namespace wcf\system\package\plugin;
 use wcf\data\acp\search\provider\ACPSearchProviderEditor;
+use wcf\data\acp\search\provider\ACPSearchProviderList;
 use wcf\system\cache\builder\ACPSearchProviderCacheBuilder;
-use wcf\system\devtools\pip\IIdempotentPackageInstallationPlugin;
+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\ClassNameFormField;
+use wcf\system\form\builder\field\IntegerFormField;
+use wcf\system\form\builder\field\TextFormField;
+use wcf\system\form\builder\IFormDocument;
+use wcf\system\search\acp\IACPSearchResultProvider;
 use wcf\system\WCF;
 
 /**
@@ -14,7 +26,9 @@ use wcf\system\WCF;
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    WoltLabSuite\Core\System\Package\Plugin
  */
-class ACPSearchProviderPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IIdempotentPackageInstallationPlugin {
+class ACPSearchProviderPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IGuiPackageInstallationPlugin {
+       use TXmlGuiPackageInstallationPlugin;
+       
        /**
         * @inheritDoc
         */
@@ -90,8 +104,161 @@ class ACPSearchProviderPackageInstallationPlugin extends AbstractXMLPackageInsta
        
        /**
         * @inheritDoc
+        * @since       3.1
         */
        public static function getSyncDependencies() {
                return [];
        }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       protected function getElementData(\DOMElement $element): array {
+               $data = [
+                       'className' => $element->getElementsByTagName('classname')->item(0)->nodeValue,
+                       'packageID' => $this->installation->getPackage()->packageID,
+                       'providerName' => $element->getAttribute('name')
+               ];
+               
+               $showOrder = $element->getElementsByTagName('showorder')->item(0);
+               if ($showOrder) {
+                       $data['showOrder'] = $showOrder->nodeValue;
+               }
+               
+               return $data;
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       public function getElementIdentifier(\DOMElement $element): string {
+               return $element->getAttribute('name');
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       public function addFormFields(IFormDocument $form) {
+               /** @var FormContainer $dataContainer */
+               $dataContainer = $form->getNodeById('data');
+               
+               $dataContainer->appendChildren([
+                       TextFormField::create('providerName')
+                               ->objectProperty('name')
+                               ->label('wcf.acp.pip.acpSearchProvider.providerName')
+                               ->description('wcf.acp.pip.acpSearchProvider.providerName.description', ['project' => $this->installation->getProject()])
+                               ->required()
+                               ->addValidator(ObjectTypePackageInstallationPlugin::getObjectTypeAlikeValueValidator('wcf.acp.pip.acpSearchProvider.providerName'))
+                               ->addValidator(new FormFieldValidator('uniqueness', function(TextFormField $formField) {
+                                       if (
+                                               $formField->getDocument()->getFormMode() === IFormDocument::FORM_MODE_CREATE ||
+                                               $this->editedEntry->getAttribute('name') !== $formField->getValue()
+                                       ) {
+                                               $providerList = new ACPSearchProviderList();
+                                               $providerList->getConditionBuilder()->add('providerName <> ?', [$formField->getValue()]);
+                                               
+                                               if ($providerList->countObjects() > 0) {
+                                                       $formField->addValidationError(
+                                                               new FormFieldValidationError(
+                                                                       'notUnique',
+                                                                       'wcf.acp.pip.acpSearchProvider.providerName.error.notUnique'
+                                                               )
+                                                       );
+                                               }
+                                       }
+                               })),
+                       
+                       ClassNameFormField::create('className')
+                               ->objectProperty('classname')
+                               ->required()
+                               ->implementedInterface(IACPSearchResultProvider::class),
+                       
+                       IntegerFormField::create('showOrder')
+                               ->objectProperty('showorder')
+                               ->label('wcf.acp.pip.acpSearchProvider.showOrder')
+                               ->description('wcf.acp.pip.acpSearchProvider.showOrder.description')
+                               ->nullable()
+                               ->minimum(1),
+               ]);
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       public function getEntryList(): IDevtoolsPipEntryList {
+               $xml = $this->getProjectXml();
+               $xpath = $xml->xpath();
+               
+               $entryList = new DevtoolsPipEntryList();
+               $entryList->setKeys([
+                       'providerName' => 'wcf.acp.pip.acpSearchProvider.providerName',
+                       'className' => 'wcf.form.field.className'
+               ]);
+               
+               /** @var \DOMElement $languageItem */
+               foreach ($this->getImportElements($xpath) as $element) {
+                       $entryList->addEntry($this->getElementIdentifier($element), array_intersect_key($this->getElementData($element), $entryList->getKeys()));
+               }
+               
+               return $entryList;
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       protected function sortDocument(\DOMDocument $document) {
+               $this->sortImportDelete($document);
+               
+               $this->sortChildNodes($document->getElementsByTagName('import'), function(\DOMElement $element1, \DOMElement $element2) {
+                       $showOrder1 = PHP_INT_MAX;
+                       if ($element1->getElementsByTagName('showorder')->length === 1) {
+                               $showOrder1 = $element1->getElementsByTagName('showorder')->item(0)->nodeValue;
+                       }
+                       
+                       $showOrder2 = PHP_INT_MAX;
+                       if ($element2->getElementsByTagName('showorder')->length === 1) {
+                               $showOrder2 = $element2->getElementsByTagName('showorder')->item(0)->nodeValue;
+                       }
+                       
+                       if ($showOrder1 !== $showOrder2) {
+                               return $showOrder1 > $showOrder2;
+                       }
+                       
+                       return strcmp(
+                               $element1->getAttribute('name'),
+                               $element2->getAttribute('name')
+                       );
+               });
+               $this->sortChildNodes($document->getElementsByTagName('delete'), function(\DOMElement $element1, \DOMElement $element2) {
+                       return strcmp(
+                               $element1->getAttribute('name'),
+                               $element2->getAttribute('name')
+                       );
+               });
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       protected function writeEntry(\DOMDocument $document, IFormDocument $form): \DOMElement {
+               $acpSearchProvider = $document->createElement('acpsearchprovider');
+               $acpSearchProvider->setAttribute('name', $form->getNodeById('providerName')->getSaveValue());
+               $acpSearchProvider->appendChild($document->createElement('classname', $form->getNodeById('className')->getSaveValue()));
+               
+               /** @var IntegerFormField $showOrder */
+               $showOrder = $form->getNodeById('showOrder');
+               if ($showOrder->getSaveValue()) {
+                       $acpSearchProvider->appendChild($document->createElement('showorder', (string) $showOrder->getSaveValue()));
+               }
+               
+               $document->getElementsByTagName('import')->item(0)->appendChild($acpSearchProvider);
+               
+               return $acpSearchProvider;
+       }
 }
index 34a7fba22ae787be076b61ceacc9f402e9517b8e..610abe6d59e3873969a6bb4404b336b727de66c8 100644 (file)
@@ -227,7 +227,7 @@ class ObjectTypePackageInstallationPlugin extends AbstractXMLPackageInstallation
                                ->label('wcf.acp.pip.objectType.objectType')
                                ->description('wcf.acp.pip.objectType.objectType.description')
                                ->required()
-                               ->addValidator($this->getObjectTypeAlikeValueValidator('objectType'))
+                               ->addValidator(self::getObjectTypeAlikeValueValidator('wcf.acp.pip.objectType.objectType'))
                                ->addValidator(new FormFieldValidator('uniqueness', function(TextFormField $formField) {
                                        $definitionName = $formField->getDocument()->getNodeById('definitionName')->getValue();
                                        if ($definitionName) {
@@ -295,7 +295,7 @@ class ObjectTypePackageInstallationPlugin extends AbstractXMLPackageInstallation
                                        ->objectProperty('categoryname')
                                        ->label('wcf.acp.pip.objectType.com.woltlab.wcf.adLocation.categoryName')
                                        ->description('wcf.acp.pip.objectType.com.woltlab.wcf.adLocation.categoryName.description')
-                                       ->addValidator($this->getObjectTypeAlikeValueValidator('com.woltlab.wcf.adLocation.categoryName')),
+                                       ->addValidator(self::getObjectTypeAlikeValueValidator('wcf.acp.pip.objectType.com.woltlab.wcf.adLocation.categoryName')),
                                ItemListFormField::create('adLocationCssClassName')
                                        ->objectProperty('cssclassname')
                                        ->label('wcf.acp.pip.objectType.com.woltlab.wcf.adLocation.cssClassName')
@@ -508,7 +508,7 @@ class ObjectTypePackageInstallationPlugin extends AbstractXMLPackageInstallation
                                        ->objectProperty('categoryname')
                                        ->label('wcf.acp.pip.objectType.com.woltlab.wcf.statDailyHandler.categoryName')
                                        ->description('wcf.acp.pip.objectType.com.woltlab.wcf.statDailyHandler.categoryName.description')
-                                       ->addValidator($this->getObjectTypeAlikeValueValidator('com.woltlab.wcf.statDailyHandler.categoryName')),
+                                       ->addValidator(self::getObjectTypeAlikeValueValidator('wcf.acp.pip.objectType.com.woltlab.wcf.statDailyHandler.categoryName')),
                                
                                BooleanFormField::create('statDailyHandlerIsDefault')
                                        ->objectProperty('default')
@@ -606,48 +606,6 @@ class ObjectTypePackageInstallationPlugin extends AbstractXMLPackageInstallation
                        ]);
        }
        
-       /**
-        * Returns a form field validator to validate a string value that has a
-        * object type-alike structure.
-        * 
-        * @param       string          $languageItemSegment    used for error language items: `wcf.acp.pip.objectType.{$languageItemSegment}.error.{errorType}`
-        * @return      FormFieldValidator
-        */
-       protected function getObjectTypeAlikeValueValidator($languageItemSegment): FormFieldValidator {
-               return new FormFieldValidator('format', function(TextFormField $formField) use ($languageItemSegment) {
-                       if ($formField->getValue()) {
-                               $segments = explode('.', $formField->getValue());
-                               if (count($segments) < 4) {
-                                       $formField->addValidationError(
-                                               new FormFieldValidationError(
-                                                       'tooFewSegments',
-                                                       'wcf.acp.pip.objectType.' . $languageItemSegment . '.error.tooFewSegments',
-                                                       ['segmentCount' => count($segments)]
-                                               )
-                                       );
-                               }
-                               else {
-                                       $invalidSegments = [];
-                                       foreach ($segments as $key => $segment) {
-                                               if (!preg_match('~^[A-z0-9\-\_]+$~', $segment)) {
-                                                       $invalidSegments[$key] = $segment;
-                                               }
-                                       }
-                                       
-                                       if (!empty($invalidSegments)) {
-                                               $formField->addValidationError(
-                                                       new FormFieldValidationError(
-                                                               'invalidSegments',
-                                                               'wcf.acp.pip.objectType.' . $languageItemSegment . '.error.invalidSegments',
-                                                               ['invalidSegments' => $invalidSegments]
-                                                       )
-                                               );
-                                       }
-                               }
-                       }
-               });
-       }
-       
        /**
         * @inheritDoc
         * @since       3.2
@@ -822,7 +780,7 @@ XML;
                                        ->label('wcf.acp.pip.objectType.condition.conditionObject')
                                        ->description('wcf.acp.pip.objectType.condition.conditionObject.description')
                                        ->required()
-                                       ->addValidator($this->getObjectTypeAlikeValueValidator('condition.conditionObject'))
+                                       ->addValidator(self::getObjectTypeAlikeValueValidator('wcf.acp.pip.objectType.condition.conditionObject'))
                        );
                }
                
@@ -982,4 +940,46 @@ XML;
                $parameters = ['dataContainer' => $dataContainer];
                EventHandler::getInstance()->fireAction($this, 'addConditionFields', $parameters);
        }
+       
+       /**
+        * Returns a form field validator to validate a string value that has a
+        * object type-alike structure.
+        * 
+        * @param       string          $languageItemPrefix     used for error language items: `{$languageItemPrefix}.error.{errorType}`
+        * @return      FormFieldValidator
+        */
+       public static function getObjectTypeAlikeValueValidator($languageItemPrefix): FormFieldValidator {
+               return new FormFieldValidator('format', function(TextFormField $formField) use ($languageItemPrefix) {
+                       if ($formField->getValue()) {
+                               $segments = explode('.', $formField->getValue());
+                               if (count($segments) < 4) {
+                                       $formField->addValidationError(
+                                               new FormFieldValidationError(
+                                                       'tooFewSegments',
+                                                       $languageItemPrefix . '.error.tooFewSegments',
+                                                       ['segmentCount' => count($segments)]
+                                               )
+                                       );
+                               }
+                               else {
+                                       $invalidSegments = [];
+                                       foreach ($segments as $key => $segment) {
+                                               if (!preg_match('~^[A-z0-9\-\_]+$~', $segment)) {
+                                                       $invalidSegments[$key] = $segment;
+                                               }
+                                       }
+                                       
+                                       if (!empty($invalidSegments)) {
+                                               $formField->addValidationError(
+                                                       new FormFieldValidationError(
+                                                               'invalidSegments',
+                                                               $languageItemPrefix . '.error.invalidSegments',
+                                                               ['invalidSegments' => $invalidSegments]
+                                                       )
+                                               );
+                                       }
+                               }
+                       }
+               });
+       }
 }
index 8e4e7dc7596a5e3aa3a9905a7fde6c112b59cf1d..484988cecaa7a10c09be5ee7406a0449cd069a66 100644 (file)
@@ -1876,6 +1876,13 @@ When prompted for the notification URL for the instant payment notifications, pl
                <item name="wcf.acp.pip.templateListener.templateName"><![CDATA[Template]]></item>
                <item name="wcf.acp.pip.templateListener.templateName.description"><![CDATA[The template listeners’ code will be inserted into the selected template.]]></item>
                <item name="wcf.acp.pip.coreObject.objectName.error.notUnique"><![CDATA[The entered class is already registered as a core object.]]></item>
+               <item name="wcf.acp.pip.acpSearchProvider.providerName"><![CDATA[Provider Name]]></item>
+               <item name="wcf.acp.pip.acpSearchProvider.providerName.description"><![CDATA[The name of an ACP search result provider consists of least four segments separated by dots. Each segment must not be empty and may only contain letters, numbers, underscores, and dashes. In general, the first part of the definition name matches the package identifier. Example: <code>{$project->getPackage()->package}.searchedObject</code>]]></item>
+               <item name="wcf.acp.pip.acpSearchProvider.providerName.error.notUnique"><![CDATA[The entered names is already used by another acp search result provider.]]></item>
+               <item name="wcf.acp.pip.acpSearchProvider.providerName.error.invalidSegments"><![CDATA[The following segments are invalid: {implode from=$invalidSegments key=segmentNumber item=segment}{if $segment !== ''}<code>{$segment}</code>{else}(empty){/if} (segment {#$segmentNumber + 1}){/implode}.]]></item>
+               <item name="wcf.acp.pip.acpSearchProvider.providerName.error.tooFewSegments"><![CDATA[The entered provider name only contains {#$segmentCount} segment{if $segmentCount > 1}s{/if}.]]></item>
+               <item name="wcf.acp.pip.acpSearchProvider.showOrder"><![CDATA[Position]]></item>
+               <item name="wcf.acp.pip.acpSearchProvider.showOrder.description"><![CDATA[The entered value determines in which order the different types of search results are shown.]]></item>
        </category>
        
        <category name="wcf.acp.rebuildData">