2008-06-27 06:18:51 +00:00
|
|
|
<?php
|
|
|
|
|
2021-01-01 17:28:36 +00:00
|
|
|
namespace MediaWiki\Extension\AbuseFilter\Special;
|
|
|
|
|
|
|
|
use DifferenceEngine;
|
|
|
|
use ExtensionRegistry;
|
|
|
|
use InvalidArgumentException;
|
|
|
|
use ManualLogEntry;
|
2020-08-07 21:48:47 +00:00
|
|
|
use MediaWiki\Cache\LinkBatchFactory;
|
2020-09-18 14:49:13 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\AbuseFilterPermissionManager;
|
|
|
|
use MediaWiki\Extension\AbuseFilter\AbuseFilterServices;
|
2024-09-12 14:45:57 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\AbuseLoggerFactory;
|
2021-01-07 16:17:43 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\CentralDBNotAvailableException;
|
2020-12-18 14:05:33 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\Consequences\ConsequencesRegistry;
|
2021-01-07 16:17:43 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\Filter\FilterNotFoundException;
|
2024-05-16 10:40:53 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\Filter\Flags;
|
2024-08-14 14:11:10 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\FilterUtils;
|
2020-12-02 22:47:40 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\GlobalNameUtils;
|
2020-12-03 14:11:19 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\Pager\AbuseLogPager;
|
2020-10-02 15:08:48 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\SpecsFormatter;
|
2021-01-02 13:41:31 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\Variables\UnsetVariableException;
|
2024-08-14 14:11:10 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\Variables\VariableHolder;
|
2021-01-02 13:41:31 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\Variables\VariablesBlobStore;
|
|
|
|
use MediaWiki\Extension\AbuseFilter\Variables\VariablesFormatter;
|
|
|
|
use MediaWiki\Extension\AbuseFilter\Variables\VariablesManager;
|
2020-12-06 22:59:14 +00:00
|
|
|
use MediaWiki\Extension\AbuseFilter\View\HideAbuseLog;
|
2023-12-10 19:03:19 +00:00
|
|
|
use MediaWiki\Html\Html;
|
|
|
|
use MediaWiki\Html\ListToggle;
|
2024-06-12 18:01:35 +00:00
|
|
|
use MediaWiki\HTMLForm\HTMLForm;
|
2023-12-10 19:03:19 +00:00
|
|
|
use MediaWiki\Linker\Linker;
|
2019-09-18 21:48:40 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2021-09-15 18:23:36 +00:00
|
|
|
use MediaWiki\Permissions\Authority;
|
2020-09-17 06:54:06 +00:00
|
|
|
use MediaWiki\Permissions\PermissionManager;
|
2021-09-15 18:23:36 +00:00
|
|
|
use MediaWiki\Revision\RevisionRecord;
|
2023-12-10 19:03:19 +00:00
|
|
|
use MediaWiki\SpecialPage\SpecialPage;
|
|
|
|
use MediaWiki\Status\Status;
|
2023-08-19 17:49:36 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2022-06-27 20:23:24 +00:00
|
|
|
use MediaWiki\User\UserIdentity;
|
|
|
|
use MediaWiki\User\UserIdentityLookup;
|
2023-04-25 09:52:41 +00:00
|
|
|
use MediaWiki\WikiMap\WikiMap;
|
2018-10-16 19:58:22 +00:00
|
|
|
use OOUI\ButtonInputWidget;
|
2021-01-01 17:28:36 +00:00
|
|
|
use stdClass;
|
2024-05-17 20:20:30 +00:00
|
|
|
use Wikimedia\Rdbms\IExpression;
|
2023-02-26 12:51:08 +00:00
|
|
|
use Wikimedia\Rdbms\LBFactory;
|
2024-05-17 20:20:30 +00:00
|
|
|
use Wikimedia\Rdbms\LikeValue;
|
2019-08-11 13:11:20 +00:00
|
|
|
|
2019-02-08 17:45:25 +00:00
|
|
|
class SpecialAbuseLog extends AbuseFilterSpecialPage {
|
2021-01-09 13:40:10 +00:00
|
|
|
public const PAGE_NAME = 'AbuseLog';
|
|
|
|
|
2021-09-15 18:23:36 +00:00
|
|
|
/** Visible entry */
|
|
|
|
public const VISIBILITY_VISIBLE = 'visible';
|
|
|
|
/** Explicitly hidden entry */
|
|
|
|
public const VISIBILITY_HIDDEN = 'hidden';
|
|
|
|
/** Visible entry but the associated revision is hidden */
|
|
|
|
public const VISIBILITY_HIDDEN_IMPLICIT = 'implicit';
|
|
|
|
|
2011-07-25 22:09:05 +00:00
|
|
|
/**
|
2022-06-27 20:23:24 +00:00
|
|
|
* @var string|null The user whose AbuseLog entries are being searched
|
2011-07-25 22:09:05 +00:00
|
|
|
*/
|
2023-06-23 10:28:06 +00:00
|
|
|
private $mSearchUser;
|
2011-07-25 22:09:05 +00:00
|
|
|
|
2018-11-08 14:34:32 +00:00
|
|
|
/**
|
|
|
|
* @var string The start time of the search period
|
|
|
|
*/
|
2023-06-23 10:28:06 +00:00
|
|
|
private $mSearchPeriodStart;
|
2018-06-28 14:45:30 +00:00
|
|
|
|
2018-11-08 14:34:32 +00:00
|
|
|
/**
|
|
|
|
* @var string The end time of the search period
|
|
|
|
*/
|
2023-06-23 10:28:06 +00:00
|
|
|
private $mSearchPeriodEnd;
|
2018-06-28 14:45:30 +00:00
|
|
|
|
2011-07-25 22:09:05 +00:00
|
|
|
/**
|
2022-06-29 10:19:24 +00:00
|
|
|
* @var string The page of which AbuseLog entries are being searched
|
2011-07-25 22:09:05 +00:00
|
|
|
*/
|
2023-06-23 10:28:06 +00:00
|
|
|
private $mSearchTitle;
|
2011-07-25 22:09:05 +00:00
|
|
|
|
2018-03-04 14:02:45 +00:00
|
|
|
/**
|
2018-11-08 14:34:32 +00:00
|
|
|
* @var string The action performed by the user
|
2018-03-04 14:02:45 +00:00
|
|
|
*/
|
2023-06-23 10:28:06 +00:00
|
|
|
private $mSearchAction;
|
2018-03-04 14:02:45 +00:00
|
|
|
|
2018-02-24 13:49:39 +00:00
|
|
|
/**
|
2018-11-08 14:34:32 +00:00
|
|
|
* @var string The action taken by AbuseFilter
|
2018-02-24 13:49:39 +00:00
|
|
|
*/
|
2023-06-23 10:28:06 +00:00
|
|
|
private $mSearchActionTaken;
|
2018-02-24 13:49:39 +00:00
|
|
|
|
2018-11-08 14:34:32 +00:00
|
|
|
/**
|
|
|
|
* @var string The wiki name where we're performing the search
|
|
|
|
*/
|
2023-06-23 10:28:06 +00:00
|
|
|
private $mSearchWiki;
|
2012-08-03 21:55:35 +00:00
|
|
|
|
2018-11-08 14:34:32 +00:00
|
|
|
/**
|
|
|
|
* @var string|null The filter IDs we're looking for. Either a single one, or a pipe-separated list
|
|
|
|
*/
|
2023-06-23 10:28:06 +00:00
|
|
|
private $mSearchFilter;
|
2011-07-25 22:09:05 +00:00
|
|
|
|
2018-11-08 14:34:32 +00:00
|
|
|
/**
|
|
|
|
* @var string The visibility of entries we're interested in
|
|
|
|
*/
|
2023-06-23 10:28:06 +00:00
|
|
|
private $mSearchEntries;
|
2017-07-28 08:49:19 +00:00
|
|
|
|
2018-11-08 14:34:32 +00:00
|
|
|
/**
|
|
|
|
* @var string The impact of the user action, i.e. if the change has been saved
|
|
|
|
*/
|
2023-06-23 10:28:06 +00:00
|
|
|
private $mSearchImpact;
|
2017-08-24 08:52:06 +00:00
|
|
|
|
2020-10-09 12:55:44 +00:00
|
|
|
/** @var string|null The filter group to search, as defined in $wgAbuseFilterValidGroups */
|
2023-06-23 10:28:06 +00:00
|
|
|
private $mSearchGroup;
|
2019-01-17 17:34:07 +00:00
|
|
|
|
2023-02-26 12:51:08 +00:00
|
|
|
/** @var LBFactory */
|
|
|
|
private $lbFactory;
|
|
|
|
|
2020-08-07 21:48:47 +00:00
|
|
|
/** @var LinkBatchFactory */
|
|
|
|
private $linkBatchFactory;
|
|
|
|
|
2020-09-17 06:54:06 +00:00
|
|
|
/** @var PermissionManager */
|
|
|
|
private $permissionManager;
|
|
|
|
|
2022-06-27 20:23:24 +00:00
|
|
|
/** @var UserIdentityLookup */
|
|
|
|
private $userIdentityLookup;
|
|
|
|
|
2020-11-29 22:55:34 +00:00
|
|
|
/** @var ConsequencesRegistry */
|
|
|
|
private $consequencesRegistry;
|
|
|
|
|
2020-09-29 14:52:05 +00:00
|
|
|
/** @var VariablesBlobStore */
|
|
|
|
private $varBlobStore;
|
|
|
|
|
2020-10-02 15:08:48 +00:00
|
|
|
/** @var SpecsFormatter */
|
|
|
|
private $specsFormatter;
|
|
|
|
|
2020-12-31 13:29:00 +00:00
|
|
|
/** @var VariablesFormatter */
|
|
|
|
private $variablesFormatter;
|
|
|
|
|
2020-10-18 22:25:05 +00:00
|
|
|
/** @var VariablesManager */
|
|
|
|
private $varManager;
|
|
|
|
|
2024-09-12 14:45:57 +00:00
|
|
|
private AbuseLoggerFactory $abuseLoggerFactory;
|
|
|
|
|
2019-01-11 16:34:08 +00:00
|
|
|
/**
|
2023-02-26 12:51:08 +00:00
|
|
|
* @param LBFactory $lbFactory
|
2020-08-07 21:48:47 +00:00
|
|
|
* @param LinkBatchFactory $linkBatchFactory
|
2020-09-17 06:54:06 +00:00
|
|
|
* @param PermissionManager $permissionManager
|
2022-06-27 20:23:24 +00:00
|
|
|
* @param UserIdentityLookup $userIdentityLookup
|
2020-09-18 14:49:13 +00:00
|
|
|
* @param AbuseFilterPermissionManager $afPermissionManager
|
2020-11-29 22:55:34 +00:00
|
|
|
* @param ConsequencesRegistry $consequencesRegistry
|
2020-09-29 14:52:05 +00:00
|
|
|
* @param VariablesBlobStore $varBlobStore
|
2020-10-02 15:08:48 +00:00
|
|
|
* @param SpecsFormatter $specsFormatter
|
2020-12-31 13:29:00 +00:00
|
|
|
* @param VariablesFormatter $variablesFormatter
|
2020-10-18 22:25:05 +00:00
|
|
|
* @param VariablesManager $varManager
|
2024-09-12 14:45:57 +00:00
|
|
|
* @param AbuseLoggerFactory $abuseLoggerFactory
|
2019-01-11 16:34:08 +00:00
|
|
|
*/
|
2020-09-18 14:49:13 +00:00
|
|
|
public function __construct(
|
2023-02-26 12:51:08 +00:00
|
|
|
LBFactory $lbFactory,
|
2020-09-18 14:49:13 +00:00
|
|
|
LinkBatchFactory $linkBatchFactory,
|
|
|
|
PermissionManager $permissionManager,
|
2022-06-27 20:23:24 +00:00
|
|
|
UserIdentityLookup $userIdentityLookup,
|
2020-11-29 22:55:34 +00:00
|
|
|
AbuseFilterPermissionManager $afPermissionManager,
|
2020-09-29 14:52:05 +00:00
|
|
|
ConsequencesRegistry $consequencesRegistry,
|
2020-10-02 15:08:48 +00:00
|
|
|
VariablesBlobStore $varBlobStore,
|
2020-12-31 13:29:00 +00:00
|
|
|
SpecsFormatter $specsFormatter,
|
2020-10-18 22:25:05 +00:00
|
|
|
VariablesFormatter $variablesFormatter,
|
2024-09-12 14:45:57 +00:00
|
|
|
VariablesManager $varManager,
|
|
|
|
AbuseLoggerFactory $abuseLoggerFactory
|
2020-09-18 14:49:13 +00:00
|
|
|
) {
|
2021-01-09 13:40:10 +00:00
|
|
|
parent::__construct( self::PAGE_NAME, 'abusefilter-log', $afPermissionManager );
|
2023-02-26 12:51:08 +00:00
|
|
|
$this->lbFactory = $lbFactory;
|
2020-08-07 21:48:47 +00:00
|
|
|
$this->linkBatchFactory = $linkBatchFactory;
|
2020-09-17 06:54:06 +00:00
|
|
|
$this->permissionManager = $permissionManager;
|
2022-06-27 20:23:24 +00:00
|
|
|
$this->userIdentityLookup = $userIdentityLookup;
|
2020-11-29 22:55:34 +00:00
|
|
|
$this->consequencesRegistry = $consequencesRegistry;
|
2020-09-29 14:52:05 +00:00
|
|
|
$this->varBlobStore = $varBlobStore;
|
2020-10-02 15:08:48 +00:00
|
|
|
$this->specsFormatter = $specsFormatter;
|
2021-11-09 22:02:32 +00:00
|
|
|
$this->specsFormatter->setMessageLocalizer( $this );
|
2020-12-31 13:29:00 +00:00
|
|
|
$this->variablesFormatter = $variablesFormatter;
|
2021-11-09 22:02:32 +00:00
|
|
|
$this->variablesFormatter->setMessageLocalizer( $this );
|
2020-10-18 22:25:05 +00:00
|
|
|
$this->varManager = $varManager;
|
2024-09-12 14:45:57 +00:00
|
|
|
$this->abuseLoggerFactory = $abuseLoggerFactory;
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2018-04-04 21:14:25 +00:00
|
|
|
/**
|
2021-01-17 11:54:43 +00:00
|
|
|
* @codeCoverageIgnore Merely declarative
|
2019-02-08 17:45:25 +00:00
|
|
|
* @inheritDoc
|
2018-04-04 21:14:25 +00:00
|
|
|
*/
|
2016-06-01 21:42:22 +00:00
|
|
|
public function doesWrites() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-02-08 17:45:25 +00:00
|
|
|
/**
|
2021-01-17 11:54:43 +00:00
|
|
|
* @codeCoverageIgnore Merely declarative
|
2019-02-08 17:45:25 +00:00
|
|
|
* @inheritDoc
|
|
|
|
*/
|
|
|
|
protected function getGroupName() {
|
|
|
|
return 'changes';
|
|
|
|
}
|
|
|
|
|
2016-12-12 16:30:06 +00:00
|
|
|
/**
|
|
|
|
* Main routine
|
|
|
|
*
|
|
|
|
* $parameter string is converted into the $args array, which can come in
|
|
|
|
* three shapes:
|
|
|
|
*
|
|
|
|
* An array of size 2: only if the URL is like Special:AbuseLog/private/id
|
|
|
|
* where id is the log identifier. In this case, the private details of the
|
|
|
|
* log (e.g. IP address) will be shown.
|
|
|
|
*
|
|
|
|
* An array of size 1: either the URL is like Special:AbuseLog/id where
|
|
|
|
* the id is log identifier, in which case the details of the log except for
|
2020-12-06 22:59:14 +00:00
|
|
|
* private bits (e.g. IP address) are shown, or Special:AbuseLog/hide for hiding entries,
|
2023-09-04 10:52:35 +00:00
|
|
|
* or the URL is incomplete as in Special:AbuseLog/private (without specifying id),
|
2020-12-06 22:59:14 +00:00
|
|
|
* in which case a warning is shown to the user
|
2016-12-12 16:30:06 +00:00
|
|
|
*
|
|
|
|
* An array of size 0 when URL is like Special:AbuseLog or an array of size
|
|
|
|
* 1 when the URL is like Special:AbuseFilter/ (i.e. without anything after
|
2020-12-06 22:59:14 +00:00
|
|
|
* the slash). Otherwise, the abuse logs are shown as a list, with a search form above the list.
|
2016-12-12 16:30:06 +00:00
|
|
|
*
|
2019-05-15 15:34:57 +00:00
|
|
|
* @param string|null $parameter URL parameters
|
2016-12-12 16:30:06 +00:00
|
|
|
*/
|
2009-10-07 13:57:06 +00:00
|
|
|
public function execute( $parameter ) {
|
2011-11-16 05:34:24 +00:00
|
|
|
$out = $this->getOutput();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2019-02-08 17:45:25 +00:00
|
|
|
$this->addNavigationLinks( 'log' );
|
2008-06-27 06:18:51 +00:00
|
|
|
|
|
|
|
$this->setHeaders();
|
2019-07-03 15:43:44 +00:00
|
|
|
$this->addHelpLink( 'Extension:AbuseFilter' );
|
2008-06-27 06:18:51 +00:00
|
|
|
$this->loadParameters();
|
|
|
|
|
2022-02-04 19:29:43 +00:00
|
|
|
$out->disableClientCache();
|
2009-01-26 19:23:27 +00:00
|
|
|
|
2011-11-16 05:34:24 +00:00
|
|
|
$out->addModuleStyles( 'ext.abuseFilter' );
|
2009-01-27 17:32:30 +00:00
|
|
|
|
2019-01-11 16:34:08 +00:00
|
|
|
$this->checkPermissions();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2022-06-29 00:14:07 +00:00
|
|
|
$args = $parameter !== null ? explode( '/', $parameter ) : [];
|
2011-07-04 22:00:06 +00:00
|
|
|
|
2016-12-12 16:30:06 +00:00
|
|
|
if ( count( $args ) === 2 && $args[0] === 'private' ) {
|
2019-12-07 17:20:10 +00:00
|
|
|
$this->showPrivateDetails( (int)$args[1] );
|
2016-12-12 16:30:06 +00:00
|
|
|
} elseif ( count( $args ) === 1 && $args[0] !== '' ) {
|
|
|
|
if ( $args[0] === 'private' ) {
|
|
|
|
$out->addWikiMsg( 'abusefilter-invalid-request-noid' );
|
2020-12-06 22:59:14 +00:00
|
|
|
} elseif ( $args[0] === 'hide' ) {
|
|
|
|
$this->showHideView();
|
2016-12-12 16:30:06 +00:00
|
|
|
} else {
|
|
|
|
$this->showDetails( $args[0] );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
} else {
|
2021-02-01 14:50:06 +00:00
|
|
|
$this->outputHeader( 'abusefilter-log-summary' );
|
2019-01-23 11:01:06 +00:00
|
|
|
$this->searchForm();
|
|
|
|
$this->showList();
|
2009-03-22 23:33:27 +00:00
|
|
|
}
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2022-08-17 20:07:25 +00:00
|
|
|
/**
|
|
|
|
* @inheritDoc
|
|
|
|
*/
|
|
|
|
public function getShortDescription( string $path = '' ): string {
|
|
|
|
return $this->msg( 'abusefilter-topnav-log' )->text();
|
|
|
|
}
|
|
|
|
|
2018-04-04 21:14:25 +00:00
|
|
|
/**
|
|
|
|
* Loads parameters from request
|
|
|
|
*/
|
|
|
|
public function loadParameters() {
|
2011-11-16 05:34:24 +00:00
|
|
|
$request = $this->getRequest();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2018-04-05 10:23:57 +00:00
|
|
|
$searchUsername = trim( $request->getText( 'wpSearchUser' ) );
|
|
|
|
$userTitle = Title::newFromText( $searchUsername, NS_USER );
|
|
|
|
$this->mSearchUser = $userTitle ? $userTitle->getText() : null;
|
2018-03-10 21:47:33 +00:00
|
|
|
if ( $this->getConfig()->get( 'AbuseFilterIsCentral' ) ) {
|
2012-08-03 21:55:35 +00:00
|
|
|
$this->mSearchWiki = $request->getText( 'wpSearchWiki' );
|
|
|
|
}
|
2009-04-01 03:38:57 +00:00
|
|
|
|
2018-06-28 14:45:30 +00:00
|
|
|
$this->mSearchPeriodStart = $request->getText( 'wpSearchPeriodStart' );
|
|
|
|
$this->mSearchPeriodEnd = $request->getText( 'wpSearchPeriodEnd' );
|
2011-11-16 05:34:24 +00:00
|
|
|
$this->mSearchTitle = $request->getText( 'wpSearchTitle' );
|
2020-10-09 12:55:44 +00:00
|
|
|
|
2009-08-04 17:56:18 +00:00
|
|
|
$this->mSearchFilter = null;
|
2020-10-09 12:55:44 +00:00
|
|
|
$this->mSearchGroup = null;
|
2022-07-02 13:35:00 +00:00
|
|
|
if ( $this->afPermissionManager->canSeeLogDetails( $this->getAuthority() ) ) {
|
2013-07-09 22:49:19 +00:00
|
|
|
$this->mSearchFilter = $request->getText( 'wpSearchFilter' );
|
2020-10-09 12:55:44 +00:00
|
|
|
if ( count( $this->getConfig()->get( 'AbuseFilterValidGroups' ) ) > 1 ) {
|
|
|
|
$this->mSearchGroup = $request->getText( 'wpSearchGroup' );
|
|
|
|
}
|
2009-05-22 06:42:10 +00:00
|
|
|
}
|
2017-07-28 08:49:19 +00:00
|
|
|
|
2020-10-09 12:55:44 +00:00
|
|
|
$this->mSearchAction = $request->getText( 'wpSearchAction' );
|
|
|
|
$this->mSearchActionTaken = $request->getText( 'wpSearchActionTaken' );
|
2017-07-28 08:49:19 +00:00
|
|
|
$this->mSearchEntries = $request->getText( 'wpSearchEntries' );
|
2017-08-24 08:52:06 +00:00
|
|
|
$this->mSearchImpact = $request->getText( 'wpSearchImpact' );
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2018-03-04 14:02:45 +00:00
|
|
|
/**
|
|
|
|
* @return string[]
|
|
|
|
*/
|
|
|
|
private function getAllFilterableActions() {
|
|
|
|
return [
|
|
|
|
'edit',
|
|
|
|
'move',
|
|
|
|
'upload',
|
|
|
|
'stashupload',
|
|
|
|
'delete',
|
|
|
|
'createaccount',
|
|
|
|
'autocreateaccount',
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2018-04-04 21:14:25 +00:00
|
|
|
/**
|
|
|
|
* Builds the search form
|
|
|
|
*/
|
|
|
|
public function searchForm() {
|
2022-07-02 13:35:00 +00:00
|
|
|
$performer = $this->getAuthority();
|
2017-06-15 14:23:34 +00:00
|
|
|
$formDescriptor = [
|
|
|
|
'SearchUser' => [
|
2016-04-10 10:05:01 +00:00
|
|
|
'label-message' => 'abusefilter-log-search-user',
|
|
|
|
'type' => 'user',
|
2018-04-05 10:23:57 +00:00
|
|
|
'ipallowed' => true,
|
2016-04-10 10:05:01 +00:00
|
|
|
'default' => $this->mSearchUser,
|
2017-06-15 14:23:34 +00:00
|
|
|
],
|
2018-06-28 14:45:30 +00:00
|
|
|
'SearchPeriodStart' => [
|
|
|
|
'label-message' => 'abusefilter-test-period-start',
|
|
|
|
'type' => 'datetime',
|
|
|
|
'default' => $this->mSearchPeriodStart
|
|
|
|
],
|
|
|
|
'SearchPeriodEnd' => [
|
|
|
|
'label-message' => 'abusefilter-test-period-end',
|
|
|
|
'type' => 'datetime',
|
|
|
|
'default' => $this->mSearchPeriodEnd
|
|
|
|
],
|
2017-06-15 14:23:34 +00:00
|
|
|
'SearchTitle' => [
|
2016-04-10 10:05:01 +00:00
|
|
|
'label-message' => 'abusefilter-log-search-title',
|
|
|
|
'type' => 'title',
|
2021-08-04 19:27:50 +00:00
|
|
|
'interwiki' => false,
|
2016-04-10 10:05:01 +00:00
|
|
|
'default' => $this->mSearchTitle,
|
2018-05-10 20:20:05 +00:00
|
|
|
'required' => false
|
2017-08-24 08:52:06 +00:00
|
|
|
],
|
|
|
|
'SearchImpact' => [
|
|
|
|
'label-message' => 'abusefilter-log-search-impact',
|
|
|
|
'type' => 'select',
|
|
|
|
'options' => [
|
|
|
|
$this->msg( 'abusefilter-log-search-impact-all' )->text() => 0,
|
|
|
|
$this->msg( 'abusefilter-log-search-impact-saved' )->text() => 1,
|
|
|
|
$this->msg( 'abusefilter-log-search-impact-not-saved' )->text() => 2,
|
|
|
|
],
|
|
|
|
],
|
2017-06-15 14:23:34 +00:00
|
|
|
];
|
2018-03-04 14:02:45 +00:00
|
|
|
$filterableActions = $this->getAllFilterableActions();
|
|
|
|
$actions = array_combine( $filterableActions, $filterableActions );
|
2020-06-16 17:18:49 +00:00
|
|
|
ksort( $actions );
|
|
|
|
$actions = array_merge(
|
|
|
|
[ $this->msg( 'abusefilter-log-search-action-any' )->text() => 'any' ],
|
|
|
|
$actions,
|
|
|
|
[ $this->msg( 'abusefilter-log-search-action-other' )->text() => 'other' ]
|
|
|
|
);
|
2018-03-04 14:02:45 +00:00
|
|
|
$formDescriptor['SearchAction'] = [
|
|
|
|
'label-message' => 'abusefilter-log-search-action-label',
|
|
|
|
'type' => 'select',
|
|
|
|
'options' => $actions,
|
|
|
|
'default' => 'any',
|
|
|
|
];
|
2020-06-16 17:18:49 +00:00
|
|
|
$options = [];
|
2020-11-29 22:55:34 +00:00
|
|
|
foreach ( $this->consequencesRegistry->getAllActionNames() as $action ) {
|
2020-10-02 15:08:48 +00:00
|
|
|
$key = $this->specsFormatter->getActionDisplay( $action );
|
2018-02-24 13:49:39 +00:00
|
|
|
$options[$key] = $action;
|
|
|
|
}
|
|
|
|
ksort( $options );
|
2020-06-16 17:18:49 +00:00
|
|
|
$options = array_merge(
|
|
|
|
[ $this->msg( 'abusefilter-log-search-action-taken-any' )->text() => '' ],
|
|
|
|
$options,
|
|
|
|
[ $this->msg( 'abusefilter-log-noactions-filter' )->text() => 'noactions' ]
|
|
|
|
);
|
2018-02-24 13:49:39 +00:00
|
|
|
$formDescriptor['SearchActionTaken'] = [
|
|
|
|
'label-message' => 'abusefilter-log-search-action-taken-label',
|
|
|
|
'type' => 'select',
|
|
|
|
'options' => $options,
|
|
|
|
];
|
2022-07-02 13:35:00 +00:00
|
|
|
if ( $this->afPermissionManager->canSeeHiddenLogEntries( $performer ) ) {
|
2017-07-28 08:49:19 +00:00
|
|
|
$formDescriptor['SearchEntries'] = [
|
|
|
|
'type' => 'select',
|
|
|
|
'label-message' => 'abusefilter-log-search-entries-label',
|
|
|
|
'options' => [
|
|
|
|
$this->msg( 'abusefilter-log-search-entries-all' )->text() => 0,
|
|
|
|
$this->msg( 'abusefilter-log-search-entries-hidden' )->text() => 1,
|
|
|
|
$this->msg( 'abusefilter-log-search-entries-visible' )->text() => 2,
|
|
|
|
],
|
|
|
|
];
|
|
|
|
}
|
2019-01-17 17:34:07 +00:00
|
|
|
|
2022-07-02 13:35:00 +00:00
|
|
|
if ( $this->afPermissionManager->canSeeLogDetails( $performer ) ) {
|
2020-10-09 12:55:44 +00:00
|
|
|
$groups = $this->getConfig()->get( 'AbuseFilterValidGroups' );
|
|
|
|
if ( count( $groups ) > 1 ) {
|
|
|
|
$options = array_merge(
|
|
|
|
[ $this->msg( 'abusefilter-log-search-group-any' )->text() => 0 ],
|
|
|
|
array_combine( $groups, $groups )
|
|
|
|
);
|
|
|
|
$formDescriptor['SearchGroup'] = [
|
|
|
|
'label-message' => 'abusefilter-log-search-group',
|
|
|
|
'type' => 'select',
|
|
|
|
'options' => $options
|
|
|
|
];
|
|
|
|
}
|
2019-11-18 03:08:28 +00:00
|
|
|
$helpmsg = $this->getConfig()->get( 'AbuseFilterIsCentral' )
|
2020-09-29 12:58:37 +00:00
|
|
|
? $this->msg( 'abusefilter-log-search-filter-help-central' )->escaped()
|
|
|
|
: $this->msg( 'abusefilter-log-search-filter-help' )
|
2020-12-02 22:47:40 +00:00
|
|
|
->params( GlobalNameUtils::GLOBAL_FILTER_PREFIX )->escaped();
|
2018-02-25 00:04:31 +00:00
|
|
|
$formDescriptor['SearchFilter'] = [
|
|
|
|
'label-message' => 'abusefilter-log-search-filter',
|
|
|
|
'type' => 'text',
|
|
|
|
'default' => $this->mSearchFilter,
|
2019-11-18 03:08:28 +00:00
|
|
|
'help' => $helpmsg
|
2018-02-25 00:04:31 +00:00
|
|
|
];
|
|
|
|
}
|
2018-03-10 21:47:33 +00:00
|
|
|
if ( $this->getConfig()->get( 'AbuseFilterIsCentral' ) ) {
|
2019-01-17 17:34:07 +00:00
|
|
|
// @todo Add free form input for wiki name. Would be nice to generate
|
2018-02-25 00:04:31 +00:00
|
|
|
// a select with unique names in the db at some point.
|
|
|
|
$formDescriptor['SearchWiki'] = [
|
|
|
|
'label-message' => 'abusefilter-log-search-wiki',
|
|
|
|
'type' => 'text',
|
|
|
|
'default' => $this->mSearchWiki,
|
|
|
|
];
|
|
|
|
}
|
2012-08-03 21:55:35 +00:00
|
|
|
|
2018-04-29 17:52:45 +00:00
|
|
|
HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
|
2017-08-04 06:43:40 +00:00
|
|
|
->setWrapperLegendMsg( 'abusefilter-log-search' )
|
2017-08-24 07:46:27 +00:00
|
|
|
->setSubmitTextMsg( 'abusefilter-log-search-submit' )
|
2016-04-10 10:05:01 +00:00
|
|
|
->setMethod( 'get' )
|
2020-05-14 18:54:45 +00:00
|
|
|
->setCollapsibleOptions( true )
|
2016-04-10 10:05:01 +00:00
|
|
|
->prepareForm()
|
|
|
|
->displayForm( false );
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2010-08-19 21:12:09 +00:00
|
|
|
|
2020-12-06 22:59:14 +00:00
|
|
|
private function showHideView() {
|
|
|
|
$view = new HideAbuseLog(
|
2023-02-26 12:51:08 +00:00
|
|
|
$this->lbFactory,
|
2020-12-06 22:59:14 +00:00
|
|
|
$this->afPermissionManager,
|
|
|
|
$this->getContext(),
|
|
|
|
$this->getLinkRenderer(),
|
2021-01-09 13:40:10 +00:00
|
|
|
self::PAGE_NAME
|
2010-08-19 21:12:09 +00:00
|
|
|
);
|
2020-12-06 22:59:14 +00:00
|
|
|
$view->show();
|
2010-06-25 20:04:55 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2018-04-04 21:14:25 +00:00
|
|
|
/**
|
|
|
|
* Shows the results list
|
|
|
|
*/
|
|
|
|
public function showList() {
|
2011-11-16 05:34:24 +00:00
|
|
|
$out = $this->getOutput();
|
2022-07-02 13:35:00 +00:00
|
|
|
$performer = $this->getAuthority();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2008-06-27 06:18:51 +00:00
|
|
|
// Generate conditions list.
|
2017-06-15 14:23:34 +00:00
|
|
|
$conds = [];
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2022-06-29 10:19:24 +00:00
|
|
|
if ( $this->mSearchUser !== null ) {
|
2022-06-27 20:23:24 +00:00
|
|
|
$searchedUser = $this->userIdentityLookup->getUserIdentityByName( $this->mSearchUser );
|
2010-08-19 21:12:09 +00:00
|
|
|
|
2019-08-27 09:40:01 +00:00
|
|
|
if ( !$searchedUser ) {
|
2010-06-25 00:06:07 +00:00
|
|
|
$conds['afl_user'] = 0;
|
|
|
|
$conds['afl_user_text'] = $this->mSearchUser;
|
2010-08-19 21:12:09 +00:00
|
|
|
} else {
|
2019-08-27 09:40:01 +00:00
|
|
|
$conds['afl_user'] = $searchedUser->getId();
|
|
|
|
$conds['afl_user_text'] = $searchedUser->getName();
|
2010-06-25 00:06:07 +00:00
|
|
|
}
|
2009-05-22 06:42:10 +00:00
|
|
|
}
|
2009-05-26 13:08:15 +00:00
|
|
|
|
2023-02-26 12:51:08 +00:00
|
|
|
$dbr = $this->lbFactory->getReplicaDatabase();
|
2018-06-28 14:45:30 +00:00
|
|
|
if ( $this->mSearchPeriodStart ) {
|
2024-04-30 18:21:20 +00:00
|
|
|
$conds[] = $dbr->expr( 'afl_timestamp', '>=',
|
|
|
|
$dbr->timestamp( strtotime( $this->mSearchPeriodStart ) ) );
|
2018-06-28 14:45:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( $this->mSearchPeriodEnd ) {
|
2024-04-30 18:21:20 +00:00
|
|
|
$conds[] = $dbr->expr( 'afl_timestamp', '<=',
|
|
|
|
$dbr->timestamp( strtotime( $this->mSearchPeriodEnd ) ) );
|
2018-06-28 14:45:30 +00:00
|
|
|
}
|
|
|
|
|
2012-08-03 21:55:35 +00:00
|
|
|
if ( $this->mSearchWiki ) {
|
2020-01-08 13:33:10 +00:00
|
|
|
if ( $this->mSearchWiki === WikiMap::getCurrentWikiDbDomain()->getId() ) {
|
2014-12-25 18:29:00 +00:00
|
|
|
$conds['afl_wiki'] = null;
|
|
|
|
} else {
|
|
|
|
$conds['afl_wiki'] = $this->mSearchWiki;
|
|
|
|
}
|
2012-08-03 21:55:35 +00:00
|
|
|
}
|
|
|
|
|
2019-01-17 17:34:07 +00:00
|
|
|
$groupFilters = [];
|
|
|
|
if ( $this->mSearchGroup ) {
|
2024-04-30 18:21:20 +00:00
|
|
|
$groupFilters = $dbr->newSelectQueryBuilder()
|
|
|
|
->select( 'af_id' )
|
|
|
|
->from( 'abuse_filter' )
|
|
|
|
->where( [ 'af_group' => $this->mSearchGroup ] )
|
|
|
|
->caller( __METHOD__ )
|
|
|
|
->fetchFieldValues();
|
2019-01-17 17:34:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$searchFilters = [];
|
2009-05-26 13:08:15 +00:00
|
|
|
if ( $this->mSearchFilter ) {
|
2019-03-28 19:59:53 +00:00
|
|
|
$rawFilters = array_map( 'trim', explode( '|', $this->mSearchFilter ) );
|
|
|
|
// Map of [ [ id, global ], ... ]
|
|
|
|
$filtersList = [];
|
|
|
|
$foundInvalid = false;
|
|
|
|
foreach ( $rawFilters as $filter ) {
|
|
|
|
try {
|
2020-12-02 22:47:40 +00:00
|
|
|
$filtersList[] = GlobalNameUtils::splitGlobalName( $filter );
|
2019-03-28 19:59:53 +00:00
|
|
|
} catch ( InvalidArgumentException $e ) {
|
|
|
|
$foundInvalid = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-28 18:03:35 +00:00
|
|
|
// if a filter is hidden, users who can't view private filters should
|
|
|
|
// not be able to find log entries generated by it.
|
2022-07-02 13:35:00 +00:00
|
|
|
if ( !$this->afPermissionManager->canViewPrivateFiltersLogs( $performer ) ) {
|
2014-01-05 21:06:24 +00:00
|
|
|
$searchedForPrivate = false;
|
2019-03-28 19:59:53 +00:00
|
|
|
foreach ( $filtersList as $index => $filterData ) {
|
2021-01-07 16:17:43 +00:00
|
|
|
try {
|
|
|
|
$filter = AbuseFilterServices::getFilterLookup()->getFilter( ...$filterData );
|
|
|
|
} catch ( FilterNotFoundException $_ ) {
|
|
|
|
unset( $filtersList[$index] );
|
|
|
|
$foundInvalid = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ( $filter->isHidden() ) {
|
2019-03-28 19:59:53 +00:00
|
|
|
unset( $filtersList[$index] );
|
2014-01-05 21:06:24 +00:00
|
|
|
$searchedForPrivate = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( $searchedForPrivate ) {
|
|
|
|
$out->addWikiMsg( 'abusefilter-log-private-not-included' );
|
|
|
|
}
|
2012-01-03 17:29:10 +00:00
|
|
|
}
|
2019-03-28 19:59:53 +00:00
|
|
|
|
2024-06-12 12:42:17 +00:00
|
|
|
// if a filter is protected, users who can't view protected filters should
|
|
|
|
// not be able to find log entries generated by it.
|
|
|
|
if ( !$this->afPermissionManager->canViewProtectedVariables( $performer ) ) {
|
|
|
|
$searchedForProtected = false;
|
|
|
|
foreach ( $filtersList as $index => $filterData ) {
|
|
|
|
try {
|
|
|
|
$filter = AbuseFilterServices::getFilterLookup()->getFilter( ...$filterData );
|
|
|
|
} catch ( FilterNotFoundException $_ ) {
|
|
|
|
unset( $filtersList[$index] );
|
|
|
|
$foundInvalid = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ( $filter->isProtected() ) {
|
|
|
|
unset( $filtersList[$index] );
|
|
|
|
$searchedForProtected = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( $searchedForProtected ) {
|
|
|
|
$out->addWikiMsg( 'abusefilter-log-protected-not-included' );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-07 16:17:43 +00:00
|
|
|
if ( $foundInvalid ) {
|
|
|
|
// @todo Tell what the invalid IDs are
|
|
|
|
$out->addHTML(
|
|
|
|
Html::rawElement(
|
|
|
|
'p',
|
|
|
|
[],
|
|
|
|
Html::warningBox( $this->msg( 'abusefilter-log-invalid-filter' )->escaped() )
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-03-28 19:59:53 +00:00
|
|
|
foreach ( $filtersList as $filterData ) {
|
2020-12-02 22:47:40 +00:00
|
|
|
$searchFilters[] = GlobalNameUtils::buildGlobalName( ...$filterData );
|
2019-03-28 19:59:53 +00:00
|
|
|
}
|
2019-01-17 17:34:07 +00:00
|
|
|
}
|
2015-09-28 18:03:35 +00:00
|
|
|
|
2019-01-17 17:34:07 +00:00
|
|
|
$searchIDs = null;
|
|
|
|
if ( $this->mSearchGroup && !$this->mSearchFilter ) {
|
|
|
|
$searchIDs = $groupFilters;
|
|
|
|
} elseif ( !$this->mSearchGroup && $this->mSearchFilter ) {
|
|
|
|
$searchIDs = $searchFilters;
|
|
|
|
} elseif ( $this->mSearchGroup && $this->mSearchFilter ) {
|
|
|
|
$searchIDs = array_intersect( $groupFilters, $searchFilters );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( $searchIDs !== null ) {
|
|
|
|
if ( !count( $searchIDs ) ) {
|
|
|
|
$out->addWikiMsg( 'abusefilter-log-noresults' );
|
2014-01-05 21:06:24 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-01-17 17:34:07 +00:00
|
|
|
|
2019-02-06 16:07:01 +00:00
|
|
|
$filterConds = [ 'local' => [], 'global' => [] ];
|
|
|
|
foreach ( $searchIDs as $filter ) {
|
2024-03-16 18:52:48 +00:00
|
|
|
[ $filterID, $isGlobal ] = GlobalNameUtils::splitGlobalName( $filter );
|
2019-02-06 16:07:01 +00:00
|
|
|
$key = $isGlobal ? 'global' : 'local';
|
|
|
|
$filterConds[$key][] = $filterID;
|
2018-09-11 16:57:49 +00:00
|
|
|
}
|
2019-02-06 16:07:01 +00:00
|
|
|
$filterWhere = [];
|
|
|
|
if ( $filterConds['local'] ) {
|
2024-07-21 15:18:31 +00:00
|
|
|
$filterWhere[] = $dbr->andExpr( [
|
|
|
|
'afl_global' => 0,
|
|
|
|
// @phan-suppress-previous-line PhanTypeMismatchArgument Array is non-empty
|
|
|
|
'afl_filter_id' => $filterConds['local'],
|
|
|
|
] );
|
2019-02-06 16:07:01 +00:00
|
|
|
}
|
|
|
|
if ( $filterConds['global'] ) {
|
2024-07-21 15:18:31 +00:00
|
|
|
$filterWhere[] = $dbr->andExpr( [
|
|
|
|
'afl_global' => 1,
|
|
|
|
// @phan-suppress-previous-line PhanTypeMismatchArgument Array is non-empty
|
|
|
|
'afl_filter_id' => $filterConds['global'],
|
|
|
|
] );
|
2019-02-06 16:07:01 +00:00
|
|
|
}
|
2024-07-21 15:18:31 +00:00
|
|
|
$conds[] = $dbr->orExpr( $filterWhere );
|
2009-05-22 06:42:10 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2008-06-27 06:18:51 +00:00
|
|
|
$searchTitle = Title::newFromText( $this->mSearchTitle );
|
2022-06-29 10:19:24 +00:00
|
|
|
if ( $searchTitle ) {
|
2008-06-27 06:18:51 +00:00
|
|
|
$conds['afl_namespace'] = $searchTitle->getNamespace();
|
2009-05-24 08:33:57 +00:00
|
|
|
$conds['afl_title'] = $searchTitle->getDBkey();
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2022-07-02 13:35:00 +00:00
|
|
|
if ( $this->afPermissionManager->canSeeHiddenLogEntries( $performer ) ) {
|
2018-08-26 08:34:42 +00:00
|
|
|
if ( $this->mSearchEntries === '1' ) {
|
2017-07-28 08:49:19 +00:00
|
|
|
$conds['afl_deleted'] = 1;
|
2018-08-26 08:34:42 +00:00
|
|
|
} elseif ( $this->mSearchEntries === '2' ) {
|
2019-02-09 10:17:09 +00:00
|
|
|
$conds['afl_deleted'] = 0;
|
2017-08-24 08:52:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-17 20:20:30 +00:00
|
|
|
if ( $this->mSearchImpact === '1' ) {
|
|
|
|
$conds[] = $dbr->expr( 'afl_rev_id', '!=', null );
|
|
|
|
} elseif ( $this->mSearchImpact === '2' ) {
|
|
|
|
$conds[] = $dbr->expr( 'afl_rev_id', '=', null );
|
2017-07-28 08:49:19 +00:00
|
|
|
}
|
|
|
|
|
2018-02-24 13:49:39 +00:00
|
|
|
if ( $this->mSearchActionTaken ) {
|
2020-11-29 22:55:34 +00:00
|
|
|
if ( in_array( $this->mSearchActionTaken, $this->consequencesRegistry->getAllActionNames() ) ) {
|
2024-05-17 20:20:30 +00:00
|
|
|
$conds[] = $dbr->expr( 'afl_actions', '=', $this->mSearchActionTaken )
|
|
|
|
->or( 'afl_actions', IExpression::LIKE, new LikeValue(
|
|
|
|
$this->mSearchActionTaken, ',', $dbr->anyString()
|
|
|
|
) )
|
|
|
|
->or( 'afl_actions', IExpression::LIKE, new LikeValue(
|
|
|
|
$dbr->anyString(), ',', $this->mSearchActionTaken
|
|
|
|
) )
|
|
|
|
->or( 'afl_actions', IExpression::LIKE, new LikeValue(
|
|
|
|
$dbr->anyString(),
|
|
|
|
',', $this->mSearchActionTaken, ',',
|
|
|
|
$dbr->anyString()
|
|
|
|
) );
|
2018-02-24 13:49:39 +00:00
|
|
|
} elseif ( $this->mSearchActionTaken === 'noactions' ) {
|
|
|
|
$conds['afl_actions'] = '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-04 14:02:45 +00:00
|
|
|
if ( $this->mSearchAction ) {
|
|
|
|
$filterableActions = $this->getAllFilterableActions();
|
|
|
|
if ( in_array( $this->mSearchAction, $filterableActions ) ) {
|
|
|
|
$conds['afl_action'] = $this->mSearchAction;
|
|
|
|
} elseif ( $this->mSearchAction === 'other' ) {
|
2024-05-17 20:20:30 +00:00
|
|
|
$conds[] = $dbr->expr( 'afl_action', '!=', $filterableActions );
|
2018-03-04 14:02:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-11 14:30:33 +00:00
|
|
|
$pager = new AbuseLogPager(
|
2020-12-07 13:56:16 +00:00
|
|
|
$this->getContext(),
|
|
|
|
$this->getLinkRenderer(),
|
2018-03-11 14:30:33 +00:00
|
|
|
$conds,
|
|
|
|
$this->linkBatchFactory,
|
2020-12-07 13:56:16 +00:00
|
|
|
$this->permissionManager,
|
|
|
|
$this->afPermissionManager,
|
|
|
|
$this->getName()
|
2018-03-11 14:30:33 +00:00
|
|
|
);
|
2011-07-05 00:09:09 +00:00
|
|
|
$pager->doQuery();
|
2011-07-04 22:00:06 +00:00
|
|
|
$result = $pager->getResult();
|
2019-01-23 11:01:06 +00:00
|
|
|
|
2024-03-29 12:40:23 +00:00
|
|
|
$form = Html::rawElement(
|
2019-01-23 11:01:06 +00:00
|
|
|
'form',
|
|
|
|
[
|
|
|
|
'method' => 'GET',
|
2018-10-16 19:58:22 +00:00
|
|
|
'action' => $this->getPageTitle( 'hide' )->getLocalURL()
|
2019-01-23 11:01:06 +00:00
|
|
|
],
|
2022-07-03 15:52:28 +00:00
|
|
|
$this->getDeleteButton() . $this->getListToggle() .
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::rawElement( 'ul', [ 'class' => 'plainlinks' ], $pager->getBody() ) .
|
2022-07-03 15:52:28 +00:00
|
|
|
$this->getListToggle() . $this->getDeleteButton()
|
2019-01-23 11:01:06 +00:00
|
|
|
);
|
|
|
|
|
2015-09-28 18:03:35 +00:00
|
|
|
if ( $result && $result->numRows() !== 0 ) {
|
2023-01-24 14:11:19 +00:00
|
|
|
$out->addModuleStyles( 'mediawiki.interface.helpers.styles' );
|
2019-01-23 11:01:06 +00:00
|
|
|
$out->addHTML( $pager->getNavigationBar() . $form . $pager->getNavigationBar() );
|
2011-06-06 23:46:13 +00:00
|
|
|
} else {
|
2011-11-16 05:34:24 +00:00
|
|
|
$out->addWikiMsg( 'abusefilter-log-noresults' );
|
2011-06-06 23:46:13 +00:00
|
|
|
}
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2018-10-16 19:58:22 +00:00
|
|
|
/**
|
|
|
|
* Returns the HTML for a button to hide selected entries
|
|
|
|
*
|
|
|
|
* @return string|ButtonInputWidget
|
|
|
|
*/
|
|
|
|
private function getDeleteButton() {
|
2022-07-02 13:35:00 +00:00
|
|
|
if ( !$this->afPermissionManager->canHideAbuseLog( $this->getAuthority() ) ) {
|
2018-10-16 19:58:22 +00:00
|
|
|
return '';
|
|
|
|
}
|
|
|
|
return new ButtonInputWidget( [
|
|
|
|
'label' => $this->msg( 'abusefilter-log-hide-entries' )->text(),
|
|
|
|
'type' => 'submit'
|
|
|
|
] );
|
|
|
|
}
|
|
|
|
|
2022-07-03 15:52:28 +00:00
|
|
|
/**
|
|
|
|
* Get the All / Invert / None options provided by
|
|
|
|
* ToggleList.php to mass select the checkboxes.
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private function getListToggle() {
|
|
|
|
if ( !$this->afPermissionManager->canHideAbuseLog( $this->getUser() ) ) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
return ( new ListToggle( $this->getOutput() ) )->getHtml();
|
|
|
|
}
|
|
|
|
|
2012-03-11 20:51:54 +00:00
|
|
|
/**
|
2019-12-07 17:20:10 +00:00
|
|
|
* @param string|int $id
|
2020-09-16 20:17:12 +00:00
|
|
|
* @suppress SecurityCheck-SQLInjection
|
2012-03-11 20:51:54 +00:00
|
|
|
*/
|
2018-04-04 21:14:25 +00:00
|
|
|
public function showDetails( $id ) {
|
2011-11-16 05:34:24 +00:00
|
|
|
$out = $this->getOutput();
|
2022-07-02 13:35:00 +00:00
|
|
|
$performer = $this->getAuthority();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2018-03-11 14:30:33 +00:00
|
|
|
$pager = new AbuseLogPager(
|
2020-12-07 13:56:16 +00:00
|
|
|
$this->getContext(),
|
|
|
|
$this->getLinkRenderer(),
|
2018-03-11 14:30:33 +00:00
|
|
|
[],
|
|
|
|
$this->linkBatchFactory,
|
2020-12-07 13:56:16 +00:00
|
|
|
$this->permissionManager,
|
|
|
|
$this->afPermissionManager,
|
|
|
|
$this->getName()
|
2018-03-11 14:30:33 +00:00
|
|
|
);
|
2020-09-16 20:17:12 +00:00
|
|
|
|
|
|
|
[
|
|
|
|
'tables' => $tables,
|
|
|
|
'fields' => $fields,
|
|
|
|
'join_conds' => $join_conds,
|
|
|
|
] = $pager->getQueryInfo();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2023-02-26 12:51:08 +00:00
|
|
|
$dbr = $this->lbFactory->getReplicaDatabase();
|
2024-04-30 18:21:20 +00:00
|
|
|
$row = $dbr->newSelectQueryBuilder()
|
|
|
|
->tables( $tables )
|
|
|
|
->fields( $fields )
|
|
|
|
->where( [ 'afl_id' => $id ] )
|
|
|
|
->caller( __METHOD__ )
|
|
|
|
->joinConds( $join_conds )
|
|
|
|
->fetchRow();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2018-11-19 14:58:53 +00:00
|
|
|
$error = null;
|
2024-09-12 14:45:57 +00:00
|
|
|
$privacyLevel = Flags::FILTER_PUBLIC;
|
2009-05-22 06:42:10 +00:00
|
|
|
if ( !$row ) {
|
2018-11-19 14:58:53 +00:00
|
|
|
$error = 'abusefilter-log-nonexistent';
|
2012-01-03 17:29:10 +00:00
|
|
|
} else {
|
2019-02-06 16:07:01 +00:00
|
|
|
$filterID = $row->afl_filter_id;
|
|
|
|
$global = $row->afl_global;
|
2018-09-11 16:57:49 +00:00
|
|
|
|
2024-09-12 14:45:57 +00:00
|
|
|
$privacyLevel = $row->af_hidden;
|
2019-03-28 19:59:53 +00:00
|
|
|
if ( $global ) {
|
2021-01-07 16:17:43 +00:00
|
|
|
try {
|
2024-05-16 10:40:53 +00:00
|
|
|
$privacyLevel = AbuseFilterServices::getFilterLookup()->getFilter( $filterID, $global )
|
|
|
|
->getPrivacyLevel();
|
2021-01-07 16:17:43 +00:00
|
|
|
} catch ( CentralDBNotAvailableException $_ ) {
|
2024-07-02 20:53:28 +00:00
|
|
|
// Conservatively assume that it's hidden and protected, like in AbuseLogPager::doFormatRow
|
2024-05-23 14:49:17 +00:00
|
|
|
$privacyLevel = Flags::FILTER_HIDDEN & Flags::FILTER_USES_PROTECTED_VARS;
|
2021-01-07 16:17:43 +00:00
|
|
|
}
|
2018-11-19 14:58:53 +00:00
|
|
|
}
|
2015-09-28 18:03:35 +00:00
|
|
|
|
2024-05-16 10:40:53 +00:00
|
|
|
if ( !$this->afPermissionManager->canSeeLogDetailsForFilter( $performer, $privacyLevel ) ) {
|
2018-11-19 14:58:53 +00:00
|
|
|
$error = 'abusefilter-log-cannot-see-details';
|
2021-09-15 18:23:36 +00:00
|
|
|
} else {
|
2022-07-02 13:35:00 +00:00
|
|
|
$visibility = self::getEntryVisibilityForUser( $row, $performer, $this->afPermissionManager );
|
2021-09-15 18:23:36 +00:00
|
|
|
if ( $visibility === self::VISIBILITY_HIDDEN ) {
|
|
|
|
$error = 'abusefilter-log-details-hidden';
|
|
|
|
} elseif ( $visibility === self::VISIBILITY_HIDDEN_IMPLICIT ) {
|
2018-11-19 14:58:53 +00:00
|
|
|
$error = 'abusefilter-log-details-hidden-implicit';
|
|
|
|
}
|
|
|
|
}
|
2024-08-14 14:11:10 +00:00
|
|
|
|
2024-09-23 10:38:57 +00:00
|
|
|
// Only show the preference error if another error isn't already set
|
|
|
|
// as this error shouldn't take precedence over a view permission error
|
2024-08-14 14:11:10 +00:00
|
|
|
if (
|
|
|
|
FilterUtils::isProtected( $privacyLevel ) &&
|
2024-09-23 10:38:57 +00:00
|
|
|
!$this->afPermissionManager->canViewProtectedVariableValues( $performer ) &&
|
|
|
|
!$error
|
2024-08-14 14:11:10 +00:00
|
|
|
) {
|
|
|
|
$error = 'abusefilter-examine-protected-vars-permission';
|
|
|
|
}
|
2012-01-03 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
2018-11-19 14:58:53 +00:00
|
|
|
if ( $error ) {
|
|
|
|
$out->addWikiMsg( $error );
|
2018-07-13 22:34:54 +00:00
|
|
|
return;
|
2010-06-25 20:04:55 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2024-03-29 12:40:23 +00:00
|
|
|
$output = Html::element(
|
2012-09-02 11:07:02 +00:00
|
|
|
'legend',
|
2024-03-29 12:40:23 +00:00
|
|
|
[],
|
2017-11-01 00:22:14 +00:00
|
|
|
$this->msg( 'abusefilter-log-details-legend' )
|
2024-08-09 05:58:23 +00:00
|
|
|
->params( $this->getLanguage()->formatNumNoSeparators( $id ) )
|
2017-11-01 00:22:14 +00:00
|
|
|
->text()
|
2012-09-02 11:07:02 +00:00
|
|
|
);
|
2024-03-29 12:40:23 +00:00
|
|
|
$output .= Html::rawElement( 'p', [], $pager->doFormatRow( $row, false ) );
|
2009-03-22 23:33:27 +00:00
|
|
|
|
2010-02-13 14:10:36 +00:00
|
|
|
// Load data
|
2024-03-22 13:40:06 +00:00
|
|
|
$vars = $this->varBlobStore->loadVarDump( $row );
|
2024-09-12 14:45:57 +00:00
|
|
|
$varsArray = $this->varManager->dumpAllVars( $vars, true );
|
|
|
|
$shouldLogProtectedVarAccess = false;
|
2024-03-22 13:40:06 +00:00
|
|
|
|
2024-08-14 14:11:10 +00:00
|
|
|
// If a non-protected filter and a protected filter have overlapping conditions,
|
|
|
|
// it's possible for a hit to contain a protected variable and for that variable
|
|
|
|
// to be dumped and displayed on a detail page that wouldn't be considered
|
|
|
|
// protected (because it caught on the public filter).
|
|
|
|
// We shouldn't block access to the details of an otherwise public filter hit so
|
|
|
|
// instead only check for access to the protected variables and redact them if the user
|
|
|
|
// shouldn't see them.
|
2024-09-12 14:45:57 +00:00
|
|
|
$userAuthority = $this->getAuthority();
|
|
|
|
$canViewProtectedVars = $this->afPermissionManager->canViewProtectedVariableValues( $userAuthority );
|
|
|
|
foreach ( $this->afPermissionManager->getProtectedVariables() as $protectedVariable ) {
|
|
|
|
if ( isset( $varsArray[$protectedVariable] ) ) {
|
|
|
|
if ( !$canViewProtectedVars ) {
|
2024-08-14 14:11:10 +00:00
|
|
|
$varsArray[$protectedVariable] = '';
|
2024-09-12 14:45:57 +00:00
|
|
|
} else {
|
|
|
|
// Protected variables in protected filters logs access in the general permission check
|
|
|
|
// Log access to non-protected filters that happen to expose protected variables here
|
|
|
|
if ( !FilterUtils::isProtected( $privacyLevel ) ) {
|
|
|
|
$shouldLogProtectedVarAccess = true;
|
|
|
|
}
|
2024-08-14 14:11:10 +00:00
|
|
|
}
|
|
|
|
}
|
2024-09-12 14:45:57 +00:00
|
|
|
}
|
|
|
|
$vars = VariableHolder::newFromArray( $varsArray );
|
|
|
|
|
|
|
|
// Log if protected variables are accessed
|
|
|
|
if (
|
|
|
|
FilterUtils::isProtected( $privacyLevel ) &&
|
|
|
|
$canViewProtectedVars
|
|
|
|
) {
|
|
|
|
$shouldLogProtectedVarAccess = true;
|
2024-08-14 14:11:10 +00:00
|
|
|
}
|
|
|
|
|
2024-09-12 14:45:57 +00:00
|
|
|
if ( $shouldLogProtectedVarAccess ) {
|
|
|
|
$logger = $this->abuseLoggerFactory->getProtectedVarsAccessLogger();
|
|
|
|
$logger->logViewProtectedVariableValue(
|
|
|
|
$userAuthority->getUser(),
|
|
|
|
$varsArray['user_name']
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$out->addJsConfigVars( 'wgAbuseFilterVariables', $varsArray );
|
2023-01-24 14:11:19 +00:00
|
|
|
$out->addModuleStyles( 'mediawiki.interface.helpers.styles' );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-18 05:45:36 +00:00
|
|
|
// Diff, if available
|
2020-03-03 18:03:02 +00:00
|
|
|
if ( $row->afl_action === 'edit' ) {
|
2020-10-18 22:25:05 +00:00
|
|
|
// Guard for exception because these variables may be unset in case of data corruption (T264513)
|
|
|
|
// No need to lazy-load as these come from a DB dump.
|
|
|
|
try {
|
|
|
|
$old_wikitext = $vars->getComputedVariable( 'old_wikitext' )->toString();
|
|
|
|
} catch ( UnsetVariableException $_ ) {
|
|
|
|
$old_wikitext = '';
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
$new_wikitext = $vars->getComputedVariable( 'new_wikitext' )->toString();
|
|
|
|
} catch ( UnsetVariableException $_ ) {
|
|
|
|
$new_wikitext = '';
|
|
|
|
}
|
2009-05-26 13:08:15 +00:00
|
|
|
|
2013-06-10 19:53:33 +00:00
|
|
|
$diffEngine = new DifferenceEngine( $this->getContext() );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-05-26 13:08:15 +00:00
|
|
|
$diffEngine->showDiffStyle();
|
2013-06-10 16:57:27 +00:00
|
|
|
|
2018-08-20 14:48:54 +00:00
|
|
|
$formattedDiff = $diffEngine->addHeader(
|
|
|
|
$diffEngine->generateTextDiffBody( $old_wikitext, $new_wikitext ),
|
|
|
|
'', ''
|
|
|
|
);
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
$output .=
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::rawElement(
|
2009-10-07 13:57:06 +00:00
|
|
|
'h3',
|
2024-03-29 12:40:23 +00:00
|
|
|
[],
|
2012-09-02 11:07:02 +00:00
|
|
|
$this->msg( 'abusefilter-log-details-diff' )->parse()
|
2009-10-07 13:57:06 +00:00
|
|
|
);
|
|
|
|
|
2009-03-18 05:45:36 +00:00
|
|
|
$output .= $formattedDiff;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2024-03-29 12:40:23 +00:00
|
|
|
$output .= Html::element( 'h3', [], $this->msg( 'abusefilter-log-details-vars' )->text() );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-22 23:33:27 +00:00
|
|
|
// Build a table.
|
2020-12-31 13:29:00 +00:00
|
|
|
$output .= $this->variablesFormatter->buildVarDumpTable( $vars );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2022-07-02 13:35:00 +00:00
|
|
|
if ( $this->afPermissionManager->canSeePrivateDetails( $performer ) ) {
|
2016-12-12 16:30:06 +00:00
|
|
|
$formDescriptor = [
|
|
|
|
'Reason' => [
|
2019-01-07 15:23:21 +00:00
|
|
|
'label-message' => 'abusefilter-view-privatedetails-reason',
|
2016-12-12 16:30:06 +00:00
|
|
|
'type' => 'text',
|
|
|
|
'size' => 45,
|
|
|
|
],
|
|
|
|
];
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2018-04-02 16:58:43 +00:00
|
|
|
$htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() );
|
2022-03-27 12:39:10 +00:00
|
|
|
$htmlForm->setTitle( $this->getPageTitle( 'private/' . $id ) )
|
|
|
|
->setWrapperLegendMsg( 'abusefilter-view-privatedetails-legend' )
|
2019-01-07 15:23:21 +00:00
|
|
|
->setSubmitTextMsg( 'abusefilter-view-privatedetails-submit' )
|
2016-12-12 16:30:06 +00:00
|
|
|
->prepareForm();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2016-12-12 16:30:06 +00:00
|
|
|
$output .= $htmlForm->getHTML( false );
|
2008-06-27 08:11:09 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2024-03-29 12:40:23 +00:00
|
|
|
$out->addHTML( Html::rawElement( 'fieldset', [], $output ) );
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2016-12-12 16:30:06 +00:00
|
|
|
/**
|
2018-12-04 09:37:48 +00:00
|
|
|
* Helper function to select a row with private details and some more context
|
|
|
|
* for an AbuseLog entry.
|
2023-02-26 12:51:08 +00:00
|
|
|
* @todo Create a service for this
|
2018-12-04 09:37:48 +00:00
|
|
|
*
|
2022-06-27 20:23:24 +00:00
|
|
|
* @param Authority $authority The user who's trying to view the row
|
2018-12-04 09:37:48 +00:00
|
|
|
* @param int $id The ID of the log entry
|
|
|
|
* @return Status A status object with the requested row stored in the value property,
|
|
|
|
* or an error and no row.
|
2016-12-12 16:30:06 +00:00
|
|
|
*/
|
2022-06-27 20:23:24 +00:00
|
|
|
public static function getPrivateDetailsRow( Authority $authority, $id ) {
|
2024-09-12 14:45:57 +00:00
|
|
|
$afPermissionManager = AbuseFilterServices::getPermissionManager();
|
2024-08-11 15:27:28 +00:00
|
|
|
$dbr = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase();
|
2018-09-11 16:57:49 +00:00
|
|
|
|
2024-04-30 18:21:20 +00:00
|
|
|
$row = $dbr->newSelectQueryBuilder()
|
|
|
|
->select( [ 'afl_id', 'afl_user_text', 'afl_filter_id', 'afl_global', 'afl_timestamp', 'afl_ip',
|
|
|
|
'af_id', 'af_public_comments', 'af_hidden' ] )
|
|
|
|
->from( 'abuse_filter_log' )
|
|
|
|
->leftJoin( 'abuse_filter', null, [ 'af_id=afl_filter_id', 'afl_global' => 0 ] )
|
|
|
|
->where( [ 'afl_id' => $id ] )
|
|
|
|
->caller( __METHOD__ )
|
|
|
|
->fetchRow();
|
2016-12-12 16:30:06 +00:00
|
|
|
|
2018-12-04 09:37:48 +00:00
|
|
|
$status = Status::newGood();
|
2016-12-12 16:30:06 +00:00
|
|
|
if ( !$row ) {
|
2018-12-04 09:37:48 +00:00
|
|
|
$status->fatal( 'abusefilter-log-nonexistent' );
|
|
|
|
return $status;
|
2016-12-12 16:30:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-06 16:07:01 +00:00
|
|
|
$filterID = $row->afl_filter_id;
|
|
|
|
$global = $row->afl_global;
|
2018-09-11 16:57:49 +00:00
|
|
|
|
2018-12-04 09:37:48 +00:00
|
|
|
if ( $global ) {
|
2020-11-18 19:24:02 +00:00
|
|
|
$lookup = AbuseFilterServices::getFilterLookup();
|
2024-05-16 10:40:53 +00:00
|
|
|
$privacyLevel = $lookup->getFilter( $filterID, $global )->getPrivacyLevel();
|
2018-12-04 09:37:48 +00:00
|
|
|
} else {
|
2024-05-16 10:40:53 +00:00
|
|
|
$privacyLevel = $row->af_hidden;
|
2016-12-12 16:30:06 +00:00
|
|
|
}
|
|
|
|
|
2024-09-12 14:45:57 +00:00
|
|
|
if ( !$afPermissionManager->canSeeLogDetailsForFilter( $authority, $privacyLevel ) ) {
|
2018-12-04 09:37:48 +00:00
|
|
|
$status->fatal( 'abusefilter-log-cannot-see-details' );
|
|
|
|
return $status;
|
2016-12-12 16:30:06 +00:00
|
|
|
}
|
2018-12-04 09:37:48 +00:00
|
|
|
$status->setResult( true, $row );
|
|
|
|
return $status;
|
|
|
|
}
|
2016-12-12 16:30:06 +00:00
|
|
|
|
2018-12-04 09:37:48 +00:00
|
|
|
/**
|
|
|
|
* Builds an HTML table with the private details for a given abuseLog entry.
|
|
|
|
*
|
|
|
|
* @param stdClass $row The row, as returned by self::getPrivateDetailsRow()
|
|
|
|
* @return string The HTML output
|
|
|
|
*/
|
2023-06-23 10:28:06 +00:00
|
|
|
private function buildPrivateDetailsTable( $row ) {
|
2024-05-13 10:36:12 +00:00
|
|
|
$output = '';
|
2016-12-12 16:30:06 +00:00
|
|
|
|
|
|
|
// Log ID
|
|
|
|
$linkRenderer = $this->getLinkRenderer();
|
|
|
|
$output .=
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::rawElement( 'tr', [],
|
|
|
|
Html::element( 'td',
|
2016-12-12 16:30:06 +00:00
|
|
|
[ 'style' => 'width: 30%;' ],
|
|
|
|
$this->msg( 'abusefilter-log-details-id' )->text()
|
|
|
|
) .
|
2024-05-13 10:36:12 +00:00
|
|
|
Html::rawElement( 'td', [], $linkRenderer->makeKnownLink(
|
2016-12-12 16:30:06 +00:00
|
|
|
$this->getPageTitle( $row->afl_id ),
|
2024-08-09 05:58:23 +00:00
|
|
|
$this->getLanguage()->formatNumNoSeparators( $row->afl_id )
|
2024-05-13 10:36:12 +00:00
|
|
|
) )
|
2016-12-12 16:30:06 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Timestamp
|
|
|
|
$output .=
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::rawElement( 'tr', [],
|
|
|
|
Html::element( 'td',
|
2016-12-12 16:30:06 +00:00
|
|
|
[ 'style' => 'width: 30%;' ],
|
|
|
|
$this->msg( 'abusefilter-edit-builder-vars-timestamp-expanded' )->text()
|
|
|
|
) .
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::element( 'td',
|
|
|
|
[],
|
2021-03-08 23:54:03 +00:00
|
|
|
$this->getLanguage()->userTimeAndDate( $row->afl_timestamp, $this->getUser() )
|
2016-12-12 16:30:06 +00:00
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// User
|
|
|
|
$output .=
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::rawElement( 'tr', [],
|
|
|
|
Html::element( 'td',
|
2016-12-12 16:30:06 +00:00
|
|
|
[ 'style' => 'width: 30%;' ],
|
|
|
|
$this->msg( 'abusefilter-edit-builder-vars-user-name' )->text()
|
|
|
|
) .
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::element( 'td',
|
|
|
|
[],
|
2016-12-12 16:30:06 +00:00
|
|
|
$row->afl_user_text
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Filter ID
|
|
|
|
$output .=
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::rawElement( 'tr', [],
|
|
|
|
Html::element( 'td',
|
2016-12-12 16:30:06 +00:00
|
|
|
[ 'style' => 'width: 30%;' ],
|
|
|
|
$this->msg( 'abusefilter-list-id' )->text()
|
|
|
|
) .
|
2024-05-13 10:36:12 +00:00
|
|
|
Html::rawElement( 'td', [], $linkRenderer->makeKnownLink(
|
2016-12-12 16:30:06 +00:00
|
|
|
SpecialPage::getTitleFor( 'AbuseFilter', $row->af_id ),
|
2018-12-04 09:37:48 +00:00
|
|
|
$this->getLanguage()->formatNum( $row->af_id )
|
2024-05-13 10:36:12 +00:00
|
|
|
) )
|
2016-12-12 16:30:06 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Filter description
|
|
|
|
$output .=
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::rawElement( 'tr', [],
|
|
|
|
Html::element( 'td',
|
2016-12-12 16:30:06 +00:00
|
|
|
[ 'style' => 'width: 30%;' ],
|
|
|
|
$this->msg( 'abusefilter-list-public' )->text()
|
|
|
|
) .
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::element( 'td',
|
|
|
|
[],
|
2016-12-12 16:30:06 +00:00
|
|
|
$row->af_public_comments
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// IP address
|
2018-03-01 10:31:25 +00:00
|
|
|
if ( $row->afl_ip !== '' ) {
|
|
|
|
if ( ExtensionRegistry::getInstance()->isLoaded( 'CheckUser' ) &&
|
2020-09-17 06:54:06 +00:00
|
|
|
$this->permissionManager->userHasRight( $this->getUser(), 'checkuser' )
|
|
|
|
) {
|
|
|
|
$CULink = ' · ' . $linkRenderer->makeKnownLink(
|
|
|
|
SpecialPage::getTitleFor(
|
|
|
|
'CheckUser',
|
|
|
|
$row->afl_ip
|
|
|
|
),
|
|
|
|
$this->msg( 'abusefilter-log-details-checkuser' )->text()
|
|
|
|
);
|
2018-03-01 10:31:25 +00:00
|
|
|
} else {
|
|
|
|
$CULink = '';
|
|
|
|
}
|
|
|
|
$output .=
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::rawElement( 'tr', [],
|
|
|
|
Html::element( 'td',
|
2018-03-01 10:31:25 +00:00
|
|
|
[ 'style' => 'width: 30%;' ],
|
|
|
|
$this->msg( 'abusefilter-log-details-ip' )->text()
|
|
|
|
) .
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::rawElement(
|
2018-03-01 10:31:25 +00:00
|
|
|
'td',
|
2024-03-29 12:40:23 +00:00
|
|
|
[],
|
2018-03-09 08:26:58 +00:00
|
|
|
self::getUserLinks( 0, $row->afl_ip ) . $CULink
|
2018-03-01 10:31:25 +00:00
|
|
|
)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$output .=
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::rawElement( 'tr', [],
|
|
|
|
Html::element( 'td',
|
2018-03-01 10:31:25 +00:00
|
|
|
[ 'style' => 'width: 30%;' ],
|
|
|
|
$this->msg( 'abusefilter-log-details-ip' )->text()
|
|
|
|
) .
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::element(
|
2018-03-01 10:31:25 +00:00
|
|
|
'td',
|
2024-03-29 12:40:23 +00:00
|
|
|
[],
|
2018-03-01 10:31:25 +00:00
|
|
|
$this->msg( 'abusefilter-log-ip-not-available' )->text()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2016-12-12 16:30:06 +00:00
|
|
|
|
2024-05-13 10:36:12 +00:00
|
|
|
return Html::rawElement( 'fieldset', [],
|
|
|
|
Html::element( 'legend', [],
|
|
|
|
$this->msg( 'abusefilter-log-details-privatedetails' )->text()
|
|
|
|
) .
|
|
|
|
Html::rawElement( 'table',
|
|
|
|
[
|
|
|
|
'class' => 'wikitable mw-abuselog-private',
|
|
|
|
'style' => 'width: 80%;'
|
|
|
|
],
|
|
|
|
Html::rawElement( 'thead', [],
|
|
|
|
Html::rawElement( 'tr', [],
|
|
|
|
Html::element( 'th', [],
|
|
|
|
$this->msg( 'abusefilter-log-details-var' )->text()
|
|
|
|
) .
|
|
|
|
Html::element( 'th', [],
|
|
|
|
$this->msg( 'abusefilter-log-details-val' )->text()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
) .
|
|
|
|
Html::rawElement( 'tbody', [], $output )
|
|
|
|
)
|
|
|
|
);
|
2018-12-04 09:37:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-12-07 17:20:10 +00:00
|
|
|
* @param int $id
|
2018-12-04 09:37:48 +00:00
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function showPrivateDetails( $id ) {
|
|
|
|
$out = $this->getOutput();
|
2019-08-27 09:40:01 +00:00
|
|
|
$user = $this->getUser();
|
|
|
|
|
2020-09-18 14:49:13 +00:00
|
|
|
if ( !$this->afPermissionManager->canSeePrivateDetails( $user ) ) {
|
2020-08-28 21:29:25 +00:00
|
|
|
$out->addWikiMsg( 'abusefilter-log-cannot-see-privatedetails' );
|
2018-12-04 09:37:48 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
$request = $this->getRequest();
|
|
|
|
|
|
|
|
// Make sure it is a valid request
|
|
|
|
$token = $request->getVal( 'wpEditToken' );
|
2019-08-27 09:40:01 +00:00
|
|
|
if ( !$request->wasPosted() || !$user->matchEditToken( $token ) ) {
|
2018-12-04 09:37:48 +00:00
|
|
|
$out->addHTML(
|
2024-03-29 12:40:23 +00:00
|
|
|
Html::rawElement(
|
2018-12-04 09:37:48 +00:00
|
|
|
'p',
|
2024-03-29 12:40:23 +00:00
|
|
|
[],
|
2018-12-04 09:37:48 +00:00
|
|
|
Html::errorBox( $this->msg( 'abusefilter-invalid-request' )->params( $id )->parse() )
|
|
|
|
)
|
|
|
|
);
|
2016-12-12 16:30:06 +00:00
|
|
|
|
2018-12-04 09:37:48 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$reason = $request->getText( 'wpReason' );
|
|
|
|
if ( !self::checkPrivateDetailsAccessReason( $reason ) ) {
|
|
|
|
$out->addWikiMsg( 'abusefilter-noreason' );
|
|
|
|
$this->showDetails( $id );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-27 09:40:01 +00:00
|
|
|
$status = self::getPrivateDetailsRow( $user, $id );
|
2018-12-04 09:37:48 +00:00
|
|
|
if ( !$status->isGood() ) {
|
2024-04-12 20:48:59 +00:00
|
|
|
$out->addWikiMsg( $status->getMessages()[0] );
|
2018-12-04 09:37:48 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
$row = $status->getValue();
|
|
|
|
|
|
|
|
// Log accessing private details
|
|
|
|
if ( $this->getConfig()->get( 'AbuseFilterLogPrivateDetailsAccess' ) ) {
|
2019-08-27 09:40:01 +00:00
|
|
|
self::addPrivateDetailsAccessLogEntry( $id, $reason, $user );
|
2018-12-04 09:37:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Show private details (IP).
|
|
|
|
$table = $this->buildPrivateDetailsTable( $row );
|
|
|
|
$out->addHTML( $table );
|
2016-12-12 16:30:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If specifying a reason for viewing private details of abuse log is required
|
|
|
|
* then it makes sure that a reason is provided.
|
|
|
|
*
|
|
|
|
* @param string $reason
|
|
|
|
* @return bool
|
|
|
|
*/
|
2019-01-07 15:23:21 +00:00
|
|
|
public static function checkPrivateDetailsAccessReason( $reason ) {
|
|
|
|
global $wgAbuseFilterPrivateDetailsForceReason;
|
|
|
|
return ( !$wgAbuseFilterPrivateDetailsForceReason || strlen( $reason ) > 0 );
|
2016-12-12 16:30:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param int $logID int The ID of the AbuseFilter log that was accessed
|
|
|
|
* @param string $reason The reason provided for accessing private details
|
2022-06-27 20:23:24 +00:00
|
|
|
* @param UserIdentity $userIdentity The user who accessed the private details
|
2016-12-12 16:30:06 +00:00
|
|
|
* @return void
|
|
|
|
*/
|
2022-06-27 20:23:24 +00:00
|
|
|
public static function addPrivateDetailsAccessLogEntry( $logID, $reason, UserIdentity $userIdentity ) {
|
2021-01-09 13:40:10 +00:00
|
|
|
$target = self::getTitleFor( self::PAGE_NAME, (string)$logID );
|
2016-12-12 16:30:06 +00:00
|
|
|
|
|
|
|
$logEntry = new ManualLogEntry( 'abusefilterprivatedetails', 'access' );
|
2022-06-27 20:23:24 +00:00
|
|
|
$logEntry->setPerformer( $userIdentity );
|
2016-12-12 16:30:06 +00:00
|
|
|
$logEntry->setTarget( $target );
|
|
|
|
$logEntry->setParameters( [
|
|
|
|
'4::logid' => $logID,
|
|
|
|
] );
|
|
|
|
$logEntry->setComment( $reason );
|
|
|
|
|
|
|
|
$logEntry->insert();
|
|
|
|
}
|
|
|
|
|
2017-10-20 17:46:31 +00:00
|
|
|
/**
|
|
|
|
* @param int $userId
|
|
|
|
* @param string $userName
|
|
|
|
* @return string
|
|
|
|
*/
|
2020-12-07 13:56:16 +00:00
|
|
|
public static function getUserLinks( $userId, $userName ) {
|
2017-06-15 14:23:34 +00:00
|
|
|
static $cache = [];
|
2016-08-15 10:08:17 +00:00
|
|
|
|
|
|
|
if ( !isset( $cache[$userName][$userId] ) ) {
|
|
|
|
$cache[$userName][$userId] = Linker::userLink( $userId, $userName ) .
|
|
|
|
Linker::userToolLinks( $userId, $userName, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
return $cache[$userName][$userId];
|
|
|
|
}
|
|
|
|
|
2021-09-15 18:23:36 +00:00
|
|
|
/**
|
|
|
|
* @param stdClass $row
|
|
|
|
* @param Authority $authority
|
2024-09-12 14:45:57 +00:00
|
|
|
* @param AbuseFilterPermissionManager $afPermissionManager
|
2021-09-15 18:23:36 +00:00
|
|
|
* @return string One of the self::VISIBILITY_* constants
|
|
|
|
*/
|
|
|
|
public static function getEntryVisibilityForUser(
|
|
|
|
stdClass $row,
|
|
|
|
Authority $authority,
|
2024-09-12 14:45:57 +00:00
|
|
|
AbuseFilterPermissionManager $afPermissionManager
|
2021-09-15 18:23:36 +00:00
|
|
|
): string {
|
2024-09-12 14:45:57 +00:00
|
|
|
if ( $row->afl_deleted && !$afPermissionManager->canSeeHiddenLogEntries( $authority ) ) {
|
2021-09-15 18:23:36 +00:00
|
|
|
return self::VISIBILITY_HIDDEN;
|
|
|
|
}
|
|
|
|
if ( !$row->afl_rev_id ) {
|
|
|
|
return self::VISIBILITY_VISIBLE;
|
|
|
|
}
|
|
|
|
$revRec = MediaWikiServices::getInstance()
|
|
|
|
->getRevisionLookup()
|
|
|
|
->getRevisionById( (int)$row->afl_rev_id );
|
|
|
|
if ( !$revRec || $revRec->getVisibility() === 0 ) {
|
|
|
|
return self::VISIBILITY_VISIBLE;
|
|
|
|
}
|
|
|
|
return $revRec->audienceCan( RevisionRecord::SUPPRESSED_ALL, RevisionRecord::FOR_THIS_USER, $authority )
|
|
|
|
? self::VISIBILITY_VISIBLE
|
|
|
|
: self::VISIBILITY_HIDDEN_IMPLICIT;
|
|
|
|
}
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|