jenkins-bot has submitted this change and it was merged.

Change subject: Provide a JSON recent changes feed.
......................................................................


Provide a JSON recent changes feed.

This introduces a new configuration variable, $wgRCFeeds, which allows the user
to configure multiple destinations for RC notifications. It also allows the
notification format to be customized. Two formats are included by default: the
older IRC format and a new JSON format.

Change-Id: I270bde418a82985c94372ac4579100435b6ee026
---
M RELEASE-NOTES-1.22
M includes/AutoLoader.php
M includes/DefaultSettings.php
M includes/RecentChange.php
M includes/Setup.php
M includes/logging/LogEntry.php
A includes/rcfeed/IRCColourfulRCFeedFormatter.php
A includes/rcfeed/JSONRCFeedFormatter.php
A includes/rcfeed/RCFeedEngine.php
A includes/rcfeed/RCFeedFormatter.php
A includes/rcfeed/UDPRCFeedEngine.php
M tests/phpunit/includes/RecentChangeTest.php
12 files changed, 380 insertions(+), 123 deletions(-)

Approvals:
  Parent5446: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/RELEASE-NOTES-1.22 b/RELEASE-NOTES-1.22
index 239d997..50e2732 100644
--- a/RELEASE-NOTES-1.22
+++ b/RELEASE-NOTES-1.22
@@ -49,6 +49,11 @@
 * The checkbox for staying in HTTPS displayed on the login form when 
$wgSecureLogin is
   enabled has been removed. Instead, whether the user stays in HTTPS will be 
determined
   based on the user's preferences, and whether they came from HTTPS or not.
+* $wgRC2UDPAddress, $wgRC2UDPInterwikiPrefix, $wgRC2UDPOmitBots, $wgRC2UDPPort,
+  and $wgRC2UDPPrefix configuration options have been deprecated in favor of a
+  $wgRCFeeds configuration array. $wgRCFeeds makes both the format and
+  destination of recent change notifications customizable, and allows for
+  multiple destinations to be specified.
 
 === New features in 1.22 ===
 * (bug 44525) mediawiki.jqueryMsg can now parse (whitelisted) HTML elements 
and attributes.
@@ -403,6 +408,11 @@
   have been deprecated in favour of using mw.hook.
 * The 'showjumplinks' user preference has been removed, jump links are now
   always included.
+* Methods RecentChange::notifyRC2UDP, RecentChange::sendToUDP, and
+  RecentChange::cleanupForIRC have been deprecated, as it is now the
+  responsibility of classes implementing the RCFeedFormatter and RCFeedEngine
+  interfaces to implement the formatting and delivery for recent change
+  notifications.
 
 == Compatibility ==
 
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 2f92ab0..b830b16 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -835,6 +835,13 @@
        'ProfilerStub' => 'includes/profiler/ProfilerStub.php',
        'ProfileSection' => 'includes/profiler/Profiler.php',
 
+       # includes/rcfeed
+       'RCFeedEngine' => 'includes/rcfeed/RCFeedEngine.php',
+       'UDPRCFeedEngine' => 'includes/rcfeed/UDPRCFeedEngine.php',
+       'RCFeedFormatter' => 'includes/rcfeed/RCFeedFormatter.php',
+       'IRCColourfulRCFeedFormatter' => 
'includes/rcfeed/IRCColourfulRCFeedFormatter.php',
+       'JSONRCFeedFormatter' => 'includes/rcfeed/JSONRCFeedFormatter.php',
+
        # includes/resourceloader
        'ResourceLoader' => 'includes/resourceloader/ResourceLoader.php',
        'ResourceLoaderContext' => 
'includes/resourceloader/ResourceLoaderContext.php',
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 92cbab3..20c72ee 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -5394,11 +5394,15 @@
 /**
  * Send recent changes updates via UDP. The updates will be formatted for IRC.
  * Set this to the IP address of the receiver.
+ *
+ * @deprecated since 1.22, use $wgRCFeeds
  */
 $wgRC2UDPAddress = false;
 
 /**
  * Port number for RC updates
+ *
+ * @deprecated since 1.22, use $wgRCFeeds
  */
 $wgRC2UDPPort = false;
 
