Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
85.48% covered (warning)
85.48%
106 / 124
57.14% covered (warning)
57.14%
4 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiQueryWatchlistRaw
86.18% covered (warning)
86.18%
106 / 123
57.14% covered (warning)
57.14%
4 / 7
31.22
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 executeGenerator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 run
82.76% covered (warning)
82.76%
48 / 58
0.00% covered (danger)
0.00%
0 / 1
25.71
 getAllowedParams
100.00% covered (success)
100.00%
51 / 51
100.00% covered (success)
100.00%
1 / 1
1
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getHelpUrls
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Copyright © 2008 Roan Kattouw <roan.kattouw@gmail.com>
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 */
8
9namespace MediaWiki\Api;
10
11use MediaWiki\Cache\GenderCache;
12use MediaWiki\Language\Language;
13use MediaWiki\ParamValidator\TypeDef\UserDef;
14use MediaWiki\Title\NamespaceInfo;
15use MediaWiki\Title\Title;
16use MediaWiki\Title\TitleValue;
17use MediaWiki\Watchlist\WatchedItemQueryService;
18use MediaWiki\Watchlist\WatchedItemStore;
19use Wikimedia\ParamValidator\ParamValidator;
20use Wikimedia\ParamValidator\TypeDef\IntegerDef;
21use Wikimedia\Timestamp\TimestampFormat as TS;
22
23/**
24 * This query action allows clients to retrieve a list of pages
25 * on the logged-in user's watchlist.
26 *
27 * @ingroup API
28 */
29class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase {
30
31    private WatchedItemQueryService $watchedItemQueryService;
32    private Language $contentLanguage;
33    private NamespaceInfo $namespaceInfo;
34    private GenderCache $genderCache;
35
36    public function __construct(
37        ApiQuery $query,
38        string $moduleName,
39        WatchedItemQueryService $watchedItemQueryService,
40        Language $contentLanguage,
41        NamespaceInfo $namespaceInfo,
42        GenderCache $genderCache
43    ) {
44        parent::__construct( $query, $moduleName, 'wr' );
45        $this->watchedItemQueryService = $watchedItemQueryService;
46        $this->contentLanguage = $contentLanguage;
47        $this->namespaceInfo = $namespaceInfo;
48        $this->genderCache = $genderCache;
49    }
50
51    public function execute() {
52        $this->run();
53    }
54
55    /** @inheritDoc */
56    public function executeGenerator( $resultPageSet ) {
57        $this->run( $resultPageSet );
58    }
59
60    /**
61     * @param ApiPageSet|null $resultPageSet
62     * @return void
63     */
64    private function run( $resultPageSet = null ) {
65        $params = $this->extractRequestParams();
66
67        $user = $this->getWatchlistUser( $params );
68
69        $prop = array_fill_keys( (array)$params['prop'], true );
70        $show = array_fill_keys( (array)$params['show'], true );
71        if ( isset( $show[WatchedItemQueryService::FILTER_CHANGED] )
72            && isset( $show[WatchedItemQueryService::FILTER_NOT_CHANGED] )
73        ) {
74            $this->dieWithError( 'apierror-show' );
75        }
76
77        $options = [];
78        if ( $params['namespace'] ) {
79            $options['namespaceIds'] = $params['namespace'];
80        }
81        if ( isset( $show[WatchedItemQueryService::FILTER_CHANGED] ) ) {
82            $options['filter'] = WatchedItemQueryService::FILTER_CHANGED;
83        }
84        if ( isset( $show[WatchedItemQueryService::FILTER_NOT_CHANGED] ) ) {
85            $options['filter'] = WatchedItemQueryService::FILTER_NOT_CHANGED;
86        }
87
88        if ( isset( $params['continue'] ) ) {
89            $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'int', 'string' ] );
90            $options['startFrom'] = TitleValue::tryNew( $cont[0], $cont[1] );
91            $this->dieContinueUsageIf( !$options['startFrom'] );
92        }
93
94        if ( isset( $params['fromtitle'] ) ) {
95            $options['from'] = $this->parsePrefixedTitlePart( $params['fromtitle'] );
96        }
97
98        if ( isset( $params['totitle'] ) ) {
99            $options['until'] = $this->parsePrefixedTitlePart( $params['totitle'] );
100        }
101
102        $options['sort'] = WatchedItemStore::SORT_ASC;
103        if ( $params['dir'] === 'descending' ) {
104            $options['sort'] = WatchedItemStore::SORT_DESC;
105        }
106        $options['limit'] = $params['limit'] + 1;
107
108        $titles = [];
109        $count = 0;
110        $items = $this->watchedItemQueryService->getWatchedItemsForUser( $user, $options );
111
112        // Get gender information
113        if ( $items !== [] && $resultPageSet === null &&
114            $this->contentLanguage->needsGenderDistinction()
115        ) {
116            $usernames = [];
117            foreach ( $items as $item ) {
118                $target = $item->getTarget();
119                if ( $this->namespaceInfo->hasGenderDistinction( $target->getNamespace() ) ) {
120                    // GenderCache is able to deal with underscores in usernames
121                    $usernames[] = $target->getDBkey();
122                }
123            }
124            if ( $usernames !== [] ) {
125                $this->genderCache->doQuery( $usernames, __METHOD__ );
126            }
127        }
128
129        foreach ( $items as $item ) {
130            $ns = $item->getTarget()->getNamespace();
131            $dbKey = $item->getTarget()->getDBkey();
132            if ( ++$count > $params['limit'] ) {
133                // We've reached the one extra which shows that there are
134                // additional pages to be had. Stop here...
135                $this->setContinueEnumParameter( 'continue', $ns . '|' . $dbKey );
136                break;
137            }
138            $t = Title::makeTitle( $ns, $dbKey );
139
140            if ( $resultPageSet === null ) {
141                $vals = [];
142                ApiQueryBase::addTitleInfo( $vals, $t );
143                if ( isset( $prop['changed'] ) && $item->getNotificationTimestamp() !== null ) {
144                    $vals['changed'] = wfTimestamp( TS::ISO_8601, $item->getNotificationTimestamp() );
145                }
146                $fit = $this->getResult()->addValue( $this->getModuleName(), null, $vals );
147                if ( !$fit ) {
148                    $this->setContinueEnumParameter( 'continue', $ns . '|' . $dbKey );
149                    break;
150                }
151            } else {
152                $titles[] = $t;
153            }
154        }
155        if ( $resultPageSet === null ) {
156            $this->getResult()->addIndexedTagName( $this->getModuleName(), 'wr' );
157        } else {
158            $resultPageSet->populateFromTitles( $titles );
159        }
160    }
161
162    /** @inheritDoc */
163    public function getAllowedParams() {
164        return [
165            'continue' => [
166                ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
167            ],
168            'namespace' => [
169                ParamValidator::PARAM_ISMULTI => true,
170                ParamValidator::PARAM_TYPE => 'namespace'
171            ],
172            'limit' => [
173                ParamValidator::PARAM_DEFAULT => 10,
174                ParamValidator::PARAM_TYPE => 'limit',
175                IntegerDef::PARAM_MIN => 1,
176                IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
177                IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2
178            ],
179            'prop' => [
180                ParamValidator::PARAM_ISMULTI => true,
181                ParamValidator::PARAM_TYPE => [
182                    'changed',
183                ],
184                ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
185            ],
186            'show' => [
187                ParamValidator::PARAM_ISMULTI => true,
188                ParamValidator::PARAM_TYPE => [
189                    WatchedItemQueryService::FILTER_CHANGED,
190                    WatchedItemQueryService::FILTER_NOT_CHANGED
191                ]
192            ],
193            'owner' => [
194                ParamValidator::PARAM_TYPE => 'user',
195                UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name' ],
196            ],
197            'token' => [
198                ParamValidator::PARAM_TYPE => 'string',
199                ParamValidator::PARAM_SENSITIVE => true,
200            ],
201            'dir' => [
202                ParamValidator::PARAM_DEFAULT => 'ascending',
203                ParamValidator::PARAM_TYPE => [
204                    'ascending',
205                    'descending'
206                ],
207            ],
208            'fromtitle' => [
209                ParamValidator::PARAM_TYPE => 'string'
210            ],
211            'totitle' => [
212                ParamValidator::PARAM_TYPE => 'string'
213            ],
214        ];
215    }
216
217    /** @inheritDoc */
218    protected function getExamplesMessages() {
219        return [
220            'action=query&list=watchlistraw'
221                => 'apihelp-query+watchlistraw-example-simple',
222            'action=query&generator=watchlistraw&gwrshow=changed&prop=info'
223                => 'apihelp-query+watchlistraw-example-generator',
224        ];
225    }
226
227    /** @inheritDoc */
228    public function getHelpUrls() {
229        return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Watchlistraw';
230    }
231}
232
233/** @deprecated class alias since 1.43 */
234class_alias( ApiQueryWatchlistRaw::class, 'ApiQueryWatchlistRaw' );