Add concept of form node availability for form builder
authorMatthias Schmidt <gravatronics@live.com>
Sat, 13 Jan 2018 17:37:17 +0000 (18:37 +0100)
committerMatthias Schmidt <gravatronics@live.com>
Sat, 13 Jan 2018 17:37:17 +0000 (18:37 +0100)
See #2509

wcfsetup/install/files/acp/templates/__form.tpl
wcfsetup/install/files/acp/templates/__formContainerChildren.tpl
wcfsetup/install/files/acp/templates/__tabMenuFormContainer.tpl
wcfsetup/install/files/acp/templates/__tabTabMenuFormContainer.tpl
wcfsetup/install/files/lib/system/form/builder/FormDocument.class.php
wcfsetup/install/files/lib/system/form/builder/IFormNode.class.php
wcfsetup/install/files/lib/system/form/builder/TFormNode.class.php
wcfsetup/install/files/lib/system/form/builder/TFormParentNode.class.php
wcfsetup/install/files/lib/system/form/builder/field/TSelectionFormField.class.php
wcfsetup/install/files/lib/system/form/builder/field/data/DefaultFormFieldDataProcessor.class.php

index 7d869e3bb2bb263a2facd9f79d6776f5929ab1c1..a8ad558bb958704d6a4f34792434ed9f40148593 100644 (file)
@@ -1,6 +1,8 @@
 <form method="{@$form->getMethod()}" action="{@$form->getAction()}" id="{@$form->getId()}"{if !$form->getClasses()|empty} class="{implode from=$form->getClasses() item='class'}{$class}{/implode}"{/if}{foreach from=$form->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}>
        {foreach from=$form item='child'}
-               {@$child->getHtml()}
+               {if $child->isAvailable()}
+                       {@$child->getHtml()}
+               {/if}
        {/foreach}
        
        <div class="formSubmit">
index f9c9a8c5e09b6a46935bb91d2a26352baecb66d3..67486e390664519c2da34c37b3e04156af247c11 100644 (file)
@@ -1,3 +1,5 @@
 {foreach from=$container item='child'}
-       {@$child->getHtml()}
+       {if $child->isAvailable()}
+               {@$child->getHtml()}
+       {/if}
 {/foreach}
\ No newline at end of file
index 752775dc540879c3b51af3f5df5e7716599a5893..f8f3e15b805c4bbfd99c43b9f503e7305d3ab2d8 100644 (file)
@@ -2,8 +2,10 @@
        <nav class="tabMenu">
                <ul>
                        {foreach from=$container item='child'}
-                               {assign var='__tabMenuFormContainerChildId' value=$child->getPrefixedId()|concat:'Container'}
-                               <li{if !$child->checkDependencies()} style="display: none;"{/if}><a href="{@$__wcf->getAnchor($__tabMenuFormContainerChildId)}">{@$child->getLabel()}</a></li>
+                               {if $child->isAvailable()}
+                                       {assign var='__tabMenuFormContainerChildId' value=$child->getPrefixedId()|concat:'Container'}
+                                       <li{if !$child->checkDependencies()} style="display: none;"{/if}><a href="{@$__wcf->getAnchor($__tabMenuFormContainerChildId)}">{@$child->getLabel()}</a></li>
+                               {/if}
                        {/foreach}
                </ul>
        </nav>
index 3f84fca1ec1edf6dae166339d70993c4562f3138..a181463413feafaabfe4e9327d2d7478ca344f0a 100644 (file)
@@ -2,7 +2,10 @@
        <nav class="menu">
                <ul>
                        {foreach from=$container item='child'}
-                               <li><a href="{@$__wcf->getAnchor($child->getPrefixedId())}">{@$container->getLabel()}</a></li>
+                               {if $child->isAvailable()}
+                                       {assign var='__tabMenuFormContainerChildId' value=$child->getPrefixedId()|concat:'Container'}
+                                       <li{if !$child->checkDependencies()} style="display: none;"{/if}><a href="{@$__wcf->getAnchor($__tabMenuFormContainerChildId)}">{@$child->getLabel()}</a></li>
+                               {/if}
                        {/foreach}
                </ul>
        </nav>
