wiki.techinc.nl/includes/htmlform/fields/HTMLAutoCompleteSelectField.php
Daimona Eaytoy bc0db4dcc3 Apply ID and class more consistently in HTMLSelect(Or|And)OtherField
There were several inconsistencies in how ID vs cssclass vs own classes
were applied, both within the same widget + mode, between different
modes for the same widget, and different widgets. So uniform all of them
according to the plan detailed on phabricator. Include
HTMLSelectAndOtherField as well (that wasn't mentioned on phab) for
consistency. Also remove the select-or-other class from
HTMLAutoCompleteSelectField (which was only applied in HTML mode), since
the widget is not technically a SelectOrOther.

Adjust the logic in selectorother.js so that it works fine in HTML mode,
and disable it for OOUI mode since SelectWithInputWidget does that
already (and the field is autoinfused).

Also, in HTMLAutoCompleteSelectField, don't assign an array to
$this->mClass. This happens to be working right now, but it violates the
type definition of the property.

Luckily, according to codesearch this change seems to be
backwards-compatible because these fields are not commonly used, and
nobody seems to be passing cssclass to them.

Bug: T309883
Change-Id: If950f131d1c4bc1645de6021781045dccfc61966
2022-06-06 18:59:29 +02:00

184 lines
5.3 KiB
PHP

<?php
/**
* Text field for selecting a value from a large list of possible values, with
* auto-completion and optionally with a select dropdown for selecting common
* options.
*
* HTMLComboboxField implements most of the same functionality and should be
* used instead, if possible.
*
* If one of 'options-messages', 'options', or 'options-message' is provided
* and non-empty, the select dropdown will be shown. An 'other' key will be
* appended using message 'htmlform-selectorother-other' if not already
* present.
*
* Besides the parameters recognized by HTMLTextField, the following are
* recognized:
* options-messages - As for HTMLSelectField
* options - As for HTMLSelectField
* options-message - As for HTMLSelectField
* autocomplete-data - Associative array mapping display text to values.
* autocomplete-data-messages - Like autocomplete, but keys are message names.
* require-match - Boolean, if true the value must be in the options or the
* autocomplete.
* other-message - Message to use instead of htmlform-selectorother-other for
* the 'other' message.
* other - Raw text to use for the 'other' message
*
* @stable to extend
*/
class HTMLAutoCompleteSelectField extends HTMLTextField {
protected $autocompleteData = [];
/**
* @stable to call
* @inheritDoc
*/
public function __construct( $params ) {
$params += [
'require-match' => false,
];
parent::__construct( $params );
if ( array_key_exists( 'autocomplete-data-messages', $this->mParams ) ) {
foreach ( $this->mParams['autocomplete-data-messages'] as $key => $value ) {
// @phan-suppress-next-line PhanTypeMismatchArgument False positive, $key is documented as string
$key = $this->msg( $key )->plain();
$this->autocompleteData[$key] = strval( $value );
}
} elseif ( array_key_exists( 'autocomplete-data', $this->mParams ) ) {
foreach ( $this->mParams['autocomplete-data'] as $key => $value ) {
$this->autocompleteData[$key] = strval( $value );
}
}
if ( !is_array( $this->autocompleteData ) || !$this->autocompleteData ) {
throw new MWException( 'HTMLAutoCompleteSelectField called without any autocompletions' );
}
$this->getOptions();
if ( $this->mOptions && !in_array( 'other', $this->mOptions, true ) ) {
if ( isset( $params['other-message'] ) ) {
$msg = $this->getMessage( $params['other-message'] )->text();
} elseif ( isset( $params['other'] ) ) {
$msg = $params['other'];
} else {
$msg = wfMessage( 'htmlform-selectorother-other' )->text();
}
$this->mOptions[$msg] = 'other';
}
}
public function loadDataFromRequest( $request ) {
if ( $request->getCheck( $this->mName ) ) {
$val = $request->getText( $this->mName . '-select', 'other' );
if ( $val === 'other' ) {
$val = $request->getText( $this->mName );
if ( isset( $this->autocompleteData[$val] ) ) {
$val = $this->autocompleteData[$val];
}
}
return $val;
} else {
return $this->getDefault();
}
}
public function validate( $value, $alldata ) {
$p = parent::validate( $value, $alldata );
if ( $p !== true ) {
return $p;
}
$validOptions = HTMLFormField::flattenOptions( $this->getOptions() ?: [] );
if ( in_array( strval( $value ), $validOptions, true ) ) {
return true;
} elseif ( in_array( strval( $value ), $this->autocompleteData, true ) ) {
return true;
} elseif ( $this->mParams['require-match'] ) {
return $this->msg( 'htmlform-select-badoption' );
}
return true;
}
// FIXME Ewww, this shouldn't be adding any attributes not requested in $list :(
public function getAttributes( array $list ) {
$attribs = [
'type' => 'text',
'data-autocomplete' => FormatJson::encode( array_keys( $this->autocompleteData ) ),
] + parent::getAttributes( $list );
if ( $this->getOptions() ) {
$attribs['data-cond-state'] = FormatJson::encode( [
'hide' => [ '!==', $this->mName . '-select', 'other' ],
] );
}
return $attribs;
}
public function getInputHTML( $value ) {
$oldClass = $this->mClass;
$classes = (array)$this->mClass;
$valInSelect = false;
$ret = '';
if ( $this->getOptions() ) {
if ( $value !== false ) {
$value = strval( $value );
$valInSelect = in_array(
$value, HTMLFormField::flattenOptions( $this->getOptions() ), true
);
}
$selected = $valInSelect ? $value : 'other';
$select = new XmlSelect( $this->mName . '-select', $this->mID . '-select', $selected );
$select->addOptions( $this->getOptions() );
if ( !empty( $this->mParams['disabled'] ) ) {
$select->setAttribute( 'disabled', 'disabled' );
}
if ( isset( $this->mParams['tabindex'] ) ) {
$select->setAttribute( 'tabindex', $this->mParams['tabindex'] );
}
$ret = $select->getHTML() . "<br />\n";
$classes[] = 'mw-htmlform-hide-if';
}
if ( $valInSelect ) {
$value = '';
} else {
$key = array_search( strval( $value ), $this->autocompleteData, true );
if ( $key !== false ) {
$value = $key;
}
}
$classes[] = 'mw-htmlform-autocomplete';
$this->mClass = implode( ' ', $classes );
$ret .= parent::getInputHTML( $valInSelect ? '' : $value );
$this->mClass = $oldClass;
return $ret;
}
/**
* Get the OOUI version of this input.
* @param string $value
* @return false
*/
public function getInputOOUI( $value ) {
// To be implemented, for now override the function from HTMLTextField
return false;
}
}