<Add GUI for menu package installation plugin
authorMatthias Schmidt <gravatronics@live.com>
Sat, 30 Jun 2018 09:00:34 +0000 (11:00 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Sat, 30 Jun 2018 09:00:34 +0000 (11:00 +0200)
See #2545

wcfsetup/install/files/acp/templates/__menuPipGui.tpl [new file with mode: 0644]
wcfsetup/install/files/lib/system/package/plugin/MenuPackageInstallationPlugin.class.php
wcfsetup/install/lang/en.xml

diff --git a/wcfsetup/install/files/acp/templates/__menuPipGui.tpl b/wcfsetup/install/files/acp/templates/__menuPipGui.tpl
new file mode 100644 (file)
index 0000000..a5086f5
--- /dev/null
@@ -0,0 +1,25 @@
+<script data-relocate="true">
+       require(['Language'], function(Language) {
+               Language.addObject({
+                       'wcf.acp.pip.menu.boxVisibilityExceptions.hiddenEverywhere': '{lang}wcf.acp.pip.menu.boxVisibilityExceptions.hiddenEverywhere{/lang}',
+                       'wcf.acp.pip.menu.boxVisibilityExceptions.visibleEverywhere': '{lang}wcf.acp.pip.menu.boxVisibilityExceptions.visibleEverywhere{/lang}'
+               });
+               
+               var boxVisibleEverywhere = elById('boxVisibleEverywhere');
+               var boxVisibilityExceptionsLabel = elBySel('#boxVisibilityExceptionsContainer > dt > label');
+               
+               function updateVisibilityExceptions() {
+                       if (boxVisibleEverywhere.checked) {
+                               boxVisibilityExceptionsLabel.innerHTML = Language.get('wcf.acp.pip.menu.boxVisibilityExceptions.visibleEverywhere');
+                       }
+                       else {
+                               boxVisibilityExceptionsLabel.innerHTML = Language.get('wcf.acp.pip.menu.boxVisibilityExceptions.hiddenEverywhere');
+                       }
+               }
+               
+               boxVisibleEverywhere.addEventListener('change', updateVisibilityExceptions);
+               elById('boxVisibleEverywhere_no').addEventListener('change', updateVisibilityExceptions);
+               
+               updateVisibilityExceptions();
+       });
+</script>
index 7605785a6450b88e77a56b2bd2a1f97acd4ec857..7a972d0de5ef362a6b04796b3db084d072e3b2bc 100644 (file)
@@ -6,9 +6,25 @@ use wcf\data\box\BoxEditor;
 use wcf\data\menu\Menu;
 use wcf\data\menu\MenuEditor;
 use wcf\data\menu\MenuList;
+use wcf\data\page\PageNode;
+use wcf\data\page\PageNodeTree;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
-use wcf\system\devtools\pip\IIdempotentPackageInstallationPlugin;
+use wcf\system\devtools\pip\IDevtoolsPipEntryList;
+use wcf\system\devtools\pip\IGuiPackageInstallationPlugin;
+use wcf\system\devtools\pip\TXmlGuiPackageInstallationPlugin;
 use wcf\system\exception\SystemException;
+use wcf\system\form\builder\field\BooleanFormField;
+use wcf\system\form\builder\field\dependency\NonEmptyFormFieldDependency;
+use wcf\system\form\builder\field\dependency\ValueFormFieldDependency;
+use wcf\system\form\builder\field\ItemListFormField;
+use wcf\system\form\builder\field\MultipleSelectionFormField;
+use wcf\system\form\builder\field\SingleSelectionFormField;
+use wcf\system\form\builder\field\TextFormField;
+use wcf\system\form\builder\field\TitleFormField;
+use wcf\system\form\builder\field\validation\FormFieldValidationError;
+use wcf\system\form\builder\field\validation\FormFieldValidator;
+use wcf\system\form\builder\IFormDocument;
+use wcf\system\language\LanguageFactory;
 use wcf\system\WCF;
 
 /**
@@ -20,7 +36,9 @@ use wcf\system\WCF;
  * @package    WoltLabSuite\Core\Acp\Package\Plugin
  * @since      3.0
  */
-class MenuPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IIdempotentPackageInstallationPlugin {
+class MenuPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IGuiPackageInstallationPlugin {
+       use TXmlGuiPackageInstallationPlugin;
+       
        /**
         * box meta data per menu
         * @var array
@@ -269,8 +287,296 @@ class MenuPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
        
        /**
         * @inheritDoc
+        * @since       3.1
         */
        public static function getSyncDependencies() {
                return ['language'];
        }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       public function getAdditionalTemplateCode(): string {
+               return WCF::getTPL()->fetch('__menuPipGui');
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       public function addFormFields(IFormDocument $form) {
+               $form->getNodeById('data')->appendChildren([
+                       TextFormField::create('identifier')
+                               ->label('wcf.acp.pip.menu.identifier')
+                               ->description('wcf.acp.pip.menu.identifier.description')
+                               ->required()
+                               ->addValidator(ObjectTypePackageInstallationPlugin::getObjectTypeAlikeValueValidator(
+                                       'wcf.acp.pip.menu.identifier', 
+                                       4
+                               ))
+                               ->addValidator(new FormFieldValidator('uniqueness', function(TextFormField $formField) {
+                                       if (
+                                               $formField->getDocument()->getFormMode() === IFormDocument::FORM_MODE_CREATE ||
+                                               $this->editedEntry->getAttribute('identifier') !== $formField->getValue()
+                                       ) {
+                                               $menuList = new MenuList();
+                                               $menuList->getConditionBuilder()->add('identifier = ?', [$formField->getValue()]);
+                                               
+                                               if ($menuList->countObjects() > 0) {
+                                                       $formField->addValidationError(
+                                                               new FormFieldValidationError(
+                                                                       'notUnique',
+                                                                       'wcf.acp.pip.menu.identifier.error.notUnique'
+                                                               )
+                                                       );
+                                               }
+                                       }
+                               })),
+                       
+                       TitleFormField::create()
+                               ->description('wcf.acp.pip.menu.title.description')
+                               ->required()
+                               ->i18n()
+                               ->i18nRequired()
+                               ->languageItemPattern('__NONE__'),
+                       
+                       BooleanFormField::create('createBox')
+                               ->label('wcf.acp.pip.menu.createBox')
+                               ->description('wcf.acp.pip.menu.createBox.description'),
+                       
+                       SingleSelectionFormField::create('boxPosition')
+                               ->label('wcf.acp.pip.menu.boxPosition')
+                               ->description('wcf.acp.pip.menu.boxPosition.description')
+                               ->options(array_combine(Box::$availablePositions, Box::$availablePositions)),
+                       
+                       BooleanFormField::create('boxShowHeader')
+                               ->label('wcf.acp.pip.menu.boxShowHeader')
+                               ->description('wcf.acp.pip.menu.boxShowHeader.description'),
+                       
+                       BooleanFormField::create('boxVisibleEverywhere')
+                               ->label('wcf.acp.pip.menu.boxVisibleEverywhere'),
+                       
+                       MultipleSelectionFormField::create('boxVisibilityExceptions')
+                               ->label('wcf.acp.pip.menu.boxVisibilityExceptions.hiddenEverywhere')
+                               ->filterable()
+                               ->options(function(): array {
+                                       $pageNodeList = (new PageNodeTree())->getNodeList();
+                                       
+                                       $nestedOptions = [];
+                                       /** @var PageNode $pageNode */
+                                       foreach ($pageNodeList as $pageNode) {
+                                               $nestedOptions[] = [
+                                                       'depth' => $pageNode->getDepth() - 1,
+                                                       'label' => $pageNode->name,
+                                                       'value' => $pageNode->identifier
+                                               ];
+                                       }
+                                       
+                                       return $nestedOptions;
+                               }, true),
+                       
+                       ItemListFormField::create('boxCssClassName')
+                               ->label('wcf.acp.pip.menu.boxCssClassName')
+                               ->description('wcf.acp.pip.menu.boxCssClassName.description')
+                               ->saveValueType(ItemListFormField::SAVE_VALUE_TYPE_SSV)
+               ]);
+               
+               /** @var BooleanFormField $createBox */
+               $createBox = $form->getNodeById('createBox');
+               foreach (['boxPosition', 'boxShowHeader', 'boxVisibleEverywhere', 'boxVisibilityExceptions', 'boxCssClassName'] as $boxField) {
+                       $form->getNodeById($boxField)->addDependency(
+                               NonEmptyFormFieldDependency::create('createBox')
+                                       ->field($createBox)
+                       );
+               }
+               
+               $form->getNodeById('boxPosition')->addDependency(
+                       ValueFormFieldDependency::create('identifier')
+                               ->field($form->getNodeById('identifier'))
+                               ->values(['com.woltlab.wcf.MainMenu'])
+                               ->negate()
+               );
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       protected function getElementData(\DOMElement $element, bool $saveData = false): array {
+               $data = [
+                       'identifier' => $element->getAttribute('identifier'),
+                       'packageID' => $this->installation->getPackageID(),
+                       'title' => []
+               ];
+               
+               /** @var \DOMElement $title */
+               foreach ($element->getElementsByTagName('title') as $title) {
+                       $data['title'][LanguageFactory::getInstance()->getLanguageByCode($title->getAttribute('language'))->languageID] = $title->nodeValue;
+               }
+               
+               $box = $element->getElementsByTagName('box')->item(0);
+               $boxData = [];
+               if ($box !== null) {
+                       $boxData['position'] = $box->getElementsByTagName('position')->item(0)->nodeValue;
+                       
+                       // work-around for unofficial position `mainMenu`
+                       if ($data['identifier'] === 'com.woltlab.wcf.MainMenu' && !$saveData) {
+                               unset($boxData['position']);
+                       }
+                       
+                       $showHeader = $element->getElementsByTagName('showHeader')->item(0);
+                       if ($showHeader !== null) {
+                               $boxData['showHeader'] = $showHeader->nodeValue;
+                       }
+                       
+                       $visibleEverywhere = $element->getElementsByTagName('visibleEverywhere')->item(0);
+                       if ($visibleEverywhere !== null) {
+                               $boxData['visibleEverywhere'] = $visibleEverywhere->nodeValue;
+                       }
+                       
+                       $cssClassName = $element->getElementsByTagName('cssClassName')->item(0);
+                       if ($cssClassName !== null) {
+                               $boxData['cssClassName'] = $cssClassName->nodeValue;
+                       }
+                       
+                       $visibilityExceptions = $element->getElementsByTagName('visibilityExceptions');
+                       if ($visibilityExceptions->length > 0) {
+                               $boxData['visibilityExceptions'] = [];
+                               
+                               /** @var \DOMElement $visibilityException */
+                               foreach ($visibilityExceptions as $visibilityException) {
+                                       $boxData['visibilityExceptions'] = $visibilityException->nodeValue;
+                               }
+                       }
+               }
+               
+               if ($saveData) {
+                       if (!empty($boxData)) {
+                               $this->boxData[$data['identifier']] = [
+                                       'identifier' => $data['identifier'],
+                                       'name' => $this->getI18nValues($data['title'], true),
+                                       'boxType' => 'menu',
+                                       'position' => $boxData['position'],
+                                       'showHeader' => $boxData['showHeader'] ?? 0,
+                                       'visibleEverywhere' => $boxData['visibleEverywhere'] ?? 0,
+                                       'cssClassName' => $boxData['cssClassName'] ?? '',
+                                       'originIsSystem' => 1,
+                                       'packageID' => $this->installation->getPackageID()
+                               ];
+                               
+                               if (!empty($boxData['visibilityExceptions'])) {
+                                       $this->visibilityExceptions[$data['identifier']] = $boxData['visibilityExceptions'];
+                               }
+                       }
+                       
+                       // update menus is not supported thus handling the title
+                       // array causes issues
+                       if ($this->editedEntry !== null) {
+                               unset($data['title']);
+                       }
+                       else {
+                               $titles = [];
+                               foreach ($data['title'] as $languageID => $title) {
+                                       $titles[LanguageFactory::getInstance()->getLanguage($languageID)->languageCode] = $title;
+                               }
+                               
+                               $data['title'] = $titles;
+                       }
+               }
+               else {
+                       $data['createBox'] = $box !== null;
+                       
+                       foreach ($boxData as $key => $value) {
+                               $data['box' . ucfirst($key)] = $value;
+                       }
+               }
+               
+               return $data;
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       public function getElementIdentifier(\DOMElement $element): string {
+               return $element->getAttribute('identifier');
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       protected function setEntryListKeys(IDevtoolsPipEntryList $entryList) {
+               $entryList->setKeys([
+                       'identifier' => 'wcf.acp.pip.menu.identifier'
+               ]);
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       protected function sortDocument(\DOMDocument $document) {
+               $this->sortImportDelete($document);
+               
+               $compareFunction = function(\DOMElement $element1, \DOMElement $element2) {
+                       return strcmp(
+                               $element1->getAttribute('identifier'),
+                               $element2->getAttribute('identifier')
+                       );
+               };
+               
+               $this->sortChildNodes($document->getElementsByTagName('import'), $compareFunction);
+               $this->sortChildNodes($document->getElementsByTagName('delete'), $compareFunction);
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       protected function writeEntry(\DOMDocument $document, IFormDocument $form): \DOMElement {
+               $formData = $form->getData();
+               
+               if ($formData['data']['identifier'] === 'com.woltlab.wcf.MainMenu') {
+                       $formData['data']['boxPosition'] = 'mainMenu';
+               }
+               
+               $menu = $document->createElement($this->tagName);
+               $menu->setAttribute('identifier', $formData['data']['identifier']);
+               
+               foreach ($formData['title_i18n'] as $languageID => $title) {
+                       $title = $document->createElement('title', $this->getAutoCdataValue($title));
+                       $title->setAttribute('language', LanguageFactory::getInstance()->getLanguage($languageID)->languageCode);
+                       
+                       $menu->appendChild($title);
+               }
+               
+               if ($formData['data']['createBox']) {
+                       $box = $document->createElement('box');
+                       
+                       $box->appendChild($document->createElement('position', $formData['data']['boxPosition']));
+                       
+                       foreach (['showHeader' => 0, 'visibleEverywhere' => 0, 'cssClassName' => ''] as $boxProperty => $defaultValue) {
+                               $index = 'box' . ucfirst($boxProperty);
+                               if (isset($formData['data'][$index]) && $formData['data'][$index] !== $defaultValue) {
+                                       $box->appendChild($document->createElement($boxProperty, (string)$formData['data'][$index]));
+                               }
+                       }
+                       
+                       if (!empty($formData['data']['boxVisibilityExceptions'])) {
+                               $visibilityExceptions = $box->appendChild($document->createElement('visibilityExceptions'));
+                               
+                               foreach ($formData['data']['boxVisibilityExceptions'] as $pageIdentifier) {
+                                       $visibilityExceptions->appendChild($document->createElement('page', $pageIdentifier));
+                               }
+                       }
+                       
+                       $menu->appendChild($box);
+               }
+               
+               $document->getElementsByTagName('import')->item(0)->appendChild($menu);
+               
+               return $menu;
+       }
 }
index 96f17ed891ddb04c0372f7c7051d8c8a99db2bdf..f464ffbb0e9156714d65b7585423d61144258397 100644 (file)
@@ -1947,6 +1947,23 @@ If you have <strong>already bought the licenses for the listed apps</strong>, th
                <item name="wcf.acp.pip.cronjob.startHour.error.format"><![CDATA[TODO]]></item>
                <item name="wcf.acp.pip.cronjob.startMinute.error.format"><![CDATA[TODO]]></item>
                <item name="wcf.acp.pip.cronjob.startMonth.error.format"><![CDATA[TODO]]></item>
+               <item name="wcf.acp.pip.menu.identifier"><![CDATA[Menu Identifier]]></item>
+               <item name="wcf.acp.pip.menu.identifier.description"><![CDATA[The menu identifier is used to update menus and for menu-related language item names. The identifier 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 menu identifier is the package identifier. Example: <code>com.foo.bar.menu</code>]]></item>
+               <item name="wcf.acp.pip.menu.identifier.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.menu.identifier.error.notUnique"><![CDATA[The entered identifier is already used by another menu.]]></item>
+               <item name="wcf.acp.pip.menu.identifier.error.tooFewSegments"><![CDATA[The entered menu identifier only contains {#$segmentCount} segment{if $segmentCount > 1}s{/if}.]]></item>
+               <item name="wcf.acp.pip.menu.title.description"><![CDATA[]]></item>
+               <item name="wcf.acp.pip.menu.createBox"><![CDATA[Automatically Create Box for Menu]]></item>
+               <item name="wcf.acp.pip.menu.createBox.description"><![CDATA[If this option is selected, after creating the menu, a box for the menu will also be created.]]></item>
+               <item name="wcf.acp.pip.menu.boxPosition"><![CDATA[Box Position]]></item>
+               <item name="wcf.acp.pip.menu.boxPosition.description"><![CDATA[Determines the position at which the box containing the menu will be shown.]]></item>
+               <item name="wcf.acp.pip.menu.boxShowHeader"><![CDATA[Show Box Header]]></item>
+               <item name="wcf.acp.pip.menu.boxShowHeader.description"><![CDATA[The title in the box header is the title of the menu.]]></item>
+               <item name="wcf.acp.pip.menu.boxVisibleEverywhere"><![CDATA[Box Visible Everywhere]]></item>
+               <item name="wcf.acp.pip.menu.boxVisibilityExceptions.hiddenEverywhere"><![CDATA[Pages on Which the Box Will be Explicitly <strong>Visible</strong>]]></item>
+               <item name="wcf.acp.pip.menu.boxVisibilityExceptions.visibleEverywhere"><![CDATA[Pages on Which the Box Will be Explicitly <strong>Hidden</strong>]]></item>
+               <item name="wcf.acp.pip.menu.boxCssClassName"><![CDATA[Box CSS Classes]]></item>
+               <item name="wcf.acp.pip.menu.boxCssClassName.description"><![CDATA[The entered comma-separated CSS classes are assigned to the box containing the menu.]]></item>
        </category>
        
        <category name="wcf.acp.rebuildData">