Convert EditPage buttons, checkboxes and summary input to OOUI

Several methods now have a new implementation using OOjs UI widgets
(ButtonInputWidget/ButtonWidget, CheckboxInputWidget, TextInputWidget).
The existing (public) methods are unchanged. The OOjs UI version is
used by default.

Because this change can cause problems for extensions and on-wiki
scripts depending on the exact HTML, the old version is still available
and can be used by setting $wgOOUIEditPage = false; in LocalSettings.php.
This will be removed later and OOjs UI will become the only option.
To make testing easier, users can also force either mode by adding
&ooui=true or &ooui=false to the action=edit URL.

* EditPage::getSummaryInput() and EditPage::getSummaryInputOOUI()
* EditPage::getCheckboxes() and EditPage::getCheckboxesOOUI()
* EditPage::getCancelLink()
* EditPage::getEditButtons()

Bug: T111088
Co-Authored-By: Amir Sarabadani <ladsgroup@gmail.com>
Co-Authored-By: Florian Schmidt <florian.schmidt.welzow@t-online.de>
Change-Id: I25aa78ac59082789938ecfb5878eb16614392995
This commit is contained in:
Florian 2015-08-14 20:07:35 +02:00 committed by Jforrester
parent 72dee0e141
commit 97d7de0b84
6 changed files with 297 additions and 64 deletions

View file

@ -63,6 +63,13 @@ production.
* Completely new user interface for the RecentChanges page, which
structures filters into user-friendly groups. This has corresponding
changes to how filters are registered by core and extensions.
* The edit form now uses pretty OOjs UI buttons, checkboxes and summary input.
Because this change can cause problems for extensions and on-wiki
scripts depending on the exact HTML, the old version is still available
and can be used by setting $wgOOUIEditPage = false; in LocalSettings.php.
This will be removed later and OOjs UI will become the only option.
To make testing easier, users can also force either mode by adding
&ooui=true or &ooui=false to the action=edit URL.
=== External library changes in 1.29 ===

View file

