From 2961884b43b505b7ddc30212f14eed56250e8e18 Mon Sep 17 00:00:00 2001 From: Victor Vasiliev Date: Mon, 4 Mar 2013 14:43:22 -0500 Subject: [PATCH] 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 --- RELEASE-NOTES-1.22 | 10 + includes/AutoLoader.php | 7 + includes/DefaultSettings.php | 55 ++++++ includes/RecentChange.php | 187 +++++++----------- includes/Setup.php | 9 + includes/logging/LogEntry.php | 2 +- .../rcfeed/IRCColourfulRCFeedFormatter.php | 99 ++++++++++ includes/rcfeed/JSONRCFeedFormatter.php | 89 +++++++++ includes/rcfeed/RCFeedEngine.php | 12 ++ includes/rcfeed/RCFeedFormatter.php | 13 ++ includes/rcfeed/UDPRCFeedEngine.php | 10 + tests/phpunit/includes/RecentChangeTest.php | 4 +- 12 files changed, 377 insertions(+), 120 deletions(-) create mode 100644 includes/rcfeed/IRCColourfulRCFeedFormatter.php create mode 100644 includes/rcfeed/JSONRCFeedFormatter.php create mode 100644 includes/rcfeed/RCFeedEngine.php create mode 100644 includes/rcfeed/RCFeedFormatter.php create mode 100644 includes/rcfeed/UDPRCFeedEngine.php diff --git a/RELEASE-NOTES-1.22 b/RELEASE-NOTES-1.22 index 239d997b8e..50e273236d 100644 --- a/RELEASE-NOTES-1.22 +++ b/RELEASE-NOTES-1.22 @@ -49,6 +49,11 @@ production. * 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 @@ changes to languages because of Bugzilla reports. 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 2f92ab0927..b830b16835 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -835,6 +835,13 @@ $wgAutoloadLocalClasses = array( '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 92cbab353a..20c72ee7a2 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -5394,11 +5394,15 @@ $wgRCLinkDays = array( 1, 3, 7, 14, 30 ); /** * 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,21 +5411,72 @@ $wgRC2UDPPort = false; * 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. diff --git a/includes/RecentChange.php b/includes/RecentChange.php index 24db569a4b..939e9246db 100644 --- a/includes/RecentChange.php +++ b/includes/RecentChange.php @@ -263,7 +263,7 @@ class RecentChange { # Notify external application via UDP if ( !$noudp ) { - $this->notifyRC2UDP(); + $this->notifyRCFeeds(); } # E-mail notifications @@ -284,54 +284,90 @@ class RecentChange { } } + /** + * @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; + 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 ); + } + + /** + * 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 { - wfDebug( __METHOD__ . ": failed to create UDP socket\n" ); + $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 ); } - return false; } /** - * Remove newlines, carriage returns and decode html entities - * @param $text String - * @return String + * 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 ); } /** @@ -730,89 +766,6 @@ class RecentChange { 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; - } - /** * Returns the change size (HTML). * The lengths can be given optionally. diff --git a/includes/Setup.php b/includes/Setup.php index cfe5f1c69b..2e083d83cd 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -393,6 +393,15 @@ if ( $wgCookieSecure === 'detect' ) { $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 16b72ea63c..226a1ed3eb 100644 --- a/includes/logging/LogEntry.php +++ b/includes/logging/LogEntry.php @@ -544,7 +544,7 @@ class ManualLogEntry extends LogEntryBase { } 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 0000000000..507369f390 --- /dev/null +++ b/includes/rcfeed/IRCColourfulRCFeedFormatter.php @@ -0,0 +1,99 @@ +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 0000000000..d50139c61f --- /dev/null +++ b/includes/rcfeed/JSONRCFeedFormatter.php @@ -0,0 +1,89 @@ +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 0000000000..f733bcb71d --- /dev/null +++ b/includes/rcfeed/RCFeedEngine.php @@ -0,0 +1,12 @@ +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, -- 2.20.1