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