wiki.techinc.nl/includes/changes/CategoryMembershipChange.php
Petr Pchelko a2cf98ef70 Remove usages and hard deprecate Revision::newFromRow
Also updates CategoryMembershipChange to accept a RevisionRecord
instead of a Revision.

Bug: T246284
Change-Id: I30024b7278e108b0f4e20ef1eb44647916fad47c
2020-03-25 11:42:36 -07:00

301 lines
8.6 KiB
PHP

<?php
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Revision\RevisionStore;
/**
* Helper class for category membership changes
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @author Kai Nissen
* @author Addshore
* @since 1.27
*/
class CategoryMembershipChange {
const CATEGORY_ADDITION = 1;
const CATEGORY_REMOVAL = -1;
/**
* @var string Current timestamp, set during CategoryMembershipChange::__construct()
*/
private $timestamp;
/**
* @var Title Title instance of the categorized page
*/
private $pageTitle;
/**
* @var RevisionRecord|null Latest Revision instance of the categorized page
*/
private $revision;
/**
* @var int
* Number of pages this WikiPage is embedded by
* Set by CategoryMembershipChange::checkTemplateLinks()
*/
private $numTemplateLinks = 0;
/**
* @var callable|null
*/
private $newForCategorizationCallback = null;
/**
* @param Title $pageTitle Title instance of the categorized page
* @param RevisionRecord|Revision|null $revision Latest Revision instance of the categorized page.
* Since 1.35 passing a Revision object is deprecated in favor of RevisionRecord.
*
* @throws MWException
*/
public function __construct( Title $pageTitle, $revision = null ) {
$this->pageTitle = $pageTitle;
if ( $revision === null ) {
$this->timestamp = wfTimestampNow();
} else {
$this->timestamp = $revision->getTimestamp();
}
if ( $revision instanceof Revision ) {
wfDeprecated(
'Revision for ' . __METHOD__,
'1.35'
);
$this->revision = $revision->getRevisionRecord();
} else {
$this->revision = $revision;
}
$this->newForCategorizationCallback = [ RecentChange::class, 'newForCategorization' ];
}
/**
* 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( callable $callback ) {
if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
throw new MWException( 'Cannot override newForCategorization callback in operation.' );
}
$this->newForCategorizationCallback = $callback;
}
/**
* Determines the number of template links for recursive link updates
*/
public function checkTemplateLinks() {
$this->numTemplateLinks = $this->pageTitle->getBacklinkCache()->getNumLinks( 'templatelinks' );
}
/**
* Create a recentchanges entry for category additions
*
* @param Title $categoryTitle
*/
public function triggerCategoryAddedNotification( Title $categoryTitle ) {
$this->createRecentChangesEntry( $categoryTitle, self::CATEGORY_ADDITION );
}
/**
* Create a recentchanges entry for category removals
*
* @param Title $categoryTitle
*/
public function triggerCategoryRemovedNotification( Title $categoryTitle ) {
$this->createRecentChangesEntry( $categoryTitle, self::CATEGORY_REMOVAL );
}
/**
* Create a recentchanges entry using RecentChange::notifyCategorization()
*
* @param Title $categoryTitle
* @param int $type
*/
private function createRecentChangesEntry( Title $categoryTitle, $type ) {
$this->notifyCategorization(
$this->timestamp,
$categoryTitle,
$this->getUser(),
$this->getChangeMessageText(
$type,
$this->pageTitle->getPrefixedText(),
$this->numTemplateLinks
),
$this->pageTitle,
$this->getPreviousRevisionTimestamp(),
$this->revision,
$type === self::CATEGORY_ADDITION
);
}
/**
* @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|null $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 RevisionRecord|null $revision
* @param bool $added true, if the category was added, false for removed
*
* @throws MWException
*/
private function notifyCategorization(
$timestamp,
Title $categoryTitle,
?User $user,
$comment,
Title $pageTitle,
$lastTimestamp,
$revision,
$added
) {
$deleted = $revision ? $revision->getVisibility() & RevisionRecord::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 replica DB lag
*/
$bot = 1;
$lastRevId = 0;
$ip = '';
# If no revision is given, the change was probably triggered by parser functions
if ( $revision !== null ) {
$revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
$correspondingRc = $revisionStore->getRecentChange( $this->revision );
if ( $correspondingRc === null ) {
$correspondingRc = $revisionStore->getRecentChange(
$this->revision,
RevisionStore::READ_LATEST
);
}
if ( $correspondingRc !== null ) {
$bot = $correspondingRc->getAttribute( 'rc_bot' ) ?: 0;
$ip = $correspondingRc->getAttribute( 'rc_ip' ) ?: '';
$lastRevId = $correspondingRc->getAttribute( 'rc_last_oldid' ) ?: 0;
}
}
/** @var RecentChange $rc */
$rc = ( $this->newForCategorizationCallback )(
$timestamp,
$categoryTitle,
$user,
$comment,
$pageTitle,
$lastRevId,
$newRevId,
$lastTimestamp,
$bot,
$ip,
$deleted,
$added
);
$rc->save();
}
/**
* 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 getUser() {
if ( $this->revision ) {
$userIdentity = $this->revision->getUser( RevisionRecord::RAW );
if ( $userIdentity ) {
return User::newFromIdentity( $userIdentity );
}
}
$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;
}
/**
* Returns the change message according to the type of category membership change
*
* The message keys created in this method may be one of:
* - recentchanges-page-added-to-category
* - recentchanges-page-added-to-category-bundled
* - recentchanges-page-removed-from-category
* - recentchanges-page-removed-from-category-bundled
*
* @param int $type may be CategoryMembershipChange::CATEGORY_ADDITION
* or CategoryMembershipChange::CATEGORY_REMOVAL
* @param string $prefixedText result of Title::->getPrefixedText()
* @param int $numTemplateLinks
*
* @return string
*/
private function getChangeMessageText( $type, $prefixedText, $numTemplateLinks ) {
$array = [
self::CATEGORY_ADDITION => 'recentchanges-page-added-to-category',
self::CATEGORY_REMOVAL => 'recentchanges-page-removed-from-category',
];
$msgKey = $array[$type];
if ( intval( $numTemplateLinks ) > 0 ) {
$msgKey .= '-bundled';
}
return wfMessage( $msgKey, $prefixedText )->inContentLanguage()->text();
}
/**
* 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() {
$rl = MediaWikiServices::getInstance()->getRevisionLookup();
$latestRev = $rl->getRevisionByTitle( $this->pageTitle );
if ( $latestRev ) {
$previousRev = $rl->getPreviousRevision( $latestRev );
if ( $previousRev ) {
return $previousRev->getTimestamp();
}
}
return null;
}
}