1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
<?php
namespace MediaWiki\Extension\AbuseFilter;
use InvalidArgumentException;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\Revision\RevisionLookup;
use MediaWiki\Revision\RevisionRecord;
use Wikimedia\Rdbms\LBFactory;
use WikiPage;
/**
* This service allows "linking" the edit filter hook and the page save hook
*/
class EditRevUpdater {
public const SERVICE_NAME = 'AbuseFilterEditRevUpdater';
/** @var CentralDBManager */
private $centralDBManager;
/** @var RevisionLookup */
private $revisionLookup;
/** @var LBFactory */
private $lbFactory;
/** @var string */
private $wikiID;
/** @var WikiPage|null */
private $wikiPage;
/**
* @var int[][][] IDs of logged filters like [ page title => [ 'local' => [ids], 'global' => [ids] ] ].
* @phan-var array<string,array{local:int[],global:int[]}>
*/
private $logIds = [];
/**
* @param CentralDBManager $centralDBManager
* @param RevisionLookup $revisionLookup
* @param LBFactory $lbFactory
* @param string $wikiID
*/
public function __construct(
CentralDBManager $centralDBManager,
RevisionLookup $revisionLookup,
LBFactory $lbFactory,
string $wikiID
) {
$this->centralDBManager = $centralDBManager;
$this->revisionLookup = $revisionLookup;
$this->lbFactory = $lbFactory;
$this->wikiID = $wikiID;
}
/**
* Set the WikiPage object used for the ongoing edit
*
* @param WikiPage $page
*/
public function setLastEditPage( WikiPage $page ): void {
$this->wikiPage = $page;
}
/**
* Clear the WikiPage object used for the ongoing edit
*/
public function clearLastEditPage(): void {
$this->wikiPage = null;
}
/**
* @param LinkTarget $target
* @param int[][] $logIds
* @phan-param array{local:int[],global:int[]} $logIds
*/
public function setLogIdsForTarget( LinkTarget $target, array $logIds ): void {
if ( count( $logIds ) !== 2 || array_diff( array_keys( $logIds ), [ 'local', 'global' ] ) ) {
throw new InvalidArgumentException( 'Wrong keys; got: ' . implode( ', ', array_keys( $logIds ) ) );
}
$key = $this->getCacheKey( $target );
$this->logIds[$key] = $logIds;
}
/**
* @param WikiPage $wikiPage
* @param RevisionRecord $revisionRecord
* @return bool Whether the DB was updated
*/
public function updateRev( WikiPage $wikiPage, RevisionRecord $revisionRecord ): bool {
$key = $this->getCacheKey( $wikiPage->getTitle() );
if ( !isset( $this->logIds[ $key ] ) || $wikiPage !== $this->wikiPage ) {
// This isn't the edit $this->logIds was set for
$this->logIds = [];
return false;
}
// Ignore null edit.
$parentRevId = $revisionRecord->getParentId();
if ( $parentRevId !== null ) {
$parentRev = $this->revisionLookup->getRevisionById( $parentRevId );
if ( $parentRev && $revisionRecord->hasSameContent( $parentRev ) ) {
$this->logIds = [];
return false;
}
}
$this->clearLastEditPage();
$ret = false;
$logs = $this->logIds[ $key ];
if ( $logs[ 'local' ] ) {
$dbw = $this->lbFactory->getPrimaryDatabase();
$dbw->newUpdateQueryBuilder()
->update( 'abuse_filter_log' )
->set( [ 'afl_rev_id' => $revisionRecord->getId() ] )
->where( [ 'afl_id' => $logs['local'] ] )
->caller( __METHOD__ )
->execute();
$ret = true;
}
if ( $logs[ 'global' ] ) {
$fdb = $this->centralDBManager->getConnection( DB_PRIMARY );
$fdb->newUpdateQueryBuilder()
->update( 'abuse_filter_log' )
->set( [ 'afl_rev_id' => $revisionRecord->getId() ] )
->where( [ 'afl_id' => $logs['global'], 'afl_wiki' => $this->wikiID ] )
->caller( __METHOD__ )
->execute();
$ret = true;
}
return $ret;
}
/**
* @param LinkTarget $target
* @return string
*/
private function getCacheKey( LinkTarget $target ): string {
return $target->getNamespace() . '|' . $target->getText();
}
}
|