HTMLAutoCompleteSelectField.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. <?php
  2. /**
  3. * Text field for selecting a value from a large list of possible values, with
  4. * auto-completion and optionally with a select dropdown for selecting common
  5. * options.
  6. *
  7. * HTMLComboboxField implements most of the same functionality and should be
  8. * used instead, if possible.
  9. *
  10. * If one of 'options-messages', 'options', or 'options-message' is provided
  11. * and non-empty, the select dropdown will be shown. An 'other' key will be
  12. * appended using message 'htmlform-selectorother-other' if not already
  13. * present.
  14. *
  15. * Besides the parameters recognized by HTMLTextField, the following are
  16. * recognized:
  17. * options-messages - As for HTMLSelectField
  18. * options - As for HTMLSelectField
  19. * options-message - As for HTMLSelectField
  20. * autocomplete-data - Associative array mapping display text to values.
  21. * autocomplete-data-messages - Like autocomplete, but keys are message names.
  22. * require-match - Boolean, if true the value must be in the options or the
  23. * autocomplete.
  24. * other-message - Message to use instead of htmlform-selectorother-other for
  25. * the 'other' message.
  26. * other - Raw text to use for the 'other' message
  27. *
  28. * The old name of autocomplete-data[-messages] was autocomplete[-messages] which is still
  29. * recognized but deprecated since MediaWiki 1.29 since it conflicts with how autocomplete is
  30. * used in HTMLTextField.
  31. */
  32. class HTMLAutoCompleteSelectField extends HTMLTextField {
  33. protected $autocompleteData = [];
  34. public function __construct( $params ) {
  35. $params += [
  36. 'require-match' => false,
  37. ];
  38. // FIXME B/C, remove in 1.30
  39. if (
  40. array_key_exists( 'autocomplete', $params )
  41. && !array_key_exists( 'autocomplete-data', $params )
  42. ) {
  43. $params['autocomplete-data'] = $params['autocomplete'];
  44. unset( $params['autocomplete'] );
  45. }
  46. if (
  47. array_key_exists( 'autocomplete-messages', $params )
  48. && !array_key_exists( 'autocomplete-data-messages', $params )
  49. ) {
  50. $params['autocomplete-data-messages'] = $params['autocomplete-messages'];
  51. unset( $params['autocomplete-messages'] );
  52. }
  53. parent::__construct( $params );
  54. if ( array_key_exists( 'autocomplete-data-messages', $this->mParams ) ) {
  55. foreach ( $this->mParams['autocomplete-data-messages'] as $key => $value ) {
  56. $key = $this->msg( $key )->plain();
  57. $this->autocompleteData[$key] = strval( $value );
  58. }
  59. } elseif ( array_key_exists( 'autocomplete-data', $this->mParams ) ) {
  60. foreach ( $this->mParams['autocomplete-data'] as $key => $value ) {
  61. $this->autocompleteData[$key] = strval( $value );
  62. }
  63. }
  64. if ( !is_array( $this->autocompleteData ) || !$this->autocompleteData ) {
  65. throw new MWException( 'HTMLAutoCompleteSelectField called without any autocompletions' );
  66. }
  67. $this->getOptions();
  68. if ( $this->mOptions && !in_array( 'other', $this->mOptions, true ) ) {
  69. if ( isset( $params['other-message'] ) ) {
  70. $msg = $this->getMessage( $params['other-message'] )->text();
  71. } elseif ( isset( $params['other'] ) ) {
  72. $msg = $params['other'];
  73. } else {
  74. $msg = wfMessage( 'htmlform-selectorother-other' )->text();
  75. }
  76. $this->mOptions[$msg] = 'other';
  77. }
  78. }
  79. public function loadDataFromRequest( $request ) {
  80. if ( $request->getCheck( $this->mName ) ) {
  81. $val = $request->getText( $this->mName . '-select', 'other' );
  82. if ( $val === 'other' ) {
  83. $val = $request->getText( $this->mName );
  84. if ( isset( $this->autocompleteData[$val] ) ) {
  85. $val = $this->autocompleteData[$val];
  86. }
  87. }
  88. return $val;
  89. } else {
  90. return $this->getDefault();
  91. }
  92. }
  93. public function validate( $value, $alldata ) {
  94. $p = parent::validate( $value, $alldata );
  95. if ( $p !== true ) {
  96. return $p;
  97. }
  98. $validOptions = HTMLFormField::flattenOptions( $this->getOptions() ?: [] );
  99. if ( in_array( strval( $value ), $validOptions, true ) ) {
  100. return true;
  101. } elseif ( in_array( strval( $value ), $this->autocompleteData, true ) ) {
  102. return true;
  103. } elseif ( $this->mParams['require-match'] ) {
  104. return $this->msg( 'htmlform-select-badoption' );
  105. }
  106. return true;
  107. }
  108. // FIXME Ewww, this shouldn't be adding any attributes not requested in $list :(
  109. public function getAttributes( array $list ) {
  110. $attribs = [
  111. 'type' => 'text',
  112. 'data-autocomplete' => FormatJson::encode( array_keys( $this->autocompleteData ) ),
  113. ] + parent::getAttributes( $list );
  114. if ( $this->getOptions() ) {
  115. $attribs['data-hide-if'] = FormatJson::encode(
  116. [ '!==', $this->mName . '-select', 'other' ]
  117. );
  118. }
  119. return $attribs;
  120. }
  121. public function getInputHTML( $value ) {
  122. $oldClass = $this->mClass;
  123. $this->mClass = (array)$this->mClass;
  124. $valInSelect = false;
  125. $ret = '';
  126. if ( $this->getOptions() ) {
  127. if ( $value !== false ) {
  128. $value = strval( $value );
  129. $valInSelect = in_array(
  130. $value, HTMLFormField::flattenOptions( $this->getOptions() ), true
  131. );
  132. }
  133. $selected = $valInSelect ? $value : 'other';
  134. $select = new XmlSelect( $this->mName . '-select', $this->mID . '-select', $selected );
  135. $select->addOptions( $this->getOptions() );
  136. $select->setAttribute( 'class', 'mw-htmlform-select-or-other' );
  137. if ( !empty( $this->mParams['disabled'] ) ) {
  138. $select->setAttribute( 'disabled', 'disabled' );
  139. }
  140. if ( isset( $this->mParams['tabindex'] ) ) {
  141. $select->setAttribute( 'tabindex', $this->mParams['tabindex'] );
  142. }
  143. $ret = $select->getHTML() . "<br />\n";
  144. $this->mClass[] = 'mw-htmlform-hide-if';
  145. }
  146. if ( $valInSelect ) {
  147. $value = '';
  148. } else {
  149. $key = array_search( strval( $value ), $this->autocompleteData, true );
  150. if ( $key !== false ) {
  151. $value = $key;
  152. }
  153. }
  154. $this->mClass[] = 'mw-htmlform-autocomplete';
  155. $ret .= parent::getInputHTML( $valInSelect ? '' : $value );
  156. $this->mClass = $oldClass;
  157. return $ret;
  158. }
  159. /**
  160. * Get the OOUI version of this input.
  161. * @param string $value
  162. * @return false
  163. */
  164. public function getInputOOUI( $value ) {
  165. // To be implemented, for now override the function from HTMLTextField
  166. return false;
  167. }
  168. }