9use MediaWiki\Cache\LinkBatchFactory;
21use MediaWiki\Pager\LogPager;
32use Wikimedia\Timestamp\TimestampException;
41 private LinkBatchFactory $linkBatchFactory;
56 LinkBatchFactory $linkBatchFactory,
64 parent::__construct(
'Log' );
65 $this->linkBatchFactory = $linkBatchFactory;
66 $this->dbProvider = $dbProvider;
67 $this->actorNormalization = $actorNormalization;
68 $this->userIdentityLookup = $userIdentityLookup;
69 $this->userNameUtils = $userNameUtils;
70 $this->logFormatterFactory = $logFormatterFactory;
72 $this->tempUserConfig = $tempUserConfig;
83 $out->addModuleStyles(
'mediawiki.interface.helpers.styles' );
87 $opts->
add(
'type',
'' );
88 $opts->add(
'user',
'' );
89 $opts->add(
'page', [] );
90 $opts->add(
'pattern',
false );
91 $opts->add(
'year',
null, FormOptions::INTNULL );
92 $opts->add(
'month',
null, FormOptions::INTNULL );
93 $opts->add(
'day',
null, FormOptions::INTNULL );
94 $opts->add(
'tagfilter',
'' );
95 $opts->add(
'tagInvert',
false );
96 $opts->add(
'offset',
'' );
97 $opts->add(
'dir',
'' );
98 $opts->add(
'offender',
'' );
99 $opts->add(
'subtype',
'' );
100 $opts->add(
'logid',
'' );
103 if ( $par !==
null ) {
104 $this->parseParams( (
string)$par );
106 $opts->fetchValuesFromRequest( $this->
getRequest() );
109 $dateString = $this->
getRequest()->getVal(
'wpdate' );
112 $dateStamp = MWTimestamp::getInstance( $dateString .
' 00:00:00' );
113 }
catch ( TimestampException ) {
119 $opts->setValue(
'year', (
int)$dateStamp->format(
'Y' ) );
120 $opts->setValue(
'month', (
int)$dateStamp->format(
'm' ) );
121 $opts->setValue(
'day', (
int)$dateStamp->format(
'd' ) );
128 $type = $opts->getValue(
'type' );
129 if ( isset( $logRestrictions[$type] )
130 && !$this->
getAuthority()->isAllowed( $logRestrictions[$type] )
135 # TODO: Move this into LogPager like other query conditions.
136 # Handle type-specific inputs
138 $offenderName = $opts->getValue(
'offender' );
139 if ( $opts->getValue(
'type' ) ==
'suppress' && $offenderName !==
'' ) {
140 $dbr = $this->dbProvider->getReplicaDatabase();
141 $offenderId = $this->actorNormalization->findActorIdByName( $offenderName, $dbr );
143 $qc = [
'ls_field' =>
'target_author_actor',
'ls_value' => strval( $offenderId ) ];
149 if ( $this->tempUserConfig->isKnown() ) {
160 $formWasSubmitted = $this->
getRequest()->getVal(
'wpFormIdentifier' ) ===
'logeventslist';
163 !$formWasSubmitted &&
164 !$this->
getRequest()->getVal(
'excludetempacct' ) &&
165 !$this->tempUserConfig->isTempName( $opts->getValue(
'user' ) )
169 $this->getRequest()->getVal(
'excludetempacct' )
172 $dbr = $this->dbProvider->getReplicaDatabase();
173 if ( $opts->getValue(
'type' ) ===
'' ) {
176 $dbr->expr(
'log_type',
'!=',
'newusers' )->orExpr(
177 $dbr->expr(
'log_type',
'=',
'newusers' )
178 ->andExpr( $this->tempUserConfig
179 ->getMatchCondition( $dbr,
'logging_actor.actor_name', IExpression::NOT_LIKE ) )
182 } elseif ( $opts->getValue(
'type' ) ===
'newusers' ) {
184 $this->tempUserConfig
185 ->getMatchCondition( $dbr,
'logging_actor.actor_name', IExpression::NOT_LIKE )
193 $opts->getValue(
'type' ), $this->getRequest(), $qc );
196 # TODO: Move this into LogEventList and use it as filter-callback in the field descriptor.
197 # Some log types are only for a 'User:' title but we might have been given
198 # only the username instead of the full title 'User:username'. This part try
199 # to lookup for a user by that name and eventually fix user input. See T3697.
200 if ( in_array( $opts->getValue(
'type' ), self::getLogTypesOnUser( $this->getHookRunner() ) ) ) {
202 foreach ( $opts->getValue(
'page' ) as $page ) {
203 $page = $this->normalizeUserPage( $page );
204 if ( $page !==
null ) {
205 $pages[] = $page->getPrefixedText();
208 $opts->setValue(
'page', $pages );
211 $this->show( $opts, $qc );
220 private function normalizeUserPage( $page ) {
221 $target = Title::newFromText( $page );
222 if ( $target && $target->getNamespace() ===
NS_MAIN ) {
223 if ( IPUtils::isValidRange( $target->getText() ) ) {
224 $page = IPUtils::sanitizeRange( $target->getText() );
226 # User forgot to add 'User:', we are adding it for them
227 $target = Title::makeTitleSafe(
NS_USER, $page );
228 } elseif ( $target && $target->getNamespace() ===
NS_USER
229 && IPUtils::isValidRange( $target->getText() )
231 $ipOrRange = IPUtils::sanitizeRange( $target->getText() );
232 if ( $ipOrRange !== $target->getText() ) {
233 $target = Title::makeTitleSafe(
NS_USER, $ipOrRange );
251 static $types =
null;
252 if ( $types !==
null ) {
263 ->onGetLogTypesOnUser( $types );
273 $subpages = LogPage::validTypes();
287 private function parseParams(
string $par ) {
288 $params = explode(
'/', $par, 2 );
289 $logType = $this->resolveLogType( $params );
292 $this->
getRequest()->setVal(
'type', $logType );
293 if ( count( $params ) === 2 ) {
294 $this->
getRequest()->setVal(
'user', $params[1] );
296 } elseif ( $par !==
'' ) {
317 private function resolveLogType( array $params ): string {
327 if ( $logType !==
'' ) {
328 $symsForAll = [
'*',
'all' ];
329 $allowedTypes = array_merge( LogPage::validTypes(), $symsForAll );
331 if ( in_array( $logType, $allowedTypes ) ) {
339 private function show( FormOptions $opts, array $extraConds ) {
340 # Create a LogPager item to get the results and a LogEventsList item to format them...
341 $loglist =
new LogEventsList(
343 $this->getLinkRenderer(),
344 LogEventsList::USE_CHECKBOXES
346 $pager =
new LogPager(
348 $opts->getValue(
'type' ),
349 $opts->getValue(
'user' ),
350 $opts->getValue(
'page' ),
351 $opts->getValue(
'pattern' ),
353 $opts->getValue(
'year' ),
354 $opts->getValue(
'month' ),
355 $opts->getValue(
'day' ),
356 $opts->getValue(
'tagfilter' ),
357 $opts->getValue(
'subtype' ),
358 $opts->getValue(
'logid' ),
359 $this->linkBatchFactory,
360 $this->actorNormalization,
361 $this->logFormatterFactory,
362 $opts->getValue(
'tagInvert' )
366 $performer = $pager->getPerformer();
368 $performerUser = $this->userIdentityLookup->getUserIdentityByName( $performer );
371 if ( $performerUser && !IPUtils::isValidRange( $performer ) &&
372 ( $this->userNameUtils->isIP( $performer ) || $performerUser->isRegistered() )
374 $this->getSkin()->setRelevantUser( $performerUser );
379 $succeed = $loglist->showOptions(
380 $opts->getValue(
'type' ),
381 $opts->getValue(
'year' ),
382 $opts->getValue(
'month' ),
383 $opts->getValue(
'day' ),
384 $opts->getValue(
'user' ),
390 $this->getOutput()->setPageTitleMsg(
391 (
new LogPage( $opts->getValue(
'type' ) ) )->getName()
395 $logBody = $pager->getBody();
397 $this->getOutput()->addHTML(
398 $pager->getNavigationBar() .
399 $this->getActionButtons(
400 $loglist->beginLogEventsList() .
402 $loglist->endLogEventsList()
404 $pager->getNavigationBar()
407 $this->getOutput()->addWikiMsg(
'logempty' );
411 private function getActionButtons(
string $formcontents ): string {
413 ->isAllowedAll(
'deletedhistory',
'deletelogentry' );
414 $showTagEditUI = ChangeTags::showTagEditingUI( $this->
getAuthority() );
415 # If the user doesn't have the ability to delete log entries nor edit tags,
416 # don't bother showing them the button(s).
417 if ( !$canRevDelete && !$showTagEditUI ) {
418 return $formcontents;
421 # Show button to hide log entries and/or edit change tags
422 $s = Html::openElement(
424 [
'action' =>
wfScript(),
'id' =>
'mw-log-deleterevision-submit' ]
426 $s .= Html::hidden(
'type',
'logging' ) .
"\n";
429 if ( $canRevDelete ) {
436 'class' =>
"deleterevision-log-submit mw-log-deleterevision-button mw-ui-button"
438 $this->
msg(
'showhideselectedlogentries' )->text()
441 if ( $showTagEditUI ) {
448 'class' =>
"editchangetags-log-submit mw-log-editchangetags-button mw-ui-button"
450 $this->
msg(
'log-edit-tags' )->text()
454 $buttons .= (
new ListToggle( $this->getOutput() ) )->getHTML();
456 $s .= $buttons . $formcontents . $buttons;
457 $s .= Html::closeElement(
'form' );
469class_alias( SpecialLog::class,
'SpecialLog' );
wfScript( $script='index')
Get the URL path to a MediaWiki entry point.
Class to simplify the use of log pages.
A class containing constants representing the names of configuration variables.
const LogRestrictions
Name constant for the LogRestrictions setting, for use with Config::get()
Parent class for all special pages.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
getConfig()
Shortcut to get main config object.
getRequest()
Get the WebRequest being used for this instance.
getOutput()
Get the OutputPage being used for this instance.
getAuthority()
Shortcut to get the Authority executing this instance.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages By default the message key is the canonical name of...
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.