@@ -5407,22 +5411,73 @@
  * This can be used to identify the wiki. A script is available called
  * mxircecho.py which listens on a UDP port, and uses a prefix ending in a
  * tab to identify the IRC channel to send the log line to.
+ *
+ * @deprecated since 1.22, use $wgRCFeeds
  */
 $wgRC2UDPPrefix = '';
 
 /**
  * If this is set to true, $wgLocalInterwiki will be prepended to links in the
  * IRC feed. If this is set to a string, that string will be used as the 
prefix.
+ *
+ * @deprecated since 1.22, use $wgRCFeeds
  */
 $wgRC2UDPInterwikiPrefix = false;
 
 /**
  * Set to true to omit "bot" edits (by users with the bot permission) from the
  * UDP feed.
+ *
+ * @deprecated since 1.22, use $wgRCFeeds
  */
 $wgRC2UDPOmitBots = false;
 
 /**
+ * Destinations to which notifications about recent changes
+ * should be sent.
+ *
+ * As of MediaWiki 1.22, the only supported 'engine' parameter option in core
+ * is 'UDPRCFeedEngine', which is used to send recent changes over UDP to the
+ * specified server.
+ * The common options are:
+ *   * 'uri' -- the address to which the notices are to be sent.
+ *   * 'formatter' -- the class name (implementing RCFeedFormatter) which will
+ *     produce the text to send.
+ *   * 'omit_bots' -- whether the bot edits should be in the feed
+ *  The IRC-specific options are:
+ *   * 'add_interwiki_prefix' -- whether the titles should be prefixed with
+ *     $wgLocalInterwiki.
+ *  The JSON-specific options are:
+ *   * 'channel' -- if set, the 'channel' parameter is also set in JSON values.
+ *
+ *  To ensure backwards-compatability, whenever $wgRC2UDPAddress is set, a
+ *  'default' feed will be created reusing the deprecated $wgRC2UDP* variables.
+ *
+ * @example $wgRCFeeds['example'] = array(
+ *             'formatter' => 'JSONRCFeedFormatter',
+ *             'uri' => "udp://localhost:1336",
+ *             'add_interwiki_prefix' => false,
+ *             'omit_bots' => true,
+ *     );
+ * @example $wgRCFeeds['exampleirc'] = array(
+ *             'formatter' => 'IRCColourfulRCFeedFormatter',
+ *             'uri' => "udp://localhost:1338",
+ *             'add_interwiki_prefix' => false,
+ *             'omit_bots' => true,
+ *     );
+ * @since 1.22
+ */
+$wgRCFeeds = array();
+
+/**
+ * Used by RecentChange::getStreamEngine to find the correct engine to use for 
a given URI protocol.
+ * Keys are scheme names, values are names of engine classes.
+ */
+$wgStreamLoggers = array(
+       'udp' => 'UDPRCFeedEngine',
+);
+
+/**
  * Enable user search in Special:Newpages
  * This is really a temporary hack around an index install bug on some 
Wikipedias.
  * Kill it once fixed.
diff --git a/includes/RecentChange.php b/includes/RecentChange.php
index 24db569..939e924 100644
--- a/includes/RecentChange.php
+++ b/includes/RecentChange.php
@@ -263,7 +263,7 @@
 
                # Notify external application via UDP
                if ( !$noudp ) {
-                       $this->notifyRC2UDP();
+                       $this->notifyRCFeeds();
                }
 
                # E-mail notifications
@@ -284,54 +284,90 @@
                }
        }
 
+       /**
+        * @deprecated since 1.22, use notifyRCFeeds instead.
+        */
        public function notifyRC2UDP() {
-               global $wgRC2UDPAddress, $wgRC2UDPOmitBots;
-               # Notify external application via UDP
-               # Omit RC_EXTERNAL changes: bots and tools can get these edits 
from the feed of the external wiki
-               if ( $wgRC2UDPAddress && $this->mAttribs['rc_type'] != 
RC_EXTERNAL &&
-                       ( !$this->mAttribs['rc_bot'] || !$wgRC2UDPOmitBots ) ) {
-                       self::sendToUDP( $this->getIRCLine() );
-               }
+               wfDeprecated( __METHOD__, '1.22' );
+               $this->notifyRCFeeds();
        }
 
        /**
         * Send some text to UDP.
-        * @see RecentChange::cleanupForIRC
-        * @param string $line text to send
-        * @param string $address defaults to $wgRC2UDPAddress.
-        * @param string $prefix defaults to $wgRC2UDPPrefix.
-        * @param int $port defaults to $wgRC2UDPPort. (Since 1.17)
-        * @return Boolean: success
+        * @deprecated since 1.22
         */
        public static function sendToUDP( $line, $address = '', $prefix = '', 
$port = '' ) {
-               global $wgRC2UDPAddress, $wgRC2UDPPrefix, $wgRC2UDPPort;
-               # Assume default for standard RC case
-               $address = $address ? $address : $wgRC2UDPAddress;
-               $prefix = $prefix ? $prefix : $wgRC2UDPPrefix;
-               $port = $port ? $port : $wgRC2UDPPort;
-               # Notify external application via UDP
-               if ( $address ) {
-                       $conn = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
-                       if ( $conn ) {
-                               $line = $prefix . $line;
-                               wfDebug( __METHOD__ . ": sending UDP line: 
$line\n" );
-                               socket_sendto( $conn, $line, strlen( $line ), 
0, $address, $port );
-                               socket_close( $conn );
-                               return true;
-                       } else {
-                               wfDebug( __METHOD__ . ": failed to create UDP 
socket\n" );
-                       }
-               }
-               return false;
+               global $wgRC2UDPPrefix, $wgRC2UDPInterwikiPrefix;
+               wfDeprecated( __METHOD__, '1.22' );
+
+               $engine = new UDPRCFeedEngine();
+               $feed = array(
+                       'uri' => "udp://$address:$port/$wgRC2UDPPrefix",
+                       'formatter' => 'IRCColourfulRCFeedFormatter',
+                       'add_interwiki_prefix' => $wgRC2UDPInterwikiPrefix,
+               );
+
+               return $engine->send( $feed, $line );
        }
 
        /**
-        * Remove newlines, carriage returns and decode html entities
-        * @param $text String
-        * @return String
+        * Notify all the feeds about the change.
+        */
+       public function notifyRCFeeds() {
+               global $wgRCFeeds;
+
+               foreach ( $wgRCFeeds as $feed ) {
+                       $engine = self::getStreamEngine( $feed['uri'] );
+
+                       if ( isset( $this->mExtras['actionCommentIRC'] ) ) {
+                               $actionComment = 
$this->mExtras['actionCommentIRC'];
+                       } else {
+                               $actionComment = null;
+                       }
+
+                       $omitBots = isset( $feed['omit_bots'] ) ? 
$feed['omit_bots'] : false;
+
+                       if (
+                               ( $omitBots && $this->mAttribs['rc_bot'] ) ||
+                               $this->mAttribs['rc_type'] == RC_EXTERNAL
+                       ) {
+                               continue;
+                       }
+
+                       $formatter = new $feed['formatter']();
+                       $line = $formatter->getLine( $feed, $this, 
$actionComment );
+
+                       $engine->send( $feed, $line );
+               }
+       }
+
+       /**
+        * Gets the stream engine object for a given URI from $wgStreamLoggers
+        *
+        * @param $uri string URI to get the engine object for
+        * @return object The engine object
+        */
+       private static function getStreamEngine( $uri ) {
+               global $wgStreamLoggers;
+
+               $scheme = parse_url( $uri, PHP_URL_SCHEME );
+               if ( !$scheme ) {
+                       throw new MWException( __FUNCTION__ . ": Invalid stream 
logger URI: '$uri'" );
+               }
+
+               if ( !isset( $wgStreamLoggers[$scheme] ) ) {
+                       throw new MWException( __FUNCTION__ . ": Unknown stream 
logger URI scheme: $scheme" );
+               }
+
+               return new $wgStreamLoggers[$scheme];
+       }
+
+       /**
+        * @deprecated since 1.22, moved to IRCColourfulRCFeedFormatter
         */
        public static function cleanupForIRC( $text ) {
-               return Sanitizer::decodeCharReferences( str_replace( array( 
"\n", "\r" ), array( " ", "" ), $text ) );
+               wfDeprecated( __METHOD__, '1.22' );
+               return IRCColourfulRCFeedFormatter::cleanupForIRC( $text );
        }
 
        /**
@@ -728,89 +764,6 @@
                        $trail = '';
                }
                return $trail;
-       }
-
-       /**
-        * @return string
-        */
-       public function getIRCLine() {
-               global $wgUseRCPatrol, $wgUseNPPatrol, 
$wgRC2UDPInterwikiPrefix, $wgLocalInterwiki,
-                       $wgCanonicalServer, $wgScript;
-
-               if ( $this->mAttribs['rc_type'] == RC_LOG ) {
-                       // Don't use SpecialPage::getTitleFor, backwards 
compatibility with
-                       // IRC API which expects "Log".
-                       $titleObj = Title::newFromText( 'Log/' . 
$this->mAttribs['rc_log_type'], NS_SPECIAL );
-               } else {
-                       $titleObj =& $this->getTitle();
-               }
-               $title = $titleObj->getPrefixedText();
-               $title = self::cleanupForIRC( $title );
-
-               if ( $this->mAttribs['rc_type'] == RC_LOG ) {
-                       $url = '';
-               } else {
-                       $url = $wgCanonicalServer . $wgScript;
-                       if ( $this->mAttribs['rc_type'] == RC_NEW ) {
-                               $query = '?oldid=' . 
$this->mAttribs['rc_this_oldid'];
-                       } else {
-                               $query = '?diff=' . 
$this->mAttribs['rc_this_oldid'] . '&oldid=' . $this->mAttribs['rc_last_oldid'];
-                       }
-                       if ( $wgUseRCPatrol || ( $this->mAttribs['rc_type'] == 
RC_NEW && $wgUseNPPatrol ) ) {
-                               $query .= '&rcid=' . $this->mAttribs['rc_id'];
-                       }
-                       // HACK: We need this hook for WMF's secure server setup
-                       wfRunHooks( 'IRCLineURL', array( &$url, &$query ) );
-                       $url .= $query;
-               }
-
-               if ( $this->mAttribs['rc_old_len'] !== null && 
$this->mAttribs['rc_new_len'] !== null ) {
-                       $szdiff = $this->mAttribs['rc_new_len'] - 
$this->mAttribs['rc_old_len'];
-                       if ( $szdiff < -500 ) {
-                               $szdiff = "\002$szdiff\002";
-                       } elseif ( $szdiff >= 0 ) {
-                               $szdiff = '+' . $szdiff;
-                       }
-                       // @todo i18n with parentheses in content language?
-                       $szdiff = '(' . $szdiff . ')';
-               } else {
-                       $szdiff = '';
-               }
-
-               $user = self::cleanupForIRC( $this->mAttribs['rc_user_text'] );
-
-               if ( $this->mAttribs['rc_type'] == RC_LOG ) {
-                       $targetText = $this->getTitle()->getPrefixedText();
-                       $comment = self::cleanupForIRC( str_replace( 
"[[$targetText]]", "[[\00302$targetText\00310]]", 
$this->mExtra['actionCommentIRC'] ) );
-                       $flag = $this->mAttribs['rc_log_action'];
-               } else {
-                       $comment = self::cleanupForIRC( 
$this->mAttribs['rc_comment'] );
-                       $flag = '';
-                       if ( !$this->mAttribs['rc_patrolled'] && ( 
$wgUseRCPatrol || $this->mAttribs['rc_type'] == RC_NEW && $wgUseNPPatrol ) ) {
-                               $flag .= '!';
-                       }
-                       $flag .= ( $this->mAttribs['rc_type'] == RC_NEW ? "N" : 
"" ) . ( $this->mAttribs['rc_minor'] ? "M" : "" ) . ( $this->mAttribs['rc_bot'] 
? "B" : "" );
-               }
-
-               if ( $wgRC2UDPInterwikiPrefix === true && $wgLocalInterwiki !== 
false ) {
-                       $prefix = $wgLocalInterwiki;
-               } elseif ( $wgRC2UDPInterwikiPrefix ) {
-                       $prefix = $wgRC2UDPInterwikiPrefix;
-               } else {
-                       $prefix = false;
-               }
-               if ( $prefix !== false ) {
-                       $titleString = 
"\00314[[\00303$prefix:\00307$title\00314]]";
-               } else {
-                       $titleString = "\00314[[\00307$title\00314]]";
-               }
-
-               # see http://www.irssi.org/documentation/formats for some 
colour codes. prefix is \003,
-               # no colour (\003) switches back to the term default
-               $fullString = "$titleString\0034 $flag\00310 " .
-                       "\00302$url\003 \0035*\003 \00303$user\003 \0035*\003 
$szdiff \00310$comment\003\n";
-
-               return $fullString;
        }
 
        /**
diff --git a/includes/Setup.php b/includes/Setup.php
index cfe5f1c..2e083d8 100644
--- a/includes/Setup.php
+++ b/includes/Setup.php
@@ -393,6 +393,15 @@
        $wgCookieSecure = ( WebRequest::detectProtocol() === 'https' );
 }
 
+if ( $wgRC2UDPAddress ) {
+       $wgRCFeeds['default'] = array(
+               'formatter' => 'IRCColourfulRCFeedFormatter',
+               'uri' => "udp://$wgRC2UDPAddress:$wgRC2UDPPort/$wgRC2UDPPrefix",
+               'add_interwiki_prefix' => &$wgRC2UDPInterwikiPrefix,
+               'omit_bots' => &$wgRC2UDPOmitBots,
+       );
+}
+
 // Disable MWDebug for command line mode, this prevents MWDebug from eating up
 // all the memory from logging SQL queries on maintenance scripts
 global $wgCommandLineMode;
diff --git a/includes/logging/LogEntry.php b/includes/logging/LogEntry.php
index 16b72ea..226a1ed 100644
--- a/includes/logging/LogEntry.php
+++ b/includes/logging/LogEntry.php
@@ -544,7 +544,7 @@
                }
 
                if ( $to === 'udp' || $to === 'rcandudp' ) {
-                       $rc->notifyRC2UDP();
+                       $rc->notifyRCFeeds();
                }
        }
 
diff --git a/includes/rcfeed/IRCColourfulRCFeedFormatter.php 
b/includes/rcfeed/IRCColourfulRCFeedFormatter.php
new file mode 100644
index 0000000..507369f
--- /dev/null
+++ b/includes/rcfeed/IRCColourfulRCFeedFormatter.php
@@ -0,0 +1,99 @@
+<?php
+class IRCColourfulRCFeedFormatter implements RCFeedFormatter {
+       /**
+        * Generates a colourful notification intended for humans on IRC.
+        * @see RCFeedFormatter::getLine
+        */
+       public function getLine( array $feed, RecentChange $rc, $actionComment 
) {
+               global $wgUseRCPatrol, $wgUseNPPatrol, $wgLocalInterwiki,
+                       $wgCanonicalServer, $wgScript;
+               $attribs = $rc->getAttributes();
+               if ( $attribs['rc_type'] == RC_LOG ) {
+                       // Don't use SpecialPage::getTitleFor, backwards 
compatibility with
+                       // IRC API which expects "Log".
+                       $titleObj = Title::newFromText( 'Log/' . 
$attribs['rc_log_type'], NS_SPECIAL );
+               } else {
+                       $titleObj =& $rc->getTitle();
+               }
+               $title = $titleObj->getPrefixedText();
+               $title = self::cleanupForIRC( $title );
+
+               if ( $attribs['rc_type'] == RC_LOG ) {
+                       $url = '';
+               } else {
+                       $url = $wgCanonicalServer . $wgScript;
+                       if ( $attribs['rc_type'] == RC_NEW ) {
+                               $query = '?oldid=' . $attribs['rc_this_oldid'];
+                       } else {
+                               $query = '?diff=' . $attribs['rc_this_oldid'] . 
'&oldid=' . $attribs['rc_last_oldid'];
+                       }
+                       if ( $wgUseRCPatrol || ( $attribs['rc_type'] == RC_NEW 
&& $wgUseNPPatrol ) ) {
+                               $query .= '&rcid=' . $attribs['rc_id'];
+                       }
+                       // HACK: We need this hook for WMF's secure server setup
+                       wfRunHooks( 'IRCLineURL', array( &$url, &$query ) );
+                       $url .= $query;
+               }
+
+               if ( $attribs['rc_old_len'] !== null && $attribs['rc_new_len'] 
!== null ) {
+                       $szdiff = $attribs['rc_new_len'] - 
$attribs['rc_old_len'];
+                       if ( $szdiff < -500 ) {
+                               $szdiff = "\002$szdiff\002";
+                       } elseif ( $szdiff >= 0 ) {
+                               $szdiff = '+' . $szdiff;
+                       }
+                       // @todo i18n with parentheses in content language?
+                       $szdiff = '(' . $szdiff . ')';
+               } else {
+                       $szdiff = '';
+               }
+
+               $user = self::cleanupForIRC( $attribs['rc_user_text'] );
+
+               if ( $attribs['rc_type'] == RC_LOG ) {
+                       $targetText = $rc->getTitle()->getPrefixedText();
+                       $comment = self::cleanupForIRC( str_replace( 
"[[$targetText]]", "[[\00302$targetText\00310]]", $actionComment ) );
+                       $flag = $attribs['rc_log_action'];
+               } else {
+                       $comment = self::cleanupForIRC( $attribs['rc_comment'] 
);
+                       $flag = '';
+                       if ( !$attribs['rc_patrolled'] && ( $wgUseRCPatrol || 
$attribs['rc_type'] == RC_NEW && $wgUseNPPatrol ) ) {
+                               $flag .= '!';
+                       }
+                       $flag .= ( $attribs['rc_type'] == RC_NEW ? "N" : "" ) . 
( $attribs['rc_minor'] ? "M" : "" ) . ( $attribs['rc_bot'] ? "B" : "" );
+               }
+
+               if ( $feed['add_interwiki_prefix'] === true && 
$wgLocalInterwiki !== false ) {
+                       $prefix = $wgLocalInterwiki;
+               } elseif ( $feed['add_interwiki_prefix'] ) {
+                       $prefix = $feed['add_interwiki_prefix'];
+               } else {
+                       $prefix = false;
+               }
+               if ( $prefix !== false ) {
+                       $titleString = 
"\00314[[\00303$prefix:\00307$title\00314]]";
+               } else {
+                       $titleString = "\00314[[\00307$title\00314]]";
+               }
+
+               # see http://www.irssi.org/documentation/formats for some 
colour codes. prefix is \003,
+               # no colour (\003) switches back to the term default
+               $fullString = "$titleString\0034 $flag\00310 " .
+                       "\00302$url\003 \0035*\003 \00303$user\003 \0035*\003 
$szdiff \00310$comment\003\n";
+
+               return $fullString;
+       }
+
+       /**
+        * Remove newlines, carriage returns and decode html entites
+        * @param string $text
+        * @return string
+        */
+       public static function cleanupForIRC( $text ) {
+               return Sanitizer::decodeCharReferences( str_replace(
+                       array( "\n", "\r" ),
+                       array( " ", "" ),
+                       $text
+               ) );
+       }
+}
diff --git a/includes/rcfeed/JSONRCFeedFormatter.php 
b/includes/rcfeed/JSONRCFeedFormatter.php
new file mode 100644
index 0000000..d50139c
--- /dev/null
+++ b/includes/rcfeed/JSONRCFeedFormatter.php
@@ -0,0 +1,89 @@
+<?php
+class JSONRCFeedFormatter implements RCFeedFormatter {
+       /**
+        * Generates a notification that can be easily interpreted by a machine.
+        * @see RCFeedFormatter::getLine
+        */
+       public function getLine( array $feed, RecentChange $rc, $actionComment 
) {
+               global $wgCanonicalServer, $wgScriptPath, $wgArticlePath, 
$wgDBname;
+               $attrib = $rc->getAttributes();
+
+               $packet = array(
+                       // Usually, RC ID is exposed only for patrolling 
purposes,
+                       // but there is no real reason not to expose it in 
other cases,
+                       // and I can see how this may be potentially useful for 
clients.
+                       'id' => $attrib['rc_id'],
+                       'type' => $attrib['rc_type'],
+                       'namespace' => $rc->getTitle()->getNamespace(),
+                       'title' => $rc->getTitle()->getPrefixedText(),
+                       'comment' => $attrib['rc_comment'],
+                       'timestamp' => (int)wfTimestamp( TS_UNIX, 
$attrib['rc_timestamp'] ),
+                       'user' => $attrib['rc_user_text'],
+                       'bot' => (bool)$attrib['rc_bot'],
+               );
+
+               if ( isset( $feed['channel'] ) ) {
+                       $packet['channel'] = $feed['channel'];
+               }
+
+               $type = $attrib['rc_type'];
+               if ( $type == RC_EDIT || $type == RC_NEW ) {
+                       global $wgUseRCPatrol, $wgUseNPPatrol;
+
+                       $packet['minor'] = $attrib['rc_minor'];
+                       if ( $wgUseRCPatrol || ( $type == RC_NEW && 
$wgUseNPPatrol ) ) {
+                               $packet['patrolled'] = $attrib['rc_patrolled'];
+                       }
+               }
+
+               switch ( $type ) {
+                       case RC_EDIT:
+                               $packet['length'] = array( 'old' => 
$attrib['rc_old_len'], 'new' => $attrib['rc_new_len'] );
+                               $packet['revision'] = array( 'old' => 
$attrib['rc_last_oldid'], 'new' => $attrib['rc_this_oldid'] );
+                               break;
+
+                       case RC_NEW:
+                               $packet['length'] = array( 'old' => NULL, 'new' 
=> $attrib['rc_new_len'] );
+                               $packet['revision'] = array( 'old' => NULL, 
'new' => $attrib['rc_this_oldid'] );
+                               break;
+
+                       case RC_LOG:
+                               $packet['log_type'] = $attrib['rc_log_type'];
+                               $packet['log_action'] = 
$attrib['rc_log_action'];
+                               if ( $attrib['rc_params'] ) {
+                                       wfSuppressWarnings();
+                                       $params = unserialize( 
$attrib['rc_params'] );
+                                       wfRestoreWarnings();
+                                       if (
+                                               // If it's an actual serialised 
false...
+                                               $attrib['rc_params'] == 
serialize( false ) ||
+                                               // Or if we did not get false 
back when trying to unserialise
+                                               $params !== false
+                                       ) {
+                                               // From 
ApiQueryLogEvents::addLogParams
+                                               $logParams = array();
+                                               // Keys like "4::paramname" 
can't be used for output so we change them to "paramname"
+                                               foreach ( $params as $key => 
$value ) {
+                                                       if ( strpos( $key, ':' 
) === false ) {
+                                                               
$logParams[$key] = $value;
+                                                               continue;
+                                                       }
+                                                       $logParam = explode( 
':', $key, 3 );
+                                                       
$logParams[$logParam[2]] = $value;
+                                               }
+                                               $packet['log_params'] = 
$logParams;
+                                       } else {
+                                               $packet['log_params'] = 
explode( "\n", $attrib['rc_params'] );
+                                       }
+                               }
+                               $packet['log_action_comment'] = $actionComment;
+                               break;
+               }
+
+               $packet['server_url'] = $wgCanonicalServer;
+               $packet['server_script_path'] = $wgScriptPath ?: '/';
+               $packet['wiki'] = $wgDBname;
+
+               return FormatJson::encode( $packet );
+       }
+}
diff --git a/includes/rcfeed/RCFeedEngine.php b/includes/rcfeed/RCFeedEngine.php
new file mode 100644
index 0000000..f733bcb
--- /dev/null
+++ b/includes/rcfeed/RCFeedEngine.php
@@ -0,0 +1,12 @@
+<?php
+interface RCFeedEngine {
+       /**
+        * Sends some text to the specified live feed.
+        *
+        * @see RecentChange::cleanupForIRC
+        * @param array $feed The feed, as configured in an associative array.
+        * @param string $line The text to send.
+        * @return boolean success
+        */
+       public function send( array $feed, $line );
+}
diff --git a/includes/rcfeed/RCFeedFormatter.php 
b/includes/rcfeed/RCFeedFormatter.php
new file mode 100644
index 0000000..6c9f804
--- /dev/null
+++ b/includes/rcfeed/RCFeedFormatter.php
@@ -0,0 +1,13 @@
+<?php
+interface RCFeedFormatter {
+       /**
+        * Formats the line for the live feed.
+        *
+        * @param array $feed The feed, as configured in an associative array.
+        * @param RecentChange $rc The RecentChange object showing what sort
+        *                         of event has taken place.
+        * @param string|null $actionComment
+        * @return string The text to send.
+        */
+       public function getLine( array $feed, RecentChange $rc, $actionComment 
);
+}
diff --git a/includes/rcfeed/UDPRCFeedEngine.php 
b/includes/rcfeed/UDPRCFeedEngine.php
new file mode 100644
index 0000000..beeb73b
--- /dev/null
+++ b/includes/rcfeed/UDPRCFeedEngine.php
@@ -0,0 +1,10 @@
+<?php
+class UDPRCFeedEngine implements RCFeedEngine {
+       /**
+        * Sends the notification to the specified host in a UDP packet.
+        * @see RCFeedEngine::send
+        */
+       public function send( array $feed, $line ) {
+               wfErrorLog( $line, $feed['uri'] );
+       }
+}
diff --git a/tests/phpunit/includes/RecentChangeTest.php 
b/tests/phpunit/includes/RecentChangeTest.php
index 8e476b31b..f01fb235 100644
--- a/tests/phpunit/includes/RecentChangeTest.php
+++ b/tests/phpunit/includes/RecentChangeTest.php
@@ -267,8 +267,8 @@
                $formatter = LogFormatter::newFromEntry( $logEntry );
                $formatter->setContext( $this->context );
 
-               // Apply the same transformation as done in 
RecentChange::getIRCLine for rc_comment
-               $ircRcComment = RecentChange::cleanupForIRC( 
$formatter->getIRCActionComment() );
+               // Apply the same transformation as done in 
IRCColourfulRCFeedFormatter::getLine for rc_comment
+               $ircRcComment = IRCColourfulRCFeedFormatter::cleanupForIRC( 
$formatter->getIRCActionComment() );
 
                $this->assertEquals(
                        $expected,

-- 
To view, visit https://gerrit.wikimedia.org/r/52922
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I270bde418a82985c94372ac4579100435b6ee026
Gerrit-PatchSet: 25
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Victor Vasiliev <[email protected]>
Gerrit-Reviewer: Alex Monk <[email protected]>
Gerrit-Reviewer: Anomie <[email protected]>
Gerrit-Reviewer: Aude <[email protected]>
Gerrit-Reviewer: Brian Wolff <[email protected]>
Gerrit-Reviewer: CSteipp <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Hashar <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: MZMcBride <[email protected]>
Gerrit-Reviewer: Mattflaschen <[email protected]>
Gerrit-Reviewer: Nikerabbit <[email protected]>
Gerrit-Reviewer: Ori.livneh <[email protected]>
Gerrit-Reviewer: Parent5446 <[email protected]>
Gerrit-Reviewer: Petrb <[email protected]>
Gerrit-Reviewer: Victor Vasiliev <[email protected]>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to