Hoist validation errors from hidden fields to the top of the form
[lhc/web/wiklou.git] / includes / htmlform / HTMLFormField.php
index e19273b..5d405c3 100644 (file)
@@ -49,12 +49,21 @@ abstract class HTMLFormField {
         * Defaults to false, which getOOUI will interpret as "use the HTML version"
         *
         * @param string $value
-        * @return OOUI\Widget|false
+        * @return OOUI\\Widget|false
         */
        function getInputOOUI( $value ) {
                return false;
        }
 
+       /**
+        * True if this field type is able to display errors; false if validation errors need to be
+        * displayed in the main HTMLForm error area.
+        * @return bool
+        */
+       public function canDisplayErrors() {
+               return true;
+       }
+
        /**
         * Get a translated interface message
         *
@@ -558,38 +567,47 @@ abstract class HTMLFormField {
         *
         * @param string $value The value to set the input to.
         *
-        * @return string
+        * @return OOUI\\FieldLayout|OOUI\\ActionFieldLayout
         */
        public function getOOUI( $value ) {
-               list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value );
-
                $inputField = $this->getInputOOUI( $value );
 
                if ( !$inputField ) {
-                       // This field doesn't have an OOUI implementation yet at all.
-                       // OK, use this trick:
-                       return $this->getDiv( $value );
+                       // This field doesn't have an OOUI implementation yet at all. Fall back to getDiv() to
+                       // generate the whole field, label and errors and all, then wrap it in a Widget.
+                       // It might look weird, but it'll work OK.
+                       return $this->getFieldLayoutOOUI(
+                               new OOUI\Widget( array( 'content' => new OOUI\HtmlSnippet( $this->getDiv( $value ) ) ) ),
+                               array( 'infusable' => false )
+                       );
                }
 
                $infusable = true;
                if ( is_string( $inputField ) ) {
-                       // Mmm… We have an OOUI implementation, but it's not complete, and we got a load of HTML.
-                       // Cheat a little and wrap it in a widget! It won't be infusable, though, since client-side
+                       // We have an OOUI implementation, but it's not proper, and we got a load of HTML.
+                       // Cheat a little and wrap it in a widget. It won't be infusable, though, since client-side
                        // JavaScript doesn't know how to rebuilt the contents.
                        $inputField = new OOUI\Widget( array( 'content' => new OOUI\HtmlSnippet( $inputField ) ) );
                        $infusable = false;
                }
 
                $fieldType = get_class( $this );
-               $field = new OOUI\FieldLayout( $inputField, array(
-                       'classes' => array( "mw-htmlform-field-$fieldType", $this->mClass, $errorClass ),
+               $helpText = $this->getHelpText();
+               $errors = $this->getErrorsRaw( $value );
+               foreach ( $errors as &$error ) {
+                       $error = new OOUI\HtmlSnippet( $error );
+               }
+
+               $config = array(
+                       'classes' => array( "mw-htmlform-field-$fieldType", $this->mClass ),
                        'align' => $this->getLabelAlignOOUI(),
                        'label' => $this->getLabel(),
-                       'help' => $this->getHelpText(),
+                       'help' => $helpText !== null ? new OOUI\HtmlSnippet( $helpText ) : null,
+                       'errors' => $errors,
                        'infusable' => $infusable,
-               ) );
+               );
 
-               return $field . $errors;
+               return $this->getFieldLayoutOOUI( $inputField, $config );
        }
 
        /**
@@ -600,6 +618,18 @@ abstract class HTMLFormField {
                return 'top';
        }
 
+       /**
+        * Get a FieldLayout (or subclass thereof) to wrap this field in when using OOUI output.
+        * @return OOUI\\FieldLayout|OOUI\\ActionFieldLayout
+        */
+       protected function getFieldLayoutOOUI( $inputField, $config ) {
+               if ( isset( $this->mClassWithButton ) ) {
+                       $buttonWidget = $this->mClassWithButton->getInputOOUI( '' );
+                       return new OOUI\ActionFieldLayout( $inputField, $buttonWidget, $config );
+               }
+               return new OOUI\FieldLayout( $inputField, $config );
+       }
+
        /**
         * Get the complete raw fields for the input, including help text,
         * labels, and whatever.
@@ -729,7 +759,7 @@ abstract class HTMLFormField {
        /**
         * Determine the help text to display
         * @since 1.20
-        * @return string
+        * @return string HTML
         */
        public function getHelpText() {
                $helptext = null;
@@ -764,7 +794,7 @@ abstract class HTMLFormField {
         * @since 1.20
         *
         * @param string $value The value of the input
-        * @return array
+        * @return array array( $errors, $errorClass )
         */
        public function getErrorsAndErrorClass( $value ) {
                $errors = $this->validate( $value, $this->mParent->mFieldData );
@@ -780,6 +810,32 @@ abstract class HTMLFormField {
                return array( $errors, $errorClass );
        }
 
+       /**
+        * Determine form errors to display, returning them in an array.
+        *
+        * @since 1.26
+        * @param string $value The value of the input
+        * @return string[] Array of error HTML strings
+        */
+       public function getErrorsRaw( $value ) {
+               $errors = $this->validate( $value, $this->mParent->mFieldData );
+
+               if ( is_bool( $errors ) || !$this->mParent->wasSubmitted() ) {
+                       $errors = array();
+               }
+
+               if ( !is_array( $errors ) ) {
+                       $errors = array( $errors );
+               }
+               foreach ( $errors as &$error ) {
+                       if ( $error instanceof Message ) {
+                               $error = $error->parse();
+                       }
+               }
+
+               return $errors;
+       }
+
        /**
         * @return string
         */
@@ -873,7 +929,7 @@ abstract class HTMLFormField {
         * @return array Attributes
         */
        public function getAttributes( array $list, array $mappings = null ) {
-               static $boolAttribs = array( 'disabled', 'required', 'autofocus', 'multiple', 'readonly' );
+               static $boolAttribs = array( 'disabled', 'required', 'multiple', 'readonly' );
 
                $ret = array();
                foreach ( $list as $key ) {