Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
86.67% |
65 / 75 |
|
50.00% |
4 / 8 |
CRAP | |
0.00% |
0 / 1 |
ReviewTranslationActionApi | |
86.67% |
65 / 75 |
|
50.00% |
4 / 8 |
18.77 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
85.00% |
17 / 20 |
|
0.00% |
0 / 1 |
5.08 | |||
doReview | |
96.00% |
24 / 25 |
|
0.00% |
0 / 1 |
2 | |||
getReviewBlockers | |
84.62% |
11 / 13 |
|
0.00% |
0 / 1 |
6.13 | |||
isWriteMode | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
needsToken | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getAllowedParams | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
getExamplesMessages | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace MediaWiki\Extension\Translate\TranslatorInterface; |
5 | |
6 | use ManualLogEntry; |
7 | use MediaWiki\Api\ApiBase; |
8 | use MediaWiki\Api\ApiMain; |
9 | use MediaWiki\Extension\Translate\HookRunner; |
10 | use MediaWiki\Extension\Translate\MessageLoading\MessageHandle; |
11 | use MediaWiki\Revision\RevisionLookup; |
12 | use MediaWiki\Revision\RevisionRecord; |
13 | use MediaWiki\Status\Status; |
14 | use MediaWiki\Title\TitleFormatter; |
15 | use MediaWiki\User\User; |
16 | use Wikimedia\ParamValidator\ParamValidator; |
17 | use Wikimedia\Rdbms\ILoadBalancer; |
18 | |
19 | /** |
20 | * API module for marking translations as reviewed |
21 | * @author Niklas Laxström |
22 | * @license GPL-2.0-or-later |
23 | * @ingroup API TranslateAPI |
24 | */ |
25 | class ReviewTranslationActionApi extends ApiBase { |
26 | /** @var string */ |
27 | protected static $right = 'translate-messagereview'; |
28 | private RevisionLookup $revisionLookup; |
29 | private TitleFormatter $titleFormatter; |
30 | private ILoadBalancer $loadBalancer; |
31 | private HookRunner $hookRunner; |
32 | |
33 | public function __construct( |
34 | ApiMain $main, |
35 | string $moduleName, |
36 | RevisionLookup $revisionLookup, |
37 | TitleFormatter $titleFormatter, |
38 | ILoadBalancer $loadBalancer, |
39 | HookRunner $hookRunner |
40 | ) { |
41 | parent::__construct( $main, $moduleName ); |
42 | $this->revisionLookup = $revisionLookup; |
43 | $this->titleFormatter = $titleFormatter; |
44 | $this->loadBalancer = $loadBalancer; |
45 | $this->hookRunner = $hookRunner; |
46 | } |
47 | |
48 | public function execute() { |
49 | $this->checkUserRightsAny( self::$right ); |
50 | |
51 | $params = $this->extractRequestParams(); |
52 | |
53 | $revRecord = $this->revisionLookup->getRevisionById( $params['revision'] ); |
54 | if ( !$revRecord ) { |
55 | $this->dieWithError( [ 'apierror-nosuchrevid', $params['revision'] ], 'invalidrevision' ); |
56 | } |
57 | |
58 | $status = $this->getReviewBlockers( $this->getUser(), $revRecord ); |
59 | if ( !$status->isGood() ) { |
60 | if ( $status->hasMessage( 'blocked' ) ) { |
61 | $this->dieBlocked( $this->getUser()->getBlock() ); |
62 | } else { |
63 | $this->dieStatus( $status ); |
64 | } |
65 | } |
66 | |
67 | $ok = $this->doReview( $this->getUser(), $revRecord ); |
68 | if ( !$ok ) { |
69 | $this->addWarning( 'apiwarn-translate-alreadyreviewedbyyou' ); |
70 | } |
71 | |
72 | $prefixedText = $this->titleFormatter->getPrefixedText( $revRecord->getPageAsLinkTarget() ); |
73 | $output = [ 'review' => [ |
74 | 'title' => $prefixedText, |
75 | 'pageid' => $revRecord->getPageId(), |
76 | 'revision' => $revRecord->getId() |
77 | ] ]; |
78 | |
79 | $this->getResult()->addValue( null, $this->getModuleName(), $output ); |
80 | } |
81 | |
82 | /** |
83 | * Executes the real stuff. No checks done! |
84 | * @return bool whether the action was recorded. |
85 | */ |
86 | private function doReview( User $user, RevisionRecord $revRecord ): bool { |
87 | $dbw = $this->loadBalancer->getConnection( DB_PRIMARY ); |
88 | $dbw->newInsertQueryBuilder() |
89 | ->insertInto( 'translate_reviews' ) |
90 | ->ignore() |
91 | ->row( [ |
92 | 'trr_user' => $user->getId(), |
93 | 'trr_page' => $revRecord->getPageId(), |
94 | 'trr_revision' => $revRecord->getId(), |
95 | ] ) |
96 | ->caller( __METHOD__ ) |
97 | ->execute(); |
98 | |
99 | if ( !$dbw->affectedRows() ) { |
100 | return false; |
101 | } |
102 | |
103 | $title = $revRecord->getPageAsLinkTarget(); |
104 | |
105 | $entry = new ManualLogEntry( 'translationreview', 'message' ); |
106 | $entry->setPerformer( $user ); |
107 | $entry->setTarget( $title ); |
108 | $entry->setParameters( [ |
109 | '4::revision' => $revRecord->getId(), |
110 | ] ); |
111 | |
112 | $logid = $entry->insert(); |
113 | $entry->publish( $logid ); |
114 | |
115 | $handle = new MessageHandle( $title ); |
116 | $this->hookRunner->onTranslateEventTranslationReview( $handle ); |
117 | |
118 | return true; |
119 | } |
120 | |
121 | /** |
122 | * Validates review action by checking permissions and other things. |
123 | * @return Status Contains error key that describes the review blocker. |
124 | */ |
125 | private function getReviewBlockers( User $user, RevisionRecord $revRecord ): Status { |
126 | if ( !$user->isAllowed( self::$right ) ) { |
127 | return Status::newFatal( 'apierror-permissiondenied-generic' ); |
128 | } |
129 | |
130 | if ( $user->getBlock() ) { |
131 | return Status::newFatal( 'blocked' ); |
132 | } |
133 | |
134 | $title = $revRecord->getPageAsLinkTarget(); |
135 | $handle = new MessageHandle( $title ); |
136 | if ( !$handle->isValid() ) { |
137 | return Status::newFatal( 'apierror-translate-unknownmessage' ); |
138 | } |
139 | |
140 | if ( $user->equals( $revRecord->getUser() ) ) { |
141 | return Status::newFatal( 'apierror-translate-owntranslation' ); |
142 | } |
143 | |
144 | if ( $handle->isFuzzy() ) { |
145 | return Status::newFatal( 'apierror-translate-fuzzymessage' ); |
146 | } |
147 | |
148 | return Status::newGood(); |
149 | } |
150 | |
151 | public function isWriteMode(): bool { |
152 | return true; |
153 | } |
154 | |
155 | public function needsToken(): string { |
156 | return 'csrf'; |
157 | } |
158 | |
159 | protected function getAllowedParams(): array { |
160 | return [ |
161 | 'revision' => [ |
162 | ParamValidator::PARAM_TYPE => 'integer', |
163 | ParamValidator::PARAM_REQUIRED => true, |
164 | ], |
165 | ]; |
166 | } |
167 | |
168 | protected function getExamplesMessages(): array { |
169 | return [ |
170 | 'action=translationreview&revision=1&token=foo' |
171 | => 'apihelp-translationreview-example-1', |
172 | ]; |
173 | } |
174 | } |