Add GUI support for user option package installation plugin
authorMatthias Schmidt <gravatronics@live.com>
Sat, 13 Oct 2018 08:40:38 +0000 (10:40 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Sat, 13 Oct 2018 08:40:38 +0000 (10:40 +0200)
See #2545

wcfsetup/install/files/lib/system/package/plugin/UserGroupOptionPackageInstallationPlugin.class.php
wcfsetup/install/files/lib/system/package/plugin/UserOptionPackageInstallationPlugin.class.php
wcfsetup/install/lang/en.xml

index dda019f57f8068baf7f7acec7a983bed83d2834c..596bd7347d7321cf1af2de3ec834368a005db1f6 100644 (file)
@@ -21,7 +21,7 @@ use wcf\util\StringUtil;
 /**
  * Installs, updates and deletes user group options.
  * 
- * @author     Alexander Ebert
+ * @author     Alexander Ebert, Matthias Schmidt
  * @copyright  2001-2018 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    WoltLabSuite\Core\System\Package\Plugin
index 8aac2d462b3ac4afbf02ac770928a950159d4019..d07ffdb53c1da4dd7ec53667c3f5621fec49209f 100644 (file)
@@ -1,28 +1,48 @@
 <?php
 namespace wcf\system\package\plugin;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\option\category\OptionCategory;
+use wcf\data\option\Option;
 use wcf\data\user\option\category\UserOptionCategory;
 use wcf\data\user\option\category\UserOptionCategoryEditor;
 use wcf\data\user\option\UserOption;
 use wcf\data\user\option\UserOptionEditor;
-use wcf\system\devtools\pip\IIdempotentPackageInstallationPlugin;
+use wcf\system\devtools\pip\IGuiPackageInstallationPlugin;
 use wcf\system\exception\SystemException;
+use wcf\system\form\builder\container\IFormContainer;
+use wcf\system\form\builder\field\BooleanFormField;
+use wcf\system\form\builder\field\ClassNameFormField;
+use wcf\system\form\builder\field\dependency\ValueFormFieldDependency;
+use wcf\system\form\builder\field\SingleSelectionFormField;
+use wcf\system\form\builder\field\TextFormField;
+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\option\user\IUserOptionOutput;
+use wcf\system\option\user\UserOptionHandler;
+use wcf\system\Regex;
 use wcf\system\WCF;
 use wcf\util\StringUtil;
 
 /**
  * Installs, updates and deletes user options.
  * 
- * @author     Alexander Ebert
+ * @author     Alexander Ebert, Matthias Schmidt
  * @copyright  2001-2018 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    WoltLabSuite\Core\System\Package\Plugin
  */
-class UserOptionPackageInstallationPlugin extends AbstractOptionPackageInstallationPlugin implements IIdempotentPackageInstallationPlugin {
+class UserOptionPackageInstallationPlugin extends AbstractOptionPackageInstallationPlugin implements IGuiPackageInstallationPlugin {
        /**
         * @inheritDoc
         */
        public $tableName = 'user_option';
        
+       /**
+        * @inheritDoc
+        */
+       public $tagName = 'option';
+       
        /**
         * list of names of tags which aren't considered as additional data
         * @var string[]
@@ -36,8 +56,8 @@ class UserOptionPackageInstallationPlugin extends AbstractOptionPackageInstallat
                // use for create and update
                $data = [
                        'parentCategoryName' => $category['parentCategoryName'],
-                       'permissions' => $category['permissions'],
-                       'options' => $category['options']
+                       'permissions' => $category['permissions'] ?? '',
+                       'options' => $category['options'] ?? ''
                ];
                // append show order if explicitly stated
                if ($category['showOrder'] !== null) $data['showOrder'] = $category['showOrder'];
@@ -163,4 +183,220 @@ class UserOptionPackageInstallationPlugin extends AbstractOptionPackageInstallat
                // uninstall options and categories
                parent::uninstall();
        }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       public function addFormFields(IFormDocument $form) {
+               parent::addFormFields($form);
+               
+               if ($this->entryType === 'options') {
+                       /** @var IFormContainer $dataContainer */
+                       $dataContainer = $form->getNodeById('data');
+                       
+                       /** @var SingleSelectionFormField $optionType */
+                       $optionType = $form->getNodeById('optionType');
+                       
+                       $dataContainer->appendChildren([
+                               BooleanFormField::create('required')
+                                       ->label('wcf.acp.pip.userOption.options.required')
+                                       ->description('wcf.acp.pip.userOption.options.required.description'),
+                               
+                               BooleanFormField::create('askDuringRegistration')
+                                       ->objectProperty('askduringregistration')
+                                       ->label('wcf.acp.pip.userOption.options.askDuringRegistration')
+                                       ->description('wcf.acp.pip.userOption.options.askDuringRegistration.description'),
+                               
+                               SingleSelectionFormField::create('editable')
+                                       ->label('wcf.acp.pip.userOption.options.editable')
+                                       ->description('wcf.acp.pip.userOption.options.editable.description')
+                                       ->options([
+                                               1 => 'wcf.acp.user.option.editable.1',
+                                               2 => 'wcf.acp.user.option.editable.2',
+                                               3 => 'wcf.acp.user.option.editable.3',
+                                               6 => 'wcf.acp.user.option.editable.6'
+                                       ]),
+                               
+                               SingleSelectionFormField::create('visible')
+                                       ->label('wcf.acp.pip.userOption.options.visible')
+                                       ->description('wcf.acp.pip.userOption.options.visible.description')
+                                       ->options([
+                                               0 => 'wcf.acp.user.option.visible.0',
+                                               1 => 'wcf.acp.user.option.visible.1',
+                                               2 => 'wcf.acp.user.option.visible.2',
+                                               3 => 'wcf.acp.user.option.visible.3',
+                                               7 => 'wcf.acp.user.option.visible.7',
+                                               15 => 'wcf.acp.user.option.visible.15',
+                                       ]),
+                               
+                               ClassNameFormField::create('outputClass')
+                                       ->objectProperty('outputclass')
+                                       ->label('wcf.acp.pip.userOption.options.outputClass')
+                                       ->implementedInterface(IUserOptionOutput::class),
+                               
+                               BooleanFormField::create('searchable')
+                                       ->label('wcf.acp.pip.userOption.options.searchable')
+                                       ->description('wcf.acp.pip.userOption.options.searchable.description'),
+                               
+                               BooleanFormField::create('isDisabled')
+                                       ->objectProperty('isdisabled')
+                                       ->label('wcf.acp.pip.userOption.options.isDisabled')
+                                       ->description('wcf.acp.pip.userOption.options.isDisabled.description'),
+                               
+                               // option type-specific fields
+                               SingleSelectionFormField::create('messageObjectType')
+                                       ->label('wcf.acp.pip.userOption.options.messageObjectType')
+                                       ->description('wcf.acp.pip.userOption.options.messageObjectType.description')
+                                       ->options(function() {
+                                               $options = [];
+                                               foreach (ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.message') as $objectType) {
+                                                       $options[$objectType->objectType] = $objectType->objectType;
+                                               }
+                                               
+                                               asort($options);
+                                               
+                                               return $options;
+                                       }, false, false)
+                                       ->addDependency(
+                                               ValueFormFieldDependency::create('optionType')
+                                                       ->field($optionType)
+                                                       ->values(['aboutMe', 'message'])
+                                       ),
+                               
+                               TextFormField::create('contentPattern')
+                                       ->objectProperty('contentpattern')
+                                       ->label('wcf.acp.pip.userOption.options.contentPattern')
+                                       ->description('wcf.acp.pip.userOption.options.contentPattern.description')
+                                       ->addValidator(new FormFieldValidator('regex', function(TextFormField $formField) {
+                                               if ($formField->getSaveValue() !== '') {
+                                                       if (!Regex::compile($formField->getSaveValue())->isValid()) {
+                                                               $formField->addValidationError(
+                                                                       new FormFieldValidationError(
+                                                                               'invalid',
+                                                                               'wcf.acp.pip.userOption.options.contentPattern.error.invalid'
+                                                                       )
+                                                               );
+                                                       }
+                                               }
+                                       }))
+                                       ->addDependency(
+                                               ValueFormFieldDependency::create('optionType')
+                                                       ->field($optionType)
+                                                       ->values(['text'])
+                                       ),
+                       ]);
+               }
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       protected function getElementData(\DOMElement $element, $saveData = false) {
+               $data = parent::getElementData($element, $saveData);
+               
+               switch ($this->entryType) {
+                       case 'options':
+                               $optionals = [
+                                       'required',
+                                       'askDuringRegistration',
+                                       'editable',
+                                       'visible',
+                                       'outputClass',
+                                       'searchable',
+                                       'isDisabled',
+                                       'contentPattern'
+                               ];
+                               
+                               foreach ($optionals as $optionalPropertyName) {
+                                       $optionalProperty = $element->getElementsByTagName(strtolower($optionalPropertyName))->item(0);
+                                       if ($optionalProperty !== null) {
+                                               if ($saveData) {
+                                                       $data[strtolower($optionalPropertyName)] = $optionalProperty->nodeValue;
+                                               }
+                                               else {
+                                                       $data[$optionalPropertyName] = $optionalProperty->nodeValue;
+                                               }
+                                       }
+                               }
+                               
+                               $messageObjectType = $element->getElementsByTagName('messageObjectType')->item(0);
+                               if ($messageObjectType !== null) {
+                                       $data['messageObjectType'] = $messageObjectType->nodeValue;
+                               }
+                               
+                               break;
+               }
+               
+               return $data;
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       protected function getSortOptionHandler() {
+               // reuse UserGroupOptionHandler
+               return new class(true) extends UserOptionHandler {
+                       /**
+                        * @inheritDoc
+                        */
+                       protected function checkCategory(OptionCategory $category) {
+                               // we do not care for category checks here
+                               return true;
+                       }
+                       
+                       /**
+                        * @inheritDoc
+                        */
+                       protected function checkOption(Option $option) {
+                               // we do not care for option checks here
+                               return true;
+                       }
+                       
+                       /**
+                        * @inheritDoc
+                        */
+                       public function getCategoryOptions($categoryName = '', $inherit = true) {
+                               // we just need to ensure that the category is not empty
+                               return [new Option(null, [])];
+                       }
+               };
+       }
+       
+       /**
+        * @inheritDoc
+        * @since       3.2
+        */
+       protected function writeEntry(\DOMDocument $document, IFormDocument $form) {
+               $option = parent::writeEntry($document, $form);
+               
+               $formData = $form->getData()['data'];
+               
+               switch ($this->entryType) {
+                       case 'options':
+                               $fields = [
+                                       'outputclass' => '',
+                                       'required' => 0,
+                                       'askduringregistration' => 0,
+                                       'editable' => 0,
+                                       'visible' => 0,
+                                       'searchable' => 0,
+                                       'isdisabled' => 0,
+                                       'messageObjectType' => '',
+                                       'contentpattern' => ''
+                               ];
+                               
+                               foreach ($fields as $field => $defaultValue) {
+                                       if (isset($formData[$field]) && $formData[$field] !== $defaultValue) {
+                                               $option->appendChild($document->createElement($field, (string) $formData[$field]));
+                                       }
+                               }
+                               
+                               break;
+               }
+               
+               return $option;
+       }
 }
index 1f6ad773493e03aabc3e7509db0369c474422916..f0da18b35e54b0632e443ce1ceb66ee3ef0f097f 100644 (file)
@@ -2204,6 +2204,25 @@ If you have <strong>already bought the licenses for the listed apps</strong>, th
                <item name="wcf.acp.pip.userGroupOption.options.excludedInTinyBuild.description"><![CDATA[If the accelared guest view is enabled, the user group option’s value is “Never” for guests regardless of the actual value set for the guests user group.]]></item>
                <item name="wcf.acp.pip.userGroupOption.options.wildcard"><![CDATA[Wildcard]]></item>
                <item name="wcf.acp.pip.userGroupOption.options.wildcard.description"><![CDATA[If a wildcard is specified and if one line of the user group option value only contains this wildcare, the entire option value will be replaced by the wildcard.]]></item>
+               <item name="wcf.acp.pip.userOption.options.required"><![CDATA[Required User Option]]></item>
+               <item name="wcf.acp.pip.userOption.options.required.description"><![CDATA[Users must provide a value for this user option.]]></item>
+               <item name="wcf.acp.pip.userOption.options.askDuringRegistration"><![CDATA[Available During Registration]]></item>
+               <item name="wcf.acp.pip.userOption.options.askDuringRegistration.description"><![CDATA[The user option is available during registration.]]></item>
+               <item name="wcf.acp.pip.userOption.options.editable"><![CDATA[Editability]]></item>
+               <item name="wcf.acp.pip.userOption.options.editable.description"><![CDATA[The user option value can only be edited by the selected group of users.]]></item>
+               <item name="wcf.acp.pip.userOption.options.visible"><![CDATA[Visibility]]></item>
+               <item name="wcf.acp.pip.userOption.options.visible.description"><![CDATA[The user option value is only visible for the selected group of users.]]></item>
+               <item name="wcf.acp.pip.userOption.options.outputClass"><![CDATA[PHP class for custom output format]]></item>
+               <item name="wcf.acp.pip.userOption.options.searchable"><![CDATA[User option value is searchable]]></item>
+               <item name="wcf.acp.pip.userOption.options.searchable.description"><![CDATA[Users can search this user option in the member search.]]></item>
+               <item name="wcf.acp.pip.userOption.options.isDisabled"><![CDATA[Disable User Option]]></item>
+               <item name="wcf.acp.pip.userOption.options.isDisabled.description"><![CDATA[The user option is not visible for users and its value cannot be set.]]></item>
+               <item name="wcf.acp.pip.userOption.options.optionType.description"><![CDATA[The option type determines the interface with which the value of the user group option is set and which (types of) values the option may have.]]></item>
+               <item name="wcf.acp.pip.userOption.options.messageObjectType"><![CDATA[Message Object Type]]></item>
+               <item name="wcf.acp.pip.userOption.options.messageObjectType.description"><![CDATA[The message object type is passed to the <code>wcf\system\html\input\HtmlInputProcessor</code> object used to process the user option value.]]></item>
+               <item name="wcf.acp.pip.userOption.options.contentPattern"><![CDATA[Content Pattern]]></item>
+               <item name="wcf.acp.pip.userOption.options.contentPattern.description"><![CDATA[The entered content pattern is used to extract specific parts of the user-entered value to be stored as the option value.]]></item>
+               <item name="wcf.acp.pip.userOption.options.contentPattern.error.invalid"><![CDATA[The entered regular expression is invalid.]]></item>
        </category>
        
        <category name="wcf.acp.reactionType">