Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
92.78% covered (success)
92.78%
90 / 97
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractEditEventRegistrationHandler
92.78% covered (success)
92.78%
90 / 97
75.00% covered (warning)
75.00%
3 / 4
11.05
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 validate
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 checkPermissions
n/a
0 / 0
n/a
0 / 0
0
 execute
66.67% covered (warning)
66.67%
14 / 21
0.00% covered (danger)
0.00%
0 / 1
8.81
 getSuccessResponse
n/a
0 / 0
n/a
0 / 0
0
 getBodyParamSettings
100.00% covered (success)
100.00%
66 / 66
100.00% covered (success)
100.00%
1 / 1
2
 createEventObject
n/a
0 / 0
n/a
0 / 0
0
1<?php
2
3declare( strict_types=1 );
4
5namespace MediaWiki\Extension\CampaignEvents\Rest;
6
7use MediaWiki\Config\Config;
8use MediaWiki\Extension\CampaignEvents\Event\EditEventCommand;
9use MediaWiki\Extension\CampaignEvents\Event\EventFactory;
10use MediaWiki\Extension\CampaignEvents\Event\EventRegistration;
11use MediaWiki\Extension\CampaignEvents\Event\InvalidEventDataException;
12use MediaWiki\Extension\CampaignEvents\MWEntity\CampaignsCentralUserLookup;
13use MediaWiki\Extension\CampaignEvents\MWEntity\ICampaignsAuthority;
14use MediaWiki\Extension\CampaignEvents\MWEntity\MWAuthorityProxy;
15use MediaWiki\Extension\CampaignEvents\MWEntity\UserNotGlobalException;
16use MediaWiki\Extension\CampaignEvents\MWEntity\WikiLookup;
17use MediaWiki\Extension\CampaignEvents\Organizers\OrganizersStore;
18use MediaWiki\Extension\CampaignEvents\Permissions\PermissionChecker;
19use MediaWiki\Extension\CampaignEvents\Questions\EventQuestionsRegistry;
20use MediaWiki\Permissions\PermissionStatus;
21use MediaWiki\Rest\Handler;
22use MediaWiki\Rest\Response;
23use MediaWiki\Rest\TokenAwareHandlerTrait;
24use MediaWiki\Rest\Validator\Validator;
25use RuntimeException;
26use StatusValue;
27use Wikimedia\ParamValidator\ParamValidator;
28use Wikimedia\ParamValidator\TypeDef\TimestampDef;
29
30abstract class AbstractEditEventRegistrationHandler extends Handler {
31    use TokenAwareHandlerTrait;
32    use FailStatusUtilTrait;
33
34    protected EventFactory $eventFactory;
35    protected PermissionChecker $permissionChecker;
36    protected EditEventCommand $editEventCommand;
37    private OrganizersStore $organizersStore;
38    private CampaignsCentralUserLookup $centralUserLookup;
39    protected EventQuestionsRegistry $eventQuestionsRegistry;
40    protected WikiLookup $wikiLookup;
41    protected bool $eventWikisEnabled;
42
43    public function __construct(
44        EventFactory $eventFactory,
45        PermissionChecker $permissionChecker,
46        EditEventCommand $editEventCommand,
47        OrganizersStore $organizersStore,
48        CampaignsCentralUserLookup $centralUserLookup,
49        EventQuestionsRegistry $eventQuestionsRegistry,
50        WikiLookup $wikiLookup,
51        Config $config
52    ) {
53        $this->eventFactory = $eventFactory;
54        $this->permissionChecker = $permissionChecker;
55        $this->editEventCommand = $editEventCommand;
56        $this->organizersStore = $organizersStore;
57        $this->centralUserLookup = $centralUserLookup;
58        $this->eventQuestionsRegistry = $eventQuestionsRegistry;
59        $this->wikiLookup = $wikiLookup;
60        $this->eventWikisEnabled = $config->get( 'CampaignEventsEnableEventWikis' );
61    }
62
63    /**
64     * @inheritDoc
65     */
66    public function validate( Validator $restValidator ): void {
67        parent::validate( $restValidator );
68        $this->validateToken();
69    }
70
71    /**
72     * @param ICampaignsAuthority $performer
73     */
74    abstract protected function checkPermissions( ICampaignsAuthority $performer ): void;
75
76    /**
77     * @inheritDoc
78     */
79    public function execute() {
80        $body = $this->getValidatedBody();
81
82        $performer = new MWAuthorityProxy( $this->getAuthority() );
83        $this->checkPermissions( $performer );
84
85        try {
86            $event = $this->createEventObject( $body );
87        } catch ( InvalidEventDataException $e ) {
88            $this->exitWithStatus( $e->getStatus() );
89        }
90
91        $eventID = $event->getID();
92        if ( $eventID === null ) {
93            $organizerNames = [ $this->getAuthority()->getUser()->getName() ];
94        } else {
95            $organizers = $this->organizersStore->getEventOrganizers( $eventID );
96            $organizerNames = [];
97            foreach ( $organizers as $organizer ) {
98                $user = $organizer->getUser();
99                try {
100                    $organizerNames[] = $this->centralUserLookup->getUserName( $user );
101                } catch ( UserNotGlobalException $_ ) {
102                    // Should never happen.
103                    throw new RuntimeException( "Organizer in the database has no central account." );
104                }
105            }
106        }
107
108        $saveStatus = $this->editEventCommand->doEditIfAllowed( $event, $performer, $organizerNames );
109        if ( !$saveStatus->isOK() ) {
110            $httptStatus = $saveStatus instanceof PermissionStatus ? 403 : 400;
111            $this->exitWithStatus( $saveStatus, $httptStatus );
112        }
113
114        return $this->getSuccessResponse( $saveStatus );
115    }
116
117    /**
118     * @param StatusValue $saveStatus
119     * @return Response
120     */
121    abstract protected function getSuccessResponse( StatusValue $saveStatus ): Response;
122
123    /**
124     * @return array
125     */
126    public function getBodyParamSettings(): array {
127        $params = [
128            'event_page' => [
129                static::PARAM_SOURCE => 'body',
130                ParamValidator::PARAM_TYPE => 'title',
131                ParamValidator::PARAM_REQUIRED => true,
132            ],
133            'chat_url' => [
134                static::PARAM_SOURCE => 'body',
135                ParamValidator::PARAM_TYPE => 'string',
136            ],
137            'tracking_tool_id' => [
138                static::PARAM_SOURCE => 'body',
139                ParamValidator::PARAM_TYPE => 'string',
140            ],
141            'tracking_tool_event_id' => [
142                static::PARAM_SOURCE => 'body',
143                ParamValidator::PARAM_TYPE => 'string',
144            ],
145            'timezone' => [
146                static::PARAM_SOURCE => 'body',
147                ParamValidator::PARAM_TYPE => 'string',
148                ParamValidator::PARAM_REQUIRED => true,
149            ],
150            'start_time' => [
151                static::PARAM_SOURCE => 'body',
152                ParamValidator::PARAM_TYPE => 'timestamp',
153                TimestampDef::PARAM_TIMESTAMP_FORMAT => TS_MW,
154                ParamValidator::PARAM_REQUIRED => true,
155            ],
156            'end_time' => [
157                static::PARAM_SOURCE => 'body',
158                ParamValidator::PARAM_TYPE => 'timestamp',
159                TimestampDef::PARAM_TIMESTAMP_FORMAT => TS_MW,
160                ParamValidator::PARAM_REQUIRED => true,
161            ],
162            /* TODO MVP: Re-add this
163            'type' => [
164                static::PARAM_SOURCE => 'body',
165                ParamValidator::PARAM_TYPE => EventRegistration::VALID_TYPES,
166                ParamValidator::PARAM_REQUIRED => true,
167            ],
168            */
169            'online_meeting' => [
170                static::PARAM_SOURCE => 'body',
171                ParamValidator::PARAM_TYPE => 'boolean',
172            ],
173            'inperson_meeting' => [
174                static::PARAM_SOURCE => 'body',
175                ParamValidator::PARAM_TYPE => 'boolean',
176            ],
177            'meeting_url' => [
178                static::PARAM_SOURCE => 'body',
179                ParamValidator::PARAM_TYPE => 'string',
180            ],
181            'meeting_country' => [
182                static::PARAM_SOURCE => 'body',
183                ParamValidator::PARAM_TYPE => 'string',
184            ],
185            'meeting_address' => [
186                static::PARAM_SOURCE => 'body',
187                ParamValidator::PARAM_TYPE => 'string',
188            ],
189        ] + $this->getTokenParamDefinition();
190
191        if ( $this->eventWikisEnabled ) {
192            $params['wikis'] = [
193                static::PARAM_SOURCE => 'body',
194                ParamValidator::PARAM_TYPE => $this->wikiLookup->getAllWikis(),
195                ParamValidator::PARAM_ISMULTI => true,
196                ParamValidator::PARAM_ISMULTI_LIMIT1 => EventFactory::MAX_WIKIS,
197                ParamValidator::PARAM_ALL => true,
198                ParamValidator::PARAM_REQUIRED => true,
199            ];
200        }
201
202        return $params;
203    }
204
205    /**
206     * Creates an EventRegistration object with the data from the request body, with
207     * appropriate validation.
208     *
209     * @param array $body Request body data
210     * @return EventRegistration
211     * @throws InvalidEventDataException
212     */
213    abstract protected function createEventObject( array $body ): EventRegistration;
214}