'namespaceselect' => 'HTMLSelectNamespace',
'namespaceselectwithbutton' => 'HTMLSelectNamespaceWithButton',
'tagfilter' => 'HTMLTagFilter',
+ 'sizefilter' => 'HTMLSizeFilterField',
'submit' => 'HTMLSubmitField',
'hidden' => 'HTMLHiddenField',
'edittools' => 'HTMLEditTools',
'checkmatrix' => 'HTMLCheckMatrix',
'cloner' => 'HTMLFormFieldCloner',
'autocompleteselect' => 'HTMLAutoCompleteSelectField',
+ 'date' => 'HTMLDateTimeField',
+ 'time' => 'HTMLDateTimeField',
+ 'datetime' => 'HTMLDateTimeField',
// HTMLTextField will output the correct type="" attribute automagically.
// There are about four zillion other HTML5 input types, like range, but
// we don't use those at the moment, so no point in adding all of them.
protected $mFieldTree;
protected $mShowReset = false;
protected $mShowSubmit = true;
- protected $mSubmitFlags = [ 'constructive', 'primary' ];
+ protected $mSubmitFlags = [ 'primary', 'progressive' ];
protected $mShowCancel = false;
protected $mCancelTarget;
$this->mFieldTree = $loadedDescriptor;
}
+ /**
+ * @param string $fieldname
+ * @return bool
+ */
+ public function hasField( $fieldname ) {
+ return isset( $this->mFlatFields[$fieldname] );
+ }
+
+ /**
+ * @param string $fieldname
+ * @return HTMLFormField
+ * @throws DomainException on invalid field name
+ */
+ public function getField( $fieldname ) {
+ if ( !$this->hasField( $fieldname ) ) {
+ throw new DomainException( __METHOD__ . ': no field named ' . $fieldname );
+ }
+ return $this->mFlatFields[$fieldname];
+ }
+
/**
* Set format in which to display the form
*
*/
public function trySubmit() {
$valid = true;
- $hoistedErrors = [];
- $hoistedErrors[] = isset( $this->mValidationErrorMessage )
- ? $this->mValidationErrorMessage
- : [ 'htmlform-invalid-input' ];
+ $hoistedErrors = Status::newGood();
+ if ( $this->mValidationErrorMessage ) {
+ foreach ( (array)$this->mValidationErrorMessage as $error ) {
+ call_user_func_array( [ $hoistedErrors, 'fatal' ], $error );
+ }
+ } else {
+ $hoistedErrors->fatal( 'htmlform-invalid-input' );
+ }
$this->mWasSubmitted = true;
if ( $res !== true ) {
$valid = false;
if ( $res !== false && !$field->canDisplayErrors() ) {
- $hoistedErrors[] = [ 'rawmessage', $res ];
+ if ( is_string( $res ) ) {
+ $hoistedErrors->fatal( 'rawmessage', $res );
+ } else {
+ $hoistedErrors->fatal( $res );
+ }
}
}
}
if ( !$valid ) {
- if ( count( $hoistedErrors ) === 1 ) {
- $hoistedErrors = $hoistedErrors[0];
- }
return $hoistedErrors;
}
$this->getOutput()->addModuleStyles( 'mediawiki.htmlform.styles' );
$html = ''
- . $this->getErrors( $submitResult )
+ . $this->getErrorsOrWarnings( $submitResult, 'error' )
+ . $this->getErrorsOrWarnings( $submitResult, 'warning' )
. $this->getHeaderText()
. $this->getBody()
. $this->getHiddenFields()
: 'application/x-www-form-urlencoded';
# Attributes
$attribs = [
+ 'class' => 'mw-htmlform',
'action' => $this->getAction(),
'method' => $this->getMethod(),
'enctype' => $encType,
if ( $this->mName ) {
$attribs['name'] = $this->mName;
}
+ if ( $this->needsJSForHtml5FormValidation() ) {
+ $attribs['novalidate'] = true;
+ }
return $attribs;
}
return Html::rawElement(
'form',
- $this->getFormAttributes() + [ 'class' => 'visualClear' ],
+ $this->getFormAttributes(),
$html
);
}
*
* @param string|array|Status $errors
*
+ * @deprecated since 1.28, use getErrorsOrWarnings() instead
+ *
* @return string
*/
public function getErrors( $errors ) {
- if ( $errors instanceof Status ) {
- if ( $errors->isOK() ) {
- $errorstr = '';
+ wfDeprecated( __METHOD__ );
+ return $this->getErrorsOrWarnings( $errors, 'error' );
+ }
+
+ /**
+ * Returns a formatted list of errors or warnings from the given elements.
+ *
+ * @param string|array|Status $elements The set of errors/warnings to process.
+ * @param string $elementsType Should warnings or errors be returned. This is meant
+ * for Status objects, all other valid types are always considered as errors.
+ * @return string
+ */
+ public function getErrorsOrWarnings( $elements, $elementsType ) {
+ if ( !in_array( $elementsType, [ 'error', 'warning' ], true ) ) {
+ throw new DomainException( $elementsType . ' is not a valid type.' );
+ }
+ $elementstr = false;
+ if ( $elements instanceof Status ) {
+ list( $errorStatus, $warningStatus ) = $elements->splitByErrorType();
+ $status = $elementsType === 'error' ? $errorStatus : $warningStatus;
+ if ( $status->isGood() ) {
+ $elementstr = '';
} else {
- $errorstr = $this->getOutput()->parse( $errors->getWikiText() );
+ $elementstr = $this->getOutput()->parse(
+ $status->getWikiText()
+ );
}
- } elseif ( is_array( $errors ) ) {
- $errorstr = $this->formatErrors( $errors );
- } else {
- $errorstr = $errors;
+ } elseif ( is_array( $elements ) && $elementsType === 'error' ) {
+ $elementstr = $this->formatErrors( $elements );
+ } elseif ( $elementsType === 'error' ) {
+ $elementstr = $elements;
}
- return $errorstr
- ? Html::rawElement( 'div', [ 'class' => 'error' ], $errorstr )
+ return $elementstr
+ ? Html::rawElement( 'div', [ 'class' => $elementsType ], $elementstr )
: '';
}
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;
+ }
}