(bug 27528) Incorporate Paul Copperman's minifier
[lhc/web/wiklou.git] / includes / HTMLForm.php
index f71c7d3..e3e6f4b 100644 (file)
@@ -54,7 +54,6 @@
  * TODO: Document 'section' / 'subsection' stuff
  */
 class HTMLForm {
-       static $jsAdded = false;
 
        # A mapping of 'type' inputs onto standard HTMLFormField subclasses
        static $typeMappings = array(
@@ -69,6 +68,7 @@ class HTMLForm {
                'float' => 'HTMLFloatField',
                'info' => 'HTMLInfoField',
                'selectorother' => 'HTMLSelectOrOtherField',
+               'selectandother' => 'HTMLSelectAndOtherField',
                'submit' => 'HTMLSubmitField',
                'hidden' => 'HTMLHiddenField',
                'edittools' => 'HTMLEditTools',
@@ -159,14 +159,9 @@ class HTMLForm {
        /**
         * Add the HTMLForm-specific JavaScript, if it hasn't been
         * done already.
+        * @deprecated since 1.18 load modules with ResourceLoader instead
         */
-       static function addJS() {
-               if ( self::$jsAdded ) return;
-
-               global $wgOut;
-
-               $wgOut->addModules( 'mediawiki.legacy.htmlform' );
-       }
+       static function addJS() { }
 
        /**
         * Initialise a new Object for the field
@@ -201,9 +196,6 @@ class HTMLForm {
                        throw new MWException( "You must call setTitle() on an HTMLForm" );
                }
 
-               // FIXME shouldn't this be closer to displayForm() ?
-               self::addJS();
-
                # Load data from the request.
                $this->loadData();
        }
@@ -898,8 +890,10 @@ abstract class HTMLFormField {
 
                if ( $errors === true || ( !$wgRequest->wasPosted() && ( $this->mParent->getMethod() == 'post' ) ) ) {
                        $errors = '';
+                       $errorClass = '';
                } else {
-                       $errors = Html::rawElement( 'span', array( 'class' => 'error' ), $errors );
+                       $errors = self::formatErrors( $errors );
+                       $errorClass = 'mw-htmlform-invalid-input';
                }
 
                $label = $this->getLabelHtml( $cellAttributes );
@@ -911,15 +905,15 @@ abstract class HTMLFormField {
 
                $fieldType = get_class( $this );
 
-               if ($verticalLabel) {
+               if ( $verticalLabel ) {
                        $html = Html::rawElement( 'tr',
                                array( 'class' => 'mw-htmlform-vertical-label' ), $label );
                        $html .= Html::rawElement( 'tr',
-                               array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass}" ),
+                               array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass" ),
                                $field );
                } else {
                        $html = Html::rawElement( 'tr',
-                               array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass}" ),
+                               array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass" ),
                                $label . $field );
                }
 
@@ -928,7 +922,7 @@ abstract class HTMLFormField {
                if ( isset( $this->mParams['help-message'] ) ) {
                        $msg = $this->mParams['help-message'];
                        $helptext = wfMsgExt( $msg, 'parseinline' );
-                       if ( wfEmptyMsg( $msg, $helptext ) ) {
+                       if ( wfEmptyMsg( $msg ) ) {
                                # Never mind
                                $helptext = null;
                        }
@@ -1017,6 +1011,35 @@ abstract class HTMLFormField {
 
                return $flatOpts;
        }
+
+       /**
+        * Formats one or more errors as accepted by field validation-callback.
+        * @param $errors String|Message|Array of strings or Message instances
+        * @return String html
+        * @since 1.18
+        */
+       protected static function formatErrors( $errors ) {
+               if ( is_array( $errors ) && count( $errors ) === 1 ) {
+                       $errors = array_shift( $errors );
+               }
+
+               if ( is_array( $errors ) ) {
+                       $lines = array();
+                       foreach ( $errors as $error ) {
+                               if ( $error instanceof Message ) {
+                                       $lines[] = Html::rawElement( 'li', array(), $error->parse() );
+                               } else {
+                                       $lines[] = Html::rawElement( 'li', array(), $error );
+                               }
+                       }
+                       return Html::rawElement( 'ul', array( 'class' => 'error' ), implode( "\n", $lines ) );
+               } else {
+                       if ( $errors instanceof Message ) {
+                               $errors = $errors->parse();
+                       }
+                       return Html::rawElement( 'span', array( 'class' => 'error' ), $errors );
+               }
+       }
 }
 
 class HTMLTextField extends HTMLFormField {
@@ -1456,6 +1479,167 @@ class HTMLMultiSelectField extends HTMLFormField {
        }
 }
 
+/**
+ * Double field with a dropdown list constructed from a system message in the format
+ *     * Optgroup header
+ *     ** <option value>|<option name>
+ *     ** <option value == option name>
+ *     * New Optgroup header
+ * Plus a text field underneath for an additional reason.  The 'value' of the field is
+ * ""<select>: <extra reason>"", or "<extra reason>" if nothing has been selected in the
+ * select dropdown.
+ * FIXME: If made 'required', only the text field should be compulsory.
+ */
+class HTMLSelectAndOtherField extends HTMLSelectField {
+
+       function __construct( $params ) {
+               if ( array_key_exists( 'other', $params ) ) {
+               } elseif( array_key_exists( 'other-message', $params ) ){
+                       $params['other'] = wfMsg( $params['other-message'] );
+               } else {
+                       $params['other'] = wfMsg( 'htmlform-selectorother-other' );
+               }
+
+               if ( array_key_exists( 'options', $params ) ) {
+                       # Options array already specified
+               } elseif( array_key_exists( 'options-message', $params ) ){
+                       # Generate options array from a system message
+                       $params['options'] = self::parseMessage( wfMsg( $params['options-message'], $params['other'] ) );
+               } else {
+                       # Sulk
+                       throw new MWException( 'HTMLSelectAndOtherField called without any options' );
+               }
+               $this->mFlatOptions = self::flattenOptions( $params['options'] );
+
+               parent::__construct( $params );
+       }
+
+       /**
+        * Build a drop-down box from a textual list.
+        * @param $string String message text
+        * @param $otherName String name of "other reason" option
+        * @return Array
+        * TODO: this is copied from Xml::listDropDown(), deprecate/avoid duplication?
+        */
+       public static function parseMessage( $string, $otherName=null ) {
+               if( $otherName === null ){
+                       $otherName = wfMsg( 'htmlform-selectorother-other' );
+               }
+
+               $optgroup = false;
+               $options = array( $otherName => 'other' );
+
+               foreach ( explode( "\n", $string ) as $option ) {
+                       $value = trim( $option );
+                       if ( $value == '' ) {
+                               continue;
+                       } elseif ( substr( $value, 0, 1) == '*' && substr( $value, 1, 1) != '*' ) {
+                               # A new group is starting...
+                               $value = trim( substr( $value, 1 ) );
+                               $optgroup = $value;
+                       } elseif ( substr( $value, 0, 2) == '**' ) {
+                               # groupmember
+                               $opt = trim( substr( $value, 2 ) );
+                               $parts = array_map( 'trim', explode( '|', $opt, 2 ) );
+                               if( count( $parts ) === 1 ){
+                                       $parts[1] = $parts[0];
+                               }
+                               if( $optgroup === false ){
+                                       $options[$parts[1]] = $parts[0];
+                               } else {
+                                       $options[$optgroup][$parts[1]] = $parts[0];
+                               }
+                       } else {
+                               # groupless reason list
+                               $optgroup = false;
+                               $parts = array_map( 'trim', explode( '|', $opt, 2 ) );
+                               if( count( $parts ) === 1 ){
+                                       $parts[1] = $parts[0];
+                               }
+                               $options[$parts[1]] = $parts[0];
+                       }
+               }
+
+               return $options;
+       }
+
+       function getInputHTML( $value ) {
+               $select = parent::getInputHTML( $value[1] );
+
+               $textAttribs = array(
+                       'id' => $this->mID . '-other',
+                       'size' => $this->getSize(),
+               );
+
+               foreach ( array( 'required', 'autofocus', 'multiple', 'disabled' ) as $param ) {
+                       if ( isset( $this->mParams[$param] ) ) {
+                               $textAttribs[$param] = '';
+                       }
+               }
+
+               $textbox = Html::input(
+                       $this->mName . '-other',
+                       $value[2],
+                       'text',
+                       $textAttribs
+               );
+
+               return "$select<br />\n$textbox";
+       }
+
+       /**
+        * @param  $request WebRequest
+        * @return Array( <overall message>, <select value>, <text field value> )
+        */
+       function loadDataFromRequest( $request ) {
+               if ( $request->getCheck( $this->mName ) ) {
+
+                       $list = $request->getText( $this->mName );
+                       $text = $request->getText( $this->mName . '-other' );
+
+                       if ( $list == 'other' ) {
+                               $final = $text;
+                       } elseif( !in_array( $list, $this->mFlatOptions ) ){
+                               # User has spoofed the select form to give an option which wasn't
+                               # in the original offer.  Sulk...
+                               $final = $text;
+                       } elseif( $text == '' ) {
+                               $final = $list;
+                       } else {
+                               $final = $list . wfMsgForContent( 'colon-separator' ) . $text;
+                       }
+
+               } else {
+                       $final = $this->getDefault();
+                       $list = $text = '';
+               }
+               return array( $final, $list, $text );
+       }
+
+       function getSize() {
+               return isset( $this->mParams['size'] )
+                       ? $this->mParams['size']
+                       : 45;
+       }
+
+       function validate( $value, $alldata ) {
+               # HTMLSelectField forces $value to be one of the options in the select
+               # field, which is not useful here.  But we do want the validation further up
+               # the chain
+               $p = parent::validate( $value[1], $alldata );
+
+               if ( $p !== true ) {
+                       return $p;
+               }
+
+               if( isset( $this->mParams['required'] ) && $value[1] === '' ){
+                       return wfMsgExt( 'htmlform-required', 'parseinline' );
+               }
+
+               return true;
+       }
+}
+
 /**
  * Radio checkbox fields.
  */