Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
81.08% covered (warning)
81.08%
60 / 74
66.67% covered (warning)
66.67%
4 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
EmailUsersHandler
81.08% covered (warning)
81.08%
60 / 74
66.67% covered (warning)
66.67%
4 / 6
15.33
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 run
63.89% covered (warning)
63.89%
23 / 36
0.00% covered (danger)
0.00%
0 / 1
11.01
 validate
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getParamSettings
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getBodyValidator
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
2.01
 getBodyParams
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare( strict_types=1 );
4
5namespace MediaWiki\Extension\CampaignEvents\Rest;
6
7use MediaWiki\DAO\WikiAwareEntity;
8use MediaWiki\Extension\CampaignEvents\Event\Store\IEventLookup;
9use MediaWiki\Extension\CampaignEvents\Messaging\CampaignsUserMailer;
10use MediaWiki\Extension\CampaignEvents\MWEntity\MWAuthorityProxy;
11use MediaWiki\Extension\CampaignEvents\Participants\ParticipantsStore;
12use MediaWiki\Extension\CampaignEvents\Permissions\PermissionChecker;
13use MediaWiki\Rest\LocalizedHttpException;
14use MediaWiki\Rest\Response;
15use MediaWiki\Rest\SimpleHandler;
16use MediaWiki\Rest\TokenAwareHandlerTrait;
17use MediaWiki\Rest\Validator\JsonBodyValidator;
18use MediaWiki\Rest\Validator\UnsupportedContentTypeBodyValidator;
19use MediaWiki\Rest\Validator\Validator;
20use Wikimedia\Message\MessageValue;
21use Wikimedia\ParamValidator\ParamValidator;
22
23class EmailUsersHandler extends SimpleHandler {
24    use EventIDParamTrait;
25    use TokenAwareHandlerTrait;
26    use FailStatusUtilTrait;
27
28    private CampaignsUserMailer $userMailer;
29    private PermissionChecker $permissionChecker;
30    private ParticipantsStore $participantsStore;
31    private IEventLookup $eventLookup;
32
33    /**
34     * @param PermissionChecker $permissionChecker
35     * @param CampaignsUserMailer $userMailer
36     * @param ParticipantsStore $participantsStore
37     * @param IEventLookup $eventLookup
38     */
39    public function __construct(
40        PermissionChecker $permissionChecker,
41        CampaignsUserMailer $userMailer,
42        ParticipantsStore $participantsStore,
43        IEventLookup $eventLookup
44    ) {
45        $this->permissionChecker = $permissionChecker;
46        $this->userMailer = $userMailer;
47        $this->participantsStore = $participantsStore;
48        $this->eventLookup = $eventLookup;
49    }
50
51    /**
52     * @param int $eventId
53     * @return Response
54     */
55    public function run( int $eventId ): Response {
56        $event = $this->getRegistrationOrThrow( $this->eventLookup, $eventId );
57        $performer = new MWAuthorityProxy( $this->getAuthority() );
58        $params = $this->getValidatedBody() ?? [];
59
60        if ( !$this->permissionChecker->userCanEmailParticipants( $performer, $eventId ) ) {
61            // todo add more details to error message
62            return $this->getResponseFactory()->createHttpError( 403 );
63        }
64
65        $wikiID = $event->getPage()->getWikiId();
66        if ( $wikiID !== WikiAwareEntity::LOCAL ) {
67            throw new LocalizedHttpException(
68                MessageValue::new( 'campaignevents-rest-email-participants-nonlocal-error-message' )
69                    ->params( $wikiID ),
70                400
71            );
72        }
73
74        $userIds = $params['user_ids'] ? array_map( 'intval', $params['user_ids'] ) : [];
75        $participants = $this->participantsStore->getEventParticipants(
76            $eventId,
77            null,
78            null,
79            null,
80            $params['invert_users'] ? null : $userIds,
81            true,
82            $params['invert_users'] ? $userIds : null
83        );
84        if ( !$participants ) {
85            return $this->getResponseFactory()->createJson( [ 'sent' => 0 ] );
86        }
87        $result = $this->userMailer->sendEmail(
88            $this->getAuthority(),
89            $participants,
90            $params['subject'],
91            $params['message'],
92            $event
93        );
94
95        if ( !$result->isGood() ) {
96            $this->exitWithStatus( $result );
97        }
98        $resp = $this->getResponseFactory()->createJson( [ 'sent' => $result->getValue() ] );
99        $resp->setStatus( 202 );
100        return $resp;
101    }
102
103    /**
104     * @inheritDoc
105     */
106    public function validate( Validator $restValidator ): void {
107        parent::validate( $restValidator );
108        $this->validateToken();
109    }
110
111    /**
112     * @inheritDoc
113     */
114    public function getParamSettings(): array {
115        return $this->getIDParamSetting();
116    }
117
118    /**
119     * @inheritDoc
120     */
121    public function getBodyValidator( $contentType ) {
122        if ( $contentType !== 'application/json' ) {
123            return new UnsupportedContentTypeBodyValidator( $contentType );
124        }
125
126        return new JsonBodyValidator(
127            array_merge(
128                $this->getBodyParams(),
129                $this->getTokenParamDefinition()
130            )
131        );
132    }
133
134    /**
135     * @return array
136     */
137    private function getBodyParams(): array {
138        return [
139                'user_ids' => [
140                    static::PARAM_SOURCE => 'body',
141                    ParamValidator::PARAM_TYPE => 'array',
142                    ParamValidator::PARAM_REQUIRED => false,
143                ],
144                'invert_users' => [
145                    static::PARAM_SOURCE => 'body',
146                    ParamValidator::PARAM_TYPE => 'bool',
147                    ParamValidator::PARAM_REQUIRED => false,
148                    ParamValidator::PARAM_DEFAULT => false,
149                ],
150                'message' => [
151                    static::PARAM_SOURCE => 'body',
152                    ParamValidator::PARAM_TYPE => 'string',
153                    ParamValidator::PARAM_REQUIRED => true,
154                ],
155                'subject' => [
156                    static::PARAM_SOURCE => 'body',
157                    ParamValidator::PARAM_TYPE => 'string',
158                    ParamValidator::PARAM_REQUIRED => true,
159                ]
160            ];
161    }
162}