From 1c6b7c74859e737f12d7d5e9cfd58014eead2d3c Mon Sep 17 00:00:00 2001 From: aude Date: Tue, 26 Nov 2013 12:56:17 +0100 Subject: [PATCH] Add RCCacheEntryBuilder, split from EnhancedChangesList and cleaned up This makes it more feasible for Wikibase, Flow, etc. to support enhanced changes format, and allow better support for the rc_source column in the future. Change-Id: I873f6b86007000a94337f0c963df4bf8fec5b715 --- includes/AutoLoader.php | 1 + includes/changes/EnhancedChangesList.php | 151 ++----- includes/changes/RCCacheEntryFactory.php | 278 ++++++++++++ .../changes/RCCacheEntryFactoryTest.php | 405 ++++++++++++++++++ 4 files changed, 725 insertions(+), 110 deletions(-) create mode 100644 includes/changes/RCCacheEntryFactory.php create mode 100644 tests/phpunit/includes/changes/RCCacheEntryFactoryTest.php diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 87dc95d95e..4b898aa4b9 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -392,6 +392,7 @@ $wgAutoloadLocalClasses = array( 'EnhancedChangesList' => 'includes/changes/EnhancedChangesList.php', 'OldChangesList' => 'includes/changes/OldChangesList.php', 'RCCacheEntry' => 'includes/changes/RCCacheEntry.php', + 'RCCacheEntryFactory' => 'includes/changes/RCCacheEntryFactory.php', 'RecentChange' => 'includes/changes/RecentChange.php', # includes/clientpool diff --git a/includes/changes/EnhancedChangesList.php b/includes/changes/EnhancedChangesList.php index df60f021b0..feb6d7b2bf 100644 --- a/includes/changes/EnhancedChangesList.php +++ b/includes/changes/EnhancedChangesList.php @@ -21,9 +21,42 @@ */ class EnhancedChangesList extends ChangesList { - /** @var array Array of array of RCCacheEntry */ + + /** + * @var RCCacheEntryFactory + */ + protected $cacheEntryFactory; + + /** + * @var array Array of array of RCCacheEntry + */ protected $rc_cache; + /** + * @param IContextSource|Skin $obj + */ + public function __construct( $obj ) { + if ( $obj instanceof Skin ) { + // @todo: deprecate constructing with Skin + $context = $obj->getContext(); + } else { + if ( ! $obj instanceof IContextSource ) { + throw new MWException( 'EnhancedChangesList must be constructed with a ' + . 'context source or skin.' ); + } + + $context = $obj; + } + + parent::__construct( $context ); + + // message is set by the parent ChangesList class + $this->cacheEntryFactory = new RCCacheEntryFactory( + $context, + $this->message + ); + } + /** * Add the JavaScript file for enhanced changeslist * @return string @@ -73,127 +106,25 @@ class EnhancedChangesList extends ChangesList { $this->lastdate = $date; } - # Create a specialised object - $cacheEntry = RCCacheEntry::newFromParent( $baseRC ); - - $curIdEq = array( 'curid' => $cacheEntry->mAttribs['rc_cur_id'] ); - - # Should patrol-related stuff be shown? - $cacheEntry->unpatrolled = $this->showAsUnpatrolled( $cacheEntry ); - - $showdifflinks = true; - - # Make article link - $type = $cacheEntry->mAttribs['rc_type']; - $logType = $cacheEntry->mAttribs['rc_log_type']; - - // Page moves, very old style, not supported anymore - if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) { - $clink = ''; - // New unpatrolled pages - } elseif ( $cacheEntry->unpatrolled && $type == RC_NEW ) { - $clink = Linker::linkKnown( $cacheEntry->getTitle() ); - // Log entries - } elseif ( $type == RC_LOG ) { - if ( $logType ) { - $logtitle = SpecialPage::getTitleFor( 'Log', $logType ); - $logpage = new LogPage( $logType ); - $logname = $logpage->getName()->escaped(); - $clink = $this->msg( 'parentheses' ) - ->rawParams( Linker::linkKnown( $logtitle, $logname ) )->escaped(); - } else { - $clink = Linker::link( $cacheEntry->getTitle() ); - } - $watched = false; - // Log entries (old format) and special pages - } elseif ( $cacheEntry->mAttribs['rc_namespace'] == NS_SPECIAL ) { - wfDebug( "Unexpected special page in recentchanges\n" ); - $clink = ''; - // Edits - } else { - $clink = Linker::linkKnown( $cacheEntry->getTitle() ); - } - - # Don't show unusable diff links - if ( !ChangesList::userCan( $cacheEntry, Revision::DELETED_TEXT, $this->getUser() ) ) { - $showdifflinks = false; - } - - $time = $this->getLanguage()->userTime( $cacheEntry->mAttribs['rc_timestamp'], $this->getUser() ); - - $cacheEntry->watched = $watched; - $cacheEntry->link = $clink; - $cacheEntry->timestamp = $time; - $cacheEntry->numberofWatchingusers = $baseRC->numberofWatchingusers; - - # Make "cur" and "diff" links. Do not use link(), it is too slow if - # called too many times (50% of CPU time on RecentChanges!). - $thisOldid = $cacheEntry->mAttribs['rc_this_oldid']; - $lastOldid = $cacheEntry->mAttribs['rc_last_oldid']; - - $querycur = $curIdEq + array( 'diff' => '0', 'oldid' => $thisOldid ); - $querydiff = $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid ); - - if ( !$showdifflinks ) { - $curLink = $this->message['cur']; - $diffLink = $this->message['diff']; - } elseif ( in_array( $type, array( RC_NEW, RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) { - if ( $type != RC_NEW ) { - $curLink = $this->message['cur']; - } else { - $curUrl = htmlspecialchars( $cacheEntry->getTitle()->getLinkURL( $querycur ) ); - $curLink = "counter}\">{$this->message['cur']}"; - } - $diffLink = $this->message['diff']; - } else { - $diffUrl = htmlspecialchars( $cacheEntry->getTitle()->getLinkURL( $querydiff ) ); - $curUrl = htmlspecialchars( $cacheEntry->getTitle()->getLinkURL( $querycur ) ); - $diffLink = "counter}\">{$this->message['diff']}"; - $curLink = "counter}\">{$this->message['cur']}"; - } - - # Make "last" link - if ( !$showdifflinks || !$lastOldid ) { - $lastLink = $this->message['last']; - } elseif ( in_array( $type, array( RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) { - $lastLink = $this->message['last']; - } else { - $lastLink = Linker::linkKnown( $cacheEntry->getTitle(), $this->message['last'], - array(), $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid ) ); - } - - # Make user links - if ( $this->isDeleted( $cacheEntry, Revision::DELETED_USER ) ) { - $cacheEntry->userlink = ' ' . - $this->msg( 'rev-deleted-user' )->escaped() . ''; - } else { - $cacheEntry->userlink = Linker::userLink( - $cacheEntry->mAttribs['rc_user'], - $cacheEntry->mAttribs['rc_user_text'] - ); - - $cacheEntry->usertalklink = Linker::userToolLinks( - $cacheEntry->mAttribs['rc_user'], - $cacheEntry->mAttribs['rc_user_text'] - ); - } - - $cacheEntry->lastlink = $lastLink; - $cacheEntry->curlink = $curLink; - $cacheEntry->difflink = $diffLink; + $cacheEntry = $this->cacheEntryFactory->newFromRecentChange( $baseRC, $watched ); # Put accumulated information into the cache, for later display # Page moves go on their own line $title = $cacheEntry->getTitle(); $secureName = $title->getPrefixedDBkey(); + $type = $cacheEntry->mAttribs['rc_type']; + if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) { # Use an @ character to prevent collision with page names $this->rc_cache['@@' . ( $this->rcMoveIndex++ )] = array( $cacheEntry ); } else { # Logs are grouped by type if ( $type == RC_LOG ) { - $secureName = SpecialPage::getTitleFor( 'Log', $logType )->getPrefixedDBkey(); + $secureName = SpecialPage::getTitleFor( + 'Log', + $cacheEntry->mAttribs['rc_log_type'] + )->getPrefixedDBkey(); } if ( !isset( $this->rc_cache[$secureName] ) ) { $this->rc_cache[$secureName] = array(); diff --git a/includes/changes/RCCacheEntryFactory.php b/includes/changes/RCCacheEntryFactory.php new file mode 100644 index 0000000000..49ce1b39ce --- /dev/null +++ b/includes/changes/RCCacheEntryFactory.php @@ -0,0 +1,278 @@ +context = $context; + $this->messages = $messages; + } + + /** + * @param RecentChange $baseRC + * @param boolean $watched + * + * @return RCCacheEntry + */ + public function newFromRecentChange( RecentChange $baseRC, $watched ) { + $user = $this->context->getUser(); + $counter = $baseRC->counter; + + $cacheEntry = RCCacheEntry::newFromParent( $baseRC ); + + // Should patrol-related stuff be shown? + $cacheEntry->unpatrolled = ChangesList::isUnpatrolled( $baseRC, $user ); + + $cacheEntry->watched = $cacheEntry->mAttribs['rc_type'] == RC_LOG ? false : $watched; + $cacheEntry->numberofWatchingusers = $baseRC->numberofWatchingusers; + + $cacheEntry->link = $this->buildCLink( $cacheEntry ); + $cacheEntry->timestamp = $this->buildTimestamp( $cacheEntry ); + + // Make "cur" and "diff" links. Do not use link(), it is too slow if + // called too many times (50% of CPU time on RecentChanges!). + $showDiffLinks = $this->showDiffLinks( $cacheEntry, $user ); + + $cacheEntry->difflink = $this->buildDiffLink( $cacheEntry, $showDiffLinks, $counter ); + $cacheEntry->curlink = $this->buildCurLink( $cacheEntry, $showDiffLinks, $counter ); + $cacheEntry->lastlink = $this->buildLastLink( $cacheEntry, $showDiffLinks ); + + // Make user links + $cacheEntry->userlink = $this->getUserLink( $cacheEntry ); + + if ( !ChangesList::isDeleted( $cacheEntry, Revision::DELETED_USER ) ) { + $cacheEntry->usertalklink = Linker::userToolLinks( + $cacheEntry->mAttribs['rc_user'], + $cacheEntry->mAttribs['rc_user_text'] + ); + } + + return $cacheEntry; + } + + /** + * @param RecentChange $cacheEntry + * @param User $User + * + * @return boolean + */ + private function showDiffLinks( RecentChange $cacheEntry, User $user ) { + return ChangesList::userCan( $cacheEntry, Revision::DELETED_TEXT, $user ); + } + + /** + * @param RecentChange $cacheEntry + * + * @return string + */ + private function buildCLink( RecentChange $cacheEntry ) { + $type = $cacheEntry->mAttribs['rc_type']; + + // Page moves, very old style, not supported anymore + if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) { + $clink = ''; + // New unpatrolled pages + } elseif ( $cacheEntry->unpatrolled && $type == RC_NEW ) { + $clink = Linker::linkKnown( $cacheEntry->getTitle() ); + // Log entries + } elseif ( $type == RC_LOG ) { + $logType = $cacheEntry->mAttribs['rc_log_type']; + + if ( $logType ) { + $clink = $this->getLogLink( $logType ); + } else { + wfDebugLog( 'recentchanges', 'Unexpected log entry with no log type in recent changes' ); + $clink = Linker::link( $cacheEntry->getTitle() ); + } + // Log entries (old format) and special pages + } elseif ( $cacheEntry->mAttribs['rc_namespace'] == NS_SPECIAL ) { + wfDebugLog( 'recentchanges', 'Unexpected special page in recentchanges' ); + $clink = ''; + // Edits + } else { + $clink = Linker::linkKnown( $cacheEntry->getTitle() ); + } + + return $clink; + } + + private function getLogLink( $logType ) { + $logtitle = SpecialPage::getTitleFor( 'Log', $logType ); + $logpage = new LogPage( $logType ); + $logname = $logpage->getName()->escaped(); + + $logLink = $this->context->msg( 'parentheses' ) + ->rawParams( Linker::linkKnown( $logtitle, $logname ) )->escaped(); + + return $logLink; + } + + /** + * @param RecentChange $cacheEntry + * + * @return string + */ + private function buildTimestamp( RecentChange $cacheEntry ) { + return $this->context->getLanguage()->userTime( + $cacheEntry->mAttribs['rc_timestamp'], + $this->context->getUser() + ); + } + + /** + * @param RecentChange $recentChange + * + * @return array + */ + private function buildCurQueryParams( RecentChange $recentChange ) { + return array( + 'curid' => $recentChange->mAttribs['rc_cur_id'], + 'diff' => 0, + 'oldid' => $recentChange->mAttribs['rc_this_oldid'] + ); + } + + /** + * @param RecentChange $cacheEntry + * @param boolean $showDiffLinks + * @param int $counter + * + * @return string + */ + private function buildCurLink( RecentChange $cacheEntry, $showDiffLinks, $counter ) { + $queryParams = $this->buildCurQueryParams( $cacheEntry ); + $curMessage = $this->getMessage( 'cur' ); + $logTypes = array( RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ); + + if ( !$showDiffLinks || in_array( $cacheEntry->mAttribs['rc_type'], $logTypes ) ) { + $curLink = $curMessage; + } else { + $curUrl = htmlspecialchars( $cacheEntry->getTitle()->getLinkURL( $queryParams ) ); + $curLink = "$curMessage"; + } + + return $curLink; + } + + /** + * @param RecentChange $recentChange + * + * @return array + */ + private function buildDiffQueryParams( RecentChange $recentChange ) { + return array( + 'curid' => $recentChange->mAttribs['rc_cur_id'], + 'diff' => $recentChange->mAttribs['rc_this_oldid'], + 'oldid' => $recentChange->mAttribs['rc_last_oldid'] + ); + } + + /** + * @param RecentChange $cacheEntry + * @param boolean $showDiffLinks + * @param int $counter + * + * @return string + */ + private function buildDiffLink( RecentChange $cacheEntry, $showDiffLinks, $counter ) { + $queryParams = $this->buildDiffQueryParams( $cacheEntry ); + $diffMessage = $this->getMessage( 'diff' ); + $logTypes = array( RC_NEW, RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ); + + if ( !$showDiffLinks ) { + $diffLink = $diffMessage; + } elseif ( in_array( $cacheEntry->mAttribs['rc_type'], $logTypes ) ) { + $diffLink = $diffMessage; + } else { + $diffUrl = htmlspecialchars( $cacheEntry->getTitle()->getLinkURL( $queryParams ) ); + $diffLink = "$diffMessage"; + } + + return $diffLink; + } + + /** + * @param RecentChange $cacheEntry + * @param boolean $showDiffLinks + * + * @return string + */ + private function buildLastLink( RecentChange $cacheEntry, $showDiffLinks ) { + $lastOldid = $cacheEntry->mAttribs['rc_last_oldid']; + $lastMessage = $this->getMessage( 'last' ); + $type = $cacheEntry->mAttribs['rc_type']; + $logTypes = array( RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ); + + // Make "last" link + if ( !$showDiffLinks || !$lastOldid || in_array( $type, $logTypes ) ) { + $lastLink = $lastMessage; + } else { + $lastLink = Linker::linkKnown( + $cacheEntry->getTitle(), + $lastMessage, + array(), + $this->buildDiffQueryParams( $cacheEntry ) + ); + } + + return $lastLink; + } + + /** + * @param RecentChange $cacheEntry + * + * @return string + */ + private function getUserLink( RecentChange $cacheEntry ) { + if ( ChangesList::isDeleted( $cacheEntry, Revision::DELETED_USER ) ) { + $userLink = ' ' . + $this->context->msg( 'rev-deleted-user' )->escaped() . ''; + } else { + $userLink = Linker::userLink( + $cacheEntry->mAttribs['rc_user'], + $cacheEntry->mAttribs['rc_user_text'] + ); + } + + return $userLink; + } + + /** + * @param string $key + * + * @return string + */ + private function getMessage( $key ) { + return $this->messages[$key]; + } + +} diff --git a/tests/phpunit/includes/changes/RCCacheEntryFactoryTest.php b/tests/phpunit/includes/changes/RCCacheEntryFactoryTest.php new file mode 100644 index 0000000000..dc47e359ee --- /dev/null +++ b/tests/phpunit/includes/changes/RCCacheEntryFactoryTest.php @@ -0,0 +1,405 @@ + + */ +class RCCacheEntryFactoryTest extends MediaWikiLangTestCase { + + protected function setUp() { + parent::setUp(); + + $this->setMwGlobals( array( + 'wgArticlePath' => '/wiki/$1' + ) ); + } + + /** + * @dataProvider editChangeProvider + */ + public function testNewFromRecentChange( $expected, $context, $messages, $recentChange, $watched ) { + $cacheEntryFactory = new RCCacheEntryFactory( $context, $messages ); + $cacheEntry = $cacheEntryFactory->newFromRecentChange( $recentChange, $watched ); + + $this->assertInstanceOf( 'RCCacheEntry', $cacheEntry ); + + $this->assertEquals( $watched, $cacheEntry->watched, 'watched' ); + $this->assertEquals( $expected['timestamp'], $cacheEntry->timestamp, 'timestamp' ); + $this->assertEquals( $expected['numberofWatchingusers'], $cacheEntry->numberofWatchingusers, 'watching users' ); + $this->assertEquals( $expected['unpatrolled'], $cacheEntry->unpatrolled, 'unpatrolled' ); + + $this->assertUserLinks( 'Mary', $cacheEntry ); + $this->assertTitleLink( 'Xyz', $cacheEntry ); + + $this->assertQueryLink( 'cur', $expected['cur'], $cacheEntry->curlink, 'cur link' ); + $this->assertQueryLink( 'prev', $expected['diff'], $cacheEntry->lastlink, 'prev link' ); + $this->assertQueryLink( 'diff', $expected['diff'], $cacheEntry->difflink, 'diff link' ); + } + + public function editChangeProvider() { + return array( + array( + array( + 'title' => 'Xyz', + 'user' => 'Mary', + 'diff' => array( 'curid' => 5, 'diff' => 191, 'oldid' => 190 ), + 'cur' => array( 'curid' => 5, 'diff' => 0, 'oldid' => 191 ), + 'timestamp' => '21:21', + 'numberofWatchingusers' => 0, + 'unpatrolled' => false + ), + $this->getContext(), + $this->getMessages(), + $this->makeEditRecentChange( + 'Xyz', + $this->getTestUser(), + 5, // curid + 191, // thisid + 190, // lastid + '20131103212153', + 0, // counter + 0 // number of watching users + ), + false, + 'edit' + ) + ); + } + + private function makeEditRecentChange( $title, $user, $curid, $thisid, $lastid, + $timestamp, $counter, $watchingUsers + ) { + + $attribs = array_merge( + $this->getDefaultAttributes( $title, $timestamp ), + array( + 'rc_user' => $user->getId(), + 'rc_user_text' => $user->getName(), + 'rc_this_oldid' => $thisid, + 'rc_last_oldid' => $lastid, + 'rc_cur_id' => $curid + ) + ); + + return $this->makeRecentChange( $attribs, $counter, $watchingUsers ); + } + + /** + * @dataProvider deleteChangeProvider + */ + public function testNewForDeleteChange( $expected, $context, $messages, $recentChange, $watched ) { + $cacheEntryFactory = new RCCacheEntryFactory( $context, $messages ); + $cacheEntry = $cacheEntryFactory->newFromRecentChange( $recentChange, $watched ); + + $this->assertInstanceOf( 'RCCacheEntry', $cacheEntry ); + + $this->assertEquals( $watched, $cacheEntry->watched, 'watched' ); + $this->assertEquals( $expected['timestamp'], $cacheEntry->timestamp, 'timestamp' ); + $this->assertEquals( $expected['numberofWatchingusers'], $cacheEntry->numberofWatchingusers, 'watching users' ); + $this->assertEquals( $expected['unpatrolled'], $cacheEntry->unpatrolled, 'unpatrolled' ); + + $this->assertDeleteLogLink( $cacheEntry ); + $this->assertUserLinks( 'Mary', $cacheEntry ); + + $this->assertEquals( 'cur', $cacheEntry->curlink, 'cur link for delete log or rev' ); + $this->assertEquals( 'diff', $cacheEntry->difflink, 'diff link for delete log or rev' ); + $this->assertEquals( 'prev', $cacheEntry->lastlink, 'pref link for delete log or rev' ); + } + + public function deleteChangeProvider() { + return array( + array( + array( + 'title' => 'Abc', + 'user' => 'Mary', + 'timestamp' => '21:21', + 'numberofWatchingusers' => 0, + 'unpatrolled' => false + ), + $this->getContext(), + $this->getMessages(), + $this->makeLogRecentChange( + 'Abc', + $this->getTestUser(), + '20131103212153', + 0, // counter + 0 // number of watching users + ), + false, + 'delete' + ) + ); + } + + private function makeLogRecentChange( $title, $user, $timestamp, $counter, $watchingUsers ) { + $attribs = array_merge( + $this->getDefaultAttributes( $title, $timestamp ), + array( + 'rc_cur_id' => 0, + 'rc_user' => $user->getId(), + 'rc_user_text' => $user->getName(), + 'rc_this_oldid' => 0, + 'rc_last_oldid' => 0, + 'rc_old_len' => null, + 'rc_new_len' => null, + 'rc_type' => 3, + 'rc_logid' => 25, + 'rc_log_type' => 'delete', + 'rc_log_action' => 'delete' + ) + ); + + return $this->makeRecentChange( $attribs, $counter, $watchingUsers ); + } + + /** + * @dataProvider revUserDeleteProvider + */ + public function testNewForRevUserDeleteChange( $expected, $context, $messages, + $recentChange, $watched + ) { + $cacheEntryFactory = new RCCacheEntryFactory( $context, $messages ); + $cacheEntry = $cacheEntryFactory->newFromRecentChange( $recentChange, $watched ); + + $this->assertInstanceOf( 'RCCacheEntry', $cacheEntry ); + + $this->assertEquals( $watched, $cacheEntry->watched, 'watched' ); + $this->assertEquals( $expected['timestamp'], $cacheEntry->timestamp, 'timestamp' ); + $this->assertEquals( $expected['numberofWatchingusers'], $cacheEntry->numberofWatchingusers, 'watching users' ); + $this->assertEquals( $expected['unpatrolled'], $cacheEntry->unpatrolled, 'unpatrolled' ); + + $this->assertRevDel( $cacheEntry ); + $this->assertTitleLink( 'Zzz', $cacheEntry ); + + $this->assertEquals( 'cur', $cacheEntry->curlink, 'cur link for delete log or rev' ); + $this->assertEquals( 'diff', $cacheEntry->difflink, 'diff link for delete log or rev' ); + $this->assertEquals( 'prev', $cacheEntry->lastlink, 'pref link for delete log or rev' ); + } + + public function revUserDeleteProvider() { + return array( + array( + array( + 'title' => 'Zzz', + 'user' => 'Mary', + 'diff' => '', + 'cur' => '', + 'timestamp' => '21:21', + 'numberofWatchingusers' => 0, + 'unpatrolled' => false + ), + $this->getContext(), + $this->getMessages(), + $this->makeDeletedEditRecentChange( + 'Zzz', + $this->getTestUser(), + '20131103212153', + 191, // thisid + 190, // lastid + '20131103212153', + 0, // counter + 0 // number of watching users + ), + false, + 'deletedrevuser' + ) + ); + } + + private function makeDeletedEditRecentChange( $title, $user, $timestamp, $curid, $thisid, + $lastid, $counter, $watchingUsers + ) { + $attribs = array_merge( + $this->getDefaultAttributes( $title, $timestamp ), + array( + 'rc_user' => $user->getId(), + 'rc_user_text' => $user->getName(), + 'rc_deleted' => 5, + 'rc_cur_id' => $curid, + 'rc_this_oldid' => $thisid, + 'rc_last_oldid' => $lastid + ) + ); + + return $this->makeRecentChange( $attribs, $counter, $watchingUsers ); + } + + private function assertUserLinks( $user, $cacheEntry ) { + $this->assertTag( + array( + 'tag' => 'a', + 'attributes' => array( + 'class' => 'new mw-userlink' + ), + 'content' => $user + ), + $cacheEntry->userlink, + 'verify user link' + ); + + $this->assertTag( + array( + 'tag' => 'span', + 'attributes' => array( + 'class' => 'mw-usertoollinks' + ), + 'child' => array( + 'tag' => 'a', + 'content' => 'Talk', + ) + ), + $cacheEntry->usertalklink, + 'verify user talk link' + ); + + $this->assertTag( + array( + 'tag' => 'span', + 'attributes' => array( + 'class' => 'mw-usertoollinks' + ), + 'child' => array( + 'tag' => 'a', + 'content' => 'contribs', + ) + ), + $cacheEntry->usertalklink, + 'verify user tool links' + ); + } + + private function assertDeleteLogLink( $cacheEntry ) { + $this->assertTag( + array( + 'tag' => 'a', + 'attributes' => array( + 'href' => '/wiki/Special:Log/delete', + 'title' => 'Special:Log/delete' + ), + 'content' => 'Deletion log' + ), + $cacheEntry->link, + 'verify deletion log link' + ); + } + + private function assertRevDel( $cacheEntry ) { + $this->assertTag( + array( + 'tag' => 'span', + 'attributes' => array( + 'class' => 'history-deleted' + ), + 'content' => '(username removed)' + ), + $cacheEntry->userlink, + 'verify user link for change with deleted revision and user' + ); + } + + private function assertTitleLink( $title, $cacheEntry ) { + $this->assertTag( + array( + 'tag' => 'a', + 'attributes' => array( + 'href' => '/wiki/' . $title, + 'title' => $title + ), + 'content' => $title + ), + $cacheEntry->link, + 'verify title link' + ); + } + + private function assertQueryLink( $content, $params, $link ) { + $this->assertTag( + array( + 'tag' => 'a', + 'content' => $content + ), + $link, + 'assert query link element' + ); + + foreach( $params as $key => $value ) { + $this->assertRegExp( '/' . $key . '=' . $value . '/', $link, "verify $key link params" ); + } + } + + private function makeRecentChange( $attribs, $counter, $watchingUsers ) { + $change = new RecentChange(); + $change->setAttribs( $attribs ); + $change->counter = $counter; + $change->numberofWatchingusers = $watchingUsers; + + return $change; + } + + private function getDefaultAttributes( $title, $timestamp ) { + return array( + 'rc_id' => 545, + 'rc_user' => 0, + 'rc_user_text' => '127.0.0.1', + 'rc_ip' => '127.0.0.1', + 'rc_title' => $title, + 'rc_namespace' => 0, + 'rc_timestamp' => $timestamp, + 'rc_cur_time' => $timestamp, + 'rc_old_len' => 212, + 'rc_new_len' => 188, + 'rc_comment' => '', + 'rc_minor' => 0, + 'rc_bot' => 0, + 'rc_type' => 0, + 'rc_patrolled' => 1, + 'rc_deleted' => 0, + 'rc_logid' => 0, + 'rc_log_type' => null, + 'rc_log_action' => '', + 'rc_params' => '', + 'rc_source' => 'mw.edit' + ); + } + + private function getTestUser() { + $user = User::newFromName( 'Mary' ); + + if ( ! $user->getId() ) { + $user->addToDatabase(); + } + + return $user; + } + + private function getMessages() { + return array( + 'cur' => 'cur', + 'diff' => 'diff', + 'hist' => 'hist', + 'enhancedrc-history' => 'history', + 'last' => 'prev', + 'blocklink' => 'block', + 'history' => 'Page history', + 'semicolon-separator' => '; ', + 'pipe-separator' => ' | ' + ); + } + + private function getContext() { + $title = Title::newFromText( 'RecentChanges', NS_SPECIAL ); + + $context = new RequestContext(); + $context->setTitle( $title ); + $context->setLanguage( Language::factory( 'en' ) ); + + $user = $this->getTestUser(); + $context->setUser( $user ); + + return $context; + } + +} -- 2.20.1