<?php
declare(strict_types=1);
namespace wcf\system\package\plugin;
+use wcf\page\IPage;
+use wcf\system\devtools\pip\IDevtoolsPipEntryList;
use wcf\system\devtools\pip\IIdempotentPackageInstallationPlugin;
+use wcf\system\devtools\pip\TXmlGuiPackageInstallationPlugin;
use wcf\system\exception\SystemException;
+use wcf\system\form\builder\container\IFormContainer;
+use wcf\system\form\builder\field\ClassNameFormField;
+use wcf\system\form\builder\field\IntegerFormField;
+use wcf\system\form\builder\field\OptionFormField;
+use wcf\system\form\builder\field\SingleSelectionFormField;
+use wcf\system\form\builder\field\TextFormField;
+use wcf\system\form\builder\field\UserGroupOptionFormField;
+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\WCF;
use wcf\util\StringUtil;
+use wcf\util\Url;
/**
* Abstract implementation of a package installation plugin for menu items.
* @package WoltLabSuite\Core\System\Package\Plugin
*/
abstract class AbstractMenuPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IIdempotentPackageInstallationPlugin {
+ // we do no implement `IGuiPackageInstallationPlugin` but instead just
+ // provide the default implementation to ensure backwards compatibility
+ // with third-party packages containing classes that extend this abstract
+ // class
+ use TXmlGuiPackageInstallationPlugin;
+
/**
* @inheritDoc
*/
/**
* @inheritDoc
+ * @since 3.1
*/
public static function getSyncDependencies() {
return [];
}
+
+ /**
+ * @inheritDoc
+ * @since 3.2
+ */
+ public function addFormFields(IFormDocument $form) {
+ /** @var IFormContainer $dataContainer */
+ $dataContainer = $form->getNodeById('data');
+
+ $dataContainer->appendChildren([
+ TextFormField::create('menuItem')
+ ->objectProperty('name')
+ ->label('wcf.acp.pip.abstractMenu.menuItem'),
+
+ SingleSelectionFormField::create('parentMenuItem')
+ ->objectProperty('parent')
+ ->label('wcf.acp.pip.abstractForm.parentMenuItem')
+ ->filterable(),
+
+ ClassNameFormField::create('menuItemController')
+ ->objectProperty('controller')
+ ->label('wcf.acp.pip.abstractForm.menuItemController')
+ ->implementedInterface(IPage::class),
+
+ TextFormField::create('menuItemLink')
+ ->objectProperty('link')
+ ->label('wcf.acp.pip.abstractMenu.menuItemLink')
+ ->description('wcf.acp.pip.abstractMenu.menuItemLink.description')
+ ->objectProperty('link')
+ ->addValidator(new FormFieldValidator('linkSpecified', function(TextFormField $formField) {
+ /** @var ClassNameFormField $menuItemController */
+ $menuItemController = $formField->getDocument()->getNodeById('menuItemController');
+
+ // ensure that either a menu item controller is specified or a link
+ if ($formField->getSaveValue() === '' && $menuItemController->getSaveValue() === '') {
+ $formField->addValidationError(
+ new FormFieldValidationError(
+ 'noLinkSpecified',
+ 'wcf.acp.pip.abstractMenu.menuItemLink.error.noLinkSpecified'
+ )
+ );
+ }
+ }))
+ ->addValidator(new FormFieldValidator('format', function(TextFormField $formField) {
+ if ($formField->getSaveValue() !== '') {
+ /** @var ClassNameFormField $menuItemController */
+ $menuItemController = $formField->getDocument()->getNodeById('menuItemController');
+
+ if (!$menuItemController->getSaveValue() && !Url::is($formField->getSaveValue())) {
+ $formField->addValidationError(
+ new FormFieldValidationError(
+ 'noLink',
+ 'wcf.acp.pip.abstractMenu.menuItemLink.error.noLink'
+ )
+ );
+ }
+ else if ($menuItemController->getSaveValue() && Url::is($formField->getSaveValue())) {
+ $formField->addValidationError(
+ new FormFieldValidationError(
+ 'isLink',
+ 'wcf.acp.pip.abstractMenu.menuItemLink.error.isLink'
+ )
+ );
+ }
+ }
+ })),
+
+ OptionFormField::create()
+ ->description('wcf.acp.pip.abstractMenu.options.description')
+ ->saveValueType(OptionFormField::SAVE_VALUE_TYPE_CSV)
+ ->packageIDs(array_merge(
+ [$this->installation->getPackage()->packageID],
+ array_keys($this->installation->getPackage()->getAllRequiredPackages())
+ )),
+
+ UserGroupOptionFormField::create()
+ ->description('wcf.acp.pip.abstractMenu.options.description')
+ ->saveValueType(OptionFormField::SAVE_VALUE_TYPE_CSV)
+ ->packageIDs(array_merge(
+ [$this->installation->getPackage()->packageID],
+ array_keys($this->installation->getPackage()->getAllRequiredPackages())
+ )),
+
+ IntegerFormField::create('showOrder')
+ ->objectProperty('showorder')
+ ->label('wcf.acp.pip.abstractMenu.showOrder')
+ ->description('wcf.acp.pip.abstractMenu.showOrder.description')
+ ->objectProperty('showorder')
+ ->minimum(1)
+ ]);
+ }
+
+ /**
+ * @inheritDoc
+ * @since 3.2
+ */
+ protected function getElementData(\DOMElement $element): array {
+ $data = [
+ 'menuItem' => $element->getAttribute('name'),
+ 'packageID' => $this->installation->getPackage()->packageID
+ ];
+
+ $parentMenuItem = $element->getElementsByTagName('parent')->item(0);
+ if ($parentMenuItem !== null) {
+ $data['parentMenuItem'] = $parentMenuItem->nodeValue;
+ }
+
+ $controller = $element->getElementsByTagName('controller')->item(0);
+ if ($controller !== null) {
+ $data['menuItemController'] = $controller->nodeValue;
+ }
+
+ $link = $element->getElementsByTagName('link')->item(0);
+ if ($link !== null) {
+ $data['menuItemLink'] = $link->nodeValue;
+ }
+
+ $options = $element->getElementsByTagName('options')->item(0);
+ if ($options !== null) {
+ $data['options'] = $options->nodeValue;
+ }
+
+ $permissions = $element->getElementsByTagName('permissions')->item(0);
+ if ($permissions !== null) {
+ $data['permissions'] = $permissions->nodeValue;
+ }
+
+ $showOrder = $element->getElementsByTagName('showOrder')->item(0);
+ if ($showOrder !== null) {
+ $data['showOrder'] = $showOrder->nodeValue;
+ }
+
+ return $data;
+ }
+
+ /**
+ * @inheritDoc
+ * @since 3.2
+ */
+ public function getElementIdentifier(\DOMElement $element): string {
+ return $element->getAttribute('name');
+ }
+
+ /**
+ * @inheritDoc
+ * @since 3.2
+ */
+ protected function setEntryListKeys(IDevtoolsPipEntryList $entryList) {
+ $entryList->setKeys([
+ 'menuItem' => 'wcf.acp.pip.abstractMenu.menuItem',
+ 'parentMenuItem' => 'wcf.acp.pip.abstractMenu.parentMenuItem'
+ ]);
+ }
+
+ /**
+ * @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 {
+ $formData = $form->getData()['data'];
+
+ $menuItem = $document->createElement($this->tagName);
+ $menuItem->setAttribute('name', $formData['name']);
+
+ foreach (['parent', 'controller', 'link', 'options', 'permissions', 'showorder'] as $field) {
+ if (isset($formData[$field]) && $formData[$field] !== '') {
+ $menuItem->appendChild($document->createElement($field, (string) $formData[$field]));
+ }
+ }
+
+ $document->getElementsByTagName('import')->item(0)->appendChild($menuItem);
+
+ return $menuItem;
+ }
}
<item name="wcf.acp.pip.aclOption.objectType.categories.error.notUnique"><![CDATA[The entered name is already used by another ACL option category for the same ACL object type.]]></item>
<item name="wcf.acp.pip.aclOption.objectType.options.description"><![CDATA[The ACL object type determines to which type of objects the option belongs to. Note that only ACL object types of the package itself or its required packages are available.]]></item>
<item name="wcf.acp.pip.aclOption.objectType.options.error.notUnique"><![CDATA[The entered name is already used by another ACL option for the same ACL object type.]]></item>
+ <item name="wcf.acp.pip.abstractMenu.menuItem"><![CDATA[Menu Item Identifier]]></item>
+ <item name="wcf.acp.pip.abstractMenu.menuItem.error.notUnique"><![CDATA[The entered identifier is already used by another menu item.]]></item>
+ <item name="wcf.acp.pip.abstractForm.parentMenuItem"><![CDATA[Parent Menu Item]]></item>
+ <item name="wcf.acp.pip.abstractForm.menuItemController"><![CDATA[Menu Item Controller]]></item>
+ <item name="wcf.acp.pip.abstractMenu.menuItemLink"><![CDATA[Menu Item Link]]></item>
+ <item name="wcf.acp.pip.abstractMenu.menuItemLink.description"><![CDATA[If no menu item controller is used, the entered link will be used. If, however, a menu item controller is used, the entered link will be appended to the link to the controller.]]></item>
+ <item name="wcf.acp.pip.abstractMenu.menuItemLink.error.isLink"><![CDATA[The entered value is a valid link. If you entered a menu item controller, the menu item link is appended to the link to the controller.]]></item>
+ <item name="wcf.acp.pip.abstractMenu.menuItemLink.error.noLink"><![CDATA[The entered value is no valid link.]]></item>
+ <item name="wcf.acp.pip.abstractMenu.menuItemLink.error.noLinkSpecified"><![CDATA[You either have to enter a menu item controller or a menu item link.]]></item>
+ <item name="wcf.acp.pip.abstractMenu.options.description"><![CDATA[At least one of the entered options has to be enabled for the menu item to be visible. Options of non-required packages will be reported as non-existing.]]></item>
+ <item name="wcf.acp.pip.abstractMenu.permissions.description"><![CDATA[The active user must be granted at least one of the entered permissions in order to see the menu item. Permissions of non-required packages will be reported as non-existing.]]></item>
+ <item name="wcf.acp.pip.abstractMenu.showOrder"><![CDATA[Position]]></item>
+ <item name="wcf.acp.pip.abstractMenu.showOrder.description"><![CDATA[The entered value determines in which order the menu items with the same parent are shown.]]></item>
</category>
<category name="wcf.acp.rebuildData">