40 private $readOnlyMode;
43 private $revisionLookup;
46 private $revisionRenderer;
67 parent::__construct( $page, $context );
68 $this->readOnlyMode = $readOnlyMode;
69 $this->revisionLookup = $revisionLookup;
70 $this->revisionRenderer = $revisionRenderer;
71 $this->useRCPatrol = $config->
get( MainConfigNames::UseRCPatrol );
90 MediaWiki\Session\SessionManager::getGlobalSession()->persist();
96 $out->setRobotPolicy(
'noindex,nofollow' );
97 if ( $this->
getContext()->getConfig()->
get( MainConfigNames::UseMediaWikiUIEverywhere ) ) {
98 $out->addModuleStyles( [
100 'mediawiki.ui.checkbox',
106 if ( $this->readOnlyMode->isReadOnly() ) {
108 "<div id=\"mw-read-only-warning\">\n$1\n</div>",
109 [
'readonlywarning', $this->readOnlyMode->getReason() ]
111 } elseif ( $this->context->getUser()->isAnon() ) {
114 if ( !$this->
getRequest()->getCheck(
'wpPreview' ) ) {
121 'returnto' => $this->getTitle()->getPrefixedDBkey()
125 'returnto' => $this->getTitle()->getPrefixedDBkey()
128 'mw-anon-edit-warning'
134 $out->msg(
'anonpreviewwarning' )->parse(),
135 'mw-anon-preview-warning'
145 $this->undoafter = $this->
getRequest()->getInt(
'undoafter' );
146 $this->undo = $this->
getRequest()->getInt(
'undo' );
148 if ( $this->undo == 0 || $this->undoafter == 0 ) {
149 throw new ErrorPageError(
'mcrundofailed',
'mcrundo-missingparam' );
157 $this->cur = $this->
getRequest()->getInt(
'cur', $this->curRev->getId() );
161 parent::checkCanExecute( $user );
167 $undoRev = $this->revisionLookup->getRevisionByTitle(
$title, $this->undo );
168 $oldRev = $this->revisionLookup->getRevisionByTitle(
$title, $this->undoafter );
170 if ( $undoRev ===
null || $oldRev ===
null ||
171 $undoRev->isDeleted( RevisionRecord::DELETED_TEXT ) ||
172 $oldRev->isDeleted( RevisionRecord::DELETED_TEXT )
183 private function getNewRevision() {
184 $undoRev = $this->revisionLookup->getRevisionById( $this->undo );
185 $oldRev = $this->revisionLookup->getRevisionById( $this->undoafter );
186 $curRev = $this->curRev;
188 $isLatest = $curRev->
getId() === $undoRev->getId();
190 if ( $undoRev ===
null || $oldRev ===
null ||
191 $undoRev->isDeleted( RevisionRecord::DELETED_TEXT ) ||
192 $oldRev->isDeleted( RevisionRecord::DELETED_TEXT )
199 return MutableRevisionRecord::newFromParentRevision( $oldRev );
202 $newRev = MutableRevisionRecord::newFromParentRevision( $curRev );
206 $rolesToMerge = array_unique( array_merge(
207 $oldRev->getSlotRoles(),
208 $undoRev->getSlotRoles(),
214 $rolesToMerge = array_intersect(
215 $rolesToMerge, $oldRev->getSlots()->getRolesWithDifferentContent( $undoRev->getSlots() )
217 if ( !$rolesToMerge ) {
223 $rolesToMerge = array_intersect(
224 $rolesToMerge, $oldRev->getSlots()->getRolesWithDifferentContent(
$curRev->
getSlots() )
226 if ( !$rolesToMerge ) {
232 $diffRoles = array_intersect(
233 $rolesToMerge, $undoRev->getSlots()->getRolesWithDifferentContent(
$curRev->
getSlots() )
235 foreach ( array_diff( $rolesToMerge, $diffRoles ) as $role ) {
236 if ( $oldRev->hasSlot( $role ) ) {
237 $newRev->inheritSlot( $oldRev->getSlot( $role, RevisionRecord::RAW ) );
239 $newRev->removeSlot( $role );
242 $rolesToMerge = $diffRoles;
253 foreach ( $rolesToMerge as $role ) {
254 if ( !$oldRev->hasSlot( $role ) || !$undoRev->hasSlot( $role ) || !
$curRev->
hasSlot( $role ) ) {
260 foreach ( $rolesToMerge as $role ) {
261 $oldContent = $oldRev->getSlot( $role, RevisionRecord::RAW )->getContent();
262 $undoContent = $undoRev->getSlot( $role, RevisionRecord::RAW )->getContent();
263 $curContent =
$curRev->
getSlot( $role, RevisionRecord::RAW )->getContent();
264 $newContent = $undoContent->getContentHandler()
265 ->getUndoContent( $curContent, $undoContent, $oldContent, $isLatest );
266 if ( !$newContent ) {
269 $newRev->setSlot( SlotRecord::newUnsaved( $role, $newContent ) );
275 private function generateDiffOrPreview() {
276 $newRev = $this->getNewRevision();
277 if ( $newRev->hasSameContent( $this->curRev ) ) {
282 $diffEngine->setRevisions( $this->curRev, $newRev );
284 $oldtitle = $this->context->msg(
'currentrev' )->parse();
285 $newtitle = $this->context->msg(
'yourtext' )->parse();
287 if ( $this->
getRequest()->getCheck(
'wpPreview' ) ) {
288 $this->showPreview( $newRev );
291 $diffText = $diffEngine->getDiff( $oldtitle, $newtitle );
292 $diffEngine->showDiffStyle();
293 return '<div id="wikiDiff">' . $diffText .
'</div>';
304 # provide a anchor link to the form
305 $continueEditing =
'<span class="mw-continue-editing">' .
306 '[[#mw-mcrundo-form|' .
307 $this->context->getLanguage()->getArrow() .
' ' .
308 $this->context->msg(
'continue-editing' )->text() .
']]</span>';
310 $note = $this->context->msg(
'previewnote' )->plain() .
' ' . $continueEditing;
312 $parserOptions = $this->
getWikiPage()->makeParserOptions( $this->context );
313 $parserOptions->setIsPreview(
true );
314 $parserOptions->setIsSectionPreview(
false );
316 $parserOutput = $this->revisionRenderer
317 ->getRenderedRevision( $rev, $parserOptions, $this->
getAuthority() )
318 ->getRevisionParserOutput();
319 $previewHTML = $parserOutput->getText( [
320 'enableSectionEditLinks' =>
false,
321 'includeDebugInfo' =>
true,
324 $out->addParserOutputMetadata( $parserOutput );
325 if ( count( $parserOutput->getWarnings() ) ) {
326 $note .=
"\n\n" . implode(
"\n\n", $parserOutput->getWarnings() );
329 $m = $this->context->msg(
330 'content-failed-to-parse',
333 $note .=
"\n\n" . $m->parse();
338 'div', [
'class' =>
'previewnote' ],
340 'h2', [
'id' =>
'mw-previewheader' ],
341 $this->context->msg(
'preview' )->text()
344 $out->parseAsInterface( $note )
348 $pageViewLang = $this->
getTitle()->getPageViewLanguage();
349 $attribs = [
'lang' => $pageViewLang->getHtmlCode(),
'dir' => $pageViewLang->getDir(),
350 'class' =>
'mw-content-' . $pageViewLang->getDir() ];
353 $out->addHTML( $previewhead . $previewHTML );
357 if ( !$this->
getRequest()->getCheck(
'wpSave' ) ) {
362 $updater = $this->
getWikiPage()->newPageUpdater( $this->context->getUser() );
363 $curRev = $updater->grabParentRevision();
369 return Status::newFatal(
'mcrundo-changed' );
374 if ( !$status->isOK() ) {
378 $newRev = $this->getNewRevision();
379 if ( !$newRev->hasSameContent(
$curRev ) ) {
380 $hookRunner = Hooks::runner();
381 foreach ( $newRev->getSlotRoles() as $slotRole ) {
382 $slot = $newRev->getSlot( $slotRole, RevisionRecord::RAW );
385 $hookResult = $hookRunner->onEditFilterMergedContent(
389 trim( $this->
getRequest()->getVal(
'wpSummary' ) ??
'' ),
394 if ( !$hookResult ) {
395 if ( $status->isGood() ) {
396 $status->error(
'hookaborted' );
400 } elseif ( !$status->isOK() ) {
401 if ( !$status->getErrors() ) {
402 $status->error(
'hookaborted' );
411 foreach ( $newRev->getSlots()->getSlots() as $slot ) {
412 $updater->setSlot( $slot );
415 if ( !$newRev->hasSlot( $role ) ) {
416 $updater->removeSlot( $role );
420 $updater->markAsRevert( EditResult::REVERT_UNDO, $this->undo, $this->undoafter );
423 ->authorizeWrite(
'autopatrol', $this->
getTitle() )
425 $updater->setRcPatrolStatus( RecentChange::PRC_AUTOPATROLLED );
428 $updater->saveRevision(
429 CommentStoreComment::newUnsavedComment(
430 trim( $this->
getRequest()->getVal(
'wpSummary' ) ??
'' ) ),
434 return $updater->getStatus();
437 return Status::newGood();
450 'default' =>
function () {
451 return $this->generateDiffOrPreview();
457 'name' =>
'wpSummary',
458 'cssclass' =>
'mw-summary',
459 'label-message' =>
'summary',
460 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT,
461 'value' => $request->getVal(
'wpSummary',
'' ),
463 'spellcheck' =>
'true',
465 'summarypreview' => [
467 'label-message' =>
'summary-preview',
472 if ( $request->getCheck(
'wpSummary' ) ) {
473 $ret[
'summarypreview'][
'default'] = Xml::tags(
'div', [
'class' =>
'mw-summary-preview' ],
477 unset( $ret[
'summarypreview'] );
486 $labelAsPublish = $this->context->getConfig()->get( MainConfigNames::EditSubmitButtonLabelPublish );
488 $form->
setId(
'mw-mcrundo-form' );
491 $form->
setSubmitTextMsg( $labelAsPublish ?
'publishchanges' :
'savechanges' );
495 'name' =>
'wpPreview',
497 'label-message' =>
'showpreview',
503 'label-message' =>
'showdiff',
521 return '<div style="clear:both"></div>';
getWikiPage()
Get a WikiPage object.
getOutput()
Get the OutputPage being used for this instance.
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
getRequest()
Get the WebRequest being used for this instance.
DifferenceEngine is responsible for rendering the difference between two revisions as HTML.
An error page which can definitely be safely rendered using the OutputPage.
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
static commentBlock( $comment, $title=null, $local=false, $wikiId=null, $useParentheses=true)
Wrap a comment in standard punctuation and formatting if it's non-empty, otherwise return empty strin...
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[], $options=null, $localizer=null, $user=null, $config=null, $relevantTitle=null)
Returns the attributes for the tooltip and access key.
Exception representing a failure to serialize or unserialize a content object.
Temporary action for MCR undos.
show()
The basic pattern for actions is to display some sort of HTMLForm UI, maybe with some stuff underneat...
onSuccess()
Do something exciting on successful processing of the form.
alterForm(HTMLForm $form)
Play with the HTMLForm if you need to more substantially.
checkCanExecute(User $user)
Checks if the given user (identified by an object) can perform this action.
onSubmit( $data)
Process the form on POST submission.
__construct(Page $page, IContextSource $context, ReadOnlyMode $readOnlyMode, RevisionLookup $revisionLookup, RevisionRenderer $revisionRenderer, Config $config)
RevisionRecord null $curRev
getDescription()
Returns the description that goes below the <h1> element.
preText()
Add pre- or post-text to the form.
addStatePropagationFields(HTMLForm $form)
getName()
Return the name of the action this object responds to.
getRestriction()
Get the permission required to perform this action.
usesOOUI()
Whether the form should use OOUI.
getFormFields()
Get an HTMLForm descriptor array.
A class containing constants representing the names of configuration variables.
Show an error when a user tries to do something they do not have the necessary permissions for.
A service class for fetching the wiki's current read-only mode.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Interface for configuration instances.
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
Interface for objects which can provide a MediaWiki context on request.
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)