mediawiki-extensions-InputBox/InputBox.classes.php
Florianschmidtwelzow 99277a4d0f Hygiene: Use MediaWiki-UI for all elements
For checkboxes and text input fields, not only for buttons.

Fix missing space between input and submit button in search2, too.

Follow up: Ia608324703987371aa66beccbac962024fc6e897

Bug: T63526
Change-Id: Id9fb22885b836c0dfe6a086e2ec45e2018244210
2015-02-23 09:47:05 +01:00

722 lines
18 KiB
PHP

<?php
/**
* Classes for InputBox extension
*
* @file
* @ingroup Extensions
*/
// InputBox class
class InputBox {
/* Fields */
private $mParser;
private $mType = '';
private $mWidth = 50;
private $mPreload = '';
private $mPreloadparams = array();
private $mEditIntro = '';
private $mSummary = '';
private $mNosummary = '';
private $mMinor = '';
private $mPage = '';
private $mBR = 'yes';
private $mDefaultText = '';
private $mPlaceholderText = '';
private $mBGColor = 'transparent';
private $mButtonLabel = '';
private $mSearchButtonLabel = '';
private $mFullTextButton = '';
private $mLabelText = '';
private $mHidden = '';
private $mNamespaces = '';
private $mID = '';
private $mInline = false;
private $mPrefix = '';
private $mDir = '';
/* Functions */
public function __construct( $parser ) {
$this->mParser = $parser;
// Default value for dir taken from the page language (bug 37018)
$this->mDir = $this->mParser->getTargetLanguage()->getDir();
// Split caches by language, to make sure visitors do not see a cached
// version in a random language (since labels are in the user language)
$this->mParser->getOptions()->getUserLangObj();
$this->mParser->getOutput()->addModuleStyles( array(
'ext.inputBox.styles',
'mediawiki.ui.input',
'mediawiki.ui.checkbox',
) );
}
public function render() {
// Handle various types
switch ( $this->mType ) {
case 'create':
case 'comment':
$this->mParser->getOutput()->addModules( 'ext.inputBox' );
return $this->getCreateForm();
case 'move':
return $this->getMoveForm();
case 'commenttitle':
return $this->getCommentForm();
case 'search':
return $this->getSearchForm('search');
case 'fulltext':
return $this->getSearchForm('fulltext');
case 'search2':
return $this->getSearchForm2();
default:
return Xml::tags( 'div', null,
Xml::element( 'strong',
array(
'class' => 'error'
),
strlen( $this->mType ) > 0
? wfMessage( 'inputbox-error-bad-type', $this->mType )->text()
: wfMessage( 'inputbox-error-no-type' )->text()
)
);
}
}
/**
* Generate search form
* @param $type
* @return string HTML
*/
public function getSearchForm( $type ) {
global $wgContLang, $wgNamespaceAliases;
// Use button label fallbacks
if ( !$this->mButtonLabel ) {
$this->mButtonLabel = wfMessage( 'inputbox-tryexact' )->text();
}
if ( !$this->mSearchButtonLabel ) {
$this->mSearchButtonLabel = wfMessage( 'inputbox-searchfulltext' )->text();
}
if ( $this->mID !== '' ) {
$idArray = array( 'id' => Sanitizer::escapeId( $this->mID ) );
} else {
$idArray = array();
}
// We need a unqiue id to link <label> to checkboxes, but also
// want multiple <inputbox>'s to not be invalid html
$idRandStr = Sanitizer::escapeId( '-' . $this->mID . wfRandom(), 'noninitial' );
// Build HTML
$htmlOut = Xml::openElement( 'div',
array(
'class' => 'mw-inputbox-centered',
'style' => $this->bgColorStyle(),
)
);
$htmlOut .= Xml::openElement( 'form',
array(
'name' => 'searchbox',
'class' => 'searchbox',
'action' => SpecialPage::getTitleFor( 'Search' )->getLocalUrl(),
) + $idArray
);
$htmlOut .= Xml::element( 'input',
array(
'class' => 'searchboxInput mw-ui-input mw-ui-input-inline',
'name' => 'search',
'type' => $this->mHidden ? 'hidden' : 'text',
'value' => $this->mDefaultText,
'placeholder' => $this->mPlaceholderText,
'size' => $this->mWidth,
'dir' => $this->mDir,
)
);
if ( $this->mPrefix != '' ) {
$htmlOut .= Xml::element( 'input',
array(
'name' => 'prefix',
'type' => 'hidden',
'value' => $this->mPrefix,
)
);
}
$htmlOut .= $this->mBR;
// Determine namespace checkboxes
$namespacesArray = explode( ',', $this->mNamespaces );
if ( $this->mNamespaces ) {
$namespaces = $wgContLang->getNamespaces();
$nsAliases = array_merge( $wgContLang->getNamespaceAliases(), $wgNamespaceAliases );
$showNamespaces = array();
$checkedNS = array();
# Check for valid namespaces
foreach ( $namespacesArray as $userNS ) {
$userNS = trim( $userNS ); # no whitespace
# Namespace needs to be checked if flagged with "**"
if ( strpos( $userNS, '**' ) ) {
$userNS = str_replace( '**', '', $userNS );
$checkedNS[$userNS] = true;
}
$mainMsg = wfMessage( 'inputbox-ns-main' )->inContentLanguage()->text();
if ( $userNS == 'Main' || $userNS == $mainMsg ) {
$i = 0;
} elseif ( array_search( $userNS, $namespaces ) ) {
$i = array_search( $userNS, $namespaces );
} elseif ( isset( $nsAliases[$userNS] ) ) {
$i = $nsAliases[$userNS];
} else {
continue; # Namespace not recognized, skip
}
$showNamespaces[$i] = $userNS;
if ( isset( $checkedNS[$userNS] ) && $checkedNS[$userNS] ) {
$checkedNS[$i] = true;
}
}
# Show valid namespaces
foreach ( $showNamespaces as $i => $name ) {
$checked = array();
// Namespace flagged with "**" or if it's the only one
if ( ( isset( $checkedNS[$i] ) && $checkedNS[$i] ) || count( $showNamespaces ) == 1 ) {
$checked = array( 'checked' => 'checked' );
}
if ( count( $showNamespaces ) == 1 ) {
// Checkbox
$htmlOut .= Xml::element( 'input',
array(
'type' => 'hidden',
'name' => 'ns' . $i,
'value' => 1,
'id' => 'mw-inputbox-ns' . $i . $idRandStr
) + $checked
);
} else {
// Checkbox
$htmlOut .= ' <div class="inputbox-element mw-ui-checkbox">';
$htmlOut .= Xml::element( 'input',
array(
'type' => 'checkbox',
'name' => 'ns' . $i,
'value' => 1,
'id' => 'mw-inputbox-ns' . $i . $idRandStr
) + $checked
);
// Label
$htmlOut .= Xml::label( $name, 'mw-inputbox-ns' . $i . $idRandStr );
$htmlOut .= '</div> ';
}
}
// Line break
$htmlOut .= $this->mBR;
} elseif ( $type == 'search' ) {
// Go button
$htmlOut .= Xml::element( 'input',
array(
'type' => 'submit',
'name' => 'go',
'class' => 'mw-ui-button',
'value' => $this->mButtonLabel
)
);
$htmlOut .= '&#160;';
}
// Search button
$htmlOut .= Xml::element( 'input',
array(
'type' => 'submit',
'name' => 'fulltext',
'class' => 'mw-ui-button',
'value' => $this->mSearchButtonLabel
)
);
// Hidden fulltext param for IE (bug 17161)
if ( $type == 'fulltext' ) {
$htmlOut .= Html::hidden( 'fulltext', 'Search' );
}
$htmlOut .= Xml::closeElement( 'form' );
$htmlOut .= Xml::closeElement( 'div' );
// Return HTML
return $htmlOut;
}
/**
* Generate search form version 2
*/
public function getSearchForm2() {
// Use button label fallbacks
if ( !$this->mButtonLabel ) {
$this->mButtonLabel = wfMessage( 'inputbox-tryexact' )->text();
}
if ( $this->mID !== '' ) {
$unescapedID = $this->mID;
} else {
// The label element needs a unique id, use
// random number to avoid multiple input boxes
// having conflicts.
$unescapedID = wfRandom();
}
$id = Sanitizer::escapeId( $unescapedID, 'noninitial' );
$htmlLabel = '';
if ( isset( $this->mLabelText ) && strlen( trim( $this->mLabelText ) ) ) {
$this->mLabelText = $this->mParser->recursiveTagParse( $this->mLabelText );
$htmlLabel = Xml::openElement( 'label', array( 'for' => 'bodySearchInput' . $id ) );
$htmlLabel .= $this->mLabelText;
$htmlLabel .= Xml::closeElement( 'label' );
}
$htmlOut = Xml::openElement( 'form',
array(
'name' => 'bodySearch' . $id,
'id' => 'bodySearch' . $id,
'class' => 'bodySearch' . ( $this->mInline ? ' mw-inputbox-inline' : '' ),
'action' => SpecialPage::getTitleFor( 'Search' )->getLocalUrl(),
)
);
$htmlOut .= Xml::openElement( 'div',
array(
'class' => 'bodySearchWrap' . ( $this->mInline ? ' mw-inputbox-inline' : '' ),
'style' => $this->bgColorStyle(),
)
);
$htmlOut .= $htmlLabel;
$htmlOut .= Xml::element( 'input',
array(
'type' => $this->mHidden ? 'hidden' : 'text',
'name' => 'search',
'class' => 'mw-ui-input mw-ui-input-inline',
'size' => $this->mWidth,
'id' => 'bodySearchInput' . $id,
'dir' => $this->mDir,
)
);
$htmlOut .= '&#160;' . Xml::element( 'input',
array(
'type' => 'submit',
'name' => 'go',
'value' => $this->mButtonLabel,
'class' => 'mw-ui-button',
)
);
// Better testing needed here!
if ( !empty( $this->mFullTextButton ) ) {
$htmlOut .= Xml::element( 'input',
array(
'type' => 'submit',
'name' => 'fulltext',
'class' => 'mw-ui-button',
'value' => $this->mSearchButtonLabel
)
);
}
$htmlOut .= Xml::closeElement( 'div' );
$htmlOut .= Xml::closeElement( 'form' );
// Return HTML
return $htmlOut;
}
/**
* Generate create page form
*/
public function getCreateForm() {
global $wgScript;
if ( $this->mType == "comment" ) {
if ( !$this->mButtonLabel ) {
$this->mButtonLabel = wfMessage( 'inputbox-postcomment' )->text();
}
} else {
if ( !$this->mButtonLabel ) {
$this->mButtonLabel = wfMessage( 'inputbox-createarticle' )->text();
}
}
$htmlOut = Xml::openElement( 'div',
array(
'class' => 'mw-inputbox-centered',
'style' => $this->bgColorStyle(),
)
);
$createBoxParams = array(
'name' => 'createbox',
'class' => 'createbox',
'action' => $wgScript,
'method' => 'get'
);
if ( $this->mID !== '' ) {
$createBoxParams['id'] = Sanitizer::escapeId( $this->mID );
}
$htmlOut .= Xml::openElement( 'form', $createBoxParams );
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'action',
'value' => 'edit',
)
);
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'preload',
'value' => $this->mPreload,
)
);
foreach ( $this->mPreloadparams as $preloadparams ) {
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'preloadparams[]',
'value' => $preloadparams,
)
);
}
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'editintro',
'value' => $this->mEditIntro,
)
);
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'summary',
'value' => $this->mSummary,
)
);
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'nosummary',
'value' => $this->mNosummary,
)
);
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'prefix',
'value' => $this->mPrefix,
)
);
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'minor',
'value' => $this->mMinor,
)
);
if ( $this->mType == 'comment' ) {
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'section',
'value' => 'new',
)
);
}
$htmlOut .= Xml::openElement( 'input',
array(
'type' => $this->mHidden ? 'hidden' : 'text',
'name' => 'title',
'class' => 'createboxInput mw-ui-input mw-ui-input-inline',
'value' => $this->mDefaultText,
'placeholder' => $this->mPlaceholderText,
'size' => $this->mWidth,
'dir' => $this->mDir,
)
);
$htmlOut .= $this->mBR;
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'submit',
'name' => 'create',
'class' => 'mw-ui-button mw-ui-progressive',
'value' => $this->mButtonLabel
)
);
$htmlOut .= Xml::closeElement( 'form' );
$htmlOut .= Xml::closeElement( 'div' );
// Return HTML
return $htmlOut;
}
/**
* Generate move page form
*/
public function getMoveForm() {
global $wgScript;
if ( !$this->mButtonLabel ) {
$this->mButtonLabel = wfMessage( 'inputbox-movearticle' )->text();
}
$htmlOut = Xml::openElement( 'div',
array(
'class' => 'mw-inputbox-centered',
'style' => $this->bgColorStyle(),
)
);
$moveBoxParams = array(
'name' => 'movebox',
'class' => 'mw-movebox',
'action' => $wgScript,
'method' => 'get'
);
if ( $this->mID !== '' ) {
$moveBoxParams['id'] = Sanitizer::escapeId( $this->mID );
}
$htmlOut .= Xml::openElement( 'form', $moveBoxParams );
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'title',
'value' => SpecialPage::getTitleFor( 'Movepage', $this->mPage )->getPrefixedText(),
)
);
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'wpReason',
'value' => $this->mSummary,
)
);
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'prefix',
'value' => $this->mPrefix,
)
);
$htmlOut .= Xml::openElement( 'input',
array(
'type' => $this->mHidden ? 'hidden' : 'text',
'name' => 'wpNewTitle',
'class' => 'mw-moveboxInput mw-ui-input mw-ui-input-inline',
'value' => $this->mDefaultText,
'placeholder' => $this->mPlaceholderText,
'size' => $this->mWidth,
'dir' => $this->mDir,
)
);
$htmlOut .= $this->mBR;
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'submit',
'class' => 'mw-ui-button mw-ui-progressive',
'value' => $this->mButtonLabel
)
);
$htmlOut .= Xml::closeElement( 'form' );
$htmlOut .= Xml::closeElement( 'div' );
// Return HTML
return $htmlOut;
}
/**
* Generate new section form
*/
public function getCommentForm() {
global $wgScript;
if ( !$this->mButtonLabel ) {
$this->mButtonLabel = wfMessage( 'inputbox-postcommenttitle' )->text();
}
$htmlOut = Xml::openElement( 'div',
array(
'class' => 'mw-inputbox-centered',
'style' => $this->bgColorStyle(),
)
);
$commentFormParams = array(
'name' => 'commentbox',
'class' => 'commentbox',
'action' => $wgScript,
'method' => 'get'
);
if ( $this->mID !== '' ) {
$commentFormParams['id'] = Sanitizer::escapeId( $this->mID );
}
$htmlOut .= Xml::openElement( 'form', $commentFormParams );
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'action',
'value' => 'edit',
)
);
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'preload',
'value' => $this->mPreload,
)
);
foreach ( $this->mPreloadparams as $preloadparams ) {
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'preloadparams[]',
'value' => $preloadparams,
)
);
}
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'editintro',
'value' => $this->mEditIntro,
)
);
$htmlOut .= Xml::openElement( 'input',
array(
'type' => $this->mHidden ? 'hidden' : 'text',
'name' => 'preloadtitle',
'class' => 'commentboxInput mw-ui-input mw-ui-input-inline',
'value' => $this->mDefaultText,
'placeholder' => $this->mPlaceholderText,
'size' => $this->mWidth,
'dir' => $this->mDir,
)
);
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'section',
'value' => 'new',
)
);
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'hidden',
'name' => 'title',
'value' => $this->mPage
)
);
$htmlOut .= $this->mBR;
$htmlOut .= Xml::openElement( 'input',
array(
'type' => 'submit',
'name' => 'create',
'class' => 'mw-ui-button mw-ui-progressive',
'value' => $this->mButtonLabel
)
);
$htmlOut .= Xml::closeElement( 'form' );
$htmlOut .= Xml::closeElement( 'div' );
// Return HTML
return $htmlOut;
}
/**
* Extract options from a blob of text
*
* @param string $text Tag contents
*/
public function extractOptions( $text ) {
// Parse all possible options
$values = array();
foreach ( explode( "\n", $text ) as $line ) {
if ( strpos( $line, '=' ) === false )
continue;
list( $name, $value ) = explode( '=', $line, 2 );
$name = strtolower( trim( $name ) );
$value = Sanitizer::decodeCharReferences( trim( $value ) );
if ( $name == 'preloadparams[]' ) {
// We have to special-case this one because it's valid for it to appear more than once.
$this->mPreloadparams[] = $value;
} else {
$values[ $name ] = $value;
}
}
// Validate the dir value.
if ( isset( $values['dir'] ) && !in_array( $values['dir'], array( 'ltr', 'rtl' ) ) ) {
unset( $values['dir'] );
}
// Build list of options, with local member names
$options = array(
'type' => 'mType',
'width' => 'mWidth',
'preload' => 'mPreload',
'page' => 'mPage',
'editintro' => 'mEditIntro',
'summary' => 'mSummary',
'nosummary' => 'mNosummary',
'minor' => 'mMinor',
'break' => 'mBR',
'default' => 'mDefaultText',
'placeholder' => 'mPlaceholderText',
'bgcolor' => 'mBGColor',
'buttonlabel' => 'mButtonLabel',
'searchbuttonlabel' => 'mSearchButtonLabel',
'fulltextbutton' => 'mFullTextButton',
'namespaces' => 'mNamespaces',
'labeltext' => 'mLabelText',
'hidden' => 'mHidden',
'id' => 'mID',
'inline' => 'mInline',
'prefix' => 'mPrefix',
'dir' => 'mDir',
);
foreach ( $options as $name => $var ) {
if ( isset( $values[$name] ) ) {
$this->$var = $values[$name];
}
}
// Insert a line break if configured to do so
$this->mBR = ( strtolower( $this->mBR ) == "no" ) ? ' ' : '<br />';
// Validate the width; make sure it's a valid, positive integer
$this->mWidth = intval( $this->mWidth <= 0 ? 50 : $this->mWidth );
// Validate background color
if ( !$this->isValidColor( $this->mBGColor ) ) {
$this->mBGColor = 'transparent';
}
}
/**
* Do a security check on the bgcolor parameter
*/
public function isValidColor( $color ) {
$regex = <<<REGEX
/^ (
[a-zA-Z]* | # color names
\# [0-9a-f]{3} | # short hexadecimal
\# [0-9a-f]{6} | # long hexadecimal
rgb \s* \( \s* (
\d+ \s* , \s* \d+ \s* , \s* \d+ | # rgb integer
[0-9.]+% \s* , \s* [0-9.]+% \s* , \s* [0-9.]+% # rgb percent
) \s* \)
) $ /xi
REGEX;
return (bool) preg_match( $regex, $color );
}
private function bgColorStyle() {
if ( $this->mBGColor != 'transparent' ) {
return 'background-color: ' . $this->mBGColor . ';';
}
return '';
}
}