@ -3216,6 +3216,14 @@ $wgHTMLFormAllowTableFormat = true;
*/
$wgUseMediaWikiUIEverywhere = false;
/**
* Temporary variable that determines whether the EditPage class should use OOjs UI or not.
* This will be removed later and OOjs UI will become the only option.
*
* @since 1.29
*/
$wgOOUIEditPage = true;
/**
* Whether to label the store-to-database-and-show-to-others button in the editor
* as "Save page"/"Save changes" if false (the default) or, if true, instead as

View file

@ -413,10 +413,17 @@ class EditPage {
*/
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();
@ -426,6 +433,8 @@ class EditPage {
$handler = ContentHandler::getForModelID( $this->contentModel );
$this->contentFormat = $handler->getDefaultFormat();
$this->oouiEnabled = $wgOOUIEditPage;
}
/**
@ -476,6 +485,14 @@ class EditPage {
}
}
/**
* 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.
*
@ -843,6 +860,9 @@ class EditPage {
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' ) );
@ -2637,6 +2657,7 @@ class EditPage {
$wgOut->addHTML( Html::openElement(
'form',
[
'class' => $this->oouiEnabled ? 'mw-editform-ooui' : 'mw-editform-legacy',
'id' => self::EDITFORM_ID,
'name' => self::EDITFORM_ID,
'method' => 'post',
@ -2736,6 +2757,11 @@ class EditPage {
$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 ) );
@ -3007,6 +3033,24 @@ class EditPage {
$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.
@ -3024,14 +3068,7 @@ class EditPage {
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',
@ -3053,6 +3090,34 @@ class EditPage {
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
@ -3073,14 +3138,23 @@ class EditPage {
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}" );
}
}
/**
@ -3490,9 +3564,21 @@ HTML
$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() );
@ -3580,13 +3666,22 @@ HTML
} elseif ( $this->getContextTitle()->isRedirect() ) {
$cancelParams['redirect'] = 'no';
}
return MediaWikiServices::getInstance()->getLinkRenderer()->makeKnownLink(
$this->getContextTitle(),
new HtmlArmor( $this->context->msg( 'cancel' )->parse() ),
Html::buttonAttributes( [ 'id' => 'mw-editform-cancel' ], [ '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
);
}
}
/**
@ -4062,7 +4157,7 @@ HTML
}
/**
* Returns an array of html code of the following checkboxes:
* Returns an array of html code of the following checkboxes old style:
* minor and watch
*
* @param int $tabindex Current tabindex
@ -4119,6 +4214,68 @@ HTML
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;
}
/**
* Returns an array of html code of the following buttons:
* save, diff and preview
@ -4144,31 +4301,56 @@ HTML
'name' => 'wpSave',
'tabindex' => ++$tabindex,
] + Linker::tooltipAndAccesskeyAttribs( 'save' );
$buttons['save'] = Html::submitButton(
$this->context->msg( $buttonLabelKey )->text(),
$attribs,
[ 'mw-ui-progressive' ]
);
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,
] + 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;

View file

@ -20,7 +20,9 @@
var editBox, scrollTop, $editForm;
// Make sure edit summary does not exceed byte limit
$( '#wpSummary' ).byteLimit( 255 );
// TODO: Replace with this when $wgOOUIEditPage is removed:
// OO.ui.infuse( 'wpSummary' ).$input.byteLimit( 255 );
$( 'input#wpSummary, #wpSummary > input' ).byteLimit( 255 );
// Restore the edit box scroll state following a preview operation,
// and set up a form submission handler to remember this state.

View file

@ -12,30 +12,42 @@
min-height: 5em;
}
/* Adjustments to edit form elements */
.editCheckboxes {
/*
* Add a bit of margin space between the preview and the toolbar.
* This replaces the ugly <p><br /></p> we used to insert into the page source
*/
#wikiPreview.ontop {
margin-bottom: 1em;
}
.editCheckboxes input:first-child {
margin-left: 0;
}
.cancelLink {
margin-left: 0.5em;
}
/* Adjustments to edit form elements */
#editpage-copywarn {
font-size: 0.9em;
}
input#wpSummary {
#wpSummary {
display: block;
width: 80%;
margin-bottom: 1em;
}
/* Adjustments to edit form elements (only when $wgOOUIEditPage is false) */
.mw-editform-legacy .editCheckboxes {
margin-bottom: 1em;
}
.mw-editform-legacy .editCheckboxes input:first-child {
margin-left: 0;
}
.mw-editform-legacy .cancelLink {
margin-left: 0.5em;
}
.mw-editform-legacy input#wpSummary {
background-color: #fff;
color: #000;
width: 80%;
margin-top: 0;
margin-bottom: 1em;
padding: 0.625em 0.546875em 0.546875em;
border: 1px solid #a2a9b1;
border-radius: 2px;
@ -47,21 +59,43 @@ input#wpSummary {
transition: border-color 200ms cubic-bezier( 0.39, 0.575, 0.565, 1 ), box-shadow 200ms cubic-bezier( 0.39, 0.575, 0.565, 1 );
}
input#wpSummary:focus,
input#wpSummary:active {
.mw-editform-legacy input#wpSummary:focus,
.mw-editform-legacy input#wpSummary:active {
outline: 0;
border-color: #36c;
box-shadow: inset 0 0 0 1px #36c;
}
.editButtons input:first-child {
.mw-editform-legacy .editButtons input:first-child {
margin-left: 0.1em;
}
/*
* Add a bit of margin space between the preview and the toolbar.
* This replaces the ugly <p><br /></p> we used to insert into the page source
*/
#wikiPreview.ontop {
margin-bottom: 1em;
/* Adjustments to edit form elements (only when $wgOOUIEditPage is true) */
.mw-editform-ooui #editpage-copywarn {
line-height: 1.26;
}
.mw-editform-ooui #wpSummary {
max-width: none;
}
.mw-editform-ooui #wpSummaryLabel {
margin: 0;
}
.mw-editform-ooui .editCheckboxes .oo-ui-fieldLayout {
margin-right: 1em;
}
.mw-editform-ooui .editHelp {
margin-left: 0.5em;
vertical-align: middle;
}
.mw-editform-ooui .editHelp a {
font-weight: bold;
}
.mw-editform-ooui .editOptions {
border-radius: 0 0 2px 2px;
}

View file

@ -21,7 +21,7 @@ textarea {
}
.editOptions {
background-color: #f8f9fa;
background-color: #eaecf0;
border: 1px solid #c8ccd1;
border-top: 0;
padding: 1em 1em 1.5em 1em;