<ol class="flexibleButtonGroup">
<li>
- <input type="radio" id="{@$field->getPrefixedId()}" name="{@$field->getPrefixedId()}" value="1"{if $field->isAutofocused()} autofocus{/if}{if $field->isRequired()} required{/if}{if $field->isImmutable()} disabled{/if}{if $field->getValue()} checked{/if}>
+ <input type="radio" id="{@$field->getPrefixedId()}" name="{@$field->getPrefixedId()}" value="1" data-no-input-id="{@$field->getPrefixedId()}_no"{if $field->isAutofocused()} autofocus{/if}{if $field->isRequired()} required{/if}{if $field->isImmutable()} disabled{/if}{if $field->getValue()} checked{/if}>
<label for="{@$field->getPrefixedId()}" class="green"><span class="icon icon16 fa-check"></span> {lang}wcf.global.form.boolean.yes{/lang}</label>
</li>
<li>
-<section id="{@$container->getPrefixedId()}" class="section{foreach from=$container->getClasses() item='class'} {$class}{/foreach}"{foreach from=$container->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}>
+<section id="{@$container->getPrefixedId()}Container" class="section{foreach from=$container->getClasses() item='class'} {$class}{/foreach}"{foreach from=$container->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{if !$container->checkDependencies()} style="display: none;"{/if}>
{if $container->getLabel() !== null}
{if $container->getDescription() !== null}
<header class="sectionHeader">
{include file='__formContainerChildren'}
</section>
+
+{include file='__formContainerDependencies'}
--- /dev/null
+{if !$container->getDependencies()|empty}
+ <script data-relocate="true">
+ {foreach from=$field->getDependencies() item=dependency}
+ {@$dependency->getHtml()}
+ {/foreach}
+ </script>
+{/if}
--- /dev/null
+{if !$field->getDependencies()|empty}
+ <script data-relocate="true">
+ {foreach from=$field->getDependencies() item=dependency}
+ {@$dependency->getHtml()}
+ {/foreach}
+ </script>
+{/if}
{include file='__formFieldDescription'}
{include file='__formFieldErrors'}
+ {include file='__formFieldDependencies'}
</dd>
</dl>
-<dl id="{@$field->getPrefixedId()}Container" {if !$field->getClasses()|empty} class="{implode from=$field->getClasses() item='class'}{$class}{/implode}"{/if}{foreach from=$field->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}>
+<dl id="{@$field->getPrefixedId()}Container" {if !$field->getClasses()|empty} class="{implode from=$field->getClasses() item='class'}{$class}{/implode}"{/if}{foreach from=$field->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{if !$field->checkDependencies()} style="display: none;"{/if}>
<dt><label for="{@$field->getPrefixedId()}">{@$field->getLabel()}</label></dt>
<dd>
--- /dev/null
+require(['WoltLabSuite/Core/Form/Builder/Field/Dependency/NonEmpty'], function(NonEmptyFieldDependency) {
+ // dependency '{@$dependency->getId()}'
+ new NonEmptyFieldDependency(
+ '{@$dependency->getDependentNode()->getPrefixedId()}Container',
+ '{@$dependency->getField()->getPrefixedId()}'
+ );
+});
-<div id="{@$container->getPrefixedId()}" class="tabMenuContent{foreach from=$container->getClasses() item='class'} {$class}{/foreach}"{foreach from=$container->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}>
+<div id="{@$container->getPrefixedId()}Container" class="tabMenuContent{foreach from=$container->getClasses() item='class'} {$class}{/foreach}"{foreach from=$container->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{if !$container->checkDependencies()} style="display: none;"{/if}>
{include file='__formContainerChildren'}
</div>
+
+{include file='__formContainerDependencies'}
-<div id="{@$container->getPrefixedId()}" class="section tabMenuContainer{foreach from=$container->getClasses() item='class'} {$class}{/foreach}"{foreach from=$container->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}>
+<div id="{@$container->getPrefixedId()}Container" class="section tabMenuContainer{foreach from=$container->getClasses() item='class'} {$class}{/foreach}"{foreach from=$container->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{if !$container->checkDependencies()} style="display: none;"{/if}{if !$container->checkDependencies()} style="display: none;"{/if}>
<nav class="tabMenu">
<ul>
{foreach from=$container item='child'}
- <li><a href="{@$__wcf->getAnchor($child->getPrefixedId())}">{@$child->getLabel()}</a></li>
+ {assign var='__tabMenuFormContainerChildId' value=$child->getPrefixedId()|concat:'Container'}
+ <li><a href="{@$__wcf->getAnchor($__tabMenuFormContainerChildId)}">{@$child->getLabel()}</a></li>
{/foreach}
</ul>
</nav>
{include file='__formContainerChildren'}
</div>
+
+{include file='__formContainerDependencies'}
-<div id="{@$container->getPrefixedId()}" class="tabMenuContainer tabMenuContent{foreach from=$container->getClasses() item='class'} {$class}{/foreach}"{foreach from=$container->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}>
+<div id="{@$container->getPrefixedId()}Container" class="tabMenuContainer tabMenuContent{foreach from=$container->getClasses() item='class'} {$class}{/foreach}"{foreach from=$container->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}>
<nav class="menu">
<ul>
{foreach from=$container item='child'}
{include file='__formContainerChildren'}
</div>
+
+{include file='__formContainerDependencies'}
--- /dev/null
+require(['WoltLabSuite/Core/Form/Builder/Field/Dependency/Value'], function(ValueFieldDependency) {
+ // dependency '{@$dependency->getId()}'
+ new ValueFieldDependency(
+ '{@$dependency->getDependentNode()->getPrefixedId()}Container',
+ '{@$dependency->getField()->getPrefixedId()}'
+ ).values([ {implode from=$dependency->getValues() item=dependencyValue}'{$dependencyValue|encodeJS}'{/implode} ]);
+});
--- /dev/null
+/**
+ * Abstract implementation of a form field dependency.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 3.2
+ */
+define(['./Manager'], function(DependencyManager) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Abstract(dependentElementId, fieldId) {
+ this.init(dependentElementId, fieldId);
+ };
+ Abstract.prototype = {
+ /**
+ * Checks if the dependency is met.
+ *
+ * @abstract
+ */
+ checkDependency: function() {
+ throw new Error("Implement me!")
+ },
+
+ /**
+ * Return the node whose availability depends on the value of a field.
+ *
+ * @return {HtmlElement} dependent node
+ */
+ getDependentNode: function() {
+ return this._dependentElement;
+ },
+
+ /**
+ * Returns the field the availability of the element dependents on.
+ *
+ * @return {HtmlElement} field controlling element availability
+ */
+ getField: function() {
+ return this._field;
+ },
+
+ /**
+ * Returns all fields requiring `change` event listeners for this
+ * dependency to be properly resolved.
+ *
+ * @return {HtmlElement[]} fields to register event listeners on
+ */
+ getFields: function() {
+ return this._fields;
+ },
+
+ /**
+ * Initializes the new dependency object.
+ *
+ * @param {string} dependentElementId id of the (container of the) dependent element
+ * @param {string} fieldId id of the field controlling element availability
+ *
+ * @throws {Error} if either depenent element id or field id are invalid
+ */
+ init: function(dependentElementId, fieldId) {
+ this._dependentElement = elById(dependentElementId);
+ if (this._dependentElement === null) {
+ throw new Error("Unknown dependent element with container id '" + dependentElementId + "Container'.");
+ }
+
+ this._field = elById(fieldId);
+ if (this._field === null) {
+ throw new Error("Unknown field with id '" + fieldId + "'.");
+ }
+
+ this._fields = [this._field];
+
+ // handle special case of boolean form fields that have to form fields
+ if (this._field.tagName === 'INPUT' && this._field.type === 'radio' && elData(this._field, 'no-input-id') !== '') {
+ this._noField = elById(elData(this._field, 'no-input-id'));
+ if (this._noField === null) {
+ throw new Error("Cannot find 'no' input field for input field '" + fieldId + "'");
+ }
+
+ this._fields.push(this._noField);
+ }
+
+ DependencyManager.addDependency(this);
+ }
+ };
+
+ return Abstract;
+});
\ No newline at end of file
--- /dev/null
+/**
+ * Manages form field dependencies.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency
+ * @since 3.2
+ */
+define(['Dictionary'], function(Dictionary) {
+ "use strict";
+
+ /**
+ * list if fields for which event listeners have been registered
+ * @type {Dictionary}
+ * @private
+ */
+ var _fields = new Dictionary();
+
+ /**
+ * list of dependencies grouped by the dependent node they belong to
+ * @type {Dictionary}
+ * @private
+ */
+ var _nodeDependencies = new Dictionary();
+
+ return {
+ /**
+ * Registers a new form field dependency.
+ *
+ * @param {WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract} dependency new dependency
+ */
+ addDependency: function(dependency) {
+ var dependentNode = dependency.getDependentNode();
+ if (!_nodeDependencies.has(dependentNode.id)) {
+ _nodeDependencies.set(dependentNode.id, [dependency]);
+ }
+ else {
+ _nodeDependencies.get(dependentNode.id).push(dependency);
+ }
+
+ var fields = dependency.getFields();
+ for (var i = 0, length = fields.length; i < length; i++) {
+ var field = fields[i];
+ if (!_fields.has(field.id)) {
+ _fields.set(field.id, field);
+
+ if (field.tagName === 'INPUT' && (field.type === 'checkbox' || field.type === 'radio')) {
+ field.addEventListener('change', this.checkDependencies.bind(this));
+ }
+ else {
+ field.addEventListener('input', this.checkDependencies.bind(this));
+ }
+ }
+ }
+ },
+
+ /**
+ * Check all dependencies if they are met.
+ */
+ checkDependencies: function() {
+ var obsoleteNodes = [];
+
+ _nodeDependencies.forEach(function(nodeDependencies, nodeId) {
+ var dependentNode = elById(nodeId);
+
+ // check if dependent node still exists
+ if (dependentNode === null) {
+ obsoleteNodes.push(dependentNode);
+ return;
+ }
+
+ for (var i = 0, length = nodeDependencies.length; i < length; i++) {
+ // if any dependency is met, the element is visible
+ if (nodeDependencies[i].checkDependency()) {
+ elShow(dependentNode);
+ return;
+ }
+ }
+
+ // no node dependencies is met
+ elHide(dependentNode);
+ });
+
+ // delete dependencies for removed elements
+ for (var i = 0, length = obsoleteNodes.length; i < length; i++) {
+ _nodeDependencies.delete(obsoleteNodes.id);
+ }
+ }
+ };
+});
\ No newline at end of file
--- /dev/null
+/**
+ * Form field dependency implementation that requires the value of a field not to be empty.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 3.2
+ */
+define(['./Abstract', 'Core'], function(Abstract, Core) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function NonEmpty(dependentElementId, fieldId) {
+ this.init(dependentElementId, fieldId);
+ };
+ Core.inherit(NonEmpty, Abstract, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract#checkDependency
+ */
+ checkDependency: function() {
+ switch (this._field.tagName) {
+ case 'INPUT':
+ switch (this._field.type) {
+ case 'checkbox':
+ // TODO: check if working
+ return this._field.checked;
+
+ case 'radio':
+ if (this._noField && this._noField.checked) {
+ return false;
+ }
+
+ return this._field.checked;
+
+ default:
+ return this._field.value.trim().length !== 0;
+ }
+
+ case 'SELECT':
+ // TODO: check if working for multiselect
+ return this._field.value.length !== 0;
+
+ case 'TEXTAREA':
+ // TODO: check if working
+ return this._field.value.trim().length !== 0;
+ }
+ }
+ });
+
+ return NonEmpty;
+});
\ No newline at end of file
--- /dev/null
+/**
+ * Form field dependency implementation that requires a field to have a certain value.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 3.2
+ */
+define(['./Abstract', 'Core'], function(Abstract, Core) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Value(dependentElementId, fieldId) {
+ this.init(dependentElementId, fieldId);
+ };
+ Core.inherit(Value, Abstract, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract#checkDependency
+ */
+ checkDependency: function() {
+ if (!this._values) {
+ throw new Error("Values have not been set.");
+ }
+
+ // do not use `Array.prototype.indexOf()` as use a weak comparision
+ for (var i = 0, length = this._values.length; i < length; i++) {
+ console.log(this._values[i], this._field.value);
+ if (this._values[i] == this._field.value) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Sets the possible values the field may have for the dependency to be met.
+ *
+ * @param {array} values
+ */
+ values: function(values) {
+ this._values = values;
+ }
+ });
+
+ return Value;
+});
\ No newline at end of file
use wcf\system\form\builder\container\TabFormContainer;
use wcf\system\form\builder\container\TabMenuFormContainer;
use wcf\system\form\builder\field\data\CustomFormFieldDataProcessor;
+use wcf\system\form\builder\field\dependency\NonEmptyFormFieldDependency;
+use wcf\system\form\builder\field\dependency\ValueFormFieldDependency;
use wcf\system\form\builder\field\validation\FormFieldValidationError;
use wcf\system\form\builder\field\validation\FormFieldValidator;
use wcf\system\form\builder\field\BooleanFormField;
->description('wcf.global.description')
->addClass('someSection')
->appendChildren([
+ TextFormField::create('name')
+ ->label('wcf.global.name'),
TextFormField::create('title')
->label('wcf.global.title')
->i18n()
->label('Year')
->options(function() {
return [
- '(no selection)',
+ '' => '(no selection)',
2016 => 2016,
2017 => 2017,
2018 => 2018,
->minimum(10)
->maximum(100)
->value(20)
- ->suffix('wcf.acp.option.suffix.days')
+ ->suffix('wcf.acp.option.suffix.days'),
+ BooleanFormField::create('isCool')
+ ->label('Foo and Bar are cool names')
])
),
TabFormContainer::create('tab2')
])
]);
+ // add dependencies
+ $this->form->getNodeById('month')
+ ->addDependency(
+ NonEmptyFormFieldDependency::create('year')
+ ->field($this->form->getNodeById('year'))
+ )
+ ->addDependency(
+ NonEmptyFormFieldDependency::create('name')
+ ->field($this->form->getNodeById('name'))
+ )
+ ->addDependency(
+ NonEmptyFormFieldDependency::create('isDisabled')
+ ->field($this->form->getNodeById('isDisabled'))
+ );
+
+ $this->form->getNodeById('isCool')
+ ->addDependency(
+ ValueFormFieldDependency::create('name')
+ ->field($this->form->getNodeById('name'))
+ ->values([
+ 'Foo',
+ 'Bar'
+ ])
+ );
+
$this->form->build();
$this->form->getDataHandler()->add(new CustomFormFieldDataProcessor('isDisabledToString', function(IFormDocument $document, array $parameters) {
<?php
namespace wcf\system\form\builder;
-use wcf\system\form\builder\field\dependency\IFormFieldDependency;
/**
* Represents an element of a form that can have a description, a label and dependencies.
* @since 3.2
*/
interface IFormElement extends IFormNode {
- /**
- * Adds a dependency on the value of a `IFormField` so that this element is
- * only available if the field satisfies the given dependency and returns
- * this element.
- *
- * This method is expected to set the dependent element of the given dependency
- * to this element.
- *
- * @param IFormFieldDependency $dependency added element dependency
- * @return static this element
- */
- public function addDependency(IFormFieldDependency $dependency);
-
/**
* Sets the description of this element using the given language item
* and returns this element. If `null` is passed, the element description
*/
public function getLabel();
- /**
- * Returns `true` if this element has a dependency with the given id and
- * returns `false` otherwise.
- *
- * @param string $dependencyId id of the checked dependency
- * @return bool
- *
- * @throws \InvalidArgumentException if the given id is no string or otherwise invalid
- */
- public function hasDependency($dependencyId);
-
/**
* Sets the label of this element using the given language item and
* returns this element. If `null` is passed, the element label is
* @throws \InvalidArgumentException if the given label is no string or otherwise is invalid
*/
public function label($languageItem = null, array $variables = []);
-
- /**
- * Removes the dependency with the given id and returns this element.
- *
- * @param string $dependencyId id of the removed dependency
- * @return static this field
- *
- * @throws \InvalidArgumentException if the given id is no string or otherwise invalid or no such dependency exists
- */
- public function removeDependency($dependencyId);
}
<?php
namespace wcf\system\form\builder;
+use wcf\system\form\builder\field\dependency\IFormFieldDependency;
/**
* Represents a general form node providing common methods of all nodes.
*/
public function addClass($class);
+ /**
+ * Adds a dependency on the value of a `IFormField` so that this node is
+ * only available if the field satisfies the given dependency and returns
+ * this element.
+ *
+ * This method is expected to set the dependent node of the given dependency
+ * to this element.
+ *
+ * @param IFormFieldDependency $dependency added node dependency
+ * @return static this node
+ */
+ public function addDependency(IFormFieldDependency $dependency);
+
/**
* Adds an additional attribute to this node and returns this node.
*
*/
public function attribute($name, $value = null);
+ /**
+ * Returns `true` if the node's dependencies are met and returns `false` otherwise.
+ *
+ * @return bool
+ */
+ public function checkDependencies();
+
/**
* Returns the value of the additional attribute of this node with the given name.
*
*/
public function getClasses();
+ /**
+ * Returns all of the node's dependencies.
+ *
+ * @return IFormFieldDependency[] node's dependencies
+ */
+ public function getDependencies();
+
/**
* Returns the form document this node belongs to.
*
*/
public function hasClass($class);
+ /**
+ * Returns `true` if this node has a dependency with the given id and
+ * returns `false` otherwise.
+ *
+ * @param string $dependencyId id of the checked dependency
+ * @return bool
+ *
+ * @throws \InvalidArgumentException if the given id is no string or otherwise invalid
+ */
+ public function hasDependency($dependencyId);
+
/**
* Sets the id of the node.
*
*/
public function removeClass($class);
+ /**
+ * Removes the dependency with the given id and returns this node.
+ *
+ * @param string $dependencyId id of the removed dependency
+ * @return static this node
+ *
+ * @throws \InvalidArgumentException if the given id is no string or otherwise invalid or no such dependency exists
+ */
+ public function removeDependency($dependencyId);
+
/**
* Validates the node.
*
<?php
namespace wcf\system\form\builder;
-use wcf\system\form\builder\field\dependency\IFormFieldDependency;
use wcf\system\WCF;
/**
*/
protected $__label;
- /**
- * dependencies of this element
- * @var IFormFieldDependency[]
- */
- protected $dependencies = [];
-
- /**
- * Adds a dependency on the value of a `IFormField` so that this element is
- * only available if the field satisfies the given dependency and returns
- * this element.
- *
- * This method is expected to set the dependent element of the given dependency
- * to this element.
- *
- * @param IFormFieldDependency $dependency added element dependency
- * @return static this element
- */
- public function addDependency(IFormFieldDependency $dependency) {
- $this->dependencies[] = $dependency;
-
- $dependency->dependentElement($this);
-
- return $this;
- }
-
/**
* Sets the description of this element using the given language item
* and returns this element. If `null` is passed, the element description
/**
* Returns the label of this element or `null` if no label has been set.
- *
+ *
* @return null|string element label
*/
public function getLabel() {
return $this->__label;
}
- /**
- * Returns `true` if this element has a dependency with the given id and
- * returns `false` otherwise.
- *
- * @param string $dependencyId id of the checked dependency
- * @return bool
- *
- * @throws \InvalidArgumentException if the given id is no string or otherwise invalid
- */
- public function hasDependency($dependencyId) {
- foreach ($this->dependencies as $dependency) {
- if ($dependency->getId() === $dependencyId) {
- return true;
- }
- }
-
- return false;
- }
-
/**
* Sets the label of this element using the given language item and
* returns this element. If `null` is passed, the element label is
* removed.
- *
+ *
* @param null|string $languageItem language item containing the element label or `null` to unset label
* @param array $variables additional variables used when resolving the language item
* @return static this element
- *
+ *
* @throws \InvalidArgumentException if the given label is no string or otherwise is invalid
*/
public function label($languageItem = null, array $variables = []) {
return $this;
}
-
- /**
- * Removes the dependency with the given id and returns this element.
- *
- * @param string $dependencyId id of the removed dependency
- * @return static this field
- *
- * @throws \InvalidArgumentException if the given id is no string or otherwise invalid or no such dependency exists
- */
- public function removeDependency($dependencyId) {
- foreach ($this->dependencies as $key => $dependency) {
- if ($dependency->getId() === $dependencyId) {
- unset($this->dependencies[$key]);
-
- return $this;
- }
- }
-
- throw new \InvalidArgumentException("Unknown dependency with id '{$dependencyId}'.");
- }
}
<?php
namespace wcf\system\form\builder;
+use wcf\system\form\builder\field\dependency\IFormFieldDependency;
/**
* Provides default implementations of `IFormNode` methods.
*/
protected $__id;
+ /**
+ * dependencies of this node
+ * @var IFormFieldDependency[]
+ */
+ protected $dependencies = [];
+
/**
* is `true` if node has already been populated and is `false` otherwise
* @var bool
return $this;
}
+ /**
+ * Adds a dependency on the value of a `IFormField` so that this node is
+ * only available if the field satisfies the given dependency and returns
+ * this node.
+ *
+ * This method is expected to set the dependent node of the given dependency
+ * to this node.
+ *
+ * @param IFormFieldDependency $dependency added node dependency
+ * @return static this node
+ */
+ public function addDependency(IFormFieldDependency $dependency) {
+ $this->dependencies[] = $dependency;
+
+ $dependency->dependentNode($this);
+
+ return $this;
+ }
+
/**
* Adds an additional attribute to this node and returns this node.
*
return $this;
}
+ /**
+ * Returns `true` if the node's dependencies are met and returns `false` otherwise.
+ *
+ * @return bool
+ */
+ public function checkDependencies() {
+ foreach ($this->dependencies as $dependency) {
+ if (!$dependency->checkDependency()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
/**
* Returns the value of the additional attribute of this node with the given name.
*
return $this->__classes;
}
+ /**
+ * Returns all of the node's dependencies.
+ *
+ * @return IFormFieldDependency[] node's dependencies
+ */
+ public function getDependencies() {
+ return $this->dependencies;
+ }
+
/**
* Returns the form document this node belongs to.
*
return array_search($class, $this->__classes) !== false;
}
+ /**
+ * Returns `true` if this node has a dependency with the given id and
+ * returns `false` otherwise.
+ *
+ * @param string $dependencyId id of the checked dependency
+ * @return bool
+ *
+ * @throws \InvalidArgumentException if the given id is no string or otherwise invalid
+ */
+ public function hasDependency($dependencyId) {
+ foreach ($this->dependencies as $dependency) {
+ if ($dependency->getId() === $dependencyId) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Sets the id of the node.
*
return $this;
}
+ /**
+ * Removes the dependency with the given id and returns this node.
+ *
+ * @param string $dependencyId id of the removed dependency
+ * @return static this field
+ *
+ * @throws \InvalidArgumentException if the given id is no string or otherwise invalid or no such dependency exists
+ */
+ public function removeDependency($dependencyId) {
+ foreach ($this->dependencies as $key => $dependency) {
+ if ($dependency->getId() === $dependencyId) {
+ unset($this->dependencies[$key]);
+
+ return $this;
+ }
+ }
+
+ throw new \InvalidArgumentException("Unknown dependency with id '{$dependencyId}'.");
+ }
+
/**
* Creates a new element with the given id.
*
* @param string $id node id
* @return static
*
- * @throws \InvalidArgumentException if the given id is no string, already used by another element, or otherwise is invalid
+ * @throws \InvalidArgumentException if the given id is no string, already used by another node, or otherwise is invalid
*/
public static function create($id) {
return (new static)->id($id);
* nodes are valid. A `IFormField` object is valid if its value is valid.
*/
public function validate() {
- foreach ($this->children() as $child) {
- $child->validate();
+ if ($this->checkDependencies()) {
+ foreach ($this->children() as $child) {
+ // call `checkDependencies()` on form fields here so that their validate
+ // method does not have to do it
+ if ($child instanceof IFormField && !$child->checkDependencies()) {
+ continue;
+ }
+
+ $child->validate();
+ }
}
}
use wcf\system\form\builder\field\IFormField;
use wcf\system\form\builder\IFormDocument;
use wcf\system\form\builder\IFormNode;
+use wcf\system\form\builder\IFormParentNode;
/**
* Default field data processor that maps the form fields to entries in
*/
public function __invoke(IFormDocument $document, array $parameters) {
$parameters['data'] = [];
- /** @var IFormNode $node */
- foreach ($document->getIterator() as $node) {
- if ($node instanceof IFormField && $node->hasSaveValue()) {
- $parameters['data'][$node->getId()] = $node->getSaveValue();
- }
- }
+
+ $this->getData($document, $parameters['data']);
return $parameters;
}
+
+ protected function getData(IFormNode $node, array &$data) {
+ if ($node->checkDependencies()) {
+ if ($node instanceof IFormParentNode) {
+ foreach ($node as $childNode) {
+ $this->getData($childNode, $data);
+ }
+ }
+ else if ($node instanceof IFormField && $node->hasSaveValue()) {
+ $data[$node->getId()] = $node->getSaveValue();;
+ }
+ }
+ }
}
--- /dev/null
+<?php
+namespace wcf\system\form\builder\field\dependency;
+use wcf\system\form\builder\field\IFormField;
+use wcf\system\form\builder\IFormNode;
+use wcf\system\WCF;
+
+/**
+ * Abstract implementation of a form field dependency.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Form\Builder\Field\Dependency
+ * @since 3.2
+ */
+abstract class AbstractFormFieldDependency implements IFormFieldDependency {
+ /**
+ * node whose availability depends on the value of a field
+ * @var IFormNode
+ */
+ protected $__dependentNode;
+
+ /**
+ * field the availability of the node dependents on
+ * @var IFormField
+ */
+ protected $__field;
+
+ /**
+ * id of the dependency
+ * @var string
+ */
+ protected $__id;
+
+ /**
+ * name of the template containing the dependency JavaScript code
+ * @var null|string
+ */
+ protected $templateName;
+
+ /**
+ * @inheritDoc
+ */
+ public function dependentNode(IFormNode $node) {
+ $this->__dependentNode = $node;
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function field(IFormField $field) {
+ $this->__field = $field;
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getDependentNode() {
+ return $this->__dependentNode;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getField() {
+ return $this->__field;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getId() {
+ return $this->__id;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getHtml() {
+ if ($this->templateName === null) {
+ throw new \LogicException("Template name is not set.");
+ }
+
+ return WCF::getTPL()->fetch($this->templateName, 'wcf', [
+ 'dependency' => $this
+ ], true);
+ }
+
+ /**
+ * Sets the id of this dependency and returns this dependency.
+ *
+ * @param string $id id of the dependency
+ * @return static $this this dependency
+ *
+ * @throws \InvalidArgumentException if given id no string or otherwise invalid
+ */
+ protected function id($id) {
+ if (!is_string($id)) {
+ throw new \InvalidArgumentException("Given id is no string, " . gettype($id) . " given.");
+ }
+
+ if (preg_match('~^[a-z][A-z0-9-]*$~', $id) !== 1) {
+ throw new \InvalidArgumentException("Invalid id '{$id}' given.");
+ }
+
+ $this->__id = $id;
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function create($id) {
+ return (new static)->id($id);
+ }
+}
<?php
namespace wcf\system\form\builder\field\dependency;
use wcf\system\form\builder\field\IFormField;
-use wcf\system\form\builder\IFormElement;
+use wcf\system\form\builder\IFormNode;
/**
- * Represents a dependency of one field on (the value of) another field.
+ * Represents a dependency of one node on (the value of) a field.
*
* @author Matthias Schmidt
* @copyright 2001-2018 WoltLab GmbH
public function checkDependency();
/**
- * Sets the element whose availability depends on the value of a field.
+ * Sets the node whose availability depends on the value of a field.
*
- * @param IFormElement $element depending element
- * @return static this dependency
+ * @param IFormNode $node depending node
+ * @return static this dependency
*/
- public function dependentElement(IFormElement $element);
+ public function dependentNode(IFormNode $node);
/**
- * Sets the field the availability of the element dependents on.
+ * Sets the field the availability of the node dependents on.
*
* @param IFormField $field dependent field
* @return static this dependency
public function field(IFormField $field);
/**
- * Returns the JavaScript code required to ensure this dependency in the template.
- *
- * @return string dependency JavaScript code
+ * Returns the node whose availability depends on the value of a field.
+ *
+ * @return IFormNode dependent node
*/
- public function getHtml();
+ public function getDependentNode();
/**
- * Returns the id of the dependency.
+ * Returns the field the availability of the element dependents on.
*
- * @return string id of the dependency
+ * @return IFormField field controlling element availability
*/
- public function getId();
+ public function getField();
/**
- * Returns the element whose availability depends on the value of a field.
+ * Returns the JavaScript code required to ensure this dependency in the template.
*
- * @return IFormElement depending element
+ * @return string dependency JavaScript code
*/
- public function getDependentElement();
+ public function getHtml();
/**
- * Returns the field the availability of the element dependents on.
+ * Returns the id of this dependency.
*
- * @return IFormField dependent field
+ * @return string id of the dependency
*/
- public function getField();
+ public function getId();
/**
* Creates a new dependency with the given id.
--- /dev/null
+<?php
+namespace wcf\system\form\builder\field\dependency;
+
+/**
+ * Represents a dependency that requires the value of a field not to be empty.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Form\Builder\Field\Dependency
+ * @since 3.2
+ */
+class NonEmptyFormFieldDependency extends AbstractFormFieldDependency {
+ /**
+ * @inheritDoc
+ */
+ protected $templateName = '__nonEmptyFormFieldDependency';
+
+ /**
+ * @inheritDoc
+ */
+ public function checkDependency() {
+ return !empty($this->getField()->getValue());
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\form\builder\field\dependency;
+
+/**
+ * Represents a dependency that requires that requires a field to have a certain value.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Form\Builder\Field\Dependency
+ * @since 3.2
+ */
+class ValueFormFieldDependency extends AbstractFormFieldDependency {
+ /**
+ * possible values the field may have for the dependency to be met
+ * @var null|array
+ */
+ protected $__values;
+
+ /**
+ * @inheritDoc
+ */
+ protected $templateName = '__valueFormFieldDependency';
+
+ /**
+ * @inheritDoc
+ */
+ public function checkDependency() {
+ return !empty($this->getField()->getValue());
+ }
+
+ /**
+ * Returns the possible values the field may have for the dependency to be met.
+ *
+ * @return array possible field values
+ *
+ * @throws \BadMethodCallException if no values have been set
+ */
+ public function getValues() {
+ if ($this->__values === null) {
+ throw new \BadMethodCallException("Values have not been set for dependency '{$this->getId()}' on node '{$this->getDependentNode()->getId()}'.");
+ }
+
+ return $this->__values;
+ }
+
+ /**
+ * Sets the possible values the field may have for the dependency to be met.
+ *
+ * @param array $values possible field values
+ * @return static $this this dependency
+ *
+ * @throws \InvalidArgumentException if given values are invalid
+ */
+ public function values(array $values) {
+ if (empty($values)) {
+ throw new \InvalidArgumentException("Given values are empty.");
+ }
+ foreach ($values as $value) {
+ if (!is_string($value) && !is_numeric($value)) {
+ throw new \InvalidArgumentException("Values contains invalid value of type '" . gettype($value) . "', only strings or numbers are allowed.");
+ }
+ }
+
+ $this->__values = $values;
+
+ return $this;
+ }
+}