Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 97
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
UnreviewedPagesPager
0.00% covered (danger)
0.00%
0 / 97
0.00% covered (danger)
0.00%
0 / 9
650
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
 setLimit
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 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getQueryInfo
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
56
 getQueryCacheInfo
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
42
 getIndexField
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 doBatchLookups
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 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
1<?php
2
3use MediaWiki\MediaWikiServices;
4use MediaWiki\Pager\AlphabeticPager;
5
6/**
7 * Query to list out unreviewed pages
8 */
9class UnreviewedPagesPager extends AlphabeticPager {
10    /** @var UnreviewedPages */
11    private $mForm;
12
13    /** @var bool */
14    private $live;
15
16    /** @var int */
17    private $namespace;
18
19    /** @var string|null */
20    private $category;
21
22    /** @var bool */
23    private $showredirs;
24
25    /** @var int */
26    private $level;
27
28    // Don't get too expensive
29    private const PAGE_LIMIT = 50;
30
31    /**
32     * @param UnreviewedPages $form
33     * @param bool $live
34     * @param int|null $namespace
35     * @param bool $redirs
36     * @param string|null $category
37     * @param int $level
38     */
39    public function __construct(
40        $form, $live, $namespace, $redirs = false, $category = null, $level = 0
41    ) {
42        $this->mForm = $form;
43        $this->live = (bool)$live;
44        # Must be a content page...
45        if ( $namespace !== null ) {
46            $namespace = (int)$namespace;
47        }
48        # Must be a single NS for performance reasons
49        if ( $namespace === null || !FlaggedRevs::isReviewNamespace( $namespace ) ) {
50            $namespace = FlaggedRevs::getFirstReviewNamespace();
51        }
52        $this->namespace = $namespace;
53        $this->category = $category ? str_replace( ' ', '_', $category ) : null;
54        $this->level = intval( $level );
55        $this->showredirs = (bool)$redirs;
56        parent::__construct();
57        // Don't get too expensive
58        $this->mLimitsShown = [ 20, 50 ];
59        $this->setLimit( $this->mLimit ); // apply max limit
60    }
61
62    /**
63     * @inheritDoc
64     */
65    public function setLimit( $limit ) {
66        $this->mLimit = min( $limit, self::PAGE_LIMIT );
67    }
68
69    /**
70     * @inheritDoc
71     */
72    public function formatRow( $row ) {
73        return $this->mForm->formatRow( $row );
74    }
75
76    /**
77     * @inheritDoc
78     */
79    public function getQueryInfo() {
80        if ( !$this->live ) {
81            return $this->getQueryCacheInfo();
82        }
83        $fields = [ 'page_namespace', 'page_title', 'page_len', 'page_id',
84            'creation' => 'MIN(rev_timestamp)' ];
85        $groupBy = [ 'page_namespace', 'page_title', 'page_len', 'page_id' ];
86        # Filter by level
87        $conds = [];
88        if ( $this->level == 1 ) {
89            $conds[] = $this->mDb->expr( 'fp_page_id', '=', null )->or( 'fp_quality', '=', 0 );
90        } else {
91            $conds['fp_page_id'] = null;
92        }
93        # Reviewable pages only
94        $conds['page_namespace'] = $this->namespace;
95        # No redirects
96        if ( !$this->showredirs ) {
97            $conds['page_is_redirect'] = 0;
98        }
99        # Filter by category
100        if ( $this->category != '' ) {
101            $tables = [ 'categorylinks', 'page', 'flaggedpages', 'revision' ];
102            $fields[] = 'cl_sortkey';
103            $groupBy[] = 'cl_sortkey';
104            $conds['cl_to'] = $this->category;
105            $conds[] = 'cl_from = page_id';
106            # Note: single NS always specified
107            if ( $this->namespace === NS_FILE ) {
108                $conds['cl_type'] = 'file';
109            } elseif ( $this->namespace === NS_CATEGORY ) {
110                $conds['cl_type'] = 'subcat';
111            } else {
112                $conds['cl_type'] = 'page';
113            }
114            $this->mIndexField = 'cl_sortkey';
115            $useIndex = [ 'categorylinks' => 'cl_sortkey' ];
116        } else {
117            $tables = [ 'page', 'flaggedpages', 'revision' ];
118            $this->mIndexField = 'page_title';
119            $useIndex = [ 'page' => 'page_name_title' ];
120        }
121        $useIndex['revision'] = 'rev_page_timestamp';
122        return [
123            'tables'  => $tables,
124            'fields'  => $fields,
125            'conds'   => $conds,
126            'options' => [ 'USE INDEX' => $useIndex, 'GROUP BY' => $groupBy ],
127            'join_conds' => [
128                'revision'     => [ 'LEFT JOIN', 'rev_page=page_id' ], // Get creation date
129                'flaggedpages' => [ 'LEFT JOIN', 'fp_page_id=page_id' ]
130            ]
131        ];
132    }
133
134    /**
135     * @return array
136     */
137    private function getQueryCacheInfo() {
138        $conds = [];
139        $fields = [ 'page_namespace', 'page_title', 'page_len', 'page_id',
140            'qc_value', 'creation' => 'MIN(rev_timestamp)' ];
141        # Re-join on flaggedpages to double-check since things
142        # could have changed since the cache date. Also, use
143        # the proper cache for this level.
144        if ( $this->level == 1 ) {
145            $conds['qc_type'] = 'fr_unreviewedpages_q';
146            $conds[] = $this->mDb->expr( 'fp_page_id', '=', null )->or( 'fp_quality', '<', 1 );
147        } else {
148            $conds['qc_type'] = 'fr_unreviewedpages';
149            $conds['fp_page_id'] = null;
150        }
151        # Reviewable pages only
152        $conds['qc_namespace'] = $this->namespace;
153        # No redirects
154        if ( !$this->showredirs ) {
155            $conds['page_is_redirect'] = 0;
156        }
157        $this->mIndexField = 'qc_value'; // page_id
158        # Filter by category
159        if ( $this->category != '' ) {
160            $tables = [ 'page', 'categorylinks', 'querycache', 'flaggedpages', 'revision' ];
161            $conds['cl_to'] = $this->category;
162            $conds[] = 'cl_from = qc_value'; // page_id
163            # Note: single NS always specified
164            if ( $this->namespace === NS_FILE ) {
165                $conds['cl_type'] = 'file';
166            } elseif ( $this->namespace === NS_CATEGORY ) {
167                $conds['cl_type'] = 'subcat';
168            } else {
169                $conds['cl_type'] = 'page';
170            }
171        } else {
172            $tables = [ 'page', 'querycache', 'flaggedpages', 'revision' ];
173        }
174
175        $useIndex = [ 'querycache' => 'qc_type', 'page' => 'PRIMARY', 'revision' => 'rev_page_timestamp' ];
176        return [
177            'tables'  => $tables,
178            'fields'  => $fields,
179            'conds'   => $conds,
180            'options' => [ 'USE INDEX' => $useIndex, 'GROUP BY' => 'qc_value' ],
181            'join_conds' => [
182                'querycache'    => [ 'LEFT JOIN', 'qc_value=page_id' ],
183                'revision'      => [ 'LEFT JOIN', 'rev_page=page_id' ], // Get creation date
184                'flaggedpages'  => [ 'LEFT JOIN', 'fp_page_id=page_id' ],
185                'categorylinks' => [ 'LEFT JOIN',
186                    [ 'cl_from=page_id', 'cl_to' => $this->category ] ]
187            ]
188        ];
189    }
190
191    /**
192     * @inheritDoc
193     */
194    public function getIndexField() {
195        return $this->mIndexField;
196    }
197
198    /**
199     * @inheritDoc
200     */
201    protected function doBatchLookups() {
202        $lb = MediaWikiServices::getInstance()->getLinkBatchFactory()->newLinkBatch();
203        foreach ( $this->mResult as $row ) {
204            $lb->add( $row->page_namespace, $row->page_title );
205        }
206        $lb->execute();
207    }
208
209    /**
210     * @return string HTML
211     */
212    protected function getStartBody() {
213        return '<ul>';
214    }
215
216    /**
217     * @return string HTML
218     */
219    protected function getEndBody() {
220        return '</ul>';
221    }
222}