diff --git a/examples.module b/examples.module index e866557..f921726 100644 --- a/examples.module +++ b/examples.module @@ -41,6 +41,7 @@ function examples_toolbar() { 'dbtng_example' => 'dbtng_example', 'email_example' => 'email_example.description', 'field_example' => 'field_example.description', + 'fapi_example' => 'fapi_example.description', 'js_example' => 'js_example.info', 'node_type_example' => 'config_node_type_example.description', 'page_example' => 'page_example_description', diff --git a/fapi_example/LICENSE b/fapi_example/LICENSE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/fapi_example/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/fapi_example/README.md b/fapi_example/README.md new file mode 100644 index 0000000..c38bf4f --- /dev/null +++ b/fapi_example/README.md @@ -0,0 +1 @@ +# d8_examples diff --git a/fapi_example/fapi_example.info.yml b/fapi_example/fapi_example.info.yml new file mode 100644 index 0000000..5d9b425 --- /dev/null +++ b/fapi_example/fapi_example.info.yml @@ -0,0 +1,7 @@ +name: Form API Example +type: module +description: Sample implementations that demonstrate the Drupal Forms API +package: Example modules +core: 8.x +dependencies: + - examples diff --git a/fapi_example/fapi_example.links.menu.yml b/fapi_example/fapi_example.links.menu.yml new file mode 100644 index 0000000..6b2fa35 --- /dev/null +++ b/fapi_example/fapi_example.links.menu.yml @@ -0,0 +1,47 @@ +# Define default links for this module. +fapi_example.description: + title: From API Examples + description: Form examples using Drupal Form API. + route_name: fapi_example.description + +fapi_example.simple_form: + title: Simple Form API Example + description: A simple form example with submit processing. + route_name: fapi_example.simple_form + parent: fapi_example.description + +fapi_example.input_demo: + title: Demo of Common Input Elements + description: A form to demonstrate input elements. + route_name: fapi_example.input_demo + parent: fapi_example.description + +fapi_example.state_demo: + title: Demo of Form State Binding + description: A form to demonstrate binding of form state. + route_name: fapi_example.state_demo + parent: fapi_example.description + +fapi_example.container_demo: + title: Container Demo + description: A form to demonstrate use of containers. + route_name: fapi_example.container_demo + parent: fapi_example.description + +fapi_example.vertical_tabs_demo: + title: VerticalTabs Demo + description: A form to demonstrate use of vertical tabs. + route_name: fapi_example.vertical_tabs_demo + parent: fapi_example.description + +fapi_example.ajax_demo: + title: Ajax Form Example + description: A form to demonstrate use of AJAX. + route_name: fapi_example.ajax_demo + parent: fapi_example.description + +fapi_example.build_demo: + title: Build Form Demo + description: A form build demonstration. + route_name: fapi_example.build_demo + parent: fapi_example.description diff --git a/fapi_example/fapi_example.routing.yml b/fapi_example/fapi_example.routing.yml new file mode 100644 index 0000000..e510192 --- /dev/null +++ b/fapi_example/fapi_example.routing.yml @@ -0,0 +1,84 @@ +# This routing.yml file makes both the fapi example description page and the +# included sample forms available at specific URL's on your site. A route +# maps a URL path to a controller. For page controllers it defines the +# function or method that will be called when the page is accessed. For form +# controllers the content is determined by the buildForm method defined by the +# form controller implementation. + +# Access to these paths is restricted to users with the permission 'access content'. +# This is notated as _permission: 'access content'. + +# Menu items corresponding to these URLs are defined separately in the +# fapi_example.links.menu.yml file. +fapi_example.description: + path: 'examples/fapi_example' + defaults: + _controller: '\Drupal\fapi_example\Controller\Page::description' + _title: 'Form API Examples' + requirements: + _permission: 'access content' + +fapi_example.simple_form: + path: 'examples/fapi_example/simple_form' + defaults: + _form: '\Drupal\fapi_example\Form\SimpleForm' + _title: 'Simple Form API Example' + requirements: + _permission: 'access content' + +fapi_example.input_demo: + path: 'examples/fapi_example/input_demo' + defaults: + _form: '\Drupal\fapi_example\Form\InputDemo' + _title: 'Demo of Common Input Elements' + requirements: + _permission: 'access content' + +fapi_example.state_demo: + path: 'examples/fapi_example/state_demo' + defaults: + _form: '\Drupal\fapi_example\Form\StateDemo' + _title: 'Demo of Form State Binding' + requirements: + _permission: 'access content' + +fapi_example.container_demo: + path: 'examples/fapi_example/container_demo' + defaults: + _form: '\Drupal\fapi_example\Form\ContainerDemo' + _title: 'Container Demo' + requirements: + _permission: 'access content' + +fapi_example.vertical_tabs_demo: + path: 'examples/fapi_example/vertical_tabs_demo' + defaults: + _form: '\Drupal\fapi_example\Form\VerticalTabsDemo' + _title: 'VerticalTabs Demo' + requirements: + _permission: 'access content' + +fapi_example.modal_form: + path: 'examples/fapi_example/modal_form' + defaults: + _form: '\Drupal\fapi_example\Form\ModalForm' + _title: 'Modal Form Example' + requirements: + _permission: 'access content' + +fapi_example.ajax_demo: + path: 'examples/fapi_example/ajax_demo' + defaults: + _form: '\Drupal\fapi_example\Form\AjaxDemo' + _title: 'Ajax Form Example' + requirements: + _permission: 'access content' + +fapi_example.build_demo: + path: 'examples/fapi_example/build_demo' + defaults: + _form: '\Drupal\fapi_example\Form\BuildDemo' + _title: 'Build Form Demo' + requirements: + _permission: 'access content' + diff --git a/fapi_example/src/Controller/Page.php b/fapi_example/src/Controller/Page.php new file mode 100644 index 0000000..b63e822 --- /dev/null +++ b/fapi_example/src/Controller/Page.php @@ -0,0 +1,56 @@ + '

' . $this->t('Form examples to demonstrate comment UI solutions using the Drupal Form API.') . '

', + ); + + $content['links'] = [ + '#theme' => 'item_list', + '#items' => [ + $this->l($this->t('Simple Form'), Url::fromRoute('fapi_example.simple_form')), + $this->l($this->t('Input Demo'), Url::fromRoute('fapi_example.input_demo')), + $this->l($this->t('Form State Example'), Url::fromRoute('fapi_example.state_demo')), + $this->l($this->t('Container Demo'), Url::fromRoute('fapi_example.container_demo')), + $this->l($this->t('Veritcal Tab Demo'), Url::fromRoute('fapi_example.vertical_tabs_demo')), + $this->l($this->t('Ajax Demo'), Url::fromRoute('fapi_example.ajax_demo')), + [ + '#type' => 'link', + '#title' => $this->t('Modal Example'), + '#url' => new Url('fapi_example.modal_form'), + '#attributes' => [ + 'class' => ['use-ajax'], + 'data-dialog-type' => 'modal', + ] + ], + $this->l($this->t('Build Demo'), Url::fromRoute('fapi_example.build_demo')), + ], + ]; + $content['message'] = [ + '#type' => 'html_tag', + '#tag' => 'div', + '#attributes' => ['id' => 'fapi-example-message'], + ]; + return $content; + } + +} diff --git a/fapi_example/src/Form/AjaxDemo.php b/fapi_example/src/Form/AjaxDemo.php new file mode 100644 index 0000000..2dbc48a --- /dev/null +++ b/fapi_example/src/Form/AjaxDemo.php @@ -0,0 +1,98 @@ + [ + 'red' => 'Red', + 'orange' => 'Orange', + 'yellow' => 'Yellow', + ], + 'cool' => [ + 'blue' => 'Blue', + 'purple' => 'Purple', + 'green' => 'Green', + ], + ]; + + /** + * Build the simple form. + */ + public function buildForm(array $form, FormStateInterface $form_state) { + + $form['temperature'] = [ + '#title' => $this->t('Temperature'), + '#type' => 'select', + '#options' => [ 'warm' => 'Warm', 'cool' => 'Cool'], + '#empty_option' => $this->t('-select'), + '#ajax' => [ + // Could also use [ $this, 'colorCallback'] + 'callback' => '::colorCallback', + 'wrapper' => 'color-wrapper', + ] + ]; + $form_state->setCached(FALSE); + + $form['actions'] =[ + '#type' => 'actions', + ]; + + // Add a submit button that handles the submission of the form. + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Submit'), + ); + + // + $form['color_wrapper'] = [ + '#type' => 'container', + '#attributes' => ['id' => 'color-wrapper'] + ]; + + return $form; + } + + /** + * Getter method for Form ID. + * + * @inheritdoc + */ + public function getFormId() { + return 'fapi_example_ajax_demo'; + } + + /** + * Callback for Ajax event on color selection. + */ + public function colorCallback(array &$form, FormStateInterface $form_state) { + $temperature = $form_state->getValue('temperature'); + $form['color_wrapper']['color'] = [ + '#type' => 'select', + '#title' => $this->t('Color'), + '#options' => $this->colors[$temperature], + ]; + $form_state->setRebuild(); + return $form['color_wrapper']; + } + +} diff --git a/fapi_example/src/Form/BuildDemo.php b/fapi_example/src/Form/BuildDemo.php new file mode 100644 index 0000000..c1268b4 --- /dev/null +++ b/fapi_example/src/Form/BuildDemo.php @@ -0,0 +1,171 @@ + 'checkbox', + '#title' => $this->t('Change Me'), + '#ajax' => [ + 'callback' => '::ajaxSubmit', + 'wrapper' => 'message-wrapper', + ], + ]; + $form['actions'] = array( + '#type' => 'actions', + ); + + // Add a submit button that handles the submission of the form. + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => 'Submit', + ); + + + // Add button handlers. + $form['actions']['button'] = array( + '#type' => 'button', + '#value' => 'Rebuild', + ); + + $form['actions']['rebuild'] = array( + '#type' => 'button', + '#value' => 'Submit Rebuild', + '#submit' => ['::rebuildFormSubmit'] + ); + + $form['actions']['ajaxsubmit'] = array( + '#type' => 'submit', + '#value' => 'Ajax Submit', + '#ajax' => [ + 'callback' => '::ajaxSubmit', + 'wrapper' => 'message-wrapper', + ] + ); + + $form['messages'] = array( + '#type' => 'container', + '#attributes' => ['id' => 'message-wrapper'] + ); + + return $form; + } + + /** + * Getter method for Form ID. + * + * The form ID is used in implementations of hook_form_alter() to allow other + * modules to alter the render array built by this form controller. it must + * be unique site wide. It normally starts with the providing module's name. + * + * @return string + * The unique ID of the form defined by this class. + */ + public function getFormId() { + static $i = 0; + $i++; + drupal_set_message("getFormId $i"); + return 'fapi_example_simple_form'; + } + + /** + * Implements form validation. + * + * The validateForm method is the default method called to validate input on + * a form. + * + * @param array $form + * The render array of the currently built form. + * @param FormStateInterface $form_state + * Object describing the current state of the form. + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + static $i = 0; + $i++; + drupal_set_message("validateForm $i"); + } + + /** + * Implements a form submit handler. + * + * The submitForm method is the default method called for any submit elements. + * + * @param array $form + * The render array of the currently built form. + * @param FormStateInterface $form_state + * Object describing the current state of the form. + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + static $i = 0; + $i++; + drupal_set_message("submitForm $i"); + } + + /** + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + * Demonstrates ajax button submit. + */ + public function ajaxSubmit(array &$form, FormStateInterface $form_state) { + static $i = 0; + $i++; + drupal_set_message("ajaxSubmit $i"); + $form['messages']['status'] = [ + '#type' => 'status_messages', + ]; + + return $form['messages']; + } + + /** + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + * Test a submit that causes form state rebuild + */ + public function rebuildFormSubmit(array &$form, FormStateInterface $form_state) { + static $i = 0; + $i++; + drupal_set_message("rebuildFormSubmit $i"); + $form_state->setRebuild(TRUE); + } + +} diff --git a/fapi_example/src/Form/ContainerDemo.php b/fapi_example/src/Form/ContainerDemo.php new file mode 100644 index 0000000..2c31b89 --- /dev/null +++ b/fapi_example/src/Form/ContainerDemo.php @@ -0,0 +1,97 @@ + 'details', + '#title' => 'Author Info (type = details)', + ]; + + $form['author']['name'] = [ + '#type' => 'textfield', + '#title' => $this->t('Name'), + ]; + + $form['author']['pen_name'] = [ + '#type' => 'textfield', + '#title' => $this->t('Pen Name') + ]; + + $form['book'] = [ + '#type' => 'fieldset', + '#title' => $this->t('Book Info (type = fieldset)'), + ]; + + $form['book']['title'] = [ + '#type' => 'textfield', + '#title' => $this->t('Title'), + ]; + + $form['book']['publisher'] = [ + '#type' => 'textfield', + '#title' => $this->t('Publisher'), + ]; + + $form['accomodation'] = [ + '#type' => 'container', + ]; + + $form['accomodation']['title'] = [ + '#type' => 'html_tag', + '#tag' => 'p', + '#value' => $this->t('Special Accomodations (type = container)'), + ]; + + $form['accomodation'] ['diet'] = [ + '#type' => 'textfield', + '#title' => $this->t('Dietary Restrictions'), + ]; + + $form['actions'] = ['#type' => 'actions']; + // Add a submit button that handles the submission of the form. + $form['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Submit'), + ]; + + return $form; + } + + /** + * Getter method for Form ID. + * + * @inheritdoc + */ + public function getFormId() { + return 'fapi_example_container_demo'; + } + + +} diff --git a/fapi_example/src/Form/DemoBase.php b/fapi_example/src/Form/DemoBase.php new file mode 100644 index 0000000..5906c34 --- /dev/null +++ b/fapi_example/src/Form/DemoBase.php @@ -0,0 +1,113 @@ + 'actions', + ); + + // Add a submit button that handles the submission of the form. + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Submit'), + ); + return $form; + } + + /** + * Getter method for Form ID. + * + * The form ID is used in implementations of hook_form_alter() to allow other + * modules to alter the render array built by this form controller. it must + * be unique site wide. It normally starts with the providing module's name. + * + * @return string + * The unique ID of the form defined by this class. + */ + public function getFormId() { + return 'fapi_example_demo_base'; + } + + /** + * Implements form validation. + * + * The validateForm method is the default method called to validate input on + * a form. + * + * @param array $form + * The render array of the currently built form. + * @param FormStateInterface $form_state + * Object describing the current state of the form. + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + + } + + /** + * Implements a form submit handler. + * + * The submitForm method is the default method called for any submit elements. + * + * @param array $form + * The render array of the currently built form. + * @param FormStateInterface $form_state + * Object describing the current state of the form. + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + //Find out what was submitted + $values = $form_state->getValues(); + foreach($values as $key => $value) { + $label = isset($form[$key]['#title']) ? $form[$key]['#title'] : $key; + + // many arrays return 0 for unselected values so lets filter that out. + if (is_array($value)) $value = array_filter($value); + + // Only display for controls that have titles and values. + if ($value ) { + $display_value = is_array($value) ? preg_replace('/[\n\r\s]+/', ' ', print_r($value, 1)) : $value; + $message = $this->t('Value for %title: %value' , array('%title' => $label, '%value' => $display_value)); + drupal_set_message($message); + } + } + } + +} diff --git a/fapi_example/src/Form/InputDemo.php b/fapi_example/src/Form/InputDemo.php new file mode 100644 index 0000000..aa9b69c --- /dev/null +++ b/fapi_example/src/Form/InputDemo.php @@ -0,0 +1,226 @@ + 'checkboxes', + '#options' => array('SAT' => t('SAT'), 'ACT' => t('ACT')), + '#title' => $this->t('What standardized tests did you take?'), + '#description' => 'Checkboxes, #type = checkboxes', + ); + + // Color + $form['color'] = array( + '#type' => 'color', + '#title' => $this->t('Color'), + '#default_value' => '#ffffff', + '#description' => 'Color, #type = color', + ); + + // Date + $form['expiration'] = array( + '#type' => 'date', + '#title' => $this->t('Content expiration'), + '#default_value' => array('year' => 2020, 'month' => 2, 'day' => 15,), + '#description' => 'Date, #type = date', + ); + + // Email + $form['email'] = array( + '#type' => 'email', + '#title' => $this->t('Email'), + '#description' => 'Email, #type = email', + ); + + // Number + $form['quantity'] = array( + '#type' => 'number', + '#title' => t('Quantity'), + '#description' => $this->t('Number, #type = number'), + ); + + // Password + $form['password'] = array( + '#type' => 'password', + '#title' => $this->t('Password'), + '#description' => 'Password, #type = password', + ); + + // Password Confirm + $form['password_confirm'] = array( + '#type' => 'password_confirm', + '#title' => $this->t('New Password'), + '#description' => $this->t('PasswordConfirm, #type = password_confirm') + ); + + // Range + $form['size'] = array( + '#type' => 'range', + '#title' => t('Size'), + '#min' => 10, + '#max' => 100, + '#discription' => $this->t('Range, #type = range'), + ); + + // Radios + $form['settings']['active'] = array( + '#type' => 'radios', + '#title' => t('Poll status'), + '#options' => array(0 => $this->t('Closed'), 1 => $this->t('Active')), + '#description' => $this->t('Radios, #type = radios'), + ); + + // Search + $form['search'] = array( + '#type' => 'search', + '#title' => $this->t('Search'), + '#description' => $this->t('Search, #type = search'), + ); + + // Select + $form['favorite'] = array( + '#type' => 'select', + '#title' => $this->t('Favorite color'), + '#options' => array( + 'red' => $this->t('Red'), + 'blue' => $this->t('Blue'), + 'green' => $this->t('Green') + ), + '#empty_option' => $this->t('-select-'), + '#description' => $this->t('Select, #type = select'), + ); + + // Tel + $form['phone'] = array( + '#type' => 'tel', + '#title' => $this->t('Phone'), + '#description' => $this->t('Tel, #type = tel'), + ); + + // TableSelect + $options = [ + 1 => ['first_name' => 'Indy', 'last_name' => 'Jones'], + 2 => ['first_name' => 'Darth', 'last_name' => 'Vader'], + 3 => [ 'first_name' => 'Super', 'last_name' => 'Man'], + ]; + + $header = array( + 'first_name' => t('First Name'), + 'last_name' => t('Last Name'), + ); + + $form['table'] = array( + '#type' => 'tableselect', + '#title' => $this->t('Users'), + '#header' => $header, + '#options' => $options, + '#empty' => t('No users found'), + ); + + // Textarea + $form['text'] = array( + '#type' => 'textarea', + '#title' => $this->t('Text'), + '#description' => $this->t('Textarea, #type = textarea') + ); + + // Textfield + $form['subject'] = array( + '#type' => 'textfield', + '#title' => t('Subject'), + '#size' => 60, + '#maxlength' => 128, + '#description' => $this->t('Textfield, #type = textfield'), + ); + + // Weight + $form['weight'] = array( + '#type' => 'weight', + '#title' => t('Weight'), + '#delta' => 10, + '#description' => $this->t('Weight, #type = weight') + ); + + + // Group submit handlers in an actions element with a key of "actions" so + // that it gets styled correctly, and so that other modules may add actions + // to the form. + $form['actions'] = array( + '#type' => 'actions', + ); + + // Extra actions for the display + $form['actions']['extra_actions'] = array( + '#type' => 'dropbutton', + '#links' => array( + 'simple_form' => array( + 'title' => $this->t('Simple Form'), + 'url' => Url::fromRoute('fapi_example.simple_form'), + ), + 'demo' => array( + 'title' => $this->t('Build Demo'), + 'url' => Url::fromRoute('fapi_example.build_demo'), + ), + ), + ); + // Add a submit button that handles the submission of the form. + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Submit'), + '#description' => $this->t('Submit, #type = submit') + ); + + + return $form; + } + + /** + * The form ID as used in alter hooks. + */ + public function getFormId() { + return 'fapi_example_input_demo_form'; + } + + /** + * @param array $form + * The built version of the render array representing the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + //Find out what was submitted + $values = $form_state->getValues(); + foreach($values as $key => $value) { + $label = isset($form[$key]['#title']) ? $form[$key]['#title'] : $key; + + // many arrays return 0 for unselected values so lets filter that out. + if (is_array($value)) $value = array_filter($value); + + // Only display for controls that have titles and values. + if ($value && $label ) { + $display_value = is_array($value) ? preg_replace('/[\n\r\s]+/', ' ', print_r($value, 1)) : $value; + $message = $this->t('Value for %title: %value' , array('%title' => $label, '%value' => $display_value)); + drupal_set_message($message); + } + } + } + +} \ No newline at end of file diff --git a/fapi_example/src/Form/ModalForm.php b/fapi_example/src/Form/ModalForm.php new file mode 100644 index 0000000..a11bacf --- /dev/null +++ b/fapi_example/src/Form/ModalForm.php @@ -0,0 +1,127 @@ +'; + $form['#suffix'] = ''; + $form['title'] = array( + '#type' => 'textfield', + '#title' => $this->t('Title'), + '#required' => TRUE, + ); + + // Group submit handlers in an actions element with a key of "actions" so + // that it gets styled correctly, and so that other modules may add actions + // to the form. + $form['actions'] = array( + '#type' => 'actions', + ); + + // Add a submit button that handles the submission of the form. + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Submit'), + '#ajax' => [ + 'callback' => '::ajaxSubmitForm', + 'event' => 'click', + ] + ); + + return $form; + } + + /** + * Getter method for Form ID. + * + * The form ID is used in implementations of hook_form_alter() to allow other + * modules to alter the render array built by this form controller. it must + * be unique site wide. It normally starts with the providing module's name. + * + * @return string + * The unique ID of the form defined by this class. + */ + public function getFormId() { + return 'fapi_example_modal_form'; + } + + /** + * Implements the submit handler case. + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $title = $form_state->getValue('title'); + $message = t('Submit handler: You specified a title of %title.', ['%title' => $title]); + drupal_set_message($message); + } + + /** + * Implements the sumbit handler for the ajax call. + */ + public function ajaxSubmitForm(array &$form, FormStateInterface $form_state) { + + // At this point the submit handler has fired. + // Clear the message set by the submit handler. + drupal_get_messages(); + $response = new AjaxResponse(); + if ($form_state->getErrors()) { + unset($form['#prefix']); + unset($form['#suffix']); + $form['status_messages'] = [ + '#type' => 'status_messages', + '#weight' => -10, + ]; + $response->addCommand(new HtmlCommand('#fapi-example-modal-form', $form)); + } + else { + $title = $form_state->getValue('title'); + $message = t('You specified a title of %title.', ['%title' => $title]); + $content = [ + '#type' => 'html_tag', + '#tag' => 'p', + '#value' => $message, + ]; + $response->addCommand(new HtmlCommand('#fapi-example-message', $content)); + $response->addCommand(new CloseModalDialogCommand()); + } + return $response; + } + + +} diff --git a/fapi_example/src/Form/SimpleForm.php b/fapi_example/src/Form/SimpleForm.php new file mode 100644 index 0000000..47a6a41 --- /dev/null +++ b/fapi_example/src/Form/SimpleForm.php @@ -0,0 +1,116 @@ + 'textfield', + '#title' => $this->t('Title'), + '#description' => $this->t('Title must be at least 5 characters in length.'), + '#required' => TRUE, + ); + + // Group submit handlers in an actions element with a key of "actions" so + // that it gets styled correctly, and so that other modules may add actions + // to the form. + $form['actions'] = array( + '#type' => 'actions', + ); + + // Add a submit button that handles the submission of the form. + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Submit'), + ); + + return $form; + } + + /** + * Getter method for Form ID. + * + * The form ID is used in implementations of hook_form_alter() to allow other + * modules to alter the render array built by this form controller. it must + * be unique site wide. It normally starts with the providing module's name. + * + * @return string + * The unique ID of the form defined by this class. + */ + public function getFormId() { + return 'fapi_example_simple_form'; + } + + /** + * Implements form validation. + * + * The validateForm method is the default method called to validate input on + * a form. + * + * @param array $form + * The render array of the currently built form. + * @param FormStateInterface $form_state + * Object describing the current state of the form. + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + $title = $form_state->getValue('title'); + if (strlen($title) < 5) { + // Set an error for the form element with a key of "title". + $form_state->setErrorByName('title', $this->t('The title must be at least 5 characters long.')); + } + } + + /** + * Implements a form submit handler. + * + * The submitForm method is the default method called for any submit elements. + * + * @param array $form + * The render array of the currently built form. + * @param FormStateInterface $form_state + * Object describing the current state of the form. + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + /* + * This would normally be replaced by code that actually does something + * with the title. + */ + $title = $form_state->getValue('title'); + drupal_set_message(t('You specified a title of %title.', ['%title' => $title])); + } + +} diff --git a/fapi_example/src/Form/StateDemo.php b/fapi_example/src/Form/StateDemo.php new file mode 100644 index 0000000..c581b80 --- /dev/null +++ b/fapi_example/src/Form/StateDemo.php @@ -0,0 +1,87 @@ + 'checkbox', + '#title' => 'Need Special Accommodations?', + ]; + + $form['accommodation'] = [ + '#type' => 'container', + '#attributes' => [ + 'class' => 'accommodation', + ], + '#states' => [ + 'invisible' => [ + 'input[name="needs_accommodation"]' => array('checked' => FALSE), + ], + ], + ]; + + $form['accommodation']['diet'] = [ + '#type' => 'textfield', + '#title' => t('Dietary Restrictions'), + ]; + + // Add a submit button that handles the submission of the form. + $form['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Submit'), + ]; + + return $form; + } + + /** + * Getter method for Form ID. + * + * @inheritdoc + */ + public function getFormId() { + return 'fapi_example_state_demo'; + } + + /** + * Implements submitForm callback. + * + * @inheritdoc + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + //Find out what was submitted + $values = $form_state->getValues(); + if ($values['needs_accommodation']) { + $message = $this->t('Dietary Restriction Requested: %diet', array('%diet' => $values['diet'])); + drupal_set_message($message); + } + } + + +} diff --git a/fapi_example/src/Form/VerticalTabsDemo.php b/fapi_example/src/Form/VerticalTabsDemo.php new file mode 100644 index 0000000..dcb3637 --- /dev/null +++ b/fapi_example/src/Form/VerticalTabsDemo.php @@ -0,0 +1,79 @@ + 'vertical_tabs', + '#default_tab' => 'edit-publication', + ); + + $form['author'] = array( + '#type' => 'details', + '#title' => 'Author', + '#group' => 'information', + ); + + $form['author']['name'] = array( + '#type' => 'textfield', + '#title' => t('Name'), + ); + + $form['publication'] = array( + '#type' => 'details', + '#title' => t('Publication'), + '#group' => 'information', + ); + + $form['publication']['publisher'] = array( + '#type' => 'textfield', + '#title' => t('Publisher'), + ); + + $form['actions'] = ['#type' => 'actions']; + // Add a submit button that handles the submission of the form. + $form['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Submit'), + ]; + + return $form; + } + + /** + * Getter method for Form ID. + * + * @inheritdoc + */ + public function getFormId() { + return 'fapi_example_vertical_tabs_demo'; + } + +} diff --git a/fapi_example/src/Tests/InputDemoTest.php b/fapi_example/src/Tests/InputDemoTest.php new file mode 100644 index 0000000..2beb633 --- /dev/null +++ b/fapi_example/src/Tests/InputDemoTest.php @@ -0,0 +1,95 @@ +drupalGet(''); + $this->assertResponse(200, 'The Home page is available.'); + $this->assertLinkByHref('examples/fapi_example'); + + // Test for a link to the simple_form example on the form_example page. + $this->drupalGet('examples/fapi_example'); + $this->assertLinkByHref('examples/fapi_example/input_demo'); + + // Verify that anonymous can access the simpletest_examples page. + $this->drupalGet('examples/fapi_example/input_demo'); + $this->assertResponse(200, 'The Demo of Common Input Elements page is available.'); + + // Post the form. + $edit = [ + 'tests_taken[SAT]' => TRUE, + 'color' => '#ff6bf1', + 'expiration' => '2015-10-21', + 'email' => 'somebody@example.org', + 'quantity' => '4', + 'password' => 'letmein', + 'password_confirm[pass1]' => 'letmein', + 'password_confirm[pass2]' => 'letmein', + 'size' => '76', + 'active' => '1', + 'search' => 'my search string', + 'favorite' => 'blue', + 'phone' => '555-555-5555', + 'table[1]' => TRUE, + 'table[3]' => TRUE, + 'text' => 'This is a test of my form.', + 'subject' => 'Form test', + 'weight' => '3', + ]; + $this->drupalPostForm('/examples/fapi_example/input_demo', $edit, t('Submit')); + $this->assertText('Value for What standardized tests did you take?: Array ( [SAT] => SAT )'); + $this->assertText('Value for Color: #ff6bf1'); + $this->assertText('Value for Content expiration: 2015-10-21'); + $this->assertText('Value for Email: somebody@example.org'); + $this->assertText('Value for Quantity: 4'); + $this->assertText('Value for Password: letmein'); + $this->assertText('Value for New Password: letmein'); + $this->assertText('Value for Size: 76'); + $this->assertText('Value for active: 1'); + $this->assertText('Value for Search: my search string'); + $this->assertText('Value for Favorite color: blue'); + $this->assertText('Value for Phone: 555-555-5555'); + $this->assertText('Value for Users: Array ( [1] => 1 [3] => 3 )'); + $this->assertText('Value for Text: This is a test of my form.'); + $this->assertText('Value for Subject: Form test'); + $this->assertText('Value for Weight: 3'); + } +} \ No newline at end of file diff --git a/fapi_example/src/Tests/SimpleFormTest.php b/fapi_example/src/Tests/SimpleFormTest.php new file mode 100644 index 0000000..aaced9f --- /dev/null +++ b/fapi_example/src/Tests/SimpleFormTest.php @@ -0,0 +1,65 @@ +drupalGet(''); + $this->assertResponse(200, 'The Home page is available.'); + $this->assertLinkByHref('examples/fapi_example'); + + // Test for a link to the simple_form example on the form_example page. + $this->drupalGet('examples/fapi_example'); + $this->assertLinkByHref('examples/fapi_example/simple_form'); + + // Verify that anonymous can access the simpletest_examples page. + $this->drupalGet('examples/fapi_example/simple_form'); + $this->assertResponse(200, 'The Simple Form Example page is available.'); + + // Post a title. + $edit = ['title' => 'My Custom Title']; + $this->drupalPostForm('/examples/fapi_example/simple_form', $edit, t('Submit')); + $this->assertText('You specified a title of My Custom Title.'); + } + +} diff --git a/fapi_example/src/Tests/StateDemoTest.php b/fapi_example/src/Tests/StateDemoTest.php new file mode 100644 index 0000000..aa3a7a8 --- /dev/null +++ b/fapi_example/src/Tests/StateDemoTest.php @@ -0,0 +1,64 @@ +drupalGet(''); + $this->assertResponse(200, 'The Home page is available.'); + $this->assertLinkByHref('examples/fapi_example'); + + // Test for a link to the simple_form example on the form_example page. + $this->drupalGet('examples/fapi_example'); + $this->assertLinkByHref('examples/fapi_example/state_demo'); + + // Verify that anonymous can access the simpletest_examples page. + $this->drupalGet('examples/fapi_example/state_demo'); + $this->assertResponse(200, 'The Demo of Form State Binding page is available.'); + + // Post the form. + $edit = [ + 'needs_accommodation' => TRUE, + 'diet' => 'vegan', + ]; + $this->drupalPostForm('/examples/fapi_example/state_demo', $edit, t('Submit')); + $this->assertText('Dietary Restriction Requested: vegan'); + } +}