Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
46.51% covered (danger)
46.51%
20 / 43
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
RecentChangeRCFeedNotifier
46.51% covered (danger)
46.51%
20 / 43
33.33% covered (danger)
33.33%
1 / 3
88.49
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 notifyRCFeeds
9.09% covered (danger)
9.09%
2 / 22
0.00% covered (danger)
0.00%
0 / 1
161.26
 getNotifyUrl
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\RecentChanges;
8
9use MediaWiki\Config\ServiceOptions;
10use MediaWiki\HookContainer\HookContainer;
11use MediaWiki\HookContainer\HookRunner;
12use MediaWiki\MainConfigNames;
13use MediaWiki\RCFeed\RCFeed;
14
15/**
16 * @since 1.45
17 */
18class RecentChangeRCFeedNotifier {
19
20    public const CONSTRUCTOR_OPTIONS = [
21        MainConfigNames::RCFeeds,
22        MainConfigNames::UseRCPatrol,
23        MainConfigNames::UseNPPatrol,
24        MainConfigNames::CanonicalServer,
25        MainConfigNames::Script,
26    ];
27
28    private HookContainer $hookContainer;
29    private ServiceOptions $options;
30
31    public function __construct(
32        HookContainer $hookContainer,
33        ServiceOptions $options
34    ) {
35        $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
36
37        $this->hookContainer = $hookContainer;
38        $this->options = $options;
39    }
40
41    /**
42     * Notify all the feeds about the change.
43     *
44     * @param RecentChange $recentChange
45     * @param array|null $feeds Optional feeds to send to, defaults to $wgRCFeeds
46     */
47    public function notifyRCFeeds( RecentChange $recentChange, ?array $feeds = null ) {
48        // T403757: Don't send 'suppressed from creation' recent changes entries to the RCFeeds as they do not
49        // have systems to appropriately redact suppressed / deleted material
50        if ( $recentChange->getAttribute( 'rc_deleted' ) != 0 ) {
51            return;
52        }
53
54        $feeds ??= $this->options->get( MainConfigNames::RCFeeds );
55
56        $performer = $recentChange->getPerformerIdentity();
57
58        foreach ( $feeds as $params ) {
59            $params += [
60                'omit_bots' => false,
61                'omit_anon' => false,
62                'omit_user' => false,
63                'omit_minor' => false,
64                'omit_patrolled' => false,
65            ];
66
67            if (
68                ( $params['omit_bots'] && $recentChange->getAttribute( 'rc_bot' ) ) ||
69                ( $params['omit_anon'] && !$performer->isRegistered() ) ||
70                ( $params['omit_user'] && $performer->isRegistered() ) ||
71                ( $params['omit_minor'] && $recentChange->getAttribute( 'rc_minor' ) ) ||
72                ( $params['omit_patrolled'] && $recentChange->getAttribute( 'rc_patrolled' ) ) ||
73                !in_array( $recentChange->getAttribute( 'rc_source' ), RecentChange::INTERNAL_SOURCES )
74            ) {
75                continue;
76            }
77
78            $actionComment = $recentChange->getExtra( 'actionCommentIRC' );
79
80            $feed = RCFeed::factory( $params );
81            $feed->notify( $recentChange, $actionComment );
82        }
83    }
84
85    /**
86     * Get the extra URL that is given as part of the notification to RCFeed consumers.
87     *
88     * This is mainly to facilitate patrolling or other content review.
89     *
90     * @param RecentChange $recentChange
91     * @return string|null URL
92     */
93    public function getNotifyUrl( RecentChange $recentChange ): ?string {
94        $useRCPatrol = $this->options->get( MainConfigNames::UseRCPatrol );
95        $useNPPatrol = $this->options->get( MainConfigNames::UseNPPatrol );
96        $canonicalServer = $this->options->get( MainConfigNames::CanonicalServer );
97        $script = $this->options->get( MainConfigNames::Script );
98
99        $source = $recentChange->getAttribute( 'rc_source' );
100        if ( $source == RecentChange::SRC_LOG ) {
101            $url = null;
102        } else {
103            $url = $canonicalServer . $script;
104            if ( $source == RecentChange::SRC_NEW ) {
105                $query = '?oldid=' . $recentChange->getAttribute( 'rc_this_oldid' );
106            } else {
107                $query = '?diff=' . $recentChange->getAttribute( 'rc_this_oldid' )
108                    . '&oldid=' . $recentChange->getAttribute( 'rc_last_oldid' );
109            }
110            if ( $useRCPatrol ||
111                ( $recentChange->getAttribute( 'rc_source' ) == RecentChange::SRC_NEW && $useNPPatrol )
112            ) {
113                $query .= '&rcid=' . $recentChange->getAttribute( 'rc_id' );
114            }
115
116            ( new HookRunner( $this->hookContainer ) )->onIRCLineURL( $url, $query, $recentChange );
117            $url .= $query;
118        }
119
120        return $url;
121    }
122
123}