* TODO: Document 'section' / 'subsection' stuff
*/
class HTMLForm {
- static $jsAdded = false;
# A mapping of 'type' inputs onto standard HTMLFormField subclasses
static $typeMappings = array(
'float' => 'HTMLFloatField',
'info' => 'HTMLInfoField',
'selectorother' => 'HTMLSelectOrOtherField',
+ 'selectandother' => 'HTMLSelectAndOtherField',
'submit' => 'HTMLSubmitField',
'hidden' => 'HTMLHiddenField',
'edittools' => 'HTMLEditTools',
/**
* 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
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();
}
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 );
$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 );
}
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;
}
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 {
}
}
+/**
+ * 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.
*/