Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 118
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiPageTriageTagging
0.00% covered (danger)
0.00%
0 / 118
0.00% covered (danger)
0.00%
0 / 6
342
0.00% covered (danger)
0.00%
0 / 1
 execute
0.00% covered (danger)
0.00%
0 / 89
0.00% covered (danger)
0.00%
0 / 1
156
 needsToken
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
2
 mustBePosted
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isWriteMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTitleByPageId
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace MediaWiki\Extension\PageTriage\Api;
4
5use ApiBase;
6use ApiMain;
7use ApiUsageException;
8use ManualLogEntry;
9use MediaWiki\Extension\PageTriage\ArticleMetadata;
10use MediaWiki\Extension\PageTriage\PageTriageUtil;
11use MediaWiki\MediaWikiServices;
12use MediaWiki\Request\DerivativeRequest;
13use MediaWiki\Title\Title;
14use Wikimedia\ParamValidator\ParamValidator;
15
16class ApiPageTriageTagging extends ApiBase {
17
18    public function execute() {
19        $config = $this->getConfig();
20
21        $contLang = MediaWikiServices::getInstance()->getContentLanguage();
22
23        $params = $this->extractRequestParams();
24
25        if ( !ArticleMetadata::validatePageIds( [ $params['pageid'] ], DB_REPLICA ) ) {
26            $this->dieWithError( 'apierror-bad-pagetriage-page' );
27        }
28        $title = $this->getTitleByPageId( $params['pageid'] );
29
30        $this->checkTitleUserPermissions( $title, [ 'create', 'edit', 'patrol' ] );
31
32        if ( $this->getUser()->pingLimiter( 'pagetriage-tagging-action' ) ) {
33            $this->dieWithError( 'apierror-ratelimited' );
34        }
35
36        $apiParams = [
37            'text' => $params['wikitext']
38        ];
39
40        // Parse tags into a human readable list for the edit summary
41        $tags = $contLang->commaList( $params['taglist'] );
42
43        // Check if the page has been nominated for deletion
44        if ( $params['deletion'] ) {
45            $articleMetadata = new ArticleMetadata( [ $params['pageid'] ] );
46            $metaData = $articleMetadata->getMetadata();
47            if ( isset( $metaData[$params['pageid']] ) ) {
48                foreach ( [ 'csd_status', 'prod_status', 'blp_prod_status', 'afd_status' ] as $val ) {
49                    if ( $metaData[$params['pageid']][$val] == '1' ) {
50                        $this->dieWithError( 'pagetriage-tag-deletion-error' );
51                    }
52                }
53            } else {
54                $this->dieWithError( 'apierror-bad-pagetriage-page' );
55            }
56        }
57
58        $projectLink = '[['
59            . $config->get( 'PageTriageProjectLink' ) . '|'
60            . $this->msg( 'pagetriage-pagecuration' )->inContentLanguage()->plain()
61            . ']]';
62        if ( $params['deletion'] ) {
63            $editSummary = $this->msg(
64                'pagetriage-del-edit-summary',
65                $projectLink,
66                $tags
67            )->inContentLanguage()->plain();
68        } else {
69            $editSummary = $this->msg(
70                'pagetriage-tags-edit-summary',
71                $projectLink,
72                $tags
73            )->inContentLanguage()->plain();
74        }
75
76        // tagging something for deletion should automatically watchlist it
77        if ( $params['deletion'] ) {
78            $apiParams['watchlist'] = 'watch';
79        }
80
81        // Perform the text insertion
82        $api = new ApiMain(
83            new DerivativeRequest(
84                $this->getRequest(),
85                $apiParams + [
86                    'action' => 'edit',
87                    'title' => $title->getFullText(),
88                    'token' => $params['token'],
89                    'summary' => $editSummary,
90                    'tags' => 'pagetriage',
91                ],
92                true
93            ),
94            true
95        );
96
97        $api->execute();
98
99        $note = $contLang->truncateForDatabase( $params['note'], 150 );
100
101        // logging to the logging table
102        if ( $params['taglist'] ) {
103            if ( $params['deletion'] ) {
104                $action = 'delete';
105
106                PageTriageUtil::createNotificationEvent(
107                    $title,
108                    $this->getUser(),
109                    'pagetriage-add-deletion-tag',
110                    [
111                        'tags' => $params['taglist'],
112                        'note' => $note,
113                    ]
114                );
115            } else {
116                $action = 'tag';
117
118                PageTriageUtil::createNotificationEvent(
119                    $title,
120                    $this->getUser(),
121                    'pagetriage-add-maintenance-tag',
122                    [
123                        'tags' => $params['taglist'],
124                        'note' => $note,
125                        'revId' => $api->getResult()->getResultData( [ 'edit', 'newrevid' ] ),
126                    ]
127                );
128            }
129
130            $logEntry = new ManualLogEntry( 'pagetriage-curation', $action );
131            $logEntry->setPerformer( $this->getUser() );
132            $logEntry->setTarget( $title );
133            if ( $note ) {
134                $logEntry->setComment( $note );
135            }
136            $logEntry->setParameters( [
137                'tags' => $params['taglist']
138            ] );
139            $logEntry->addTags( 'pagetriage' );
140            $logEntry->publish( $logEntry->insert() );
141        }
142
143        $result = [ 'result' => 'success' ];
144        $this->getResult()->addValue( null, $this->getModuleName(), $result );
145    }
146
147    /** @inheritDoc */
148    public function needsToken() {
149        return 'csrf';
150    }
151
152    /**
153     * @inheritDoc
154     */
155    public function getAllowedParams() {
156        return [
157            'pageid' => [
158                ParamValidator::PARAM_REQUIRED => true,
159                ParamValidator::PARAM_TYPE => 'integer'
160            ],
161            'token' => [
162                ParamValidator::PARAM_REQUIRED => true
163            ],
164            'wikitext' => [
165                ParamValidator::PARAM_REQUIRED => true,
166                ParamValidator::PARAM_TYPE => 'string'
167            ],
168            'deletion' => [
169                ParamValidator::PARAM_REQUIRED => false,
170                ParamValidator::PARAM_TYPE => 'boolean'
171            ],
172            'note' => null,
173            'taglist' => [
174                ParamValidator::PARAM_REQUIRED => true,
175                ParamValidator::PARAM_ISMULTI => true
176            ],
177        ];
178    }
179
180    /** @inheritDoc */
181    public function mustBePosted() {
182        return true;
183    }
184
185    /** @inheritDoc */
186    public function isWriteMode() {
187        return true;
188    }
189
190    /**
191     * type helper for strict checks
192     * @param int $pageId
193     * @return Title
194     * @throws ApiUsageException
195     */
196    private function getTitleByPageId( $pageId ): Title {
197        $title = Title::newFromID( $pageId );
198        if ( $title === null ) {
199            $this->dieWithError( 'apierror-missingtitle', 'bad-page' );
200        }
201
202        // @phan-suppress-next-line PhanTypeMismatchReturnNullable Still T240141
203        return $title;
204    }
205}