HTMLForm: Suppress HTML5 form validation for non-JS users when needed
authorBartosz Dziewoński <matma.rex@gmail.com>
Fri, 2 Dec 2016 18:17:35 +0000 (19:17 +0100)
committerBartosz Dziewoński <matma.rex@gmail.com>
Tue, 10 Jan 2017 22:49:38 +0000 (22:49 +0000)
Our 'hide-if' fields are fundamentally incompatible with HTML5 form
validation attributes. If you have a checkbox field A, and field B
that is required, but hidden if A is unchecked - that's impossible to
express with HTML5 form validation. The only thing you can do is
remove the validation on B (or on the entire form).

The field contents are still validated server-side, just like if the
browser did not support HTML5 forms. The validation is also re-enabled
in JavaScript, since we have extra support for 'hide-if' field that
makes them work.

Change-Id: Ia7ffa76965a7c14af9b6d2db007b6255498398d9

includes/htmlform/HTMLForm.php
includes/htmlform/HTMLFormField.php
resources/src/mediawiki/htmlform/htmlform.js

index 5c5a9a7..e627cfd 100644 (file)
@@ -1061,6 +1061,9 @@ class HTMLForm extends ContextSource {
                if ( $this->mName ) {
                        $attribs['name'] = $this->mName;
                }
+               if ( $this->needsJSForHtml5FormValidation() ) {
+                       $attribs['novalidate'] = true;
+               }
                return $attribs;
        }
 
@@ -1876,4 +1879,22 @@ class HTMLForm extends ContextSource {
        protected function getMessage( $value ) {
                return Message::newFromSpecifier( $value )->setContext( $this );
        }
+
+       /**
+        * Whether this form, with its current fields, requires the user agent to have JavaScript enabled
+        * for the client-side HTML5 form validation to work correctly. If this function returns true, a
+        * 'novalidate' attribute will be added on the `<form>` element. It will be removed if the user
+        * agent has JavaScript support, in htmlform.js.
+        *
+        * @return boolean
+        * @since 1.29
+        */
+       public function needsJSForHtml5FormValidation() {
+               foreach ( $this->mFlatFields as $fieldname => $field ) {
+                       if ( $field->needsJSForHtml5FormValidation() ) {
+                               return true;
+                       }
+               }
+               return false;
+       }
 }
index 804bbff..487d6f6 100644 (file)
@@ -1194,4 +1194,19 @@ abstract class HTMLFormField {
        public function skipLoadData( $request ) {
                return !empty( $this->mParams['nodata'] );
        }
+
+       /**
+        * Whether this field requires the user agent to have JavaScript enabled for the client-side HTML5
+        * form validation to work correctly.
+        *
+        * @return boolean
+        * @since 1.29
+        */
+       public function needsJSForHtml5FormValidation() {
+               if ( $this->mHideIf ) {
+                       // This is probably more restrictive than it needs to be, but better safe than sorry
+                       return true;
+               }
+               return false;
+       }
 }
index 19f8f3e..bc835b5 100644 (file)
@@ -4,4 +4,11 @@
                mw.hook( 'htmlform.enhance' ).fire( $( document ) );
        } );
 
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               // Turn HTML5 form validation back on, in cases where it was disabled server-side (see
+               // HTMLForm::needsJSForHtml5FormValidation()) because we need extra logic implemented in JS to
+               // validate correctly. Currently, this is only used for forms containing fields with 'hide-if'.
+               $root.find( '.mw-htmlform' ).removeAttr( 'novalidate' );
+       } );
+
 }( mediaWiki, jQuery ) );