X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fchanges%2FCategoryMembershipChange.php;h=b4086f9be657a286ba32ba32f456c7c045f3e170;hb=6ae9367cddf8565c948f4d9458b07bb688f2ddc2;hp=8bc4cf57e428ffa9bcdf7299d24ec2e844645d7e;hpb=8a6548ee452ef08cec6248fbf4e215be812088b2;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/changes/CategoryMembershipChange.php b/includes/changes/CategoryMembershipChange.php index 8bc4cf57e4..b4086f9be6 100644 --- a/includes/changes/CategoryMembershipChange.php +++ b/includes/changes/CategoryMembershipChange.php @@ -19,140 +19,213 @@ * * @file * @author Kai Nissen - * @since 1.26 + * @author Adam Shorland + * @since 1.27 */ +use Wikimedia\Assert\Assert; + class CategoryMembershipChange { const CATEGORY_ADDITION = 1; const CATEGORY_REMOVAL = -1; - /** @var string Current timestamp, set during CategoryMembershipChange::__construct() */ + /** + * @var string Current timestamp, set during CategoryMembershipChange::__construct() + */ private $timestamp; - /** @var Title Title instance of the categorized page */ + /** + * @var Title Title instance of the categorized page + */ private $pageTitle; - /** @var WikiPage WikiPage instance of the categorized page */ - private $page; - - /** @var Revision Latest Revision instance of the categorized page */ + /** + * @var Revision|null Latest Revision instance of the categorized page + */ private $revision; /** * @var int - * Number of pages this WikiPage is embedded by; set by CategoryMembershipChange::setRecursive() + * Number of pages this WikiPage is embedded by + * Set by CategoryMembershipChange::checkTemplateLinks() */ private $numTemplateLinks = 0; /** - * @var User - * instance of the user that created CategoryMembershipChange::$revision - */ - private $user; - - /** - * @var null|RecentChange - * RecentChange that is referred to in CategoryMembershipChange::$revision + * @var callable|null */ - private $correspondingRC; + private $newForCategorizationCallback = null; /** * @param Title $pageTitle Title instance of the categorized page * @param Revision $revision Latest Revision instance of the categorized page + * * @throws MWException */ public function __construct( Title $pageTitle, Revision $revision = null ) { $this->pageTitle = $pageTitle; - $this->page = WikiPage::factory( $pageTitle ); $this->timestamp = wfTimestampNow(); + $this->revision = $revision; + $this->newForCategorizationCallback = array( 'RecentChange', 'newForCategorization' ); + } - # if no revision is given, the change was probably triggered by parser functions - if ( $revision ) { - $this->revision = $revision; - $this->correspondingRC = $this->revision->getRecentChange(); - $this->user = $this->getRevisionUser(); - } else { - $this->user = User::newFromId( 0 ); + /** + * Overrides the default new for categorization callback + * This is intended for use while testing and will fail if MW_PHPUNIT_TEST is not defined. + * + * @param callable $callback + * @see RecentChange::newForCategorization for callback signiture + * + * @throws MWException + */ + public function overrideNewForCategorizationCallback( $callback ) { + if ( !defined( 'MW_PHPUNIT_TEST' ) ) { + throw new MWException( 'Cannot override newForCategorization callback in operation.' ); } + Assert::parameterType( 'callable', $callback, '$callback' ); + $this->newForCategorizationCallback = $callback; } /** * Determines the number of template links for recursive link updates */ - public function setRecursive() { + public function checkTemplateLinks() { $this->numTemplateLinks = $this->pageTitle->getBacklinkCache()->getNumLinks( 'templatelinks' ); } /** * Create a recentchanges entry for category additions - * @param string $categoryName + * + * @param Title $categoryTitle */ - public function pageAddedToCategory( $categoryName ) { - $this->createRecentChangesEntry( $categoryName, self::CATEGORY_ADDITION ); + public function triggerCategoryAddedNotification( Title $categoryTitle ) { + $this->createRecentChangesEntry( $categoryTitle, self::CATEGORY_ADDITION ); } /** * Create a recentchanges entry for category removals - * @param string $categoryName + * + * @param Title $categoryTitle */ - public function pageRemovedFromCategory( $categoryName ) { - $this->createRecentChangesEntry( $categoryName, self::CATEGORY_REMOVAL ); + public function triggerCategoryRemovedNotification( Title $categoryTitle ) { + $this->createRecentChangesEntry( $categoryTitle, self::CATEGORY_REMOVAL ); } /** * Create a recentchanges entry using RecentChange::notifyCategorization() - * @param string $categoryName + * + * @param Title $categoryTitle * @param int $type */ - private function createRecentChangesEntry( $categoryName, $type ) { - $categoryTitle = Title::newFromText( $categoryName, NS_CATEGORY ); - if ( !$categoryTitle ) { - return; - } + private function createRecentChangesEntry( Title $categoryTitle, $type ) { + $this->notifyCategorization( + $this->timestamp, + $categoryTitle, + $this->getUser(), + $this->getChangeMessageText( $type, array( + 'prefixedText' => $this->pageTitle->getPrefixedText(), + 'numTemplateLinks' => $this->numTemplateLinks + ) ), + $this->pageTitle, + $this->getPreviousRevisionTimestamp(), + $this->revision + ); + } - $previousRevTimestamp = $this->getPreviousRevisionTimestamp(); - $unpatrolled = $this->revision ? $this->revision->isUnpatrolled() : 0; + /** + * @param string $timestamp Timestamp of the recent change to occur in TS_MW format + * @param Title $categoryTitle Title of the category a page is being added to or removed from + * @param User $user User object of the user that made the change + * @param string $comment Change summary + * @param Title $pageTitle Title of the page that is being added or removed + * @param string $lastTimestamp Parent revision timestamp of this change in TS_MW format + * @param Revision|null $revision + * + * @throws MWException + */ + private function notifyCategorization( + $timestamp, + Title $categoryTitle, + User $user = null, + $comment, + Title $pageTitle, + $lastTimestamp, + $revision + ) { + $deleted = $revision ? $revision->getVisibility() & Revision::SUPPRESSED_USER : 0; + $newRevId = $revision ? $revision->getId() : 0; + /** + * T109700 - Default bot flag to true when there is no corresponding RC entry + * This means all changes caused by parser functions & Lua on reparse are marked as bot + * Also in the case no RC entry could be found due to slave lag + */ + $bot = 1; $lastRevId = 0; - $bot = 0; $ip = ''; - if ( $this->correspondingRC ) { - $lastRevId = $this->correspondingRC->getAttribute( 'rc_last_oldid' ) ?: 0; - $bot = $this->correspondingRC->getAttribute( 'rc_bot' ) ?: 0; - $ip = $this->correspondingRC->getAttribute( 'rc_ip' ) ?: ''; + + # If no revision is given, the change was probably triggered by parser functions + if ( $revision !== null ) { + $correspondingRc = $this->revision->getRecentChange(); + if ( $correspondingRc === null ) { + $correspondingRc = $this->revision->getRecentChange( Revision::READ_LATEST ); + } + if ( $correspondingRc !== null ) { + $bot = $correspondingRc->getAttribute( 'rc_bot' ) ?: 0; + $ip = $correspondingRc->getAttribute( 'rc_ip' ) ?: ''; + $lastRevId = $correspondingRc->getAttribute( 'rc_last_oldid' ) ?: 0; + } } - RecentChange::notifyCategorization( - $this->timestamp, - $categoryTitle, - $this->user, - $this->getChangeMessage( $type, array( - 'prefixedUrl' => $this->page->getTitle()->getPrefixedURL(), - 'numTemplateLinks' => $this->numTemplateLinks - ) ), - $this->pageTitle, - $lastRevId, - $this->revision ? $this->revision->getId() : 0, - $previousRevTimestamp, - $bot, - $ip, - $unpatrolled ? 0 : 1, - $this->revision ? $this->revision->getVisibility() & Revision::SUPPRESSED_USER : 0 + $rc = call_user_func_array( + $this->newForCategorizationCallback, + array( + $timestamp, + $categoryTitle, + $user, + $comment, + $pageTitle, + $lastRevId, + $newRevId, + $lastTimestamp, + $bot, + $ip, + $deleted + ) ); + $rc->save(); } /** - * Get the user who created the revision. may be an anonymous IP - * @return User + * Get the user associated with this change. + * + * If there is no revision associated with the change and thus no editing user + * fallback to a default. + * + * False will be returned if the user name specified in the + * 'autochange-username' message is invalid. + * + * @return User|bool */ - private function getRevisionUser() { - $userId = $this->revision->getUser( Revision::RAW ); - if ( $userId === 0 ) { - return User::newFromName( $this->revision->getUserText( Revision::RAW ), false ); - } else { - return User::newFromId( $userId ); + private function getUser() { + if ( $this->revision ) { + $userId = $this->revision->getUser( Revision::RAW ); + if ( $userId === 0 ) { + return User::newFromName( $this->revision->getUserText( Revision::RAW ), false ); + } else { + return User::newFromId( $userId ); + } + } + + $username = wfMessage( 'autochange-username' )->inContentLanguage()->text(); + $user = User::newFromName( $username ); + # User::newFromName() can return false on a badly configured wiki. + if ( $user && !$user->isLoggedIn() ) { + $user->addToDatabase(); } + + return $user; } /** @@ -167,21 +240,17 @@ class CategoryMembershipChange { * @param int $type may be CategoryMembershipChange::CATEGORY_ADDITION * or CategoryMembershipChange::CATEGORY_REMOVAL * @param array $params - * - prefixedUrl: result of Title::->getPrefixedURL() - * - numTemplateLinks + * - prefixedText: result of Title::->getPrefixedText() + * * @return string */ - private function getChangeMessage( $type, array $params ) { - $msgKey = 'recentchanges-'; - - switch ( $type ) { - case self::CATEGORY_ADDITION: - $msgKey .= 'page-added-to-category'; - break; - case self::CATEGORY_REMOVAL: - $msgKey .= 'page-removed-from-category'; - break; - } + private function getChangeMessageText( $type, array $params ) { + $array = array( + self::CATEGORY_ADDITION => 'recentchanges-page-added-to-category', + self::CATEGORY_REMOVAL => 'recentchanges-page-removed-from-category', + ); + + $msgKey = $array[$type]; if ( intval( $params['numTemplateLinks'] ) > 0 ) { $msgKey .= '-bundled'; @@ -193,11 +262,13 @@ class CategoryMembershipChange { /** * Returns the timestamp of the page's previous revision or null if the latest revision * does not refer to a parent revision + * * @return null|string */ private function getPreviousRevisionTimestamp() { - $latestRev = Revision::newFromId( $this->pageTitle->getLatestRevID() ); - $previousRev = Revision::newFromId( $latestRev->getParentId() ); + $previousRev = Revision::newFromId( + $this->pageTitle->getPreviousRevisionID( $this->pageTitle->getLatestRevID() ) + ); return $previousRev ? $previousRev->getTimestamp() : null; }