Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
SignificantEditEventIngress
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 2
56
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
 handlePageLatestRevisionChangedEvent
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2
3declare( strict_types=1 );
4
5namespace ContentTranslation\Events;
6
7use ContentTranslation\Entity\RecentSignificantEdit;
8use ContentTranslation\Service\EditedSectionFinder;
9use ContentTranslation\Service\WikidataIdFetcher;
10use ContentTranslation\SiteMapper;
11use ContentTranslation\Store\RecentSignificantEditStore;
12use MediaWiki\DomainEvent\DomainEventIngress;
13use MediaWiki\Page\Event\PageLatestRevisionChangedEvent;
14use MediaWiki\Revision\RevisionStore;
15
16/**
17 * Event subscriber for significant edits.
18 *
19 * Subscribes to PageLatestRevisionChangedEvent events and stores significant edits
20 * in the cx_significant_edits table.
21 *
22 * @copyright See AUTHORS.txt
23 * @license GPL-2.0-or-later
24 */
25class SignificantEditEventIngress extends DomainEventIngress {
26    private const MINIMUM_MODIFIED_BYTES = 500;
27
28    public function __construct(
29        private readonly RevisionStore $revisionStore,
30        private readonly RecentSignificantEditStore $significantEditStore,
31        private readonly EditedSectionFinder $editedSectionFinder,
32        private readonly WikidataIdFetcher $wikidataIdFetcher
33    ) {
34    }
35
36    /**
37     * Handler for "PageLatestRevisionChangedEvent" events.
38     * It adds a new row to the "cx_significant_edits" table, if the
39     * current revision fulfils some requirements.
40     *
41     * These requirements are:
42     * 1. The changes in the revision should modify at least MINIMUM_MODIFIED_BYTES bytes
43     * 2. This change should affect at least one non-lead section
44     *
45     * @noinspection PhpUnused
46     * @param PageLatestRevisionChangedEvent $event
47     * @return void
48     */
49    public function handlePageLatestRevisionChangedEvent(
50        PageLatestRevisionChangedEvent $event
51    ): void {
52        $pageIdentity = $event->getPage();
53        $rev = $event->getLatestRevisionAfter();
54        $user = $event->getAuthor();
55
56        $isSignificantEdit = $rev->getSize() > self::MINIMUM_MODIFIED_BYTES;
57        wfDebugLog( 'cx-entrypoints-recent-edit', 'Edit size: ' . $rev->getSize() );
58        // If edit is not of a significant size,
59        // or if current wiki family is not supported for this entrypoint
60        if ( !$isSignificantEdit || !$this->significantEditStore->isCurrentWikiFamilySupported() ) {
61            return;
62        }
63
64        $currentLanguage = SiteMapper::getCurrentLanguageCode();
65        $qid = $this->wikidataIdFetcher->getWikidataId( (string)$pageIdentity, $currentLanguage );
66        if ( !$qid ) {
67            wfDebugLog( 'cx-entrypoints-recent-edit', 'qid not found' );
68            return;
69        }
70
71        // get integer from Q id ("Q123")
72        $wikidataId = (int)filter_var( $qid, FILTER_SANITIZE_NUMBER_INT );
73
74        $previousRev = $this->revisionStore->getPreviousRevision( $rev );
75        // Find all titles of non-lead sections that were edited in this revision
76        $editedSections = $this->editedSectionFinder->findEditedSectionsBetweenRevisions( $rev, $previousRev );
77        wfDebugLog( 'cx-entrypoints-recent-edit', 'Edited sections: ' . implode( ", ", $editedSections ) );
78
79        // If no non-lead section was edited, return
80        if ( !$editedSections ) {
81            return;
82        }
83
84        $language = SiteMapper::getCurrentLanguageCode();
85
86        $userEdit = $this->significantEditStore->findExistingEdit(
87            $user->getId(),
88            $wikidataId,
89            $language
90        );
91
92        if ( $userEdit instanceof RecentSignificantEdit ) {
93            wfDebugLog( 'cx-entrypoints-recent-edit', 'Recent edit already exists' );
94            $userEdit->mergeSectionTitles( $editedSections );
95            $this->significantEditStore->update( $userEdit );
96
97            return;
98        }
99
100        $edit = new RecentSignificantEdit(
101            null,
102            $user->getId(),
103            $wikidataId,
104            $language,
105            (string)$pageIdentity,
106            $editedSections,
107            null
108        );
109
110        $this->significantEditStore->insert( $edit );
111        wfDebugLog( 'cx-entrypoints-recent-edit', 'Recent edit created' );
112    }
113}