Added abstract implementation for custom options
authorAlexander Ebert <ebert@woltlab.com>
Fri, 16 Jun 2017 12:17:10 +0000 (14:17 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Fri, 16 Jun 2017 12:25:44 +0000 (14:25 +0200)
See #2308

wcfsetup/install/files/lib/acp/form/AbstractCustomOptionForm.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/custom/option/CustomOption.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/custom/option/CustomOptionAction.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/custom/option/CustomOptionEditor.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/custom/option/CustomOptionList.class.php [new file with mode: 0644]

diff --git a/wcfsetup/install/files/lib/acp/form/AbstractCustomOptionForm.class.php b/wcfsetup/install/files/lib/acp/form/AbstractCustomOptionForm.class.php
new file mode 100644 (file)
index 0000000..aa64557
--- /dev/null
@@ -0,0 +1,258 @@
+<?php
+namespace wcf\acp\form;
+use wcf\data\option\Option;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\exception\UserInputException;
+use wcf\system\language\I18nValue;
+use wcf\system\WCF;
+
+/**
+ * Default implementation for custom options utilizing the option system.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2017 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Acp\Form
+ * @since       3.1
+ */
+abstract class AbstractCustomOptionForm extends AbstractAcpForm {
+       /**
+        * option name
+        * @var string
+        */
+       public $optionTitle = '';
+       
+       /**
+        * option description
+        * @var string
+        */
+       public $optionDescription = '';
+       
+       /**
+        * option type
+        * @var string
+        */
+       public $optionType = 'text';
+       
+       /**
+        * option default value
+        * @var string
+        */
+       public $defaultValue = '';
+       
+       /**
+        * validation pattern
+        * @var string
+        */
+       public $validationPattern = '';
+       
+       /**
+        * select options
+        * @var string
+        */
+       public $selectOptions = '';
+       
+       /**
+        * field is required
+        * @var boolean
+        */
+       public $required = 0;
+       
+       /**
+        * show order
+        * @var integer
+        */
+       public $showOrder = 0;
+       
+       /**
+        * action class name
+        * @var string
+        */
+       public $actionClass = '';
+       
+       /**
+        * base class name
+        * @var string
+        */
+       public $baseClass = '';
+       
+       /**
+        * editor class name
+        * @var string
+        */
+       public $editorClass = '';
+       
+       /**
+        * object instance
+        * @var Option
+        */
+       public $object;
+       
+       /**
+        * object id
+        * @var integer
+        */
+       public $objectID;
+       
+       /**
+        * available option types
+        * @var string[]
+        */
+       public static $availableOptionTypes = [
+               'boolean',
+               'checkboxes',
+               'date',
+               'integer',
+               'float',
+               'multiSelect',
+               'radioButton',
+               'select',
+               'text',
+               'textarea',
+               'URL'
+       ];
+       
+       /**
+        * list of option type that require select options
+        * @var string[]
+        */
+       public static $optionTypesUsingSelectOptions = [
+               'checkboxes',
+               'multiSelect',
+               'radioButton',
+               'select'
+       ];
+       
+       /**
+        * @inheritDoc
+        */
+       public function readParameters() {
+               parent::readParameters();
+               
+               if (empty($this->action)) {
+                       throw new \RuntimeException("The 'action' property must equal 'add' or 'edit'.");
+               }
+               
+               if ($this->action === 'edit') {
+                       if (isset($_REQUEST['id'])) $this->objectID = intval($_REQUEST['id']);
+                       $this->object = new $this->baseClass($this->objectID);
+                       if (!$this->object->getObjectID()) {
+                               throw new IllegalLinkException();
+                       }
+               }
+               
+               $this->registerI18nValue(new I18nValue('optionTitle'));
+               
+               $optionDescription = new I18nValue('optionDescription');
+               $optionDescription->setFlags(I18nValue::ALLOW_EMPTY);
+               $this->registerI18nValue($optionDescription);
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function readFormParameters() {
+               parent::readFormParameters();
+               
+               if (isset($_POST['optionType'])) $this->optionType = $_POST['optionType'];
+               if (isset($_POST['defaultValue'])) $this->defaultValue = $_POST['defaultValue'];
+               if (isset($_POST['validationPattern'])) $this->validationPattern = $_POST['validationPattern'];
+               if (isset($_POST['selectOptions'])) $this->selectOptions = $_POST['selectOptions'];
+               if (isset($_POST['required'])) $this->required = intval($_POST['required']);
+               if (isset($_POST['showOrder'])) $this->showOrder = intval($_POST['showOrder']);
+               
+               if ($this->optionType == 'boolean' || $this->optionType == 'integer') {
+                       $this->defaultValue = intval($this->defaultValue);
+               }
+               if ($this->optionType == 'float') {
+                       $this->defaultValue = floatval($this->defaultValue);
+               }
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function validate() {
+               parent::validate();
+               
+               // option type
+               if (!in_array($this->optionType, self::$availableOptionTypes)) {
+                       throw new UserInputException('optionType');
+               }
+               
+               // select options
+               if (in_array($this->optionType, self::$optionTypesUsingSelectOptions) && empty($this->selectOptions)) {
+                       throw new UserInputException('selectOptions');
+               }
+       }
+       
+       /**
+        * Returns the list of database values including additional fields.
+        * 
+        * @return      array
+        */
+       protected function getDatabaseValues() {
+               return array_merge($this->additionalFields, [
+                       'optionTitle' => $this->optionTitle,
+                       'optionDescription' => $this->optionDescription,
+                       'optionType' => $this->optionType,
+                       'defaultValue' => $this->defaultValue,
+                       'showOrder' => $this->showOrder,
+                       'validationPattern' => $this->validationPattern,
+                       'selectOptions' => $this->selectOptions,
+                       'required' => $this->required
+               ]);
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function save() {
+               parent::save();
+               
+               if ($this->action === 'add') {
+                       $this->objectAction = new $this->actionClass([], 'create', ['data' => $this->getDatabaseValues()]);
+                       
+                       $this->saveI18n($this->objectAction->executeAction()['returnValues'], $this->editorClass);
+                       
+                       $this->reset();
+               }
+               else {
+                       $this->saved();
+                       
+                       // show success message
+                       WCF::getTPL()->assign('success', true);
+               }
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function reset() {
+               parent::reset();
+               
+               // reset values
+               $this->optionTitle = $this->optionDescription = $this->optionType = $this->defaultValue = $this->validationPattern = $this->selectOptions = '';
+               $this->optionType = 'text';
+               $this->required = $this->showOrder = 0;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function assignVariables() {
+               parent::assignVariables();
+               
+               WCF::getTPL()->assign([
+                       'defaultValue' => $this->defaultValue,
+                       'validationPattern' => $this->validationPattern,
+                       'optionType' => $this->optionType,
+                       'selectOptions' => $this->selectOptions,
+                       'required' => $this->required,
+                       'showOrder' => $this->showOrder,
+                       'action' => $this->action,
+                       'availableOptionTypes' => self::$availableOptionTypes,
+                       'optionTypesUsingSelectOptions' => self::$optionTypesUsingSelectOptions
+               ]);
+       }
+}
diff --git a/wcfsetup/install/files/lib/data/custom/option/CustomOption.class.php b/wcfsetup/install/files/lib/data/custom/option/CustomOption.class.php
new file mode 100644 (file)
index 0000000..246c53e
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+namespace wcf\data\custom\option;
+use wcf\data\option\Option;
+use wcf\system\bbcode\MessageParser;
+use wcf\system\bbcode\SimpleMessageParser;
+use wcf\system\exception\NotImplementedException;
+use wcf\system\WCF;
+use wcf\util\DateUtil;
+use wcf\util\OptionUtil;
+use wcf\util\StringUtil;
+
+/**
+ * Default implementation for custom options.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2017 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Custom\Option
+ * @since      3.1
+ * 
+ * @property-read      integer         $optionID               unique id of the option
+ * @property-read      string          $optionTitle            title of the option or name of language item which contains the title
+ * @property-read      string          $optionDescription      description of the option or name of language item which contains the description
+ * @property-read      string          $optionType             type of the option which determines its input and output
+ * @property-read      string          $defaultValue           default value of the option
+ * @property-read      string          $validationPattern      regular expression used to validate the value of the option
+ * @property-read      string          $selectOptions          possible values of the option separated by newlines
+ * @property-read      integer         $required               is `1` if the option has to be filled out, otherwise `0`
+ * @property-read      integer         $showOrder              position of the option relation tp the other options
+ * @property-read      integer         $isDisabled             is `1` if the option is disabled, otherwise `0`
+ */
+abstract class CustomOption extends Option {
+       /**
+        * option value
+        * @var string
+        */
+       protected $optionValue = '';
+       
+       /**
+        * Returns true if the option is visible
+        *
+        * @return      boolean
+        */
+       public function isVisible() {
+               return !$this->isDisabled;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public static function getDatabaseTableAlias() {
+               throw new NotImplementedException();
+       }
+       
+       /**
+        * Returns the value of this option.
+        * 
+        * @return      string
+        */
+       public function getOptionValue() {
+               return $this->optionValue;
+       }
+       
+       /**
+        * Sets the value of this option.
+        *
+        * @param       string          $value
+        */
+       public function setOptionValue($value) {
+               $this->optionValue = $value;
+       }
+       
+       /**
+        * Returns the formatted value of this option.
+        *
+        * @return      string
+        */
+       public function getFormattedOptionValue() {
+               switch ($this->optionType) {
+                       case 'boolean':
+                               return WCF::getLanguage()->get('wcf.acp.option.optionType.boolean.'.($this->optionValue ? 'yes' : 'no'));
+                               
+                       case 'date':
+                               $year = $month = $day = 0;
+                               $optionValue = explode('-', $this->optionValue);
+                               if (isset($optionValue[0])) $year = intval($optionValue[0]);
+                               if (isset($optionValue[1])) $month = intval($optionValue[1]);
+                               if (isset($optionValue[2])) $day = intval($optionValue[2]);
+                               return DateUtil::format(DateUtil::getDateTimeByTimestamp(gmmktime(12, 1, 1, $month, $day, $year)), DateUtil::DATE_FORMAT);
+                       
+                       case 'float':
+                               return StringUtil::formatDouble(intval($this->optionValue));
+                               
+                       case 'integer':
+                               return StringUtil::formatInteger(intval($this->optionValue));
+                               
+                       case 'radioButton':
+                       case 'select':
+                               $selectOptions = OptionUtil::parseSelectOptions($this->selectOptions);
+                               if (isset($selectOptions[$this->optionValue])) return WCF::getLanguage()->get($selectOptions[$this->optionValue]);
+                               return '';
+                               
+                       case 'multiSelect':
+                       case 'checkboxes':
+                               $selectOptions = OptionUtil::parseSelectOptions($this->selectOptions);
+                               $values = explode("\n", $this->optionValue);
+                               $result = '';
+                               foreach ($values as $value) {
+                                       if (isset($selectOptions[$value])) {
+                                               if (!empty($result)) $result .= "<br>\n";
+                                               $result .= WCF::getLanguage()->get($selectOptions[$value]);
+                                       }
+                               }
+                               return $result;
+                               
+                       case 'textarea':
+                               return SimpleMessageParser::getInstance()->parse($this->optionValue);
+                               
+                       case 'message':
+                               return MessageParser::getInstance()->parse($this->optionValue);
+                               
+                       case 'URL':
+                               return StringUtil::getAnchorTag($this->optionValue);
+                               
+                       default:
+                               return StringUtil::encodeHTML($this->optionValue);
+               }
+       }
+}
diff --git a/wcfsetup/install/files/lib/data/custom/option/CustomOptionAction.class.php b/wcfsetup/install/files/lib/data/custom/option/CustomOptionAction.class.php
new file mode 100644 (file)
index 0000000..9c170e5
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+namespace wcf\data\custom\option;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\data\IToggleAction;
+
+/**
+ * Executes option-related actions.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2017 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Custom\Option
+ * @since      3.1
+ * 
+ * @method     CustomOption            create()
+ * @method     CustomOptionEditor[]    getObjects()
+ * @method     CustomOptionEditor      getSingleObject()
+ */
+abstract class CustomOptionAction extends AbstractDatabaseObjectAction implements IToggleAction {
+       /**
+        * @inheritDoc
+        */
+       protected $className = CustomOptionEditor::class;
+       
+       /**
+        * @inheritDoc
+        */
+       public function validateToggle() {
+               $this->validateUpdate();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function toggle() {
+               foreach ($this->getObjects() as $optionEditor) {
+                       $optionEditor->update([
+                               'isDisabled' => 1 - $optionEditor->isDisabled
+                       ]);
+               }
+       }
+}
diff --git a/wcfsetup/install/files/lib/data/custom/option/CustomOptionEditor.class.php b/wcfsetup/install/files/lib/data/custom/option/CustomOptionEditor.class.php
new file mode 100644 (file)
index 0000000..a727088
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+namespace wcf\data\custom\option;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit file options.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2017 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Custom\Option
+ * @since      3.1
+ * 
+ * @method static      CustomOption    create(array $parameters = [])
+ * @method             CustomOption    getDecoratedObject()
+ * @mixin              CustomOption
+ */
+abstract class CustomOptionEditor extends DatabaseObjectEditor {
+       /**
+        * @inheritDoc
+        */
+       protected static $baseClass = CustomOption::class;
+}
diff --git a/wcfsetup/install/files/lib/data/custom/option/CustomOptionList.class.php b/wcfsetup/install/files/lib/data/custom/option/CustomOptionList.class.php
new file mode 100644 (file)
index 0000000..8c5bb39
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+namespace wcf\data\custom\option;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of options.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2017 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Custom\Option
+ * @since      3.1
+ * 
+ * @method     CustomOption            current()
+ * @method     CustomOption[]          getObjects()
+ * @method     CustomOption|null       search($objectID)
+ * @property   CustomOption[]          $objects
+ */
+abstract class CustomOptionList extends DatabaseObjectList {
+       /**
+        * @inheritDoc
+        */
+       public $className = CustomOption::class;
+       
+       /**
+        * @inheritDoc
+        */
+       public $sqlOrderBy = 'showOrder';
+       
+       /**
+        * @inheritDoc
+        */
+       public function __construct() {
+               parent::__construct();
+               
+               $this->sqlSelects = "CONCAT('customOption', CAST({$this->getDatabaseTableAlias()}.optionID AS CHAR)) AS optionName";
+       }
+}