index 5773b6cd8d5b6b4846d2d3c040cb33727e427855..ba0c6ab2eecf421972a9dbf2f898432379d89830 100644 (file)
@@ -166,7 +166,7 @@ class FormDocument implements IFormDocument {
        public function loadValuesFromObject(IStorableObject $object) {
                /** @var IFormNode $node */
                foreach ($this->getIterator() as $node) {
-                       if ($node instanceof IFormField) {
+                       if ($node instanceof IFormField && $node->isAvailable()) {
                                $node->loadValueFromObject($object);
                        }
                }
index ca6725ff6d1dbf93bf8308358a08005cc33804dd..59e1ec818e807b0840494b327a5a353e31151b14 100644 (file)
@@ -48,6 +48,30 @@ interface IFormNode {
         */
        public function attribute($name, $value = null);
        
+       /**
+        * Sets if this node is available and returns this node.
+        * 
+        * By default, every node is available. This methods makes it easier to create forms
+        * that contains node that are only avaiable if certain options have specific values
+        * or the active user has specific permissions, for example. Furthermore, fields
+        * themselves are also able to mark themselves as unavailable, for example, a selection
+        * field without any options. A `IFormContainer` is automatically unavailable if it
+        * contains no available children.
+        * 
+        * Unavailable fields produce no output, their value is not read, they are not validated
+        * and they are not checked for save values.
+        * 
+        * Note: Form field dependencies manage dynamic availability of form nodes based on
+        * form field values while this method manages static availability that is independent
+        * of form field values and only depends on external factors.
+        * 
+        * @param       bool            $available      determines if node is available
+        * @return      static                          this node
+        * 
+        * @throws      \InvalidArgumentException       if the given value is no bool
+        */
+       public function available($available = true);
+       
        /**
         * Returns `true` if the node's dependencies are met and returns `false` otherwise.
         *
@@ -175,6 +199,17 @@ interface IFormNode {
         */
        public function id($id);
        
+       /**
+        * Returns `true` if this node is available and returns `false` otherwise.
+        * 
+        * If the node's availability has not been explicitly set, `true` is returned.
+        * 
+        * @return      bool
+        * 
+        * @see         IFormNode::available()
+        */
+       public function isAvailable();
+       
        /**
         * Is called once after all nodes have been added to the document this node belongs to.
         * 
index 2b92600a02e0d0d32894eb8bef72bb30f6265a44..f327143710d326b5e626dbb7303085496e9c52e6 100644 (file)
@@ -18,6 +18,12 @@ trait TFormNode {
         */
        protected $__attributes = [];
        
+       /**
+        * `true` if this node is available and `false` otherwise
+        * @var bool
+        */
+       protected $__available = true;
+       
        /**
         * CSS classes of this node
         * @var string[]
@@ -108,6 +114,38 @@ trait TFormNode {
                return $this;
        }
        
+       /**
+        * Sets if this node is available and returns this node.
+        *
+        * By default, every node is available. This methods makes it easier to create forms
+        * that contains node that are only avaiable if certain options have specific values
+        * or the active user has specific permissions, for example. Furthermore, fields
+        * themselves are also able to mark themselves as unavailable, for example, a selection
+        * field without any options. A `IFormContainer` is automatically unavailable if it
+        * contains no available children.
+        *
+        * Unavailable fields produce no output, their value is not read, they are not validated
+        * and they are not checked for save values.
+        * 
+        * Note: Form field dependencies manage dynamic availability of form nodes based on
+        * form field values while this method manages static availability that is independent
+        * of form field values and only depends on external factors.
+        * 
+        * @param       bool            $available      determines if node is available
+        * @return      static                          this node
+        *
+        * @throws      \InvalidArgumentException       if the given value is no bool
+        */
+       public function available($available = true) {
+               if (!is_bool($available)) {
+                       throw new \InvalidArgumentException("Given value is no bool, " . gettype($available) . " given.");
+               }
+               
+               $this->__available = $available;
+               
+               return $this;
+       }
+       
        /**
         * Returns `true` if the node's dependencies are met and returns `false` otherwise.
         * 
@@ -307,6 +345,30 @@ trait TFormNode {
                return $this;
        }
        
+       /**
+        * Returns `true` if this node is available and returns `false` otherwise.
+        *
+        * If the node's own availability has not been explicitly set, it is assumed to be `true`.
+        *
+        * @return      bool
+        *
+        * @see         IFormNode::available()
+        */
+       public function isAvailable() {
+               if ($this->__available && $this instanceof IFormParentNode) {
+                       /** @var IFormChildNode $child */
+                       foreach ($this as $child) {
+                               if ($child->isAvailable()) {
+                                       return true;
+                               }
+                       }
+                       
+                       return false;
+               }
+               
+               return $this->__available;
+       }
+       
        /**
         * Is called once after all nodes have been added to the document this node belongs to.
         * 
index 9dbc5cf845e2113139ebcb575ac061a32dc00bfe..2e42372116205c8440816923d57c880383baa16e 100644 (file)
@@ -246,12 +246,14 @@ trait TFormParentNode {
         * @return      static          this node
         */
        public function readValues() {
-               foreach ($this->children() as $child) {
-                       if ($child instanceof IFormParentNode) {
-                               $child->readValues();
-                       }
-                       else if ($child instanceof IFormField && !$child->isImmutable()) {
-                               $child->readValue();
+               if ($this->isAvailable()) {
+                       foreach ($this->children() as $child) {
+                               if ($child instanceof IFormParentNode) {
+                                       $child->readValues();
+                               }
+                               else if ($child instanceof IFormField && $child->isAvailable() && !$child->isImmutable()) {
+                                       $child->readValue();
+                               }
                        }
                }
                
@@ -282,11 +284,11 @@ trait TFormParentNode {
         * nodes are valid. A `IFormField` object is valid if its value is valid.
         */
        public function validate() {
-               if ($this->checkDependencies()) {
+               if ($this->isAvailable() && $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()) {
+                               if ($child instanceof IFormField && $child->isAvailable() && !$child->checkDependencies()) {
                                        continue;
                                }
                                
index b90e1b58283c6e3b584251333e48bca2e29213a6..ade337f3e564d3d10f702363e98220756b1a4336 100644 (file)
@@ -36,6 +36,20 @@ trait TSelectionFormField {
                return $this->__options;
        }
        
+       /**
+        * Returns `true` if this node is available and returns `false` otherwise.
+        * 
+        * If the node's availability has not been explicitly set, `true` is returned.
+        * 
+        * @return      bool
+        * 
+        * @see         IFormNode::available()
+        */
+       public function isAvailable() {
+               // selections without any possible values are not available
+               return !empty($this->possibleValues) && parent::isAvailable();
+       }
+       
        /**
         * Sets the possible options of this selection and returns this field.
         * 
index c4300abe193891d8a303b1103d1a070bbe440bed..d3129a7d009fef1ffbb3db0c6181d1496678c73e 100644 (file)
@@ -41,7 +41,7 @@ class DefaultFormFieldDataProcessor implements IFormFieldDataProcessor {
                                        $this->getData($childNode, $data);
                                }
                        }
-                       else if ($node instanceof IFormField && $node->hasSaveValue()) {
+                       else if ($node instanceof IFormField && $node->isAvailable() && $node->hasSaveValue()) {
                                $data[$node->getId()] = $node->getSaveValue();
                        }
                }