Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
42.00% covered (danger)
42.00%
21 / 50
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
PageMoveAndDeleteHandler
42.00% covered (danger)
42.00%
21 / 50
75.00% covered (warning)
75.00%
3 / 4
20.49
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 onPageDeleteComplete
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
 onTitleMove
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 onPageMoveComplete
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3declare( strict_types=1 );
4
5namespace MediaWiki\Extension\CampaignEvents\Hooks\Handlers;
6
7use IDBAccessObject;
8use ManualLogEntry;
9use MediaWiki\Extension\CampaignEvents\Event\DeleteEventCommand;
10use MediaWiki\Extension\CampaignEvents\Event\ExistingEventRegistration;
11use MediaWiki\Extension\CampaignEvents\Event\PageEventLookup;
12use MediaWiki\Extension\CampaignEvents\Event\Store\IEventStore;
13use MediaWiki\Extension\CampaignEvents\MWEntity\CampaignsPageFactory;
14use MediaWiki\Hook\PageMoveCompleteHook;
15use MediaWiki\Hook\TitleMoveHook;
16use MediaWiki\Page\Hook\PageDeleteCompleteHook;
17use MediaWiki\Page\ProperPageIdentity;
18use MediaWiki\Permissions\Authority;
19use MediaWiki\Revision\RevisionRecord;
20use MediaWiki\Status\Status;
21use MediaWiki\Title\Title;
22use MediaWiki\Title\TitleFormatter;
23use MediaWiki\User\User;
24
25/**
26 * This handler is used for page move and deletion. If the page is an event page, we update the registration:
27 *  - Delete it in case of event page deletion
28 *  - Make it point to the new page in case of page move
29 */
30class PageMoveAndDeleteHandler implements PageMoveCompleteHook, PageDeleteCompleteHook, TitleMoveHook {
31    private PageEventLookup $pageEventLookup;
32    private IEventStore $eventStore;
33    private DeleteEventCommand $deleteEventCommand;
34    private TitleFormatter $titleFormatter;
35    private CampaignsPageFactory $campaignsPageFactory;
36
37    /**
38     * @param PageEventLookup $pageEventLookup
39     * @param IEventStore $eventStore
40     * @param DeleteEventCommand $deleteEventCommand
41     * @param TitleFormatter $titleFormatter
42     * @param CampaignsPageFactory $campaignsPageFactory
43     */
44    public function __construct(
45        PageEventLookup $pageEventLookup,
46        IEventStore $eventStore,
47        DeleteEventCommand $deleteEventCommand,
48        TitleFormatter $titleFormatter,
49        CampaignsPageFactory $campaignsPageFactory
50    ) {
51        $this->pageEventLookup = $pageEventLookup;
52        $this->eventStore = $eventStore;
53        $this->deleteEventCommand = $deleteEventCommand;
54        $this->titleFormatter = $titleFormatter;
55        $this->campaignsPageFactory = $campaignsPageFactory;
56    }
57
58    /**
59     * @inheritDoc
60     * @note We need to handle this hook, and not PageDeleteHook, to make sure that we only delete the event
61     * if the page deletion was successful. This //does// mean that we can't abort the page deletion from here.
62     */
63    public function onPageDeleteComplete(
64        ProperPageIdentity $page,
65        Authority $deleter,
66        string $reason,
67        int $pageID,
68        RevisionRecord $deletedRev,
69        ManualLogEntry $logEntry,
70        int $archivedRevisionCount
71    ) {
72        $registration = $this->pageEventLookup->getRegistrationForLocalPage(
73            $page,
74            PageEventLookup::GET_DIRECT,
75            IDBAccessObject::READ_LATEST
76        );
77        if ( !$registration ) {
78            return;
79        }
80        $this->deleteEventCommand->deleteUnsafe(
81            $registration,
82            // Skip tracking tool validation, it's too late to report any error and we also don't want to prevent
83            // a page from being deleted just for errors in an external tool that the deleting user likely has
84            // no control over.
85            DeleteEventCommand::SKIP_TRACKING_TOOL_VALIDATION
86        );
87    }
88
89    /**
90     * @inheritDoc
91     */
92    public function onTitleMove( Title $old, Title $nt, User $user, $reason, Status &$status ) {
93        $registration = $this->pageEventLookup->getRegistrationForLocalPage( $old, PageEventLookup::GET_DIRECT );
94        // Disallow moving event pages with registration enabled outside of the Event namespace, see T358704.
95        // This will change if we decide to allow event registration outside of the namespace (T318179).
96        if ( $registration && !$nt->inNamespace( NS_EVENT ) ) {
97            $status->fatal( 'campaignevents-error-move-eventpage-namespace' );
98            return false;
99        }
100        return true;
101    }
102
103    /**
104     * @inheritDoc
105     */
106    public function onPageMoveComplete( $old, $new, $user, $pageid, $redirid, $reason, $revision ) {
107        // This code runs in a DeferredUpdate, load the data from DB master (T302858#8354617)
108        $registration = $this->pageEventLookup->getRegistrationForLocalPage(
109            $old,
110            PageEventLookup::GET_DIRECT,
111            IDBAccessObject::READ_LATEST
112        );
113        if ( !$registration ) {
114            return;
115        }
116
117        $newEventPage = $this->campaignsPageFactory->newFromLocalMediaWikiPage( $new );
118        $newRegistration = new ExistingEventRegistration(
119            $registration->getID(),
120            $this->titleFormatter->getText( $new ),
121            $newEventPage,
122            $registration->getChatURL(),
123            $registration->getTrackingTools(),
124            $registration->getStatus(),
125            $registration->getTimezone(),
126            $registration->getStartLocalTimestamp(),
127            $registration->getEndLocalTimestamp(),
128            $registration->getType(),
129            $registration->getMeetingType(),
130            $registration->getMeetingURL(),
131            $registration->getMeetingCountry(),
132            $registration->getMeetingAddress(),
133            $registration->getParticipantQuestions(),
134            $registration->getCreationTimestamp(),
135            $registration->getLastEditTimestamp(),
136            $registration->getDeletionTimestamp()
137        );
138
139        $this->eventStore->saveRegistration( $newRegistration );
140    }
141}