Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 103
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
MergeHistoryPager
0.00% covered (danger)
0.00%
0 / 102
0.00% covered (danger)
0.00%
0 / 7
272
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
2
 doBatchLookups
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
30
 getStartBody
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getEndBody
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 formatRow
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 1
42
 getQueryInfo
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
2
 getIndexField
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 * @ingroup Pager
20 */
21
22namespace MediaWiki\Pager;
23
24use ChangeTags;
25use MediaWiki\Cache\LinkBatchFactory;
26use MediaWiki\CommentFormatter\CommentFormatter;
27use MediaWiki\Context\IContextSource;
28use MediaWiki\Html\Html;
29use MediaWiki\Linker\Linker;
30use MediaWiki\Linker\LinkRenderer;
31use MediaWiki\MediaWikiServices;
32use MediaWiki\Page\PageIdentity;
33use MediaWiki\Revision\RevisionRecord;
34use MediaWiki\Revision\RevisionStore;
35use MediaWiki\Xml\Xml;
36use Wikimedia\Rdbms\IConnectionProvider;
37
38/**
39 * @ingroup Pager
40 */
41class MergeHistoryPager extends ReverseChronologicalPager {
42
43    /** @inheritDoc */
44    public $mGroupByDate = true;
45
46    /** @var array */
47    public $mConds;
48
49    /** @var int */
50    private $articleID;
51
52    /** @var string */
53    private $maxTimestamp;
54
55    /** @var string */
56    private $maxRevId;
57
58    /** @var string */
59    private $mergePointTimestamp;
60
61    /** @var int[] */
62    public $prevId;
63
64    private LinkBatchFactory $linkBatchFactory;
65    private RevisionStore $revisionStore;
66    private CommentFormatter $commentFormatter;
67
68    /**
69     * @param IContextSource $context
70     * @param LinkRenderer $linkRenderer
71     * @param LinkBatchFactory $linkBatchFactory
72     * @param IConnectionProvider $dbProvider
73     * @param RevisionStore $revisionStore
74     * @param CommentFormatter $commentFormatter
75     * @param array $conds
76     * @param PageIdentity $source
77     * @param PageIdentity $dest
78     * @param string $mergePointTimestamp
79     */
80    public function __construct(
81        IContextSource $context,
82        LinkRenderer $linkRenderer,
83        LinkBatchFactory $linkBatchFactory,
84        IConnectionProvider $dbProvider,
85        RevisionStore $revisionStore,
86        CommentFormatter $commentFormatter,
87        $conds,
88        PageIdentity $source,
89        PageIdentity $dest,
90        $mergePointTimestamp
91    ) {
92        $this->mConds = $conds;
93        $this->articleID = $source->getId();
94
95        $dbr = $dbProvider->getReplicaDatabase();
96        $maxtimestamp = $dbr->newSelectQueryBuilder()
97            ->select( 'MIN(rev_timestamp)' )
98            ->from( 'revision' )
99            ->where( [ 'rev_page' => $dest->getId() ] )
100            ->caller( __METHOD__ )->fetchField();
101        $maxRevId = $dbr->newSelectQueryBuilder()
102            ->select( "MIN(rev_id)" )
103            ->from( 'revision' )
104            ->where( [ 'rev_page' => $dest->getId() ] )
105            ->where( [ 'rev_timestamp' => $maxtimestamp ] )
106            ->caller( __METHOD__ )->fetchField();
107        $this->maxTimestamp = $maxtimestamp;
108        $this->maxRevId = $maxRevId;
109        $this->mergePointTimestamp = $mergePointTimestamp;
110
111        // Set database before parent constructor to avoid setting it there
112        $this->mDb = $dbr;
113        parent::__construct( $context, $linkRenderer );
114        $this->linkBatchFactory = $linkBatchFactory;
115        $this->revisionStore = $revisionStore;
116        $this->commentFormatter = $commentFormatter;
117    }
118
119    protected function doBatchLookups() {
120        # Do a link batch query
121        $this->mResult->seek( 0 );
122        $batch = $this->linkBatchFactory->newLinkBatch();
123        # Give some pointers to make (last) links
124        $this->prevId = [];
125        $rev_id = null;
126        foreach ( $this->mResult as $row ) {
127            $batch->add( NS_USER, $row->rev_user_text );
128            $batch->add( NS_USER_TALK, $row->rev_user_text );
129
130            if ( $rev_id !== null ) {
131                if ( $rev_id > $row->rev_id ) {
132                    $this->prevId[$rev_id] = $row->rev_id;
133                } elseif ( $rev_id < $row->rev_id ) {
134                    $this->prevId[$row->rev_id] = $rev_id;
135                }
136            }
137
138            $rev_id = $row->rev_id;
139        }
140
141        $batch->execute();
142        $this->mResult->seek( 0 );
143    }
144
145    /**
146     * @inheritDoc
147     */
148    protected function getStartBody() {
149        return "<section class='mw-pager-body'>\n";
150    }
151
152    /**
153     * @inheritDoc
154     */
155    protected function getEndBody() {
156        return "</section>\n";
157    }
158
159    public function formatRow( $row ) {
160        $revRecord = $this->revisionStore->newRevisionFromRow( $row );
161
162        $linkRenderer = $this->getLinkRenderer();
163
164        $stxt = '';
165        $last = $this->msg( 'last' )->escaped();
166
167        $ts = wfTimestamp( TS_MW, $row->rev_timestamp );
168        $tsWithId = $ts . "|" . $row->rev_id;
169        $checkBox = Xml::radio(
170            'mergepoint', $tsWithId,
171            $this->mergePointTimestamp === $ts || $this->mergePointTimestamp === $tsWithId
172        );
173
174        $user = $this->getUser();
175
176        $pageLink = $linkRenderer->makeKnownLink(
177            $revRecord->getPageAsLinkTarget(),
178            $this->getLanguage()->userTimeAndDate( $ts, $user ),
179            [],
180            [ 'oldid' => $revRecord->getId() ]
181        );
182        if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
183            $class = Linker::getRevisionDeletedClass( $revRecord );
184            $pageLink = '<span class=" ' . $class . '">' . $pageLink . '</span>';
185        }
186
187        # Last link
188        if ( !$revRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
189            $last = $this->msg( 'last' )->escaped();
190        } elseif ( isset( $this->prevId[$row->rev_id] ) ) {
191            $last = $linkRenderer->makeKnownLink(
192                $revRecord->getPageAsLinkTarget(),
193                $this->msg( 'last' )->text(),
194                [],
195                [
196                    'diff' => $row->rev_id,
197                    'oldid' => $this->prevId[$row->rev_id]
198                ]
199            );
200        }
201
202        $userLink = Linker::revUserTools( $revRecord );
203
204        $size = $row->rev_len;
205        if ( $size !== null ) {
206            $stxt = Linker::formatRevisionSize( $size );
207        }
208        $comment = $this->commentFormatter->formatRevision( $revRecord, $user );
209
210        // Tags, if any.
211        [ $tagSummary, $classes ] = ChangeTags::formatSummaryRow(
212            $row->ts_tags,
213            'mergehistory',
214            $this->getContext()
215        );
216
217        return Html::rawElement( 'li', $classes,
218            $this->msg( 'mergehistory-revisionrow' )
219                ->rawParams( $checkBox, $last, $pageLink, $userLink, $stxt, $comment, $tagSummary )->escaped() );
220    }
221
222    public function getQueryInfo() {
223        $dbr = $this->getDatabase();
224        $queryBuilder = $this->revisionStore->newSelectQueryBuilder( $dbr )
225            ->joinComment()
226            ->joinPage()
227            ->joinUser()
228            ->where( $this->mConds )
229            ->andWhere( [
230                'rev_page' => $this->articleID,
231                $dbr->buildComparison( "<",
232                    [
233                        "rev_timestamp" => $this->maxTimestamp,
234                        "rev_id" => $this->maxRevId
235                    ]
236                )
237            ] );
238        MediaWikiServices::getInstance()->getChangeTagsStore()->modifyDisplayQueryBuilder( $queryBuilder, 'revision' );
239
240        return $queryBuilder->getQueryInfo( 'join_conds' );
241    }
242
243    public function getIndexField() {
244        return [ [ 'rev_timestamp', 'rev_id' ] ];
245    }
246}
247
248/**
249 * Retain the old class name for backwards compatibility.
250 * @deprecated since 1.41
251 */
252class_alias( MergeHistoryPager::class, 'MergeHistoryPager' );