78 private $mergeHistoryFactory;
81 private $linkBatchFactory;
84 private $loadBalancer;
87 private $revisionStore;
90 private $commentFormatter;
106 parent::__construct(
'MergeHistory',
'mergehistory' );
107 $this->mergeHistoryFactory = $mergeHistoryFactory;
108 $this->linkBatchFactory = $linkBatchFactory;
109 $this->loadBalancer = $loadBalancer;
110 $this->revisionStore = $revisionStore;
111 $this->commentFormatter = $commentFormatter;
121 private function loadRequestParams() {
123 $this->mAction = $request->getRawVal(
'action' );
124 $this->mTarget = $request->getVal(
'target',
'' );
125 $this->mDest = $request->getVal(
'dest',
'' );
126 $this->mSubmitted = $request->getBool(
'submitted' );
128 $this->mTargetID = intval( $request->getVal(
'targetID' ) );
129 $this->mDestID = intval( $request->getVal(
'destID' ) );
130 $this->mTimestamp = $request->getVal(
'mergepoint' );
131 if ( $this->mTimestamp ===
null || !preg_match(
'/[0-9]{14}/', $this->mTimestamp ) ) {
132 $this->mTimestamp =
'';
134 $this->mComment = $request->getText(
'wpComment' );
136 $this->mMerge = $request->wasPosted()
137 && $this->
getUser()->matchEditToken( $request->getVal(
'wpEditToken' ) );
140 if ( $this->mSubmitted ) {
141 $this->mTargetObj = Title::newFromText( $this->mTarget );
142 $this->mDestObj = Title::newFromText( $this->mDest );
144 $this->mTargetObj =
null;
145 $this->mDestObj =
null;
155 $this->loadRequestParams();
160 if ( $this->mTargetID && $this->mDestID && $this->mAction ==
'submit' && $this->mMerge ) {
166 if ( !$this->mSubmitted ) {
167 $this->showMergeForm();
173 if ( !$this->mTargetObj instanceof
Title ) {
174 $errors[] = $this->
msg(
'mergehistory-invalid-source' )->parseAsBlock();
175 } elseif ( !$this->mTargetObj->exists() ) {
176 $errors[] = $this->
msg(
'mergehistory-no-source',
181 if ( !$this->mDestObj instanceof
Title ) {
182 $errors[] = $this->
msg(
'mergehistory-invalid-destination' )->parseAsBlock();
183 } elseif ( !$this->mDestObj->exists() ) {
184 $errors[] = $this->
msg(
'mergehistory-no-destination',
189 if ( $this->mTargetObj && $this->mDestObj && $this->mTargetObj->equals( $this->mDestObj ) ) {
190 $errors[] = $this->
msg(
'mergehistory-same-destination' )->parseAsBlock();
193 if ( count( $errors ) ) {
194 $this->showMergeForm();
195 $this->
getOutput()->addHTML( implode(
"\n", $errors ) );
197 $this->showHistory();
201 private function showMergeForm() {
203 $out->addWikiMsg(
'mergehistory-header' );
211 $this->
msg(
'mergehistory-box' )->text() ) .
212 Html::hidden(
'title', $this->
getPageTitle()->getPrefixedDBkey() ) .
213 Html::hidden(
'submitted',
'1' ) .
214 Html::hidden(
'mergepoint', $this->mTimestamp ) .
217 <td>' .
Xml::label( $this->
msg(
'mergehistory-from' )->text(),
'target' ) .
'</td>
218 <td>' .
Xml::input(
'target', 30, $this->mTarget, [
'id' =>
'target' ] ) .
'</td>
220 <td>' .
Xml::label( $this->
msg(
'mergehistory-into' )->text(),
'dest' ) .
'</td>
221 <td>' .
Xml::input(
'dest', 30, $this->mDest, [
'id' =>
'dest' ] ) .
'</td>
233 private function showHistory() {
234 $this->showMergeForm();
236 # List all stored revisions
239 $this->linkBatchFactory,
241 $this->revisionStore,
246 $haveRevisions = $revisions->getNumRows() > 0;
249 $out->addModuleStyles( [
'mediawiki.interface.helpers.styles' ] );
251 $action = $titleObj->getLocalURL( [
'action' =>
'submit' ] );
252 # Start the form here
261 $out->addHTML( $top );
263 if ( $haveRevisions ) {
264 # Format the user-visible controls (comment field, submission button)
265 # in a nice little table
268 $this->
msg(
'mergehistory-merge', $this->mTargetObj->getPrefixedText(),
269 $this->mDestObj->getPrefixedText() )->parse() .
272 <td class="mw-label">' .
273 Xml::label( $this->
msg(
'mergehistory-reason' )->text(),
'wpComment' ) .
275 <td class="mw-input">' .
276 Xml::input(
'wpComment', 50, $this->mComment, [
'id' =>
'wpComment' ] ) .
281 <td class=\"mw-submit\">" .
283 $this->
msg(
'mergehistory-submit' )->text(),
284 [
'name' =>
'merge',
'id' =>
'mw-merge-submit' ]
291 $out->addHTML( $table );
295 '<h2 id="mw-mergehistory">' .
296 $this->
msg(
'mergehistory-list' )->escaped() .
"</h2>\n"
299 if ( $haveRevisions ) {
300 $out->addHTML( $revisions->getNavigationBar() );
301 $out->addHTML( $revisions->getBody() );
302 $out->addHTML( $revisions->getNavigationBar() );
304 $out->addWikiMsg(
'mergehistory-empty' );
307 # Show relevant lines from the merge log:
308 $mergeLogPage =
new LogPage(
'merge' );
309 $out->addHTML(
'<h2>' . $mergeLogPage->getName()->escaped() .
"</h2>\n" );
312 # When we submit, go by page ID to avoid some nasty but unlikely collisions.
313 # Such would happen if a page was renamed after the form loaded, but before submit
314 $misc = Html::hidden(
'targetID', $this->mTargetObj->getArticleID() );
315 $misc .= Html::hidden(
'destID', $this->mDestObj->getArticleID() );
316 $misc .= Html::hidden(
'target', $this->mTarget );
317 $misc .= Html::hidden(
'dest', $this->mDest );
318 $misc .= Html::hidden(
'wpEditToken', $this->
getUser()->getEditToken() );
320 $out->addHTML( $misc );
326 $revRecord = $this->revisionStore->newRevisionFromRow( $row );
331 $last = $this->
msg(
'last' )->escaped();
334 $checkBox =
Xml::radio(
'mergepoint', $ts, ( $this->mTimestamp === $ts ) );
338 $pageLink = $linkRenderer->makeKnownLink(
339 $revRecord->getPageAsLinkTarget(),
340 $this->getLanguage()->userTimeAndDate( $ts, $user ),
342 [
'oldid' => $revRecord->getId() ]
344 if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
345 $class = Linker::getRevisionDeletedClass( $revRecord );
346 $pageLink =
'<span class=" ' . $class .
'">' . $pageLink .
'</span>';
350 if ( !$revRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
351 $last = $this->
msg(
'last' )->escaped();
352 } elseif ( isset( $this->prevId[$row->rev_id] ) ) {
353 $last = $linkRenderer->makeKnownLink(
354 $revRecord->getPageAsLinkTarget(),
355 $this->msg(
'last' )->text(),
358 'diff' => $row->rev_id,
359 'oldid' => $this->prevId[$row->rev_id]
364 $userLink = Linker::revUserTools( $revRecord );
366 $size = $row->rev_len;
367 if ( $size !==
null ) {
368 $stxt = Linker::formatRevisionSize( $size );
370 $comment = $this->commentFormatter->formatRevision( $revRecord, $user );
379 return Html::rawElement(
'li', $classes,
380 $this->
msg(
'mergehistory-revisionrow' )
381 ->rawParams( $checkBox, $last, $pageLink, $userLink, $stxt, $comment, $tagSummary )->escaped() );
396 private function merge() {
397 # Get the titles directly from the IDs, in case the target page params
398 # were spoofed. The queries are done based on the IDs, so it's best to
399 # keep it consistent...
400 $targetTitle = Title::newFromID( $this->mTargetID );
401 $destTitle = Title::newFromID( $this->mDestID );
402 if ( $targetTitle ===
null || $destTitle ===
null ) {
405 if ( $targetTitle->getArticleID() == $destTitle->getArticleID() ) {
410 $mh = $this->mergeHistoryFactory->newMergeHistory( $targetTitle, $destTitle, $this->mTimestamp );
413 $mergeStatus = $mh->merge( $this->
getAuthority(), $this->mComment );
414 if ( !$mergeStatus->isOK() ) {
416 $this->
getOutput()->addWikiMsg( $mergeStatus->getMessage() );
422 $targetLink = $linkRenderer->makeLink(
426 [
'redirect' =>
'no' ]
430 $append = ( $mergeStatus->getValue() ===
'source-deleted' )
431 ? $this->
msg(
'mergehistory-source-deleted', $targetTitle->getPrefixedText() ) :
'';
433 $this->
getOutput()->addWikiMsg( $this->
msg(
'mergehistory-done' )
434 ->rawParams( $targetLink )
435 ->params( $destTitle->getPrefixedText(), $append )
436 ->numParams( $mh->getMergedRevisionCount() )
wfScript( $script='index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Class to simplify the use of log pages.
Special page allowing users with the appropriate permissions to merge article histories,...
doesWrites()
Indicates whether this special page may perform database writes.
bool $mSubmitted
Was submitted?
__construct(MergeHistoryFactory $mergeHistoryFactory, LinkBatchFactory $linkBatchFactory, ILoadBalancer $loadBalancer, RevisionStore $revisionStore, CommentFormatter $commentFormatter)
execute( $par)
Default execute method Checks user permissions.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
Parent class for all special pages.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getOutput()
Get the OutputPage being used for this instance.
getUser()
Shortcut to get the User executing this instance.
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getAuthority()
Shortcut to get the Authority executing this instance.
getRequest()
Get the WebRequest being used for this instance.
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
getPageTitle( $subpage=false)
Get a self-referential title object.
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
static closeElement( $element)
Shortcut to close an XML element.
static label( $label, $id, $attribs=[])
Convenience function to build an HTML form label.
static openElement( $element, $attribs=null)
This opens an XML element.
static input( $name, $size=false, $value=false, $attribs=[])
Convenience function to build an HTML text input field.
static submitButton( $value, $attribs=[])
Convenience function to build an HTML submit button When $wgUseMediaWikiUIEverywhere is true it will ...
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
static radio( $name, $value, $checked=false, $attribs=[])
Convenience function to build an HTML radio button.
Service for mergehistory actions.