diff options
Diffstat (limited to 'includes/htmlform')
-rw-r--r-- | includes/htmlform/HTMLCheckField.php | 46 | ||||
-rw-r--r-- | includes/htmlform/HTMLCheckMatrix.php | 14 | ||||
-rw-r--r-- | includes/htmlform/HTMLFloatField.php | 2 | ||||
-rw-r--r-- | includes/htmlform/HTMLForm.php | 191 | ||||
-rw-r--r-- | includes/htmlform/HTMLFormField.php | 50 | ||||
-rw-r--r-- | includes/htmlform/HTMLFormFieldCloner.php | 42 | ||||
-rw-r--r-- | includes/htmlform/HTMLHiddenField.php | 18 | ||||
-rw-r--r-- | includes/htmlform/HTMLIntField.php | 5 | ||||
-rw-r--r-- | includes/htmlform/HTMLMultiSelectField.php | 8 | ||||
-rw-r--r-- | includes/htmlform/HTMLRadioField.php | 2 | ||||
-rw-r--r-- | includes/htmlform/HTMLSelectAndOtherField.php | 11 | ||||
-rw-r--r-- | includes/htmlform/HTMLSelectNamespace.php | 18 | ||||
-rw-r--r-- | includes/htmlform/HTMLTagFilter.php | 31 | ||||
-rw-r--r-- | includes/htmlform/HTMLTextField.php | 7 | ||||
-rw-r--r-- | includes/htmlform/VFormHTMLForm.php | 141 |
15 files changed, 427 insertions, 159 deletions
diff --git a/includes/htmlform/HTMLCheckField.php b/includes/htmlform/HTMLCheckField.php index 5f70362a..4942327f 100644 --- a/includes/htmlform/HTMLCheckField.php +++ b/includes/htmlform/HTMLCheckField.php @@ -20,28 +20,19 @@ class HTMLCheckField extends HTMLFormField { $attr['class'] = $this->mClass; } - if ( $this->mParent->isVForm() ) { - // Nest checkbox inside label. - return Html::rawElement( 'label', - array( - 'class' => 'mw-ui-checkbox-label' - ), - Xml::check( $this->mName, $value, $attr ) . $this->mLabel ); - } else { - $chkLabel = Xml::check( $this->mName, $value, $attr ) - . ' ' - . Html::rawElement( 'label', array( 'for' => $this->mID ), $this->mLabel ); - - if ( $wgUseMediaWikiUIEverywhere ) { - $chkLabel = Html::rawElement( - 'div', - array( 'class' => 'mw-ui-checkbox' ), - $chkLabel - ); - } + $chkLabel = Xml::check( $this->mName, $value, $attr ) + . ' ' + . Html::rawElement( 'label', array( 'for' => $this->mID ), $this->mLabel ); - return $chkLabel; + if ( $wgUseMediaWikiUIEverywhere || $this->mParent instanceof VFormHTMLForm ) { + $chkLabel = Html::rawElement( + 'div', + array( 'class' => 'mw-ui-checkbox' ), + $chkLabel + ); } + + return $chkLabel; } /** @@ -67,23 +58,16 @@ class HTMLCheckField extends HTMLFormField { * @return string */ function loadDataFromRequest( $request ) { - $invert = false; - if ( isset( $this->mParams['invert'] ) && $this->mParams['invert'] ) { - $invert = true; - } + $invert = isset( $this->mParams['invert'] ) && $this->mParams['invert']; // GetCheck won't work like we want for checks. // Fetch the value in either one of the two following case: // - we have a valid token (form got posted or GET forged by the user) // - checkbox name has a value (false or true), ie is not null if ( $request->getCheck( 'wpEditToken' ) || $request->getVal( $this->mName ) !== null ) { - // XOR has the following truth table, which is what we want - // INVERT VALUE | OUTPUT - // true true | false - // false true | true - // false false | false - // true false | true - return $request->getBool( $this->mName ) xor $invert; + return $invert + ? !$request->getBool( $this->mName ) + : $request->getBool( $this->mName ); } else { return $this->getDefault(); } diff --git a/includes/htmlform/HTMLCheckMatrix.php b/includes/htmlform/HTMLCheckMatrix.php index 6c538fdd..83f12665 100644 --- a/includes/htmlform/HTMLCheckMatrix.php +++ b/includes/htmlform/HTMLCheckMatrix.php @@ -178,6 +178,13 @@ class HTMLCheckMatrix extends HTMLFormField implements HTMLNestedFilterable { $helptext = $this->getHelpTextHtmlTable( $this->getHelpText() ); $cellAttributes = array( 'colspan' => 2 ); + $hideClass = ''; + $hideAttributes = array(); + if ( $this->mHideIf ) { + $hideAttributes['data-hide-if'] = FormatJson::encode( $this->mHideIf ); + $hideClass = 'mw-htmlform-hide-if'; + } + $label = $this->getLabelHtml( $cellAttributes ); $field = Html::rawElement( @@ -186,9 +193,12 @@ class HTMLCheckMatrix extends HTMLFormField implements HTMLNestedFilterable { $inputHtml . "\n$errors" ); - $html = Html::rawElement( 'tr', array( 'class' => 'mw-htmlform-vertical-label' ), $label ); + $html = Html::rawElement( 'tr', + array( 'class' => "mw-htmlform-vertical-label $hideClass" ) + $hideAttributes, + $label ); $html .= Html::rawElement( 'tr', - array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass" ), + array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass $hideClass" ) + + $hideAttributes, $field ); return $html . $helptext; diff --git a/includes/htmlform/HTMLFloatField.php b/includes/htmlform/HTMLFloatField.php index 3b38fbe8..2ef49789 100644 --- a/includes/htmlform/HTMLFloatField.php +++ b/includes/htmlform/HTMLFloatField.php @@ -17,7 +17,7 @@ class HTMLFloatField extends HTMLTextField { $value = trim( $value ); - # http://dev.w3.org/html5/spec/common-microsyntaxes.html#real-numbers + # http://www.w3.org/TR/html5/infrastructure.html#floating-point-numbers # with the addition that a leading '+' sign is ok. if ( !preg_match( '/^((\+|\-)?\d+(\.\d+)?(E(\+|\-)?\d+)?)?$/i', $value ) ) { return $this->msg( 'htmlform-float-invalid' )->parseAsBlock(); diff --git a/includes/htmlform/HTMLForm.php b/includes/htmlform/HTMLForm.php index d582da3b..ce140038 100644 --- a/includes/htmlform/HTMLForm.php +++ b/includes/htmlform/HTMLForm.php @@ -115,6 +115,8 @@ class HTMLForm extends ContextSource { 'info' => 'HTMLInfoField', 'selectorother' => 'HTMLSelectOrOtherField', 'selectandother' => 'HTMLSelectAndOtherField', + 'namespaceselect' => 'HTMLSelectNamespace', + 'tagfilter' => 'HTMLTagFilter', 'submit' => 'HTMLSubmitField', 'hidden' => 'HTMLHiddenField', 'edittools' => 'HTMLEditTools', @@ -205,10 +207,42 @@ class HTMLForm extends ContextSource { 'table', 'div', 'raw', + 'inline', + ); + + /** + * Available formats in which to display the form + * @var array + */ + protected $availableSubclassDisplayFormats = array( 'vform', ); /** + * Construct a HTMLForm object for given display type. May return a HTMLForm subclass. + * + * @throws MWException When the display format requested is not known + * @param string $displayFormat + * @param mixed $arguments... Additional arguments to pass to the constructor. + * @return HTMLForm + */ + public static function factory( $displayFormat/*, $arguments...*/ ) { + $arguments = func_get_args(); + array_shift( $arguments ); + + switch ( $displayFormat ) { + case 'vform': + $reflector = new ReflectionClass( 'VFormHTMLForm' ); + return $reflector->newInstanceArgs( $arguments ); + default: + $reflector = new ReflectionClass( 'HTMLForm' ); + $form = $reflector->newInstanceArgs( $arguments ); + $form->setDisplayFormat( $displayFormat ); + return $form; + } + } + + /** * Build a new HTMLForm from an array of field attributes * * @param array $descriptor Array of Field constructs, as described above @@ -231,6 +265,11 @@ class HTMLForm extends ContextSource { $this->mMessagePrefix = $context; } + // Evil hack for mobile :( + if ( !$this->getConfig()->get( 'HTMLFormAllowTableFormat' ) && $this->displayFormat === 'table' ) { + $this->displayFormat = 'div'; + } + // Expand out into a tree. $loadedDescriptor = array(); $this->mFlatFields = array(); @@ -244,15 +283,7 @@ class HTMLForm extends ContextSource { $this->mUseMultipart = true; } - $field = self::loadInputFromParameters( $fieldname, $info ); - // FIXME During field's construct, the parent form isn't available! - // could add a 'parent' name-value to $info, could add a third parameter. - $field->mParent = $this; - - // vform gets too much space if empty labels generate HTML. - if ( $this->isVForm() ) { - $field->setShowEmptyLabel( false ); - } + $field = static::loadInputFromParameters( $fieldname, $info, $this ); $setSection =& $loadedDescriptor; if ( $section ) { @@ -287,10 +318,24 @@ class HTMLForm extends ContextSource { * @return HTMLForm $this for chaining calls (since 1.20) */ public function setDisplayFormat( $format ) { + if ( + in_array( $format, $this->availableSubclassDisplayFormats ) || + in_array( $this->displayFormat, $this->availableSubclassDisplayFormats ) + ) { + throw new MWException( 'Cannot change display format after creation, ' . + 'use HTMLForm::factory() instead' ); + } + if ( !in_array( $format, $this->availableDisplayFormats ) ) { throw new MWException( 'Display format must be one of ' . print_r( $this->availableDisplayFormats, true ) ); } + + // Evil hack for mobile :( + if ( !$this->getConfig()->get( 'HTMLFormAllowTableFormat' ) && $format === 'table' ) { + $format = 'div'; + } + $this->displayFormat = $format; return $this; @@ -302,20 +347,18 @@ class HTMLForm extends ContextSource { * @return string */ public function getDisplayFormat() { - $format = $this->displayFormat; - if ( !$this->getConfig()->get( 'HTMLFormAllowTableFormat' ) && $format === 'table' ) { - $format = 'div'; - } - return $format; + return $this->displayFormat; } /** * Test if displayFormat is 'vform' * @since 1.22 + * @deprecated since 1.25 * @return bool */ public function isVForm() { - return $this->displayFormat === 'vform'; + wfDeprecated( __METHOD__, '1.25' ); + return false; } /** @@ -338,7 +381,7 @@ class HTMLForm extends ContextSource { if ( isset( $descriptor['class'] ) ) { $class = $descriptor['class']; } elseif ( isset( $descriptor['type'] ) ) { - $class = self::$typeMappings[$descriptor['type']]; + $class = static::$typeMappings[$descriptor['type']]; $descriptor['class'] = $class; } else { $class = null; @@ -357,14 +400,18 @@ class HTMLForm extends ContextSource { * * @param string $fieldname Name of the field * @param array $descriptor Input Descriptor, as described above + * @param HTMLForm|null $parent Parent instance of HTMLForm * * @throws MWException * @return HTMLFormField Instance of a subclass of HTMLFormField */ - public static function loadInputFromParameters( $fieldname, $descriptor ) { - $class = self::getClassFromDescriptor( $fieldname, $descriptor ); + public static function loadInputFromParameters( $fieldname, $descriptor, HTMLForm $parent = null ) { + $class = static::getClassFromDescriptor( $fieldname, $descriptor ); $descriptor['fieldname'] = $fieldname; + if ( $parent ) { + $descriptor['parent'] = $parent; + } # @todo This will throw a fatal error whenever someone try to use # 'class' to feed a CSS class instead of 'cssclass'. Would be @@ -787,14 +834,6 @@ class HTMLForm extends ContextSource { # For good measure (it is the default) $this->getOutput()->preventClickjacking(); $this->getOutput()->addModules( 'mediawiki.htmlform' ); - if ( $this->isVForm() ) { - $this->getOutput()->addModuleStyles( array( - 'mediawiki.ui', - 'mediawiki.ui.button', - ) ); - // @todo Should vertical form set setWrapperLegend( false ) - // to hide ugly fieldsets? - } $html = '' . $this->getErrors( $submitResult ) @@ -810,18 +849,10 @@ class HTMLForm extends ContextSource { } /** - * Wrap the form innards in an actual "<form>" element - * - * @param string $html HTML contents to wrap. - * - * @return string Wrapped HTML. + * Get HTML attributes for the `<form>` tag. + * @return array */ - function wrapForm( $html ) { - - # Include a <fieldset> wrapper for style, if requested. - if ( $this->mWrapperLegend !== false ) { - $html = Xml::fieldset( $this->mWrapperLegend, $html ); - } + protected function getFormAttributes() { # Use multipart/form-data $encType = $this->mUseMultipart ? 'multipart/form-data' @@ -836,12 +867,23 @@ class HTMLForm extends ContextSource { if ( !empty( $this->mId ) ) { $attribs['id'] = $this->mId; } + return $attribs; + } - if ( $this->isVForm() ) { - array_push( $attribs['class'], 'mw-ui-vform', 'mw-ui-container' ); + /** + * Wrap the form innards in an actual "<form>" element + * + * @param string $html HTML contents to wrap. + * + * @return string Wrapped HTML. + */ + function wrapForm( $html ) { + # Include a <fieldset> wrapper for style, if requested. + if ( $this->mWrapperLegend !== false ) { + $html = Xml::fieldset( $this->mWrapperLegend, $html ); } - return Html::rawElement( 'form', $attribs, $html ); + return Html::rawElement( 'form', $this->getFormAttributes(), $html ); } /** @@ -897,21 +939,10 @@ class HTMLForm extends ContextSource { $attribs['class'] = array( 'mw-htmlform-submit' ); - if ( $this->isVForm() || $useMediaWikiUIEverywhere ) { + if ( $useMediaWikiUIEverywhere ) { array_push( $attribs['class'], 'mw-ui-button', $this->mSubmitModifierClass ); } - if ( $this->isVForm() ) { - // mw-ui-block is necessary because the buttons aren't necessarily in an - // immediate child div of the vform. - // @todo Let client specify if the primary submit button is progressive or destructive - array_push( - $attribs['class'], - 'mw-ui-big', - 'mw-ui-block' - ); - } - $buttons .= Xml::submitButton( $this->getSubmitText(), $attribs ) . "\n"; } @@ -920,7 +951,8 @@ class HTMLForm extends ContextSource { 'input', array( 'type' => 'reset', - 'value' => $this->msg( 'htmlform-reset' )->text() + 'value' => $this->msg( 'htmlform-reset' )->text(), + 'class' => ( $useMediaWikiUIEverywhere ? 'mw-ui-button' : null ), ) ) . "\n"; } @@ -940,15 +972,9 @@ class HTMLForm extends ContextSource { $attrs['id'] = $button['id']; } - if ( $this->isVForm() || $useMediaWikiUIEverywhere ) { - if ( isset( $attrs['class'] ) ) { - $attrs['class'] .= ' mw-ui-button'; - } else { - $attrs['class'] = 'mw-ui-button'; - } - if ( $this->isVForm() ) { - $attrs['class'] .= ' mw-ui-big mw-ui-block'; - } + if ( $useMediaWikiUIEverywhere ) { + $attrs['class'] = isset( $attrs['class'] ) ? (array)$attrs['class'] : array(); + $attrs['class'][] = 'mw-ui-button'; } $buttons .= Html::element( 'input', $attrs ) . "\n"; @@ -957,13 +983,6 @@ class HTMLForm extends ContextSource { $html = Html::rawElement( 'span', array( 'class' => 'mw-htmlform-submit-buttons' ), "\n$buttons" ) . "\n"; - // Buttons are top-level form elements in table and div layouts, - // but vform wants all elements inside divs to get spaced-out block - // styling. - if ( $this->mShowSubmit && $this->isVForm() ) { - $html = Html::rawElement( 'div', null, "\n$html" ) . "\n"; - } - return $html; } @@ -1007,7 +1026,7 @@ class HTMLForm extends ContextSource { * * @return string HTML, a "<ul>" list of errors */ - public static function formatErrors( $errors ) { + public function formatErrors( $errors ) { $errorstr = ''; foreach ( $errors as $error ) { @@ -1021,7 +1040,7 @@ class HTMLForm extends ContextSource { $errorstr .= Html::rawElement( 'li', array(), - wfMessage( $msg, $error )->parse() + $this->msg( $msg, $error )->parse() ); } @@ -1045,13 +1064,21 @@ class HTMLForm extends ContextSource { /** * Identify that the submit button in the form has a destructive action - * + * @since 1.24 */ public function setSubmitDestructive() { $this->mSubmitModifierClass = 'mw-ui-destructive'; } /** + * Identify that the submit button in the form has a progressive action + * @since 1.25 + */ + public function setSubmitProgressive() { + $this->mSubmitModifierClass = 'mw-ui-progressive'; + } + + /** * Set the text for the submit button to a message * @since 1.19 * @@ -1268,20 +1295,8 @@ class HTMLForm extends ContextSource { $subsectionHtml = ''; $hasLabel = false; - switch ( $displayFormat ) { - case 'table': - $getFieldHtmlMethod = 'getTableRow'; - break; - case 'vform': - // Close enough to a div. - $getFieldHtmlMethod = 'getDiv'; - break; - case 'div': - $getFieldHtmlMethod = 'getDiv'; - break; - default: - $getFieldHtmlMethod = 'get' . ucfirst( $displayFormat ); - } + // Conveniently, PHP method names are case-insensitive. + $getFieldHtmlMethod = $displayFormat == 'table' ? 'getTableRow' : ( 'get' . $displayFormat ); foreach ( $fields as $key => $value ) { if ( $value instanceof HTMLFormField ) { @@ -1353,7 +1368,9 @@ class HTMLForm extends ContextSource { $html = Html::rawElement( 'table', $attribs, Html::rawElement( 'tbody', array(), "\n$html\n" ) ) . "\n"; - } elseif ( $displayFormat === 'div' || $displayFormat === 'vform' ) { + } elseif ( $displayFormat === 'inline' ) { + $html = Html::rawElement( 'span', $attribs, "\n$html\n" ); + } else { $html = Html::rawElement( 'div', $attribs, "\n$html\n" ); } } diff --git a/includes/htmlform/HTMLFormField.php b/includes/htmlform/HTMLFormField.php index 4cf23942..9576c77c 100644 --- a/includes/htmlform/HTMLFormField.php +++ b/includes/htmlform/HTMLFormField.php @@ -13,6 +13,7 @@ abstract class HTMLFormField { protected $mLabel; # String label. Set on construction protected $mID; protected $mClass = ''; + protected $mVFormClass = ''; protected $mHelpClass = false; protected $mDefault; protected $mOptions = false; @@ -126,6 +127,7 @@ abstract class HTMLFormField { * @param array $alldata * @param array $params * @return bool + * @throws MWException */ protected function isHiddenRecurse( array $alldata, array $params ) { $origParams = $params; @@ -217,7 +219,7 @@ abstract class HTMLFormField { default: throw new MWException( "Unknown operation" ); } - } catch ( MWException $ex ) { + } catch ( Exception $ex ) { throw new MWException( "Invalid hide-if specification for $this->mName: " . $ex->getMessage() . " in " . var_export( $origParams, true ), @@ -343,6 +345,10 @@ abstract class HTMLFormField { function __construct( $params ) { $this->mParams = $params; + if ( isset( $params['parent'] ) && $params['parent'] instanceof HTMLForm ) { + $this->mParent = $params['parent']; + } + # Generate the label from a message, if possible if ( isset( $params['label-message'] ) ) { $msgInfo = $params['label-message']; @@ -354,7 +360,7 @@ abstract class HTMLFormField { $msgInfo = array(); } - $this->mLabel = wfMessage( $msg, $msgInfo )->parse(); + $this->mLabel = $this->msg( $msg, $msgInfo )->parse(); } elseif ( isset( $params['label'] ) ) { if ( $params['label'] === ' ' ) { // Apparently some things set   directly and in an odd format @@ -507,10 +513,7 @@ abstract class HTMLFormField { array( 'class' => $outerDivClass ) + $cellAttributes, $inputHtml . "\n$errors" ); - $divCssClasses = array( "mw-htmlform-field-$fieldType", $this->mClass, $errorClass ); - if ( $this->mParent->isVForm() ) { - $divCssClasses[] = 'mw-ui-vform-field'; - } + $divCssClasses = array( "mw-htmlform-field-$fieldType", $this->mClass, $this->mVFormClass, $errorClass ); $wrapperAttributes = array( 'class' => $divCssClasses, @@ -550,6 +553,41 @@ abstract class HTMLFormField { } /** + * Get the complete field for the input, including help text, + * labels, and whatever. Fall back from 'vform' to 'div' when not overridden. + * + * @since 1.25 + * @param string $value The value to set the input to. + * @return string Complete HTML field. + */ + public function getVForm( $value ) { + // Ewwww + $this->mVFormClass = ' mw-ui-vform-field'; + return $this->getDiv( $value ); + } + + /** + * Get the complete field as an inline element. + * @since 1.25 + * @param string $value The value to set the input to. + * @return string Complete HTML inline element + */ + public function getInline( $value ) { + list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value ); + $inputHtml = $this->getInputHTML( $value ); + $helptext = $this->getHelpTextHtmlDiv( $this->getHelpText() ); + $cellAttributes = array(); + $label = $this->getLabelHtml( $cellAttributes ); + + $html = "\n" . $errors . + $label . ' ' . + $inputHtml . + $helptext; + + return $html; + } + + /** * Generate help text HTML in table format * @since 1.20 * diff --git a/includes/htmlform/HTMLFormFieldCloner.php b/includes/htmlform/HTMLFormFieldCloner.php index 029911cd..b06f10d5 100644 --- a/includes/htmlform/HTMLFormFieldCloner.php +++ b/includes/htmlform/HTMLFormFieldCloner.php @@ -96,8 +96,7 @@ class HTMLFormFieldCloner extends HTMLFormField { } else { $info['id'] = Sanitizer::escapeId( "{$this->mID}--$key--$fieldname" ); } - $field = HTMLForm::loadInputFromParameters( $name, $info ); - $field->mParent = $this->mParent; + $field = HTMLForm::loadInputFromParameters( $name, $info, $this->mParent ); $fields[$fieldname] = $field; } return $fields; @@ -263,19 +262,11 @@ class HTMLFormFieldCloner extends HTMLFormField { ? $this->mParams['format'] : $this->mParent->getDisplayFormat(); - switch ( $displayFormat ) { - case 'table': - $getFieldHtmlMethod = 'getTableRow'; - break; - case 'vform': - // Close enough to a div. - $getFieldHtmlMethod = 'getDiv'; - break; - default: - $getFieldHtmlMethod = 'get' . ucfirst( $displayFormat ); - } + // Conveniently, PHP method names are case-insensitive. + $getFieldHtmlMethod = $displayFormat == 'table' ? 'getTableRow' : ( 'get' . $displayFormat ); $html = ''; + $hidden = ''; $hasLabel = false; $fields = $this->createFieldsForKey( $key ); @@ -283,11 +274,18 @@ class HTMLFormFieldCloner extends HTMLFormField { $v = ( empty( $field->mParams['nodata'] ) && $values !== null ) ? $values[$fieldname] : $field->getDefault(); - $html .= $field->$getFieldHtmlMethod( $v ); - $labelValue = trim( $field->getLabel() ); - if ( $labelValue != ' ' && $labelValue !== '' ) { - $hasLabel = true; + if ( $field instanceof HTMLHiddenField ) { + // HTMLHiddenField doesn't generate its own HTML + list( $name, $value, $params ) = $field->getHiddenFieldData( $v ); + $hidden .= Html::hidden( $name, $value, $params ) . "\n"; + } else { + $html .= $field->$getFieldHtmlMethod( $v ); + + $labelValue = trim( $field->getLabel() ); + if ( $labelValue != ' ' && $labelValue !== '' ) { + $hasLabel = true; + } } } @@ -302,8 +300,7 @@ class HTMLFormFieldCloner extends HTMLFormField { 'id' => Sanitizer::escapeId( "{$this->mID}--$key--delete" ), 'cssclass' => 'mw-htmlform-cloner-delete-button', 'default' => $this->msg( $label )->text(), - ) ); - $field->mParent = $this->mParent; + ), $this->mParent ); $v = $field->getDefault(); if ( $displayFormat === 'table' ) { @@ -330,11 +327,13 @@ class HTMLFormFieldCloner extends HTMLFormField { $html = Html::rawElement( 'table', $attribs, Html::rawElement( 'tbody', array(), "\n$html\n" ) ) . "\n"; - } elseif ( $displayFormat === 'div' || $displayFormat === 'vform' ) { + } else { $html = Html::rawElement( 'div', $attribs, "\n$html\n" ); } } + $html .= $hidden; + if ( !empty( $this->mParams['row-legend'] ) ) { $legend = $this->msg( $this->mParams['row-legend'] )->text(); $html = Xml::fieldset( $legend, $html ); @@ -373,8 +372,7 @@ class HTMLFormFieldCloner extends HTMLFormField { 'id' => Sanitizer::escapeId( "{$this->mID}--create" ), 'cssclass' => 'mw-htmlform-cloner-create-button', 'default' => $this->msg( $label )->text(), - ) ); - $field->mParent = $this->mParent; + ), $this->mParent ); $html .= $field->getInputHTML( $field->getDefault() ); return $html; diff --git a/includes/htmlform/HTMLHiddenField.php b/includes/htmlform/HTMLHiddenField.php index e32c0bb2..ffde9151 100644 --- a/includes/htmlform/HTMLHiddenField.php +++ b/includes/htmlform/HTMLHiddenField.php @@ -1,22 +1,36 @@ <?php class HTMLHiddenField extends HTMLFormField { + protected $outputAsDefault = true; + public function __construct( $params ) { parent::__construct( $params ); + if ( isset( $this->mParams['output-as-default'] ) ) { + $this->outputAsDefault = (bool)$this->mParams['output-as-default']; + } + # Per HTML5 spec, hidden fields cannot be 'required' # http://www.w3.org/TR/html5/forms.html#hidden-state-%28type=hidden%29 unset( $this->mParams['required'] ); } - public function getTableRow( $value ) { + public function getHiddenFieldData( $value ) { $params = array(); if ( $this->mID ) { $params['id'] = $this->mID; } - $this->mParent->addHiddenField( $this->mName, $this->mDefault, $params ); + if ( $this->outputAsDefault ) { + $value = $this->mDefault; + } + + return array( $this->mName, $value, $params ); + } + public function getTableRow( $value ) { + list( $name, $value, $params ) = $this->getHiddenFieldData( $value ); + $this->mParent->addHiddenField( $name, $value, $params ); return ''; } diff --git a/includes/htmlform/HTMLIntField.php b/includes/htmlform/HTMLIntField.php index 28876e2c..b0148d98 100644 --- a/includes/htmlform/HTMLIntField.php +++ b/includes/htmlform/HTMLIntField.php @@ -11,14 +11,13 @@ class HTMLIntField extends HTMLFloatField { return $p; } - # http://dev.w3.org/html5/spec/common-microsyntaxes.html#signed-integers + # http://www.w3.org/TR/html5/infrastructure.html#signed-integers # with the addition that a leading '+' sign is ok. Note that leading zeros # are fine, and will be left in the input, which is useful for things like # phone numbers when you know that they are integers (the HTML5 type=tel # input does not require its value to be numeric). If you want a tidier # value to, eg, save in the DB, clean it up with intval(). - if ( !preg_match( '/^((\+|\-)?\d+)?$/', trim( $value ) ) - ) { + if ( !preg_match( '/^((\+|\-)?\d+)?$/', trim( $value ) ) ) { return $this->msg( 'htmlform-int-invalid' )->parseAsBlock(); } diff --git a/includes/htmlform/HTMLMultiSelectField.php b/includes/htmlform/HTMLMultiSelectField.php index 1b71ab95..8d28b59e 100644 --- a/includes/htmlform/HTMLMultiSelectField.php +++ b/includes/htmlform/HTMLMultiSelectField.php @@ -59,6 +59,14 @@ class HTMLMultiSelectField extends HTMLFormField implements HTMLNestedFilterable $label ); + if ( $this->mParent->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) { + $checkbox = Html::rawElement( + 'div', + array( 'class' => 'mw-ui-checkbox' ), + $checkbox + ); + } + $html .= ' ' . Html::rawElement( 'div', array( 'class' => 'mw-htmlform-flatlist-item' ), diff --git a/includes/htmlform/HTMLRadioField.php b/includes/htmlform/HTMLRadioField.php index 8765407b..0f005408 100644 --- a/includes/htmlform/HTMLRadioField.php +++ b/includes/htmlform/HTMLRadioField.php @@ -56,7 +56,7 @@ class HTMLRadioField extends HTMLFormField { $html .= ' ' . Html::rawElement( 'div', - array( 'class' => 'mw-htmlform-flatlist-item' ), + array( 'class' => 'mw-htmlform-flatlist-item mw-ui-radio' ), $radio ); } diff --git a/includes/htmlform/HTMLSelectAndOtherField.php b/includes/htmlform/HTMLSelectAndOtherField.php index 65176dd7..a1c0c957 100644 --- a/includes/htmlform/HTMLSelectAndOtherField.php +++ b/includes/htmlform/HTMLSelectAndOtherField.php @@ -13,6 +13,7 @@ class HTMLSelectAndOtherField extends HTMLSelectField { function __construct( $params ) { if ( array_key_exists( 'other', $params ) ) { + // Do nothing } elseif ( array_key_exists( 'other-message', $params ) ) { $params['other'] = wfMessage( $params['other-message'] )->plain(); } else { @@ -22,7 +23,7 @@ class HTMLSelectAndOtherField extends HTMLSelectField { parent::__construct( $params ); if ( $this->getOptions() === null ) { - # Sulk + // Sulk throw new MWException( 'HTMLSelectAndOtherField called without any options' ); } if ( !in_array( 'other', $this->mOptions, true ) ) { @@ -39,10 +40,12 @@ class HTMLSelectAndOtherField extends HTMLSelectField { $textAttribs = array( 'id' => $this->mID . '-other', 'size' => $this->getSize(), + 'class' => array( 'mw-htmlform-select-and-other-field' ), + 'data-id-select' => $this->mID, ); if ( $this->mClass !== '' ) { - $textAttribs['class'] = $this->mClass; + $textAttribs['class'][] = $this->mClass; } $allowedParams = array( @@ -50,7 +53,8 @@ class HTMLSelectAndOtherField extends HTMLSelectField { 'autofocus', 'multiple', 'disabled', - 'tabindex' + 'tabindex', + 'maxlength', // gets dynamic with javascript, see mediawiki.htmlform.js ); $textAttribs += $this->getAttributes( $allowedParams ); @@ -71,6 +75,7 @@ class HTMLSelectAndOtherField extends HTMLSelectField { $list = $request->getText( $this->mName ); $text = $request->getText( $this->mName . '-other' ); + // Should be built the same as in mediawiki.htmlform.js if ( $list == 'other' ) { $final = $text; } elseif ( !in_array( $list, $this->mFlatOptions, true ) ) { diff --git a/includes/htmlform/HTMLSelectNamespace.php b/includes/htmlform/HTMLSelectNamespace.php new file mode 100644 index 00000000..96381062 --- /dev/null +++ b/includes/htmlform/HTMLSelectNamespace.php @@ -0,0 +1,18 @@ +<?php +/** + * Wrapper for Html::namespaceSelector to use in HTMLForm + */ +class HTMLSelectNamespace extends HTMLFormField { + function getInputHTML( $value ) { + return Html::namespaceSelector( + array( + 'selected' => $value, + 'all' => 'all' + ), array( + 'name' => $this->mName, + 'id' => $this->mID, + 'class' => 'namespaceselector', + ) + ); + } +} diff --git a/includes/htmlform/HTMLTagFilter.php b/includes/htmlform/HTMLTagFilter.php new file mode 100644 index 00000000..8075de5a --- /dev/null +++ b/includes/htmlform/HTMLTagFilter.php @@ -0,0 +1,31 @@ +<?php +/** + * Wrapper for ChangeTags::buildTagFilterSelector to use in HTMLForm + */ +class HTMLTagFilter extends HTMLFormField { + protected $tagFilter; + + function getTableRow( $value ) { + $this->tagFilter = ChangeTags::buildTagFilterSelector( $value ); + if ( $this->tagFilter ) { + return parent::getTableRow( $value ); + } + return ''; + } + + function getDiv( $value ) { + $this->tagFilter = ChangeTags::buildTagFilterSelector( $value ); + if ( $this->tagFilter ) { + return parent::getDiv( $value ); + } + return ''; + } + + function getInputHTML( $value ) { + if ( $this->tagFilter ) { + // we only need the select field, HTMLForm should handle the label + return $this->tagFilter[1]; + } + return ''; + } +} diff --git a/includes/htmlform/HTMLTextField.php b/includes/htmlform/HTMLTextField.php index 10bc67f0..88df49db 100644 --- a/includes/htmlform/HTMLTextField.php +++ b/includes/htmlform/HTMLTextField.php @@ -20,6 +20,7 @@ class HTMLTextField extends HTMLFormField { # @todo Enforce pattern, step, required, readonly on the server side as # well $allowedParams = array( + 'type', 'min', 'max', 'pattern', @@ -38,10 +39,13 @@ class HTMLTextField extends HTMLFormField { $attribs += $this->getAttributes( $allowedParams ); + # Extract 'type' + $type = isset( $attribs['type'] ) ? $attribs['type'] : 'text'; + unset( $attribs['type'] ); + # Implement tiny differences between some field variants # here, rather than creating a new class for each one which # is essentially just a clone of this one. - $type = 'text'; if ( isset( $this->mParams['type'] ) ) { switch ( $this->mParams['type'] ) { case 'int': @@ -60,6 +64,7 @@ class HTMLTextField extends HTMLFormField { break; } } + return Html::input( $this->mName, $value, $type, $attribs ); } } diff --git a/includes/htmlform/VFormHTMLForm.php b/includes/htmlform/VFormHTMLForm.php new file mode 100644 index 00000000..0c0e4252 --- /dev/null +++ b/includes/htmlform/VFormHTMLForm.php @@ -0,0 +1,141 @@ +<?php + +/** + * HTML form generation and submission handling, vertical-form style. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +/** + * Compact stacked vertical format for forms. + */ +class VFormHTMLForm extends HTMLForm { + /** + * Wrapper and its legend are never generated in VForm mode. + * @var boolean + */ + protected $mWrapperLegend = false; + + /** + * Symbolic display format name. + * @var string + */ + protected $displayFormat = 'vform'; + + public function isVForm() { + wfDeprecated( __METHOD__, '1.25' ); + return true; + } + + public static function loadInputFromParameters( $fieldname, $descriptor, HTMLForm $parent = null ) { + $field = parent::loadInputFromParameters( $fieldname, $descriptor, $parent ); + $field->setShowEmptyLabel( false ); + return $field; + } + + function getHTML( $submitResult ) { + // This is required for VForm HTMLForms that use that style regardless + // of wgUseMediaWikiUIEverywhere (since they pre-date it). + // When wgUseMediaWikiUIEverywhere is removed, this should be consolidated + // with the addModuleStyles in SpecialPage->setHeaders. + $this->getOutput()->addModuleStyles( array( + 'mediawiki.ui', + 'mediawiki.ui.button', + 'mediawiki.ui.input', + 'mediawiki.ui.checkbox', + ) ); + + return parent::getHTML( $submitResult ); + } + + protected function getFormAttributes() { + $attribs = parent::getFormAttributes(); + array_push( $attribs['class'], 'mw-ui-vform', 'mw-ui-container' ); + return $attribs; + } + + function wrapForm( $html ) { + // Always discard $this->mWrapperLegend + return Html::rawElement( 'form', $this->getFormAttributes(), $html ); + } + + function getButtons() { + $buttons = ''; + + if ( $this->mShowSubmit ) { + $attribs = array(); + + if ( isset( $this->mSubmitID ) ) { + $attribs['id'] = $this->mSubmitID; + } + + if ( isset( $this->mSubmitName ) ) { + $attribs['name'] = $this->mSubmitName; + } + + if ( isset( $this->mSubmitTooltip ) ) { + $attribs += Linker::tooltipAndAccesskeyAttribs( $this->mSubmitTooltip ); + } + + $attribs['class'] = array( + 'mw-htmlform-submit', + 'mw-ui-button mw-ui-big mw-ui-block', + $this->mSubmitModifierClass, + ); + + $buttons .= Xml::submitButton( $this->getSubmitText(), $attribs ) . "\n"; + } + + if ( $this->mShowReset ) { + $buttons .= Html::element( + 'input', + array( + 'type' => 'reset', + 'value' => $this->msg( 'htmlform-reset' )->text(), + 'class' => 'mw-ui-button mw-ui-big mw-ui-block', + ) + ) . "\n"; + } + + foreach ( $this->mButtons as $button ) { + $attrs = array( + 'type' => 'submit', + 'name' => $button['name'], + 'value' => $button['value'] + ); + + if ( $button['attribs'] ) { + $attrs += $button['attribs']; + } + + if ( isset( $button['id'] ) ) { + $attrs['id'] = $button['id']; + } + + $attrs['class'] = isset( $attrs['class'] ) ? (array)$attrs['class'] : array(); + $attrs['class'][] = 'mw-ui-button mw-ui-big mw-ui-block'; + + $buttons .= Html::element( 'input', $attrs ) . "\n"; + } + + $html = Html::rawElement( 'div', + array( 'class' => 'mw-htmlform-submit-buttons' ), "\n$buttons" ) . "\n"; + + return $html; + } +} |