21use MediaWiki\Pager\LogPager;
32use Wikimedia\Timestamp\TimestampException;
52 parent::__construct(
'Log' );
54 $this->tempUserConfig = $tempUserConfig;
65 $out->addModuleStyles(
'mediawiki.interface.helpers.styles' );
69 $opts->
add(
'type',
'' );
70 $opts->add(
'user',
'' );
71 $opts->add(
'page', [] );
72 $opts->add(
'pattern',
false );
73 $opts->add(
'year',
null, FormOptions::INTNULL );
74 $opts->add(
'month',
null, FormOptions::INTNULL );
75 $opts->add(
'day',
null, FormOptions::INTNULL );
76 $opts->add(
'tagfilter',
'' );
77 $opts->add(
'tagInvert',
false );
78 $opts->add(
'offset',
'' );
79 $opts->add(
'dir',
'' );
80 $opts->add(
'offender',
'' );
81 $opts->add(
'subtype',
'' );
82 $opts->add(
'logid',
'' );
85 if ( $par !==
null ) {
86 $this->parseParams( (
string)$par );
88 $opts->fetchValuesFromRequest( $this->
getRequest() );
91 $dateString = $this->
getRequest()->getVal(
'wpdate' );
94 $dateStamp = MWTimestamp::getInstance( $dateString .
' 00:00:00' );
95 }
catch ( TimestampException ) {
101 $opts->setValue(
'year', (
int)$dateStamp->format(
'Y' ) );
102 $opts->setValue(
'month', (
int)$dateStamp->format(
'm' ) );
103 $opts->setValue(
'day', (
int)$dateStamp->format(
'd' ) );
110 $type = $opts->getValue(
'type' );
111 if ( isset( $logRestrictions[$type] )
112 && !$this->
getAuthority()->isAllowed( $logRestrictions[$type] )
117 # TODO: Move this into LogPager like other query conditions.
118 # Handle type-specific inputs
120 $offenderName = $opts->getValue(
'offender' );
121 if ( $opts->getValue(
'type' ) ==
'suppress' && $offenderName !==
'' ) {
122 $dbr = $this->dbProvider->getReplicaDatabase();
123 $offenderId = $this->actorNormalization->findActorIdByName( $offenderName, $dbr );
125 $qc = [
'ls_field' =>
'target_author_actor',
'ls_value' => strval( $offenderId ) ];
131 if ( $this->tempUserConfig->isKnown() ) {
142 $formWasSubmitted = $this->
getRequest()->getVal(
'wpFormIdentifier' ) ===
'logeventslist';
145 !$formWasSubmitted &&
146 !$this->
getRequest()->getVal(
'excludetempacct' ) &&
147 !$this->tempUserConfig->isTempName( $opts->getValue(
'user' ) )
151 $this->getRequest()->getVal(
'excludetempacct' )
154 $dbr = $this->dbProvider->getReplicaDatabase();
155 if ( $opts->getValue(
'type' ) ===
'' ) {
158 $dbr->expr(
'log_type',
'!=',
'newusers' )->orExpr(
159 $dbr->expr(
'log_type',
'=',
'newusers' )
160 ->andExpr( $this->tempUserConfig
161 ->getMatchCondition( $dbr,
'logging_actor.actor_name', IExpression::NOT_LIKE ) )
164 } elseif ( $opts->getValue(
'type' ) ===
'newusers' ) {
166 $this->tempUserConfig
167 ->getMatchCondition( $dbr,
'logging_actor.actor_name', IExpression::NOT_LIKE )
175 $opts->getValue(
'type' ), $this->getRequest(), $qc );
178 # TODO: Move this into LogEventList and use it as filter-callback in the field descriptor.
179 # Some log types are only for a 'User:' title but we might have been given
180 # only the username instead of the full title 'User:username'. This part try
181 # to lookup for a user by that name and eventually fix user input. See T3697.
182 if ( in_array( $opts->getValue(
'type' ), self::getLogTypesOnUser( $this->getHookRunner() ) ) ) {
184 foreach ( $opts->getValue(
'page' ) as $page ) {
185 $page = $this->normalizeUserPage( $page );
186 if ( $page !==
null ) {
187 $pages[] = $page->getPrefixedText();
190 $opts->setValue(
'page', $pages );
193 $this->show( $opts, $qc );
202 private function normalizeUserPage( $page ) {
203 $target = Title::newFromText( $page );
204 if ( $target && $target->getNamespace() ===
NS_MAIN ) {
205 if ( IPUtils::isValidRange( $target->getText() ) ) {
206 $page = IPUtils::sanitizeRange( $target->getText() );
208 # User forgot to add 'User:', we are adding it for them
209 $target = Title::makeTitleSafe(
NS_USER, $page );
210 } elseif ( $target && $target->getNamespace() ===
NS_USER
211 && IPUtils::isValidRange( $target->getText() )
213 $ipOrRange = IPUtils::sanitizeRange( $target->getText() );
214 if ( $ipOrRange !== $target->getText() ) {
215 $target = Title::makeTitleSafe(
NS_USER, $ipOrRange );
233 static $types =
null;
234 if ( $types !==
null ) {
245 ->onGetLogTypesOnUser( $types );
255 $subpages = LogPage::validTypes();
259 $this->
getHookRunner()->onSpecialLogGetSubpagesForPrefixSearch(
267 $subpages = array_unique( $subpages );
280 private function parseParams(
string $par ) {
281 $params = explode(
'/', $par, 2 );
282 $logType = $this->resolveLogType( $params );
285 $this->
getRequest()->setVal(
'type', $logType );
286 if ( count( $params ) === 2 ) {
287 $this->
getRequest()->setVal(
'user', $params[1] );
289 } elseif ( $par !==
'' ) {
310 private function resolveLogType( array $params ): string {
320 if ( $logType !==
'' ) {
321 $symsForAll = [
'*',
'all' ];
322 $allowedTypes = array_merge( LogPage::validTypes(), $symsForAll );
324 if ( in_array( $logType, $allowedTypes ) ) {
332 private function show( FormOptions $opts, array $extraConds ) {
333 # Create a LogPager item to get the results and a LogEventsList item to format them...
334 $loglist =
new LogEventsList(
337 LogEventsList::USE_CHECKBOXES
339 $pager =
new LogPager(
341 $opts->getValue(
'type' ),
342 $opts->getValue(
'user' ),
343 $opts->getValue(
'page' ),
344 $opts->getValue(
'pattern' ),
346 $opts->getValue(
'year' ),
347 $opts->getValue(
'month' ),
348 $opts->getValue(
'day' ),
349 $opts->getValue(
'tagfilter' ),
350 $opts->getValue(
'subtype' ),
351 $opts->getValue(
'logid' ),
352 $this->linkBatchFactory,
353 $this->actorNormalization,
354 $this->logFormatterFactory,
355 $opts->getValue(
'tagInvert' )
359 $performer = $pager->getPerformer();
361 $performerUser = $this->userIdentityLookup->getUserIdentityByName( $performer );
364 if ( $performerUser && !IPUtils::isValidRange( $performer ) &&
365 ( $this->userNameUtils->isIP( $performer ) || $performerUser->isRegistered() )
367 $this->
getSkin()->setRelevantUser( $performerUser );
372 $succeed = $loglist->showOptions(
373 $opts->getValue(
'type' ),
374 $opts->getValue(
'year' ),
375 $opts->getValue(
'month' ),
376 $opts->getValue(
'day' ),
377 $opts->getValue(
'user' ),
384 (
new LogPage( $opts->getValue(
'type' ) ) )->getName()
388 $logBody = $pager->getBody();
391 $pager->getNavigationBar() .
392 $this->getActionButtons(
393 $loglist->beginLogEventsList() .
395 $loglist->endLogEventsList()
397 $pager->getNavigationBar()
400 $this->
getOutput()->addWikiMsg(
'logempty' );
404 private function getActionButtons(
string $formcontents ): string {
406 ->isAllowedAll(
'deletedhistory',
'deletelogentry' );
407 $showTagEditUI = ChangeTags::showTagEditingUI( $this->
getAuthority() );
408 # If the user doesn't have the ability to delete log entries nor edit tags,
409 # don't bother showing them the button(s).
410 if ( !$canRevDelete && !$showTagEditUI ) {
411 return $formcontents;
414 # Show button to hide log entries and/or edit change tags
415 $s = Html::openElement(
417 [
'action' =>
wfScript(),
'id' =>
'mw-log-deleterevision-submit' ]
419 $s .= Html::hidden(
'type',
'logging' ) .
"\n";
422 if ( $canRevDelete ) {
429 'class' =>
"deleterevision-log-submit mw-log-deleterevision-button mw-ui-button"
431 $this->
msg(
'showhideselectedlogentries' )->text()
434 if ( $showTagEditUI ) {
441 'class' =>
"editchangetags-log-submit mw-log-editchangetags-button mw-ui-button"
443 $this->
msg(
'log-edit-tags' )->text()
447 $buttons .= (
new ListToggle( $this->
getOutput() ) )->getHTML();
449 $s .= $buttons . $formcontents . $buttons;
450 $s .= Html::closeElement(
'form' );
462class_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()
Factory for LinkBatch objects to batch query page metadata.
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.
getContext()
Gets the context this SpecialPage is executed in.
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.