From c1f9b1f7b1b77776192048005dcc66dcf3df2bfb Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sat, 27 Dec 2014 15:41:37 +0100 Subject: Update to MediaWiki 1.24.1 --- includes/htmlform/HTMLApiField.php | 19 + includes/htmlform/HTMLAutoCompleteSelectField.php | 165 +++ includes/htmlform/HTMLButtonField.php | 42 + includes/htmlform/HTMLCheckField.php | 91 ++ includes/htmlform/HTMLCheckMatrix.php | 250 ++++ includes/htmlform/HTMLEditTools.php | 51 + includes/htmlform/HTMLFloatField.php | 46 + includes/htmlform/HTMLForm.php | 1472 ++++++++++++++++++++ includes/htmlform/HTMLFormField.php | 893 ++++++++++++ includes/htmlform/HTMLFormFieldCloner.php | 382 +++++ .../HTMLFormFieldRequiredOptionsException.php | 9 + includes/htmlform/HTMLHiddenField.php | 44 + includes/htmlform/HTMLInfoField.php | 54 + includes/htmlform/HTMLIntField.php | 27 + includes/htmlform/HTMLMultiSelectField.php | 122 ++ includes/htmlform/HTMLNestedFilterable.php | 11 + includes/htmlform/HTMLRadioField.php | 71 + includes/htmlform/HTMLSelectAndOtherField.php | 126 ++ includes/htmlform/HTMLSelectField.php | 44 + includes/htmlform/HTMLSelectLimitField.php | 35 + includes/htmlform/HTMLSelectOrOtherField.php | 83 ++ includes/htmlform/HTMLSubmitField.php | 9 + includes/htmlform/HTMLTextAreaField.php | 38 + includes/htmlform/HTMLTextField.php | 65 + 24 files changed, 4149 insertions(+) create mode 100644 includes/htmlform/HTMLApiField.php create mode 100644 includes/htmlform/HTMLAutoCompleteSelectField.php create mode 100644 includes/htmlform/HTMLButtonField.php create mode 100644 includes/htmlform/HTMLCheckField.php create mode 100644 includes/htmlform/HTMLCheckMatrix.php create mode 100644 includes/htmlform/HTMLEditTools.php create mode 100644 includes/htmlform/HTMLFloatField.php create mode 100644 includes/htmlform/HTMLForm.php create mode 100644 includes/htmlform/HTMLFormField.php create mode 100644 includes/htmlform/HTMLFormFieldCloner.php create mode 100644 includes/htmlform/HTMLFormFieldRequiredOptionsException.php create mode 100644 includes/htmlform/HTMLHiddenField.php create mode 100644 includes/htmlform/HTMLInfoField.php create mode 100644 includes/htmlform/HTMLIntField.php create mode 100644 includes/htmlform/HTMLMultiSelectField.php create mode 100644 includes/htmlform/HTMLNestedFilterable.php create mode 100644 includes/htmlform/HTMLRadioField.php create mode 100644 includes/htmlform/HTMLSelectAndOtherField.php create mode 100644 includes/htmlform/HTMLSelectField.php create mode 100644 includes/htmlform/HTMLSelectLimitField.php create mode 100644 includes/htmlform/HTMLSelectOrOtherField.php create mode 100644 includes/htmlform/HTMLSubmitField.php create mode 100644 includes/htmlform/HTMLTextAreaField.php create mode 100644 includes/htmlform/HTMLTextField.php (limited to 'includes/htmlform') diff --git a/includes/htmlform/HTMLApiField.php b/includes/htmlform/HTMLApiField.php new file mode 100644 index 00000000..f988e622 --- /dev/null +++ b/includes/htmlform/HTMLApiField.php @@ -0,0 +1,19 @@ +getTableRow( $value ); + } + + public function getRaw( $value ) { + return $this->getTableRow( $value ); + } + + public function getInputHTML( $value ) { + return ''; + } +} diff --git a/includes/htmlform/HTMLAutoCompleteSelectField.php b/includes/htmlform/HTMLAutoCompleteSelectField.php new file mode 100644 index 00000000..49053628 --- /dev/null +++ b/includes/htmlform/HTMLAutoCompleteSelectField.php @@ -0,0 +1,165 @@ + false, + ); + + parent::__construct( $params ); + + if ( array_key_exists( 'autocomplete-messages', $this->mParams ) ) { + foreach ( $this->mParams['autocomplete-messages'] as $key => $value ) { + $key = $this->msg( $key )->plain(); + $this->autocomplete[$key] = strval( $value ); + } + } elseif ( array_key_exists( 'autocomplete', $this->mParams ) ) { + foreach ( $this->mParams['autocomplete'] as $key => $value ) { + $this->autocomplete[$key] = strval( $value ); + } + } + if ( !is_array( $this->autocomplete ) || !$this->autocomplete ) { + 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 = wfMessage( $params['other-message'] )->text(); + } elseif ( isset( $params['other'] ) ) { + $msg = $params['other']; + } else { + $msg = wfMessage( 'htmlform-selectorother-other' )->text(); + } + $this->mOptions[$msg] = 'other'; + } + } + + 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->autocomplete[$val] ) ) { + $val = $this->autocomplete[$val]; + } + } + + return $val; + } else { + return $this->getDefault(); + } + } + + 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->autocomplete, true ) ) { + return true; + } elseif ( $this->mParams['require-match'] ) { + return $this->msg( 'htmlform-select-badoption' )->parse(); + } + + return true; + } + + function getAttributes( array $list ) { + $attribs = array( + 'type' => 'text', + 'data-autocomplete' => FormatJson::encode( array_keys( $this->autocomplete ) ), + ) + parent::getAttributes( $list ); + + if ( $this->getOptions() ) { + $attribs['data-hide-if'] = FormatJson::encode( + array( '!==', $this->mName . '-select', 'other' ) + ); + } + + return $attribs; + } + + function getInputHTML( $value ) { + $oldClass = $this->mClass; + $this->mClass = (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() ); + $select->setAttribute( 'class', 'mw-htmlform-select-or-other' ); + + if ( !empty( $this->mParams['disabled'] ) ) { + $select->setAttribute( 'disabled', 'disabled' ); + } + + if ( isset( $this->mParams['tabindex'] ) ) { + $select->setAttribute( 'tabindex', $this->mParams['tabindex'] ); + } + + $ret = $select->getHTML() . "
\n"; + + $this->mClass[] = 'mw-htmlform-hide-if'; + } + + if ( $valInSelect ) { + $value = ''; + } else { + $key = array_search( strval( $value ), $this->autocomplete, true ); + if ( $key !== false ) { + $value = $key; + } + } + + $this->mClass[] = 'mw-htmlform-autocomplete'; + $ret .= parent::getInputHTML( $valInSelect ? '' : $value ); + $this->mClass = $oldClass; + + return $ret; + } + +} diff --git a/includes/htmlform/HTMLButtonField.php b/includes/htmlform/HTMLButtonField.php new file mode 100644 index 00000000..09c0ad97 --- /dev/null +++ b/includes/htmlform/HTMLButtonField.php @@ -0,0 +1,42 @@ + 'mw-htmlform-submit ' . $this->mClass, + 'id' => $this->mID, + ) + $this->getAttributes( array( 'disabled', 'tabindex' ) ); + + return Html::input( $this->mName, $value, $this->buttonType, $attr ); + } + + protected function needsLabel() { + return false; + } + + /** + * Button cannot be invalid + * + * @param string $value + * @param array $alldata + * + * @return bool + */ + public function validate( $value, $alldata ) { + return true; + } +} diff --git a/includes/htmlform/HTMLCheckField.php b/includes/htmlform/HTMLCheckField.php new file mode 100644 index 00000000..5f70362a --- /dev/null +++ b/includes/htmlform/HTMLCheckField.php @@ -0,0 +1,91 @@ +mParams['invert'] ) ) { + $value = !$value; + } + + $attr = $this->getTooltipAndAccessKey(); + $attr['id'] = $this->mID; + + $attr += $this->getAttributes( array( 'disabled', 'tabindex' ) ); + + if ( $this->mClass !== '' ) { + $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 + ); + } + + return $chkLabel; + } + } + + /** + * For a checkbox, the label goes on the right hand side, and is + * added in getInputHTML(), rather than HTMLFormField::getRow() + * @return string + */ + function getLabel() { + return ' '; + } + + /** + * checkboxes don't need a label. + * @return bool + */ + protected function needsLabel() { + return false; + } + + /** + * @param WebRequest $request + * + * @return string + */ + function loadDataFromRequest( $request ) { + $invert = false; + if ( isset( $this->mParams['invert'] ) && $this->mParams['invert'] ) { + $invert = true; + } + + // 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; + } else { + return $this->getDefault(); + } + } +} diff --git a/includes/htmlform/HTMLCheckMatrix.php b/includes/htmlform/HTMLCheckMatrix.php new file mode 100644 index 00000000..6c538fdd --- /dev/null +++ b/includes/htmlform/HTMLCheckMatrix.php @@ -0,0 +1,250 @@ +mParams['rows']; + $columns = $this->mParams['columns']; + + // Make sure user-defined validation callback is run + $p = parent::validate( $value, $alldata ); + if ( $p !== true ) { + return $p; + } + + // Make sure submitted value is an array + if ( !is_array( $value ) ) { + return false; + } + + // If all options are valid, array_intersect of the valid options + // and the provided options will return the provided options. + $validOptions = array(); + foreach ( $rows as $rowTag ) { + foreach ( $columns as $columnTag ) { + $validOptions[] = $columnTag . '-' . $rowTag; + } + } + $validValues = array_intersect( $value, $validOptions ); + if ( count( $validValues ) == count( $value ) ) { + return true; + } else { + return $this->msg( 'htmlform-select-badoption' )->parse(); + } + } + + /** + * Build a table containing a matrix of checkbox options. + * The value of each option is a combination of the row tag and column tag. + * mParams['rows'] is an array with row labels as keys and row tags as values. + * mParams['columns'] is an array with column labels as keys and column tags as values. + * + * @param array $value Array of the options that should be checked + * + * @return string + */ + function getInputHTML( $value ) { + $html = ''; + $tableContents = ''; + $rows = $this->mParams['rows']; + $columns = $this->mParams['columns']; + + $attribs = $this->getAttributes( array( 'disabled', 'tabindex' ) ); + + // Build the column headers + $headerContents = Html::rawElement( 'td', array(), ' ' ); + foreach ( $columns as $columnLabel => $columnTag ) { + $headerContents .= Html::rawElement( 'td', array(), $columnLabel ); + } + $tableContents .= Html::rawElement( 'tr', array(), "\n$headerContents\n" ); + + $tooltipClass = 'mw-icon-question'; + if ( isset( $this->mParams['tooltip-class'] ) ) { + $tooltipClass = $this->mParams['tooltip-class']; + } + + // Build the options matrix + foreach ( $rows as $rowLabel => $rowTag ) { + // Append tooltip if configured + if ( isset( $this->mParams['tooltips'][$rowLabel] ) ) { + $tooltipAttribs = array( + 'class' => "mw-htmlform-tooltip $tooltipClass", + 'title' => $this->mParams['tooltips'][$rowLabel], + ); + $rowLabel .= ' ' . Html::element( 'span', $tooltipAttribs, '' ); + } + $rowContents = Html::rawElement( 'td', array(), $rowLabel ); + foreach ( $columns as $columnTag ) { + $thisTag = "$columnTag-$rowTag"; + // Construct the checkbox + $thisId = "{$this->mID}-$thisTag"; + $thisAttribs = array( + 'id' => $thisId, + 'value' => $thisTag, + ); + $checked = in_array( $thisTag, (array)$value, true ); + if ( $this->isTagForcedOff( $thisTag ) ) { + $checked = false; + $thisAttribs['disabled'] = 1; + } elseif ( $this->isTagForcedOn( $thisTag ) ) { + $checked = true; + $thisAttribs['disabled'] = 1; + } + $chkBox = Xml::check( "{$this->mName}[]", $checked, $attribs + $thisAttribs ); + if ( $this->mParent->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) { + $chkBox = Html::openElement( 'div', array( 'class' => 'mw-ui-checkbox' ) ) . + $chkBox . + Html::element( 'label', array( 'for' => $thisId ) ) . + Html::closeElement( 'div' ); + } + $rowContents .= Html::rawElement( + 'td', + array(), + $chkBox + ); + } + $tableContents .= Html::rawElement( 'tr', array(), "\n$rowContents\n" ); + } + + // Put it all in a table + $html .= Html::rawElement( 'table', + array( 'class' => 'mw-htmlform-matrix' ), + Html::rawElement( 'tbody', array(), "\n$tableContents\n" ) ) . "\n"; + + return $html; + } + + protected function isTagForcedOff( $tag ) { + return isset( $this->mParams['force-options-off'] ) + && in_array( $tag, $this->mParams['force-options-off'] ); + } + + protected function isTagForcedOn( $tag ) { + return isset( $this->mParams['force-options-on'] ) + && in_array( $tag, $this->mParams['force-options-on'] ); + } + + /** + * Get the complete table row for the input, including help text, + * labels, and whatever. + * We override this function since the label should always be on a separate + * line above the options in the case of a checkbox matrix, i.e. it's always + * a "vertical-label". + * + * @param string $value The value to set the input to + * + * @return string Complete HTML table row + */ + function getTableRow( $value ) { + list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value ); + $inputHtml = $this->getInputHTML( $value ); + $fieldType = get_class( $this ); + $helptext = $this->getHelpTextHtmlTable( $this->getHelpText() ); + $cellAttributes = array( 'colspan' => 2 ); + + $label = $this->getLabelHtml( $cellAttributes ); + + $field = Html::rawElement( + 'td', + array( 'class' => 'mw-input' ) + $cellAttributes, + $inputHtml . "\n$errors" + ); + + $html = Html::rawElement( 'tr', array( 'class' => 'mw-htmlform-vertical-label' ), $label ); + $html .= Html::rawElement( 'tr', + array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass" ), + $field ); + + return $html . $helptext; + } + + /** + * @param WebRequest $request + * + * @return array + */ + function loadDataFromRequest( $request ) { + if ( $this->mParent->getMethod() == 'post' ) { + if ( $request->wasPosted() ) { + // Checkboxes are not added to the request arrays if they're not checked, + // so it's perfectly possible for there not to be an entry at all + return $request->getArray( $this->mName, array() ); + } else { + // That's ok, the user has not yet submitted the form, so show the defaults + return $this->getDefault(); + } + } else { + // This is the impossible case: if we look at $_GET and see no data for our + // field, is it because the user has not yet submitted the form, or that they + // have submitted it with all the options unchecked. We will have to assume the + // latter, which basically means that you can't specify 'positive' defaults + // for GET forms. + return $request->getArray( $this->mName, array() ); + } + } + + function getDefault() { + if ( isset( $this->mDefault ) ) { + return $this->mDefault; + } else { + return array(); + } + } + + function filterDataForSubmit( $data ) { + $columns = HTMLFormField::flattenOptions( $this->mParams['columns'] ); + $rows = HTMLFormField::flattenOptions( $this->mParams['rows'] ); + $res = array(); + foreach ( $columns as $column ) { + foreach ( $rows as $row ) { + // Make sure option hasn't been forced + $thisTag = "$column-$row"; + if ( $this->isTagForcedOff( $thisTag ) ) { + $res[$thisTag] = false; + } elseif ( $this->isTagForcedOn( $thisTag ) ) { + $res[$thisTag] = true; + } else { + $res[$thisTag] = in_array( $thisTag, $data ); + } + } + } + + return $res; + } +} diff --git a/includes/htmlform/HTMLEditTools.php b/includes/htmlform/HTMLEditTools.php new file mode 100644 index 00000000..77924ef2 --- /dev/null +++ b/includes/htmlform/HTMLEditTools.php @@ -0,0 +1,51 @@ +formatMsg(); + + return + '' . + '
' . + $msg->parseAsBlock() . + "
\n"; + } + + /** + * @param string $value + * @return string + * @since 1.20 + */ + public function getDiv( $value ) { + $msg = $this->formatMsg(); + + return '
' . $msg->parseAsBlock() . '
'; + } + + /** + * @param string $value + * @return string + * @since 1.20 + */ + public function getRaw( $value ) { + return $this->getDiv( $value ); + } + + protected function formatMsg() { + if ( empty( $this->mParams['message'] ) ) { + $msg = $this->msg( 'edittools' ); + } else { + $msg = $this->msg( $this->mParams['message'] ); + if ( $msg->isDisabled() ) { + $msg = $this->msg( 'edittools' ); + } + } + $msg->inContentLanguage(); + + return $msg; + } +} diff --git a/includes/htmlform/HTMLFloatField.php b/includes/htmlform/HTMLFloatField.php new file mode 100644 index 00000000..3b38fbe8 --- /dev/null +++ b/includes/htmlform/HTMLFloatField.php @@ -0,0 +1,46 @@ +mParams['size'] ) ? $this->mParams['size'] : 20; + } + + function validate( $value, $alldata ) { + $p = parent::validate( $value, $alldata ); + + if ( $p !== true ) { + return $p; + } + + $value = trim( $value ); + + # http://dev.w3.org/html5/spec/common-microsyntaxes.html#real-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(); + } + + # The "int" part of these message names is rather confusing. + # They make equal sense for all numbers. + if ( isset( $this->mParams['min'] ) ) { + $min = $this->mParams['min']; + + if ( $min > $value ) { + return $this->msg( 'htmlform-int-toolow', $min )->parseAsBlock(); + } + } + + if ( isset( $this->mParams['max'] ) ) { + $max = $this->mParams['max']; + + if ( $max < $value ) { + return $this->msg( 'htmlform-int-toohigh', $max )->parseAsBlock(); + } + } + + return true; + } +} diff --git a/includes/htmlform/HTMLForm.php b/includes/htmlform/HTMLForm.php new file mode 100644 index 00000000..d582da3b --- /dev/null +++ b/includes/htmlform/HTMLForm.php @@ -0,0 +1,1472 @@ + $info, + * where $info is an Associative Array with any of the following: + * + * 'class' -- the subclass of HTMLFormField that will be used + * to create the object. *NOT* the CSS class! + * 'type' -- roughly translates into the : ", or "" if nothing has been selected in the + * select dropdown. + * @todo 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'] = wfMessage( $params['other-message'] )->plain(); + } else { + $params['other'] = wfMessage( 'htmlform-selectorother-other' )->plain(); + } + + parent::__construct( $params ); + + if ( $this->getOptions() === null ) { + # Sulk + throw new MWException( 'HTMLSelectAndOtherField called without any options' ); + } + if ( !in_array( 'other', $this->mOptions, true ) ) { + // Have 'other' always as first element + $this->mOptions = array( $params['other'] => 'other' ) + $this->mOptions; + } + $this->mFlatOptions = self::flattenOptions( $this->getOptions() ); + + } + + function getInputHTML( $value ) { + $select = parent::getInputHTML( $value[1] ); + + $textAttribs = array( + 'id' => $this->mID . '-other', + 'size' => $this->getSize(), + ); + + if ( $this->mClass !== '' ) { + $textAttribs['class'] = $this->mClass; + } + + $allowedParams = array( + 'required', + 'autofocus', + 'multiple', + 'disabled', + 'tabindex' + ); + + $textAttribs += $this->getAttributes( $allowedParams ); + + $textbox = Html::input( $this->mName . '-other', $value[2], 'text', $textAttribs ); + + return "$select
\n$textbox"; + } + + /** + * @param WebRequest $request + * + * @return array("","