Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
84.71% |
72 / 85 |
|
44.44% |
4 / 9 |
CRAP | |
0.00% |
0 / 1 |
ApiFlowThank | |
84.71% |
72 / 85 |
|
44.44% |
4 / 9 |
15.80 | |
0.00% |
0 / 1 |
execute | |
88.57% |
31 / 35 |
|
0.00% |
0 / 1 |
3.01 | |||
userAlreadySentThanksForId | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFlowData | |
71.43% |
5 / 7 |
|
0.00% |
0 / 1 |
3.21 | |||
getRecipientFromPost | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
getPageTitleFromRootPost | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getTopicTitleFromRootPost | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
sendThanks | |
90.00% |
18 / 20 |
|
0.00% |
0 / 1 |
2.00 | |||
getAllowedParams | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
1 | |||
getExamplesMessages | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\Thanks\Api; |
4 | |
5 | use ApiBase; |
6 | use Flow\Container; |
7 | use Flow\Conversion\Utils; |
8 | use Flow\Exception\FlowException; |
9 | use Flow\Model\PostRevision; |
10 | use Flow\Model\UUID; |
11 | use MediaWiki\Extension\Notifications\Model\Event; |
12 | use MediaWiki\Title\Title; |
13 | use MediaWiki\User\User; |
14 | use Wikimedia\ParamValidator\ParamValidator; |
15 | |
16 | /** |
17 | * API module to send Flow thanks notifications |
18 | * |
19 | * This API does not prevent sending thanks using post IDs that refer to topic |
20 | * titles, though Thank buttons are only shown for comments in the UI. |
21 | * |
22 | * @ingroup API |
23 | * @ingroup Extensions |
24 | */ |
25 | |
26 | class ApiFlowThank extends ApiThank { |
27 | |
28 | public function execute() { |
29 | $user = $this->getUser(); |
30 | $this->dieOnBadUser( $user ); |
31 | $this->dieOnUserBlockedFromThanks( $user ); |
32 | |
33 | $params = $this->extractRequestParams(); |
34 | |
35 | try { |
36 | $postId = UUID::create( $params['postid'] ); |
37 | } catch ( FlowException $e ) { |
38 | $this->dieWithError( 'thanks-error-invalidpostid', 'invalidpostid' ); |
39 | } |
40 | |
41 | $data = $this->getFlowData( $postId ); |
42 | |
43 | $recipient = $this->getRecipientFromPost( $data['post'] ); |
44 | $this->dieOnBadRecipient( $user, $recipient ); |
45 | |
46 | if ( $this->userAlreadySentThanksForId( $user, $postId ) ) { |
47 | $this->markResultSuccess( $recipient->getName() ); |
48 | return; |
49 | } |
50 | |
51 | $rootPost = $data['root']; |
52 | $workflowId = $rootPost->getPostId(); |
53 | $rawTopicTitleText = Utils::htmlToPlaintext( |
54 | Container::get( 'templating' )->getContent( $rootPost, 'topic-title-html' ) |
55 | ); |
56 | // Truncate the title text to prevent issues with database storage. |
57 | $topicTitleText = $this->getLanguage()->truncateForDatabase( $rawTopicTitleText, 200 ); |
58 | $pageTitle = $this->getPageTitleFromRootPost( $rootPost ); |
59 | $this->dieOnUserBlockedFromTitle( $user, $pageTitle ); |
60 | |
61 | /** @var PostRevision $post */ |
62 | $post = $data['post']; |
63 | $postText = Utils::htmlToPlaintext( $post->getContent() ); |
64 | $postText = $this->getLanguage()->truncateForDatabase( $postText, 200 ); |
65 | |
66 | $topicTitle = $this->getTopicTitleFromRootPost( $rootPost ); |
67 | |
68 | $this->sendThanks( |
69 | $user, |
70 | $recipient, |
71 | $postId, |
72 | $workflowId, |
73 | $topicTitleText, |
74 | $pageTitle, |
75 | $postText, |
76 | $topicTitle |
77 | ); |
78 | } |
79 | |
80 | private function userAlreadySentThanksForId( User $user, UUID $id ) { |
81 | return $user->getRequest()->getSessionData( "flow-thanked-{$id->getAlphadecimal()}" ); |
82 | } |
83 | |
84 | /** |
85 | * @param UUID $postId UUID of the post to thank for |
86 | * @return array containing 'post' and 'root' as keys |
87 | */ |
88 | private function getFlowData( UUID $postId ) { |
89 | $rootPostLoader = Container::get( 'loader.root_post' ); |
90 | '@phan-var \Flow\Repository\RootPostLoader $rootPostLoader'; |
91 | |
92 | try { |
93 | $data = $rootPostLoader->getWithRoot( $postId ); |
94 | } catch ( FlowException $e ) { |
95 | $this->dieWithError( 'thanks-error-invalidpostid', 'invalidpostid' ); |
96 | } |
97 | |
98 | if ( $data['post'] === null ) { |
99 | $this->dieWithError( 'thanks-error-invalidpostid', 'invalidpostid' ); |
100 | } |
101 | // @phan-suppress-next-line PhanTypeMismatchReturnNullable T240141 |
102 | return $data; |
103 | } |
104 | |
105 | /** |
106 | * @param PostRevision $post |
107 | * @return User |
108 | */ |
109 | private function getRecipientFromPost( PostRevision $post ) { |
110 | $recipient = User::newFromId( $post->getCreatorId() ); |
111 | if ( !$recipient->loadFromId() ) { |
112 | $this->dieWithError( 'thanks-error-invalidrecipient', 'invalidrecipient' ); |
113 | } |
114 | return $recipient; |
115 | } |
116 | |
117 | /** |
118 | * @param PostRevision $rootPost |
119 | * @return Title |
120 | */ |
121 | private function getPageTitleFromRootPost( PostRevision $rootPost ) { |
122 | $workflow = Container::get( 'storage' )->get( 'Workflow', $rootPost->getPostId() ); |
123 | return $workflow->getOwnerTitle(); |
124 | } |
125 | |
126 | /** |
127 | * @param PostRevision $rootPost |
128 | * @return Title |
129 | */ |
130 | private function getTopicTitleFromRootPost( PostRevision $rootPost ) { |
131 | $workflow = Container::get( 'storage' )->get( 'Workflow', $rootPost->getPostId() ); |
132 | return $workflow->getArticleTitle(); |
133 | } |
134 | |
135 | /** |
136 | * @param User $user |
137 | * @param User $recipient |
138 | * @param UUID $postId |
139 | * @param UUID $workflowId |
140 | * @param string $topicTitleText |
141 | * @param Title $pageTitle |
142 | * @param string $postTextExcerpt |
143 | * @param Title $topicTitle |
144 | * @throws FlowException |
145 | */ |
146 | private function sendThanks( |
147 | User $user, |
148 | User $recipient, |
149 | UUID $postId, |
150 | UUID $workflowId, |
151 | $topicTitleText, |
152 | Title $pageTitle, |
153 | $postTextExcerpt, |
154 | Title $topicTitle |
155 | ) { |
156 | $uniqueId = 'flow-' . $postId->getAlphadecimal(); |
157 | // Do one last check to make sure we haven't sent Thanks before |
158 | if ( $this->haveAlreadyThanked( $user, $uniqueId ) ) { |
159 | // Pretend the thanks were sent |
160 | $this->markResultSuccess( $recipient->getName() ); |
161 | return; |
162 | } |
163 | |
164 | // Create the notification via Echo extension |
165 | Event::create( [ |
166 | 'type' => 'flow-thank', |
167 | 'title' => $pageTitle, |
168 | 'extra' => [ |
169 | 'post-id' => $postId->getAlphadecimal(), |
170 | 'workflow' => $workflowId->getAlphadecimal(), |
171 | 'thanked-user-id' => $recipient->getId(), |
172 | 'topic-title' => $topicTitleText, |
173 | 'excerpt' => $postTextExcerpt, |
174 | 'target-page' => $topicTitle->getArticleID(), |
175 | ], |
176 | 'agent' => $user, |
177 | ] ); |
178 | |
179 | // And mark the thank in session for a cheaper check to prevent duplicates (T48690). |
180 | $user->getRequest()->setSessionData( "flow-thanked-{$postId->getAlphadecimal()}", true ); |
181 | // Set success message. |
182 | $this->markResultSuccess( $recipient->getName() ); |
183 | $this->logThanks( $user, $recipient, $uniqueId ); |
184 | } |
185 | |
186 | public function getAllowedParams() { |
187 | return [ |
188 | 'postid' => [ |
189 | ParamValidator::PARAM_TYPE => 'string', |
190 | ParamValidator::PARAM_REQUIRED => true, |
191 | ], |
192 | 'token' => [ |
193 | ParamValidator::PARAM_TYPE => 'string', |
194 | ParamValidator::PARAM_REQUIRED => true, |
195 | ], |
196 | ]; |
197 | } |
198 | |
199 | /** |
200 | * @see ApiBase::getExamplesMessages() |
201 | * @return array |
202 | */ |
203 | protected function getExamplesMessages() { |
204 | return [ |
205 | 'action=flowthank&postid=xyz789&token=123ABC' |
206 | => 'apihelp-flowthank-example-1', |
207 | ]; |
208 | } |
209 | } |