Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
68.75% covered (warning)
68.75%
44 / 64
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
MachineReadableRCFeedFormatter
69.84% covered (warning)
69.84%
44 / 63
0.00% covered (danger)
0.00%
0 / 1
23.02
0.00% covered (danger)
0.00%
0 / 1
 formatArray
n/a
0 / 0
n/a
0 / 0
0
 getLine
69.84% covered (warning)
69.84%
44 / 63
0.00% covered (danger)
0.00%
0 / 1
23.02
1<?php
2
3/**
4 * @license GPL-2.0-or-later
5 * @file
6 */
7
8namespace MediaWiki\RCFeed;
9
10use MediaWiki\MainConfigNames;
11use MediaWiki\MediaWikiServices;
12use MediaWiki\RecentChanges\RecentChange;
13use MediaWiki\WikiMap\WikiMap;
14use Wikimedia\Timestamp\TimestampFormat as TS;
15
16/**
17 * Abstract class so there can be multiple formatters outputting the same data
18 *
19 * @since 1.23
20 * @ingroup RecentChanges
21 */
22abstract class MachineReadableRCFeedFormatter implements RCFeedFormatter {
23
24    /**
25     * Take the packet and return the formatted string
26     *
27     * @param array $packet
28     *
29     * @return string
30     */
31    abstract protected function formatArray( array $packet );
32
33    /**
34     * Generates a notification that can be easily interpreted by a machine.
35     *
36     * @see RCFeedFormatter::getLine
37     *
38     * @param array $feed
39     * @param RecentChange $rc
40     * @param string|null $actionComment
41     *
42     * @return string|null
43     */
44    public function getLine( array $feed, RecentChange $rc, $actionComment ) {
45        $services = MediaWikiServices::getInstance();
46        $recentChangeRCFeedNotifier = $services->getRecentChangeRCFeedNotifier();
47        $recentChangeLookup = $services->getRecentChangeLookup();
48        $mainConfig = $services->getMainConfig();
49        $canonicalServer = $mainConfig->get( MainConfigNames::CanonicalServer );
50        $serverName = $mainConfig->get( MainConfigNames::ServerName );
51        $scriptPath = $mainConfig->get( MainConfigNames::ScriptPath );
52        $packet = [
53            // Usually, RC ID is exposed only for patrolling purposes,
54            // but there is no real reason not to expose it in other cases,
55            // and I can see how this may be potentially useful for clients.
56            'id' => $rc->getAttribute( 'rc_id' ),
57            'type' => $recentChangeLookup->convertSourceToType( $rc->getAttribute( 'rc_source' ) ),
58            'namespace' => $rc->getTitle()->getNamespace(),
59            'title' => $rc->getTitle()->getPrefixedText(),
60            'title_url' => $rc->getTitle()->getCanonicalURL(),
61            'comment' => $rc->getAttribute( 'rc_comment' ),
62            'timestamp' => (int)wfTimestamp( TS::UNIX, $rc->getAttribute( 'rc_timestamp' ) ),
63            'user' => $rc->getAttribute( 'rc_user_text' ),
64            'bot' => (bool)$rc->getAttribute( 'rc_bot' ),
65            'notify_url' => $recentChangeRCFeedNotifier->getNotifyUrl( $rc ),
66        ];
67
68        if ( isset( $feed['channel'] ) ) {
69            $packet['channel'] = $feed['channel'];
70        }
71
72        $source = $rc->getAttribute( 'rc_source' );
73        if ( $source == RecentChange::SRC_EDIT || $source == RecentChange::SRC_NEW ) {
74            $useRCPatrol = $services->getMainConfig()->get( MainConfigNames::UseRCPatrol );
75            $useNPPatrol = $services->getMainConfig()->get( MainConfigNames::UseNPPatrol );
76            $packet['minor'] = (bool)$rc->getAttribute( 'rc_minor' );
77            if ( $useRCPatrol || ( $source == RecentChange::SRC_NEW && $useNPPatrol ) ) {
78                $packet['patrolled'] = (bool)$rc->getAttribute( 'rc_patrolled' );
79            }
80        }
81
82        switch ( $source ) {
83            case RecentChange::SRC_EDIT:
84                $packet['length'] = [
85                    'old' => $rc->getAttribute( 'rc_old_len' ),
86                    'new' => $rc->getAttribute( 'rc_new_len' )
87                ];
88                $packet['revision'] = [
89                    'old' => $rc->getAttribute( 'rc_last_oldid' ),
90                    'new' => $rc->getAttribute( 'rc_this_oldid' )
91                ];
92                break;
93
94            case RecentChange::SRC_NEW:
95                $packet['length'] = [ 'old' => null, 'new' => $rc->getAttribute( 'rc_new_len' ) ];
96                $packet['revision'] = [ 'old' => null, 'new' => $rc->getAttribute( 'rc_this_oldid' ) ];
97                break;
98
99            case RecentChange::SRC_LOG:
100                $packet['log_id'] = $rc->getAttribute( 'rc_logid' );
101                $packet['log_type'] = $rc->getAttribute( 'rc_log_type' );
102                $packet['log_action'] = $rc->getAttribute( 'rc_log_action' );
103                if ( $rc->getAttribute( 'rc_params' ) ) {
104                    $params = $rc->parseParams();
105                    if (
106                        // If it's an actual serialised false...
107                        $rc->getAttribute( 'rc_params' ) == serialize( false ) ||
108                        // Or if we did not get false back when trying to unserialise
109                        $params !== false
110                    ) {
111                        // From ApiQueryLogEvents::addLogParams
112                        $logParams = [];
113                        // Keys like "4::paramname" can't be used for output so we change them to "paramname"
114                        foreach ( $params as $key => $value ) {
115                            if ( !str_contains( $key, ':' ) ) {
116                                $logParams[$key] = $value;
117                                continue;
118                            }
119                            $logParam = explode( ':', $key, 3 );
120                            $logParams[$logParam[2]] = $value;
121                        }
122                        $packet['log_params'] = $logParams;
123                    } else {
124                        $packet['log_params'] = explode( "\n", $rc->getAttribute( 'rc_params' ) );
125                    }
126                }
127                $packet['log_action_comment'] = $actionComment;
128                break;
129        }
130
131        $packet['server_url'] = $canonicalServer;
132        $packet['server_name'] = $serverName;
133
134        $packet['server_script_path'] = $scriptPath ?: '/';
135        $packet['wiki'] = WikiMap::getCurrentWikiId();
136
137        return $this->formatArray( $packet );
138    }
139}
140/** @deprecated class alias since 1.43 */
141class_alias( MachineReadableRCFeedFormatter::class, 'MachineReadableRCFeedFormatter' );