80 parent::__construct(
'MergeHistory',
'mergehistory' );
91 private function loadRequestParams() {
93 $this->mAction = $request->getRawVal(
'action' );
94 $this->mTarget = $request->getVal(
'target',
'' );
95 $this->mDest = $request->getVal(
'dest',
'' );
96 $this->mSubmitted = $request->getBool(
'submitted' );
98 $this->mTargetID = intval( $request->getVal(
'targetID' ) );
99 $this->mDestID = intval( $request->getVal(
'destID' ) );
100 $this->mTimestamp = $request->getVal(
'mergepoint' );
101 if ( $this->mTimestamp ===
null || !preg_match(
'/[0-9]{14}(\|[0-9]+)?/', $this->mTimestamp ) ) {
102 $this->mTimestamp =
'';
104 $this->mTimestampOld = $request->getVal(
'mergepointold' );
105 if ( $this->mTimestampOld ===
null || !preg_match(
'/[0-9]{14}(\|[0-9]+)?/', $this->mTimestamp ) ) {
106 $this->mTimestampOld =
'';
108 $this->mComment = $request->getText(
'wpComment' );
110 $this->mMerge = $request->wasPosted()
111 && $this->
getContext()->getCsrfTokenSet()->matchToken( $request->getVal(
'wpEditToken' ) );
114 if ( $this->mSubmitted ) {
115 $this->mTargetObj = Title::newFromText( $this->mTarget );
116 $this->mDestObj = Title::newFromText( $this->mDest );
118 $this->mTargetObj =
null;
119 $this->mDestObj =
null;
130 $this->loadRequestParams();
134 $status = Status::newGood();
136 if ( $this->mTargetID && $this->mDestID && $this->mAction ==
'submit' && $this->mMerge ) {
142 if ( !$this->mSubmitted ) {
143 $this->showMergeForm();
148 if ( !$this->mTargetObj instanceof
Title ) {
149 $status->merge( Status::newFatal(
'mergehistory-invalid-source' ) );
150 } elseif ( !$this->mTargetObj->exists() ) {
151 $status->merge( Status::newFatal(
152 'mergehistory-no-source',
157 if ( !$this->mDestObj instanceof
Title ) {
158 $status->merge( Status::newFatal(
'mergehistory-invalid-destination' ) );
159 } elseif ( !$this->mDestObj->exists() ) {
160 $status->merge( Status::newFatal(
161 'mergehistory-no-destination',
166 if ( $this->mTargetObj && $this->mDestObj && $this->mTargetObj->equals( $this->mDestObj ) ) {
167 $status->merge( Status::newFatal(
'mergehistory-same-destination' ) );
170 $this->mStatus = $status;
172 $this->showMergeForm();
174 if ( $this->mStatus->isGood() ) {
175 $this->showHistory();
179 private function showMergeForm() {
181 $out->addWikiMsg(
'mergehistory-header' );
187 'name' =>
'submitted'
197 'name' =>
'mergepoint'
202 'name' =>
'mergepointold'
206 'label-message' =>
'mergehistory-from',
213 'label-message' =>
'mergehistory-into',
220 $form = HTMLForm::factory(
'ooui', $fields, $this->
getContext() );
221 $form->setWrapperLegendMsg(
'mergehistory-box' )
222 ->setSubmitTextMsg(
'mergehistory-go' )
225 ->displayForm( $this->mStatus );
230 private function showHistory() {
231 # List all stored revisions
232 $revisions =
new MergeHistoryPager(
235 $this->linkBatchFactory,
237 $this->revisionStore,
238 $this->commentFormatter,
239 $this->changeTagsStore,
246 $haveRevisions = $revisions->getNumRows() > 0;
249 $out->addModuleStyles( [
250 'mediawiki.interface.helpers.styles',
253 $out->addModules(
'mediawiki.special.mergeHistory' );
255 $action = $titleObj->getLocalURL( [
'action' =>
'submit' ] );
256 # Start the form here
260 'name' =>
'targetID',
261 'default' => $this->mTargetObj->getArticleID()
266 'default' => $this->mDestObj->getArticleID()
279 if ( $haveRevisions ) {
283 'default' => $this->
msg(
'mergehistory-merge', $this->mTargetObj->getPrefixedText(),
284 $this->mDestObj->getPrefixedText() )->parse(),
286 'cssclass' =>
'mw-mergehistory-explanation',
287 'section' =>
'mergehistory-submit'
291 'name' =>
'wpComment',
292 'label-message' =>
'mergehistory-reason',
295 'section' =>
'mergehistory-submit'
299 'default' => $this->
msg(
'mergehistory-submit' ),
300 'section' =>
'mergehistory-submit',
301 'id' =>
'mw-merge-submit',
306 $form = HTMLForm::factory(
'ooui', $fields, $this->
getContext() );
307 $form->addHiddenField(
'wpEditToken', $form->getCsrfTokenSet()->getToken() )
309 ->setAction( $action )
310 ->suppressDefaultSubmit();
312 if ( $haveRevisions ) {
313 $form->setFooterHtml(
314 '<h2 id="mw-mergehistory">' . $this->
msg(
'mergehistory-list' )->escaped() .
'</h2>' .
315 $revisions->getNavigationBar() .
316 $revisions->getBody() .
317 $revisions->getNavigationBar()
320 $form->setFooterHtml( $this->
msg(
'mergehistory-empty' )->escaped() );
323 $form->prepareForm()->displayForm(
false );
325 # Show relevant lines from the merge log:
326 $mergeLogPage =
new LogPage(
'merge' );
327 $out->addHTML(
'<h2>' . $mergeLogPage->getName()->escaped() .
"</h2>\n" );
328 LogEventsList::showLogExtract( $out,
'merge', $this->mTargetObj );
343 private function merge() {
344 # Get the titles directly from the IDs, in case the target page params
345 # were spoofed. The queries are done based on the IDs, so it's best to
346 # keep it consistent...
347 $targetTitle = Title::newFromID( $this->mTargetID );
348 $destTitle = Title::newFromID( $this->mDestID );
349 if ( $targetTitle ===
null || $destTitle ===
null ) {
352 if ( $targetTitle->getArticleID() == $destTitle->getArticleID() ) {
357 $mh = $this->mergeHistoryFactory->newMergeHistory(
365 $mergeStatus = $mh->merge( $this->
getAuthority(), $this->mComment );
366 if ( !$mergeStatus->isOK() ) {
368 $this->
getOutput()->addWikiMsg( Status::wrap( $mergeStatus )->getMessage() );
374 $targetLink = $linkRenderer->makeLink(
378 [
'redirect' =>
'no' ]
382 $append = ( $mergeStatus->getValue() ===
'source-deleted' )
383 ? $this->
msg(
'mergehistory-source-deleted', $targetTitle->getPrefixedText() ) :
'';
385 $this->
getOutput()->addWikiMsg( $this->
msg(
'mergehistory-done' )
386 ->rawParams( $targetLink )
387 ->params( $destTitle->getPrefixedText(), $append )
388 ->numParams( $mh->getMergedRevisionCount() )
__construct(private readonly MergeHistoryFactory $mergeHistoryFactory, private readonly LinkBatchFactory $linkBatchFactory, private readonly IConnectionProvider $dbProvider, private readonly RevisionStore $revisionStore, private readonly CommentFormatter $commentFormatter, private readonly ChangeTagsStore $changeTagsStore,)