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    public $mGroupByDate = true;
44
45    /** @var array */
46    public $mConds;
47
48    /** @var int */
49    private $articleID;
50
51    /** @var string */
52    private $maxTimestamp;
53
54    /** @var string */
55    private $maxRevId;
56
57    /** @var string */
58    private $mergePointTimestamp;
59
60    /** @var int[] */
61    public $prevId;
62
63    private LinkBatchFactory $linkBatchFactory;
64    private RevisionStore $revisionStore;
65    private CommentFormatter $commentFormatter;
66
67    /**
68     * @param IContextSource $context
69     * @param LinkRenderer $linkRenderer
70     * @param LinkBatchFactory $linkBatchFactory
71     * @param IConnectionProvider $dbProvider
72     * @param RevisionStore $revisionStore
73     * @param CommentFormatter $commentFormatter
74     * @param array $conds
75     * @param PageIdentity $source
76     * @param PageIdentity $dest
77     * @param string $mergePointTimestamp
78     */
79    public function __construct(
80        IContextSource $context,
81        LinkRenderer $linkRenderer,
82        LinkBatchFactory $linkBatchFactory,
83        IConnectionProvider $dbProvider,
84        RevisionStore $revisionStore,
85        CommentFormatter $commentFormatter,
86        $conds,
87        PageIdentity $source,
88        PageIdentity $dest,
89        $mergePointTimestamp
90    ) {
91        $this->mConds = $conds;
92        $this->articleID = $source->getId();
93
94        $dbr = $dbProvider->getReplicaDatabase();
95        $maxtimestamp = $dbr->newSelectQueryBuilder()
96            ->select( 'MIN(rev_timestamp)' )
97            ->from( 'revision' )
98            ->where( [ 'rev_page' => $dest->getId() ] )
99            ->caller( __METHOD__ )->fetchField();
100        $maxRevId = $dbr->newSelectQueryBuilder()
101            ->select( "MIN(rev_id)" )
102            ->from( 'revision' )
103            ->where( [ 'rev_page' => $dest->getId() ] )
104            ->where( [ 'rev_timestamp' => $maxtimestamp ] )
105            ->caller( __METHOD__ )->fetchField();
106        $this->maxTimestamp = $maxtimestamp;
107        $this->maxRevId = $maxRevId;
108        $this->mergePointTimestamp = $mergePointTimestamp;
109
110        // Set database before parent constructor to avoid setting it there
111        $this->mDb = $dbr;
112        parent::__construct( $context, $linkRenderer );
113        $this->linkBatchFactory = $linkBatchFactory;
114        $this->revisionStore = $revisionStore;
115        $this->commentFormatter = $commentFormatter;
116    }
117
118    protected function doBatchLookups() {
119        # Do a link batch query
120        $this->mResult->seek( 0 );
121        $batch = $this->linkBatchFactory->newLinkBatch();
122        # Give some pointers to make (last) links
123        $this->prevId = [];
124        $rev_id = null;
125        foreach ( $this->mResult as $row ) {
126            $batch->add( NS_USER, $row->rev_user_text );
127            $batch->add( NS_USER_TALK, $row->rev_user_text );
128
129            if ( isset( $rev_id ) ) {
130                if ( $rev_id > $row->rev_id ) {
131                    $this->prevId[$rev_id] = $row->rev_id;
132                } elseif ( $rev_id < $row->rev_id ) {
133                    $this->prevId[$row->rev_id] = $rev_id;
134                }
135            }
136
137            $rev_id = $row->rev_id;
138        }
139
140        $batch->execute();
141        $this->mResult->seek( 0 );
142    }
143
144    /**
145     * @inheritDoc
146     */
147    protected function getStartBody() {
148        return "<section class='mw-pager-body'>\n";
149    }
150
151    /**
152     * @inheritDoc
153     */
154    protected function getEndBody() {
155        return "</section>\n";
156    }
157
158    public function formatRow( $row ) {
159        $revRecord = $this->revisionStore->newRevisionFromRow( $row );
160
161        $linkRenderer = $this->getLinkRenderer();
162
163        $stxt = '';
164        $last = $this->msg( 'last' )->escaped();
165
166        $ts = wfTimestamp( TS_MW, $row->rev_timestamp );
167        $tsWithId = $ts . "|" . $row->rev_id;
168        $checkBox = Xml::radio(
169            'mergepoint', $tsWithId,
170            $this->mergePointTimestamp === $ts || $this->mergePointTimestamp === $tsWithId
171        );
172
173        $user = $this->getUser();
174
175        $pageLink = $linkRenderer->makeKnownLink(
176            $revRecord->getPageAsLinkTarget(),
177            $this->getLanguage()->userTimeAndDate( $ts, $user ),
178            [],
179            [ 'oldid' => $revRecord->getId() ]
180        );
181        if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
182            $class = Linker::getRevisionDeletedClass( $revRecord );
183            $pageLink = '<span class=" ' . $class . '">' . $pageLink . '</span>';
184        }
185
186        # Last link
187        if ( !$revRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
188            $last = $this->msg( 'last' )->escaped();
189        } elseif ( isset( $this->prevId[$row->rev_id] ) ) {
190            $last = $linkRenderer->makeKnownLink(
191                $revRecord->getPageAsLinkTarget(),
192                $this->msg( 'last' )->text(),
193                [],
194                [
195                    'diff' => $row->rev_id,
196                    'oldid' => $this->prevId[$row->rev_id]
197                ]
198            );
199        }
200
201        $userLink = Linker::revUserTools( $revRecord );
202
203        $size = $row->rev_len;
204        if ( $size !== null ) {
205            $stxt = Linker::formatRevisionSize( $size );
206        }
207        $comment = $this->commentFormatter->formatRevision( $revRecord, $user );
208
209        // Tags, if any.
210        [ $tagSummary, $classes ] = ChangeTags::formatSummaryRow(
211            $row->ts_tags,
212            'mergehistory',
213            $this->getContext()
214        );
215
216        return Html::rawElement( 'li', $classes,
217            $this->msg( 'mergehistory-revisionrow' )
218                ->rawParams( $checkBox, $last, $pageLink, $userLink, $stxt, $comment, $tagSummary )->escaped() );
219    }
220
221    public function getQueryInfo() {
222        $dbr = $this->getDatabase();
223        $queryBuilder = $this->revisionStore->newSelectQueryBuilder( $dbr )
224            ->joinComment()
225            ->joinPage()
226            ->joinUser()
227            ->where( $this->mConds )
228            ->andWhere( [
229                'rev_page' => $this->articleID,
230                $dbr->buildComparison( "<",
231                    [
232                        "rev_timestamp" => $this->maxTimestamp,
233                        "rev_id" => $this->maxRevId
234                    ]
235                )
236            ] );
237        MediaWikiServices::getInstance()->getChangeTagsStore()->modifyDisplayQueryBuilder( $queryBuilder, 'revision' );
238
239        return $queryBuilder->getQueryInfo( 'join_conds' );
240    }
241
242    public function getIndexField() {
243        return [ [ 'rev_timestamp', 'rev_id' ] ];
244    }
245}
246
247/**
248 * Retain the old class name for backwards compatibility.
249 * @deprecated since 1.41
250 */
251class_alias( MergeHistoryPager::class, 'MergeHistoryPager' );