*/
private $isOldRev = false;
+ /**
+ * @var bool Whether OOUI should be enabled here
+ */
+ private $oouiEnabled = false;
+
/**
* @param Article $article
*/
public function __construct( Article $article ) {
+ global $wgOOUIEditPage;
+
$this->mArticle = $article;
$this->page = $article->getPage(); // model object
$this->mTitle = $article->getTitle();
$handler = ContentHandler::getForModelID( $this->contentModel );
$this->contentFormat = $handler->getDefaultFormat();
+
+ $this->oouiEnabled = $wgOOUIEditPage;
}
/**
}
}
+ /**
+ * Check if the edit page is using OOUI controls
+ * @return bool
+ */
+ public function isOouiEnabled() {
+ return $this->oouiEnabled;
+ }
+
/**
* Returns if the given content model is editable.
*
public function importFormData( &$request ) {
global $wgContLang, $wgUser;
+ # Allow users to change the mode for testing
+ $this->oouiEnabled = $request->getFuzzyBool( 'ooui', $this->oouiEnabled );
+
# Section edit can come from either the form or a link
$this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) );
throw new ErrorPageError(
'editpage-invalidcontentmodel-title',
'editpage-invalidcontentmodel-text',
- [ $this->contentModel ]
+ [ wfEscapeWikiText( $this->contentModel ) ]
);
}
throw new ErrorPageError(
'editpage-notsupportedcontentformat-title',
'editpage-notsupportedcontentformat-text',
- [ $this->contentFormat, ContentHandler::getLocalizedName( $this->contentModel ) ]
+ [
+ wfEscapeWikiText( $this->contentFormat ),
+ wfEscapeWikiText( ContentHandler::getLocalizedName( $this->contentModel ) )
+ ]
);
}
# Show log extract when the user is currently blocked
if ( $namespace == NS_USER || $namespace == NS_USER_TALK ) {
$username = explode( '/', $this->mTitle->getText(), 2 )[0];
- $user = User::newFromName( $username, false /* allow IP users*/ );
+ $user = User::newFromName( $username, false /* allow IP users */ );
$ip = User::isIP( $username );
$block = Block::newFromTarget( $user, $user );
if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
}
// @todo add EditForm plugin interface and use it here!
- // search for textarea1 and textares2, and allow EditForm to override all uses.
+ // search for textarea1 and textarea2, and allow EditForm to override all uses.
$wgOut->addHTML( Html::openElement(
'form',
[
+ 'class' => $this->oouiEnabled ? 'mw-editform-ooui' : 'mw-editform-legacy',
'id' => self::EDITFORM_ID,
'name' => self::EDITFORM_ID,
'method' => 'post',
$wgOut->addHTML( Html::hidden( 'format', $this->contentFormat ) );
$wgOut->addHTML( Html::hidden( 'model', $this->contentModel ) );
+ // following functions will need OOUI, enable it only once; here.
+ if ( $this->oouiEnabled ) {
+ $wgOut->enableOOUI();
+ }
+
if ( $this->section == 'new' ) {
$this->showSummaryInput( true, $this->summary );
$wgOut->addHTML( $this->getSummaryPreview( true, $this->summary ) );
$this->showHeaderCopyrightWarning();
}
+ /**
+ * Helper function for summary input functions, which returns the neccessary
+ * attributes for the input.
+ *
+ * @param array|null $inputAttrs Array of attrs to use on the input
+ * @return array
+ */
+ private function getSummaryInputAttributes( array $inputAttrs = null ) {
+ // Note: the maxlength is overridden in JS to 255 and to make it use UTF-8 bytes, not characters.
+ return ( is_array( $inputAttrs ) ? $inputAttrs : [] ) + [
+ 'id' => 'wpSummary',
+ 'maxlength' => '200',
+ 'tabindex' => '1',
+ 'size' => 60,
+ 'spellcheck' => 'true',
+ ] + Linker::tooltipAndAccesskeyAttribs( 'summary' );
+ }
+
/**
* Standard summary input and label (wgSummary), abstracted so EditPage
* subclasses may reorganize the form.
public function getSummaryInput( $summary = "", $labelText = null,
$inputAttrs = null, $spanLabelAttrs = null
) {
- // Note: the maxlength is overridden in JS to 255 and to make it use UTF-8 bytes, not characters.
- $inputAttrs = ( is_array( $inputAttrs ) ? $inputAttrs : [] ) + [
- 'id' => 'wpSummary',
- 'maxlength' => '200',
- 'tabindex' => '1',
- 'size' => 60,
- 'spellcheck' => 'true',
- ] + Linker::tooltipAndAccesskeyAttribs( 'summary' );
+ $inputAttrs = $this->getSummaryInputAttributes( $inputAttrs );
$spanLabelAttrs = ( is_array( $spanLabelAttrs ) ? $spanLabelAttrs : [] ) + [
'class' => $this->missingSummary ? 'mw-summarymissed' : 'mw-summary',
return [ $label, $input ];
}
+ /**
+ * Same as self::getSummaryInput, but uses OOUI, instead of plain HTML.
+ * Builds a standard summary input with a label.
+ *
+ * @param string $summary The value of the summary input
+ * @param string $labelText The html to place inside the label
+ * @param array $inputAttrs Array of attrs to use on the input
+ *
+ * @return OOUI\FieldLayout OOUI FieldLayout with Label and Input
+ */
+ function getSummaryInputOOUI( $summary = "", $labelText = null, $inputAttrs = null ) {
+ $inputAttrs = OOUI\Element::configFromHtmlAttributes(
+ $this->getSummaryInputAttributes( $inputAttrs )
+ );
+
+ return new OOUI\FieldLayout(
+ new OOUI\TextInputWidget( [
+ 'value' => $summary,
+ ] + $inputAttrs ),
+ [
+ 'label' => new OOUI\HtmlSnippet( $labelText ),
+ 'align' => 'top',
+ 'id' => 'wpSummaryLabel',
+ 'classes' => [ $this->missingSummary ? 'mw-summarymissed' : 'mw-summary' ],
+ ]
+ );
+ }
+
/**
* @param bool $isSubjectPreview True if this is the section subject/title
* up top, or false if this is the comment summary
*/
protected function showSummaryInput( $isSubjectPreview, $summary = "" ) {
global $wgOut;
+
# Add a class if 'missingsummary' is triggered to allow styling of the summary line
$summaryClass = $this->missingSummary ? 'mw-summarymissed' : 'mw-summary';
if ( $isSubjectPreview ) {
return;
}
}
+
$labelText = $this->context->msg( $isSubjectPreview ? 'subject' : 'summary' )->parse();
- list( $label, $input ) = $this->getSummaryInput(
- $summary,
- $labelText,
- [ 'class' => $summaryClass ],
- []
- );
- $wgOut->addHTML( "{$label} {$input}" );
+ if ( $this->oouiEnabled ) {
+ $wgOut->addHTML( $this->getSummaryInputOOUI(
+ $summary,
+ $labelText,
+ [ 'class' => $summaryClass ]
+ ) );
+ } else {
+ list( $label, $input ) = $this->getSummaryInput(
+ $summary,
+ $labelText,
+ [ 'class' => $summaryClass ]
+ );
+ $wgOut->addHTML( "{$label} {$input}" );
+ }
+
}
/**
$wgOut->addHTML( $this->getSummaryPreview( false, $this->summary ) );
}
- $checkboxes = $this->getCheckboxes( $tabindex,
- [ 'minor' => $this->minoredit, 'watch' => $this->watchthis ] );
- $wgOut->addHTML( "<div class='editCheckboxes'>" . implode( $checkboxes, "\n" ) . "</div>\n" );
+ if ( $this->oouiEnabled ) {
+ $checkboxes = $this->getCheckboxesOOUI(
+ $tabindex,
+ [ 'minor' => $this->minoredit, 'watch' => $this->watchthis ]
+ );
+ $checkboxesHTML = new OOUI\HorizontalLayout( [ 'items' => $checkboxes ] );
+ } else {
+ $checkboxes = $this->getCheckboxes(
+ $tabindex,
+ [ 'minor' => $this->minoredit, 'watch' => $this->watchthis ]
+ );
+ $checkboxesHTML = implode( $checkboxes, "\n" );
+ }
+
+ $wgOut->addHTML( "<div class='editCheckboxes'>" . $checkboxesHTML . "</div>\n" );
// Show copyright warning.
$wgOut->addWikiText( $this->getCopywarn() );
$message = $this->context->msg( 'edithelppage' )->inContentLanguage()->text();
$edithelpurl = Skin::makeInternalOrExternalUrl( $message );
- $attrs = [
- 'target' => 'helpwindow',
- 'href' => $edithelpurl,
- ];
- $edithelp = Html::linkButton( $this->context->msg( 'edithelp' )->text(),
- $attrs, [ 'mw-ui-quiet' ] ) .
+ $edithelp =
+ Html::linkButton(
+ $this->context->msg( 'edithelp' )->text(),
+ [ 'target' => 'helpwindow', 'href' => $edithelpurl ],
+ [ 'mw-ui-quiet' ]
+ ) .
$this->context->msg( 'word-separator' )->escaped() .
$this->context->msg( 'newwindow' )->parse();
*/
public function getCancelLink() {
$cancelParams = [];
- $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
if ( !$this->isConflict && $this->oldid > 0 ) {
$cancelParams['oldid'] = $this->oldid;
} elseif ( $this->getContextTitle()->isRedirect() ) {
$cancelParams['redirect'] = 'no';
}
- $attrs = [ 'id' => 'mw-editform-cancel' ];
-
- return $linkRenderer->makeKnownLink(
- $this->getContextTitle(),
- new HtmlArmor( $this->context->msg( 'cancel' )->parse() ),
- Html::buttonAttributes( $attrs, [ 'mw-ui-quiet' ] ),
- $cancelParams
- );
+ if ( $this->oouiEnabled ) {
+ return new OOUI\ButtonWidget( [
+ 'id' => 'mw-editform-cancel',
+ 'href' => $this->getContextTitle()->getLinkUrl( $cancelParams ),
+ 'label' => new OOUI\HtmlSnippet( $this->context->msg( 'cancel' )->parse() ),
+ 'framed' => false,
+ 'flags' => 'destructive',
+ ] );
+ } else {
+ return MediaWikiServices::getInstance()->getLinkRenderer()->makeKnownLink(
+ $this->getContextTitle(),
+ new HtmlArmor( $this->context->msg( 'cancel' )->parse() ),
+ Html::buttonAttributes( [ 'id' => 'mw-editform-cancel' ], [ 'mw-ui-quiet' ] ),
+ $cancelParams
+ );
+ }
}
/**
}
/**
- * Returns an array of html code of the following checkboxes:
+ * Return an array of checkbox definitions.
+ *
+ * Array keys correspond to the `<input>` 'name' attribute to use for each checkbox.
+ *
+ * Array values are associative arrays with the following keys:
+ * - 'label-message' (required): message for label text
+ * - 'id' (required): 'id' attribute for the `<input>`
+ * - 'default' (required): default checkedness (true or false)
+ * - 'title-message' (optional): used to generate 'title' attribute for the `<label>`
+ * - 'tooltip' (optional): used to generate 'title' and 'accesskey' attributes
+ * from messages like 'tooltip-foo', 'accesskey-foo'
+ * - 'label-id' (optional): 'id' attribute for the `<label>`
+ * - 'legacy-name' (optional): short name for backwards-compatibility
+ * @param array $checked Array of checkbox name (matching the 'legacy-name') => bool,
+ * where bool indicates the checked status of the checkbox
+ * @return array
+ */
+ protected function getCheckboxesDefinition( $checked ) {
+ global $wgUser;
+ $checkboxes = [];
+
+ // don't show the minor edit checkbox if it's a new page or section
+ if ( !$this->isNew && $wgUser->isAllowed( 'minoredit' ) ) {
+ $checkboxes['wpMinoredit'] = [
+ 'id' => 'wpMinoredit',
+ 'label-message' => 'minoredit',
+ // Uses messages: tooltip-minoredit, accesskey-minoredit
+ 'tooltip' => 'minoredit',
+ 'label-id' => 'mw-editpage-minoredit',
+ 'legacy-name' => 'minor',
+ 'default' => $checked['minor'],
+ ];
+ }
+
+ if ( $wgUser->isLoggedIn() ) {
+ $checkboxes['wpWatchthis'] = [
+ 'id' => 'wpWatchthis',
+ 'label-message' => 'watchthis',
+ // Uses messages: tooltip-watch, accesskey-watch
+ 'tooltip' => 'watch',
+ 'label-id' => 'mw-editpage-watch',
+ 'legacy-name' => 'watch',
+ 'default' => $checked['watch'],
+ ];
+ }
+
+ $editPage = $this;
+ Hooks::run( 'EditPageGetCheckboxesDefinition', [ $editPage, &$checkboxes ] );
+
+ return $checkboxes;
+ }
+
+ /**
+ * Returns an array of html code of the following checkboxes old style:
* minor and watch
*
* @param int $tabindex Current tabindex
- * @param array $checked Array of checkbox => bool, where bool indicates the checked
- * status of the checkbox
- *
+ * @param array $checked See getCheckboxesDefinition()
* @return array
*/
public function getCheckboxes( &$tabindex, $checked ) {
- global $wgUser, $wgUseMediaWikiUIEverywhere;
+ global $wgUseMediaWikiUIEverywhere;
$checkboxes = [];
+ $checkboxesDef = $this->getCheckboxesDefinition( $checked );
- // don't show the minor edit checkbox if it's a new page or section
+ // Backwards-compatibility for the EditPageBeforeEditChecks hook
if ( !$this->isNew ) {
$checkboxes['minor'] = '';
- $minorLabel = $this->context->msg( 'minoredit' )->parse();
- if ( $wgUser->isAllowed( 'minoredit' ) ) {
- $attribs = [
- 'tabindex' => ++$tabindex,
- 'accesskey' => $this->context->msg( 'accesskey-minoredit' )->text(),
- 'id' => 'wpMinoredit',
- ];
- $minorEditHtml =
- Xml::check( 'wpMinoredit', $checked['minor'], $attribs ) .
- " <label for='wpMinoredit' id='mw-editpage-minoredit'" .
- Xml::expandAttributes( [ 'title' => Linker::titleAttrib( 'minoredit', 'withaccess' ) ] ) .
- ">{$minorLabel}</label>";
-
- if ( $wgUseMediaWikiUIEverywhere ) {
- $checkboxes['minor'] = Html::openElement( 'div', [ 'class' => 'mw-ui-checkbox' ] ) .
- $minorEditHtml .
- Html::closeElement( 'div' );
- } else {
- $checkboxes['minor'] = $minorEditHtml;
- }
- }
}
-
- $watchLabel = $this->context->msg( 'watchthis' )->parse();
$checkboxes['watch'] = '';
- if ( $wgUser->isLoggedIn() ) {
+
+ foreach ( $checkboxesDef as $name => $options ) {
+ $legacyName = isset( $options['legacy-name'] ) ? $options['legacy-name'] : $name;
+ $label = $this->context->msg( $options['label-message'] )->parse();
$attribs = [
'tabindex' => ++$tabindex,
- 'accesskey' => $this->context->msg( 'accesskey-watch' )->text(),
- 'id' => 'wpWatchthis',
+ 'id' => $options['id'],
+ ];
+ $labelAttribs = [
+ 'for' => $options['id'],
];
- $watchThisHtml =
- Xml::check( 'wpWatchthis', $checked['watch'], $attribs ) .
- " <label for='wpWatchthis' id='mw-editpage-watch'" .
- Xml::expandAttributes( [ 'title' => Linker::titleAttrib( 'watch', 'withaccess' ) ] ) .
- ">{$watchLabel}</label>";
+ if ( isset( $options['tooltip'] ) ) {
+ $attribs['accesskey'] = $this->context->msg( "accesskey-{$options['tooltip']}" )->text();
+ $labelAttribs['title'] = Linker::titleAttrib( $options['tooltip'], 'withaccess' );
+ }
+ if ( isset( $options['title-message'] ) ) {
+ $labelAttribs['title'] = $this->context->msg( $options['title-message'] )->text();
+ }
+ if ( isset( $options['label-id'] ) ) {
+ $labelAttribs['id'] = $options['label-id'];
+ }
+ $checkboxHtml =
+ Xml::check( $name, $options['default'], $attribs ) .
+ ' ' .
+ Xml::tags( 'label', $labelAttribs, $label );
+
if ( $wgUseMediaWikiUIEverywhere ) {
- $checkboxes['watch'] = Html::openElement( 'div', [ 'class' => 'mw-ui-checkbox' ] ) .
- $watchThisHtml .
- Html::closeElement( 'div' );
- } else {
- $checkboxes['watch'] = $watchThisHtml;
+ $checkboxHtml = Html::rawElement( 'div', [ 'class' => 'mw-ui-checkbox' ], $checkboxHtml );
}
+
+ $checkboxes[ $legacyName ] = $checkboxHtml;
}
// Avoid PHP 7.1 warning of passing $this by reference
$editPage = $this;
- Hooks::run( 'EditPageBeforeEditChecks', [ &$editPage, &$checkboxes, &$tabindex ] );
+ Hooks::run( 'EditPageBeforeEditChecks', [ &$editPage, &$checkboxes, &$tabindex ], '1.29' );
+ return $checkboxes;
+ }
+
+ /**
+ * Returns an array of html code of the following checkboxes:
+ * minor and watch
+ *
+ * @param int $tabindex Current tabindex
+ * @param array $checked Array of checkbox => bool, where bool indicates the checked
+ * status of the checkbox
+ *
+ * @return array
+ */
+ public function getCheckboxesOOUI( &$tabindex, $checked ) {
+ $checkboxes = [];
+ $checkboxesDef = $this->getCheckboxesDefinition( $checked );
+
+ $origTabindex = $tabindex;
+
+ foreach ( $checkboxesDef as $name => $options ) {
+ $legacyName = isset( $options['legacy-name'] ) ? $options['legacy-name'] : $name;
+
+ $title = null;
+ $accesskey = null;
+ if ( isset( $options['tooltip'] ) ) {
+ $accesskey = $this->context->msg( "accesskey-{$options['tooltip']}" )->text();
+ $title = Linker::titleAttrib( $options['tooltip'], 'withaccess' );
+ }
+ if ( isset( $options['title-message'] ) ) {
+ $title = $this->context->msg( $options['title-message'] )->text();
+ }
+ if ( isset( $options['label-id'] ) ) {
+ $labelAttribs['id'] = $options['label-id'];
+ }
+
+ $checkboxes[ $legacyName ] = new OOUI\FieldLayout(
+ new OOUI\CheckboxInputWidget( [
+ 'tabIndex' => ++$tabindex,
+ 'accessKey' => $accesskey,
+ 'id' => $options['id'],
+ 'name' => $name,
+ 'selected' => $options['default'],
+ ] ),
+ [
+ 'align' => 'inline',
+ 'label' => new OOUI\HtmlSnippet( $this->context->msg( $options['label-message'] )->parse() ),
+ 'title' => $title,
+ 'id' => isset( $options['label-id'] ) ? $options['label-id'] : null,
+ ]
+ );
+ }
+
+ // Backwards-compatibility hack to run the EditPageBeforeEditChecks hook. It's important,
+ // people have used it for the weirdest things completely unrelated to checkboxes...
+ // And if we're gonna run it, might as well allow its legacy checkboxes to be shown.
+ $legacyCheckboxes = $this->getCheckboxes( $origTabindex, $checked );
+ foreach ( $legacyCheckboxes as $name => $html ) {
+ if ( $html && !isset( $checkboxes[$name] ) ) {
+ $checkboxes[$name] = new OOUI\Widget( [ 'content' => new OOUI\HtmlSnippet( $html ) ] );
+ }
+ }
+
return $checkboxes;
}
} else {
$buttonLabelKey = !$this->mTitle->exists() ? 'savearticle' : 'savechanges';
}
- $buttonLabel = $this->context->msg( $buttonLabelKey )->text();
$attribs = [
'id' => 'wpSave',
'name' => 'wpSave',
'tabindex' => ++$tabindex,
] + Linker::tooltipAndAccesskeyAttribs( 'save' );
- $buttons['save'] = Html::submitButton( $buttonLabel, $attribs, [ 'mw-ui-progressive' ] );
- ++$tabindex; // use the same for preview and live preview
+ if ( $this->oouiEnabled ) {
+ $saveConfig = OOUI\Element::configFromHtmlAttributes( $attribs );
+ $buttons['save'] = new OOUI\ButtonInputWidget( [
+ 'flags' => [ 'constructive', 'primary' ],
+ 'label' => $this->context->msg( $buttonLabelKey )->text(),
+ 'type' => 'submit',
+ ] + $saveConfig );
+ } else {
+ $buttons['save'] = Html::submitButton(
+ $this->context->msg( $buttonLabelKey )->text(),
+ $attribs,
+ [ 'mw-ui-progressive' ]
+ );
+ }
+
$attribs = [
'id' => 'wpPreview',
'name' => 'wpPreview',
- 'tabindex' => $tabindex,
+ 'tabindex' => ++$tabindex,
] + Linker::tooltipAndAccesskeyAttribs( 'preview' );
- $buttons['preview'] = Html::submitButton( $this->context->msg( 'showpreview' )->text(),
- $attribs );
-
+ if ( $this->oouiEnabled ) {
+ $previewConfig = OOUI\Element::configFromHtmlAttributes( $attribs );
+ $buttons['preview'] = new OOUI\ButtonInputWidget( [
+ 'label' => $this->context->msg( 'showpreview' )->text(),
+ 'type' => 'submit'
+ ] + $previewConfig );
+ } else {
+ $buttons['preview'] = Html::submitButton(
+ $this->context->msg( 'showpreview' )->text(),
+ $attribs
+ );
+ }
$attribs = [
'id' => 'wpDiff',
'name' => 'wpDiff',
'tabindex' => ++$tabindex,
] + Linker::tooltipAndAccesskeyAttribs( 'diff' );
- $buttons['diff'] = Html::submitButton( $this->context->msg( 'showdiff' )->text(),
- $attribs );
+ if ( $this->oouiEnabled ) {
+ $diffConfig = OOUI\Element::configFromHtmlAttributes( $attribs );
+ $buttons['diff'] = new OOUI\ButtonInputWidget( [
+ 'label' => $this->context->msg( 'showdiff' )->text(),
+ 'type' => 'submit',
+ ] + $diffConfig );
+ } else {
+ $buttons['diff'] = Html::submitButton(
+ $this->context->msg( 'showdiff' )->text(),
+ $attribs
+ );
+ }
// Avoid PHP 7.1 warning of passing $this by reference
$editPage = $this;
Hooks::run( 'EditPageBeforeEditButtons', [ &$editPage, &$buttons, &$tabindex ] );
+
return $buttons;
}