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