Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 67
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiDiscussionToolsThank
0.00% covered (danger)
0.00%
0 / 67
0.00% covered (danger)
0.00%
0 / 3
182
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 52
0.00% covered (danger)
0.00%
0 / 1
132
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\DiscussionTools;
4
5use MediaWiki\Api\ApiBase;
6use MediaWiki\Api\ApiMain;
7use MediaWiki\Api\ApiUsageException;
8use MediaWiki\Extension\DiscussionTools\Hooks\HookUtils;
9use MediaWiki\Extension\DiscussionTools\ThreadItem\ContentCommentItem;
10use MediaWiki\Extension\Notifications\Model\Event;
11use MediaWiki\Extension\Thanks\Api\ApiThank;
12use MediaWiki\Extension\Thanks\Storage\LogStore;
13use MediaWiki\Extension\VisualEditor\ApiParsoidTrait;
14use MediaWiki\Permissions\PermissionManager;
15use MediaWiki\Revision\RevisionLookup;
16use MediaWiki\Title\Title;
17use MediaWiki\User\UserFactory;
18use Wikimedia\ParamValidator\ParamValidator;
19
20/**
21 * API module to send DiscussionTools comment thanks notifications
22 *
23 * @ingroup API
24 * @ingroup Extensions
25 */
26class ApiDiscussionToolsThank extends ApiThank {
27
28    use ApiDiscussionToolsTrait;
29    use ApiParsoidTrait;
30
31    public function __construct(
32        ApiMain $main,
33        string $action,
34        PermissionManager $permissionManager,
35        LogStore $storage,
36        private readonly RevisionLookup $revisionLookup,
37        private readonly UserFactory $userFactory,
38    ) {
39        parent::__construct( $main, $action, $permissionManager, $storage );
40    }
41
42    /**
43     * @inheritDoc
44     * @throws ApiUsageException
45     */
46    public function execute() {
47        $user = $this->getUser();
48        $this->dieOnBadUser( $user );
49        $this->dieOnUserBlockedFromThanks( $user );
50
51        $params = $this->extractRequestParams();
52
53        $title = Title::newFromText( $params['page'] );
54        $commentId = $params['commentid'];
55
56        if ( !$title ) {
57            $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['page'] ) ] );
58        }
59
60        // TODO: Using the data in the permalinks database would be much
61        // faster, we just wouldn't have the comment content.
62
63        // Support oldid?
64        $revision = $this->revisionLookup->getRevisionByTitle( $title );
65        if ( !$revision ) {
66            throw ApiUsageException::newWithMessage(
67                $this,
68                [ 'apierror-missingrev-title', wfEscapeWikiText( $title->getPrefixedText() ) ],
69                'nosuchrevid'
70            );
71        }
72        $status = HookUtils::parseRevisionParsoidHtml( $revision, __METHOD__ );
73        if ( !$status->isOK() ) {
74            $this->dieStatus( $status );
75        }
76        $threadItemSet = $status->getValueOrThrow();
77
78        $comment = $threadItemSet->findCommentById( $commentId );
79
80        if ( !$comment || !( $comment instanceof ContentCommentItem ) ) {
81            $this->dieWithError( [ 'apierror-discussiontools-commentid-notfound', $commentId ] );
82        }
83
84        if ( $user->getRequest()->getSessionData( "discussiontools-thanked-{$comment->getId()}" ) ) {
85            $this->markResultSuccess( $comment->getAuthor() );
86            return;
87        }
88
89        $uniqueId = "discussiontools-{$comment->getId()}";
90        // Do one last check to make sure we haven't sent Thanks before
91        if ( $this->haveAlreadyThanked( $user, $uniqueId ) ) {
92            // Pretend the thanks were sent
93            $this->markResultSuccess( $comment->getAuthor() );
94            return;
95        }
96
97        $recipient = $this->userFactory->newFromName( $comment->getAuthor() );
98        if ( !$recipient || !$recipient->getId() ) {
99            $this->dieWithError( 'thanks-error-invalidrecipient', 'invalidrecipient' );
100        }
101
102        $this->dieOnBadRecipient( $user, $recipient );
103
104        $heading = $comment->getSubscribableHeading();
105        if ( !$heading ) {
106            $heading = $comment->getHeading();
107        }
108
109        // Create the notification via Echo extension
110        Event::create( [
111            'type' => 'dt-thank',
112            'title' => $title,
113            'extra' => [
114                'comment-id' => $comment->getId(),
115                'comment-name' => $comment->getName(),
116                'content' => $comment->getBodyText( true ),
117                'section-title' => $heading->getLinkableTitle(),
118                'thanked-user-id' => $recipient->getId(),
119                'revid' => $revision->getId(),
120            ],
121            'agent' => $user,
122        ] );
123
124        // And mark the thank in session for a cheaper check to prevent duplicates (T48690).
125        $user->getRequest()->setSessionData( "discussiontools-thanked-{$comment->getId()}", true );
126        // Set success message.
127        $this->markResultSuccess( $recipient->getName() );
128        $this->logThanks( $user, $recipient, $uniqueId );
129    }
130
131    /**
132     * @inheritDoc
133     */
134    public function getAllowedParams() {
135        return [
136            'page' => [
137                ParamValidator::PARAM_REQUIRED => true,
138                // Message will exist if DiscussionTools is installed as VE is a dependency
139                ApiBase::PARAM_HELP_MSG => 'apihelp-visualeditoredit-param-page',
140            ],
141            'commentid' => [
142                ParamValidator::PARAM_REQUIRED => true,
143                ParamValidator::PARAM_TYPE => 'string',
144            ],
145            'token' => [
146                ParamValidator::PARAM_REQUIRED => true,
147                ParamValidator::PARAM_TYPE => 'string',
148            ],
149        ];
150    }
151}