* @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;
+ $this->oouiEnabled = $this->context->getConfig()->get( 'OOUIEditPage' );
}
/**
return $status;
}
+ /**
+ * Log when a page was successfully saved after the edit conflict view
+ */
+ private function incrementResolvedConflicts() {
+ global $wgRequest;
+
+ if ( $wgRequest->getText( 'mode' ) !== 'conflict' ) {
+ return;
+ }
+
+ $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
+ $stats->increment( 'edit.failures.conflict.resolved' );
+ }
+
/**
* Handle status, such as after attempt save
*
if ( $status->value == self::AS_SUCCESS_UPDATE
|| $status->value == self::AS_SUCCESS_NEW_ARTICLE
) {
+ $this->incrementResolvedConflicts();
+
$this->didSave = true;
if ( !$resultDetails['nullEdit'] ) {
$this->setPostEditCookie( $status->value );
global $wgParser;
if ( $this->sectiontitle !== '' ) {
- $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle );
+ $sectionanchor = $this->guessSectionName( $this->sectiontitle );
// If no edit summary was specified, create one automatically from the section
// title and have it link to the new section. Otherwise, respect the summary as
// passed.
->rawParams( $cleanSectionTitle )->inContentLanguage()->text();
}
} elseif ( $this->summary !== '' ) {
- $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $this->summary );
+ $sectionanchor = $this->guessSectionName( $this->summary );
# This is a new section, so create a link to the new section
# in the revision summary.
$cleanSummary = $wgParser->stripSectionName( $this->summary );
* time.
*/
public function internalAttemptSave( &$result, $bot = false ) {
- global $wgUser, $wgRequest, $wgParser, $wgMaxArticleSize;
+ global $wgUser, $wgRequest, $wgMaxArticleSize;
global $wgContentHandlerUseDB;
$status = Status::newGood();
# We can't deal with anchors, includes, html etc in the header for now,
# headline would need to be parsed to improve this.
if ( $hasmatch && strlen( $matches[2] ) > 0 ) {
- $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $matches[2] );
+ $sectionanchor = $this->guessSectionName( $matches[2] );
}
}
$result['sectionanchor'] = $sectionanchor;
$wgOut->addHTML( $this->editFormTextBeforeContent );
if ( !$this->isCssJsSubpage && $showToolbar && $wgUser->getOption( 'showtoolbar' ) ) {
- $wgOut->addHTML( EditPage::getEditToolbar( $this->mTitle ) );
+ $wgOut->addHTML( self::getEditToolbar( $this->mTitle ) );
}
if ( $this->blankArticle ) {
}
}
- $buttonLabelKey = $this->getSaveButtonLabel();
+ $buttonLabel = $this->context->msg( $this->getSaveButtonLabel() )->text();
if ( $this->missingComment ) {
$wgOut->wrapWikiMsg( "<div id='mw-missingcommenttext'>\n$1\n</div>", 'missingcommenttext' );
if ( $this->missingSummary && $this->section != 'new' ) {
$wgOut->wrapWikiMsg(
"<div id='mw-missingsummary'>\n$1\n</div>",
- [ 'missingsummary', $buttonLabelKey ]
+ [ 'missingsummary', $buttonLabel ]
);
}
if ( $this->missingSummary && $this->section == 'new' ) {
$wgOut->wrapWikiMsg(
"<div id='mw-missingcommentheader'>\n$1\n</div>",
- [ 'missingcommentheader', $buttonLabelKey ]
+ [ 'missingcommentheader', $buttonLabel ]
);
}
if ( $this->blankArticle ) {
$wgOut->wrapWikiMsg(
"<div id='mw-blankarticle'>\n$1\n</div>",
- [ 'blankarticle', $buttonLabelKey ]
+ [ 'blankarticle', $buttonLabel ]
);
}
if ( $this->selfRedirect ) {
$wgOut->wrapWikiMsg(
"<div id='mw-selfredirect'>\n$1\n</div>",
- [ 'selfredirect', $buttonLabelKey ]
+ [ 'selfredirect', $buttonLabel ]
);
}
'id' => 'wpSummary',
'name' => 'wpSummary',
'maxlength' => '200',
- 'tabindex' => '1',
+ 'tabindex' => 1,
'size' => 60,
'spellcheck' => 'true',
- ] + Linker::tooltipAndAccesskeyAttribs( 'summary' );
+ ];
}
/**
$inputAttrs = null, $spanLabelAttrs = null
) {
$inputAttrs = $this->getSummaryInputAttributes( $inputAttrs );
+ $inputAttrs += Linker::tooltipAndAccesskeyAttribs( 'summary' );
$spanLabelAttrs = ( is_array( $spanLabelAttrs ) ? $spanLabelAttrs : [] ) + [
'class' => $this->missingSummary ? 'mw-summarymissed' : 'mw-summary',
$inputAttrs = OOUI\Element::configFromHtmlAttributes(
$this->getSummaryInputAttributes( $inputAttrs )
);
+ $inputAttrs += [
+ 'title' => Linker::titleAttrib( 'summary' ),
+ 'accessKey' => Linker::accesskey( 'summary' ),
+ ];
+
+ // For compatibility with old scripts and extensions, we want the legacy 'id' on the `<input>`
+ $inputAttrs['inputId'] = $inputAttrs['id'];
+ $inputAttrs['id'] = 'wpSummaryWidget';
return new OOUI\FieldLayout(
new OOUI\TextInputWidget( [
protected function showFormBeforeText() {
global $wgOut;
- $section = htmlspecialchars( $this->section );
- $wgOut->addHTML( <<<HTML
-<input type='hidden' value="{$section}" name="wpSection"/>
-<input type='hidden' value="{$this->starttime}" name="wpStarttime" />
-<input type='hidden' value="{$this->edittime}" name="wpEdittime" />
-<input type='hidden' value="{$this->editRevId}" name="editRevId" />
-<input type='hidden' value="{$this->scrolltop}" name="wpScrolltop" id="wpScrolltop" />
-
-HTML
- );
+
+ $wgOut->addHTML( Html::hidden( 'wpSection', htmlspecialchars( $this->section ) ) );
+ $wgOut->addHTML( Html::hidden( 'wpStarttime', $this->starttime ) );
+ $wgOut->addHTML( Html::hidden( 'wpEdittime', $this->edittime ) );
+ $wgOut->addHTML( Html::hidden( 'editRevId', $this->editRevId ) );
+ $wgOut->addHTML( Html::hidden( 'wpScrolltop', $this->scrolltop ) );
+
if ( !$this->checkUnicodeCompliantBrowser() ) {
$wgOut->addHTML( Html::hidden( 'safemode', '1' ) );
}
}
}
+ /**
+ * Inserts optional text shown below edit and upload forms. Can be used to offer special
+ * characters not present on most keyboards for copying/pasting.
+ */
protected function showEditTools() {
global $wgOut;
$wgOut->addHTML( '<div class="mw-editTools">' .
$accesskey = null;
if ( isset( $options['tooltip'] ) ) {
$accesskey = $this->context->msg( "accesskey-{$options['tooltip']}" )->text();
- $title = Linker::titleAttrib( $options['tooltip'], 'withaccess' );
+ $title = Linker::titleAttrib( $options['tooltip'] );
}
if ( isset( $options['title-message'] ) ) {
$title = $this->context->msg( $options['title-message'] )->text();
new OOUI\CheckboxInputWidget( [
'tabIndex' => ++$tabindex,
'accessKey' => $accesskey,
- 'id' => $options['id'],
+ 'id' => $options['id'] . 'Widget',
+ 'inputId' => $options['id'],
'name' => $name,
'selected' => $options['default'],
'infusable' => true,
public function getEditButtons( &$tabindex ) {
$buttons = [];
- $buttonLabelKey = $this->getSaveButtonLabel();
+ $buttonLabel = $this->context->msg( $this->getSaveButtonLabel() )->text();
$attribs = [
- 'id' => 'wpSave',
'name' => 'wpSave',
'tabindex' => ++$tabindex,
- ] + Linker::tooltipAndAccesskeyAttribs( 'save' );
-
+ ];
if ( $this->oouiEnabled ) {
$saveConfig = OOUI\Element::configFromHtmlAttributes( $attribs );
$buttons['save'] = new OOUI\ButtonInputWidget( [
+ 'id' => 'wpSaveWidget',
+ 'inputId' => 'wpSave',
// Support: IE 6 – Use <input>, otherwise it can't distinguish which button was clicked
'useInputTag' => true,
'flags' => [ 'constructive', 'primary' ],
- 'label' => $this->context->msg( $buttonLabelKey )->text(),
+ 'label' => $buttonLabel,
'infusable' => true,
'type' => 'submit',
+ 'title' => Linker::titleAttrib( 'save' ),
+ 'accessKey' => Linker::accesskey( 'save' ),
] + $saveConfig );
} else {
$buttons['save'] = Html::submitButton(
- $this->context->msg( $buttonLabelKey )->text(),
- $attribs,
+ $buttonLabel,
+ $attribs + Linker::tooltipAndAccesskeyAttribs( 'save' ) + [ 'id' => 'wpSave' ],
[ 'mw-ui-progressive' ]
);
}
$attribs = [
- 'id' => 'wpPreview',
'name' => 'wpPreview',
'tabindex' => ++$tabindex,
- ] + Linker::tooltipAndAccesskeyAttribs( 'preview' );
+ ];
if ( $this->oouiEnabled ) {
$previewConfig = OOUI\Element::configFromHtmlAttributes( $attribs );
$buttons['preview'] = new OOUI\ButtonInputWidget( [
+ 'id' => 'wpPreviewWidget',
+ 'inputId' => 'wpPreview',
// Support: IE 6 – Use <input>, otherwise it can't distinguish which button was clicked
'useInputTag' => true,
'label' => $this->context->msg( 'showpreview' )->text(),
'infusable' => true,
- 'type' => 'submit'
+ 'type' => 'submit',
+ 'title' => Linker::titleAttrib( 'preview' ),
+ 'accessKey' => Linker::accesskey( 'preview' ),
] + $previewConfig );
} else {
$buttons['preview'] = Html::submitButton(
$this->context->msg( 'showpreview' )->text(),
- $attribs
+ $attribs + Linker::tooltipAndAccesskeyAttribs( 'preview' ) + [ 'id' => 'wpPreview' ]
);
}
$attribs = [
- 'id' => 'wpDiff',
'name' => 'wpDiff',
'tabindex' => ++$tabindex,
- ] + Linker::tooltipAndAccesskeyAttribs( 'diff' );
+ ];
if ( $this->oouiEnabled ) {
$diffConfig = OOUI\Element::configFromHtmlAttributes( $attribs );
$buttons['diff'] = new OOUI\ButtonInputWidget( [
+ 'id' => 'wpDiffWidget',
+ 'inputId' => 'wpDiff',
// Support: IE 6 – Use <input>, otherwise it can't distinguish which button was clicked
'useInputTag' => true,
'label' => $this->context->msg( 'showdiff' )->text(),
'infusable' => true,
'type' => 'submit',
+ 'title' => Linker::titleAttrib( 'diff' ),
+ 'accessKey' => Linker::accesskey( 'diff' ),
] + $diffConfig );
} else {
$buttons['diff'] = Html::submitButton(
$this->context->msg( 'showdiff' )->text(),
- $attribs
+ $attribs + Linker::tooltipAndAccesskeyAttribs( 'diff' ) + [ 'id' => 'wpDiff' ]
);
}
protected function addExplainConflictHeader( OutputPage $out ) {
$out->wrapWikiMsg(
"<div class='mw-explainconflict'>\n$1\n</div>",
- [ 'explainconflict', $this->getSaveButtonLabel() ]
+ [ 'explainconflict', $this->context->msg( $this->getSaveButtonLabel() )->text() ]
);
}
}
return $wikitext;
}
+
+ /**
+ * Turns section name wikitext into anchors for use in HTTP redirects. Various
+ * versions of Microsoft browsers misinterpret fragment encoding of Location: headers
+ * resulting in mojibake in address bar. Redirect them to legacy section IDs,
+ * if possible. All the other browsers get HTML5 if the wiki is configured for it, to
+ * spread the new style links more efficiently.
+ *
+ * @param string $text
+ * @return string
+ */
+ private function guessSectionName( $text ) {
+ global $wgParser;
+
+ // Detect Microsoft browsers
+ $userAgent = $this->context->getRequest()->getHeader( 'User-Agent' );
+ if ( $userAgent && preg_match( '/MSIE|Edge/', $userAgent ) ) {
+ // ...and redirect them to legacy encoding, if available
+ return $wgParser->guessLegacySectionNameFromWikiText( $text );
+ }
+ // Meanwhile, real browsers get real anchors
+ return $wgParser->guessSectionNameFromWikiText( $text );
+ }
}