MediaWiki  master
SpecialLog.php
Go to the documentation of this file.
1 <?php
29 use Wikimedia\Timestamp\TimestampException;
30 
36 class SpecialLog extends SpecialPage {
37 
40 
42  private $loadBalancer;
43 
46 
49 
56  public function __construct(
61  ) {
62  parent::__construct( 'Log' );
63  $this->linkBatchFactory = $linkBatchFactory;
64  $this->loadBalancer = $loadBalancer;
65  $this->actorNormalization = $actorNormalization;
66  $this->userIdentityLookup = $userIdentityLookup;
67  }
68 
69  public function execute( $par ) {
70  $this->setHeaders();
71  $this->outputHeader();
72  $out = $this->getOutput();
73  $out->addModules( 'mediawiki.userSuggest' );
74  $out->addModuleStyles( 'mediawiki.interface.helpers.styles' );
75  $this->addHelpLink( 'Help:Log' );
76 
77  $opts = new FormOptions;
78  $opts->add( 'type', '' );
79  $opts->add( 'user', '' );
80  $opts->add( 'page', '' );
81  $opts->add( 'pattern', false );
82  $opts->add( 'year', null, FormOptions::INTNULL );
83  $opts->add( 'month', null, FormOptions::INTNULL );
84  $opts->add( 'day', null, FormOptions::INTNULL );
85  $opts->add( 'tagfilter', '' );
86  $opts->add( 'offset', '' );
87  $opts->add( 'dir', '' );
88  $opts->add( 'offender', '' );
89  $opts->add( 'subtype', '' );
90  $opts->add( 'logid', '' );
91 
92  // Set values
93  $opts->fetchValuesFromRequest( $this->getRequest() );
94  if ( $par !== null ) {
95  $this->parseParams( $opts, (string)$par );
96  }
97 
98  // Set date values
99  $dateString = $this->getRequest()->getVal( 'wpdate' );
100  if ( !empty( $dateString ) ) {
101  try {
102  $dateStamp = MWTimestamp::getInstance( $dateString . ' 00:00:00' );
103  } catch ( TimestampException $e ) {
104  // If users provide an invalid date, silently ignore it
105  // instead of letting an exception bubble up (T201411)
106  $dateStamp = false;
107  }
108  if ( $dateStamp ) {
109  $opts->setValue( 'year', (int)$dateStamp->format( 'Y' ) );
110  $opts->setValue( 'month', (int)$dateStamp->format( 'm' ) );
111  $opts->setValue( 'day', (int)$dateStamp->format( 'd' ) );
112  }
113  }
114 
115  # Don't let the user get stuck with a certain date
116  if ( $opts->getValue( 'offset' ) || $opts->getValue( 'dir' ) == 'prev' ) {
117  $opts->setValue( 'year', '' );
118  $opts->setValue( 'month', '' );
119  }
120 
121  // If the user doesn't have the right permission to view the specific
122  // log type, throw a PermissionsError
123  // If the log type is invalid, just show all public logs
124  $logRestrictions = $this->getConfig()->get( 'LogRestrictions' );
125  $type = $opts->getValue( 'type' );
126  if ( !LogPage::isLogType( $type ) ) {
127  $opts->setValue( 'type', '' );
128  } elseif ( isset( $logRestrictions[$type] )
129  && !$this->getAuthority()->isAllowed( $logRestrictions[$type] )
130  ) {
131  throw new PermissionsError( $logRestrictions[$type] );
132  }
133 
134  # Handle type-specific inputs
135  $qc = [];
136  if ( $opts->getValue( 'type' ) == 'suppress' ) {
137  $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
138  $offenderName = $opts->getValue( 'offender' );
139  $offenderId = $this->actorNormalization->findActorIdByName( $offenderName, $dbr );
140  if ( $offenderId ) {
141  $qc = [ 'ls_field' => 'target_author_actor', 'ls_value' => $offenderId ];
142  }
143  } else {
144  // Allow extensions to add relations to their search types
145  $this->getHookRunner()->onSpecialLogAddLogSearchRelations(
146  $opts->getValue( 'type' ), $this->getRequest(), $qc );
147  }
148 
149  # Some log types are only for a 'User:' title but we might have been given
150  # only the username instead of the full title 'User:username'. This part try
151  # to lookup for a user by that name and eventually fix user input. See T3697.
152  if ( in_array( $opts->getValue( 'type' ), self::getLogTypesOnUser( $this->getHookRunner() ) ) ) {
153  # ok we have a type of log which expect a user title.
154  $target = Title::newFromText( $opts->getValue( 'page' ) );
155  if ( $target && $target->getNamespace() === NS_MAIN ) {
156  # User forgot to add 'User:', we are adding it for him
157  $opts->setValue( 'page',
158  Title::makeTitleSafe( NS_USER, $opts->getValue( 'page' ) )
159  );
160  }
161  }
162 
163  $this->show( $opts, $qc );
164  }
165 
177  public static function getLogTypesOnUser( HookRunner $runner = null ) {
178  static $types = null;
179  if ( $types !== null ) {
180  return $types;
181  }
182  $types = [
183  'block',
184  'newusers',
185  'rights',
186  ];
187 
188  ( $runner ?? Hooks::runner() )->onGetLogTypesOnUser( $types );
189  return $types;
190  }
191 
197  public function getSubpagesForPrefixSearch() {
198  $subpages = LogPage::validTypes();
199  $subpages[] = 'all';
200  sort( $subpages );
201  return $subpages;
202  }
203 
213  private function parseParams( FormOptions $opts, $par ) {
214  # Get parameters
215  $par = $par ?? '';
216  $parms = explode( '/', $par );
217  $symsForAll = [ '*', 'all' ];
218  if ( $parms[0] != '' &&
219  ( in_array( $par, LogPage::validTypes() ) || in_array( $par, $symsForAll ) )
220  ) {
221  $opts->setValue( 'type', $par );
222  } elseif ( count( $parms ) == 2 ) {
223  $opts->setValue( 'type', $parms[0] );
224  $opts->setValue( 'user', $parms[1] );
225  } elseif ( $par != '' ) {
226  $opts->setValue( 'user', $par );
227  }
228  }
229 
230  private function show( FormOptions $opts, array $extraConds ) {
231  # Create a LogPager item to get the results and a LogEventsList item to format them...
232  $loglist = new LogEventsList(
233  $this->getContext(),
234  $this->getLinkRenderer(),
236  );
237 
238  $pager = new LogPager(
239  $loglist,
240  $opts->getValue( 'type' ),
241  $opts->getValue( 'user' ),
242  $opts->getValue( 'page' ),
243  $opts->getValue( 'pattern' ),
244  $extraConds,
245  $opts->getValue( 'year' ),
246  $opts->getValue( 'month' ),
247  $opts->getValue( 'day' ),
248  $opts->getValue( 'tagfilter' ),
249  $opts->getValue( 'subtype' ),
250  $opts->getValue( 'logid' ),
251  $this->linkBatchFactory,
252  $this->loadBalancer,
253  $this->actorNormalization
254  );
255 
256  $this->addHeader( $opts->getValue( 'type' ) );
257 
258  # Set relevant user
259  $performer = $pager->getPerformer();
260  if ( $performer ) {
261  $performerUser = $this->userIdentityLookup->getUserIdentityByName( $performer );
262  if ( $performerUser ) {
263  $this->getSkin()->setRelevantUser( $performerUser );
264  }
265  }
266 
267  # Show form options
268  $loglist->showOptions(
269  $pager->getType(),
270  $performer,
271  $pager->getPage(),
272  $pager->getPattern(),
273  $pager->getYear(),
274  $pager->getMonth(),
275  $pager->getDay(),
276  $pager->getFilterParams(),
277  $pager->getTagFilter(),
278  $pager->getAction()
279  );
280 
281  # Insert list
282  $logBody = $pager->getBody();
283  if ( $logBody ) {
284  $this->getOutput()->addHTML(
285  $pager->getNavigationBar() .
286  $this->getActionButtons(
287  $loglist->beginLogEventsList() .
288  $logBody .
289  $loglist->endLogEventsList()
290  ) .
291  $pager->getNavigationBar()
292  );
293  } else {
294  $this->getOutput()->addWikiMsg( 'logempty' );
295  }
296  }
297 
298  private function getActionButtons( $formcontents ) {
299  $canRevDelete = $this->getAuthority()
300  ->isAllowedAll( 'deletedhistory', 'deletelogentry' );
301  $showTagEditUI = ChangeTags::showTagEditingUI( $this->getAuthority() );
302  # If the user doesn't have the ability to delete log entries nor edit tags,
303  # don't bother showing them the button(s).
304  if ( !$canRevDelete && !$showTagEditUI ) {
305  return $formcontents;
306  }
307 
308  # Show button to hide log entries and/or edit change tags
310  'form',
311  [ 'action' => wfScript(), 'id' => 'mw-log-deleterevision-submit' ]
312  ) . "\n";
313  $s .= Html::hidden( 'action', 'historysubmit' ) . "\n";
314  $s .= Html::hidden( 'type', 'logging' ) . "\n";
315 
316  // If no title is set, the fallback is to use the main page, as defined
317  // by MediaWiki:Mainpage
318  // On wikis where the main page can be translated, MediaWiki:Mainpage
319  // is sometimes set to use Special:MyLanguage to redirect to the
320  // appropriate version. This is interpreted as a special page, and
321  // Action::getActionName forces the action to be 'view' if the title
322  // cannot be used as a WikiPage, which includes all pages in NS_SPECIAL.
323  // Set a dummy title to avoid this. The title provided is unused
324  // by the SpecialPageAction class and does not matter.
325  // See T205908
326  $s .= Html::hidden( 'title', 'Unused' ) . "\n";
327 
328  $buttons = '';
329  if ( $canRevDelete ) {
330  $buttons .= Html::element(
331  'button',
332  [
333  'type' => 'submit',
334  'name' => 'revisiondelete',
335  'value' => '1',
336  'class' => "deleterevision-log-submit mw-log-deleterevision-button mw-ui-button"
337  ],
338  $this->msg( 'showhideselectedlogentries' )->text()
339  ) . "\n";
340  }
341  if ( $showTagEditUI ) {
342  $buttons .= Html::element(
343  'button',
344  [
345  'type' => 'submit',
346  'name' => 'editchangetags',
347  'value' => '1',
348  'class' => "editchangetags-log-submit mw-log-editchangetags-button mw-ui-button"
349  ],
350  $this->msg( 'log-edit-tags' )->text()
351  ) . "\n";
352  }
353 
354  $buttons .= ( new ListToggle( $this->getOutput() ) )->getHTML();
355 
356  $s .= $buttons . $formcontents . $buttons;
357  $s .= Html::closeElement( 'form' );
358 
359  return $s;
360  }
361 
367  protected function addHeader( $type ) {
368  $page = new LogPage( $type );
369  $this->getOutput()->setPageTitle( $page->getName() );
370  $this->getOutput()->addHTML( $page->getDescription()
371  ->setContext( $this->getContext() )->parseAsBlock() );
372  }
373 
374  protected function getGroupName() {
375  return 'changes';
376  }
377 }
SpecialLog\getLogTypesOnUser
static getLogTypesOnUser(HookRunner $runner=null)
List log type for which the target is a user Thus if the given target is in NS_MAIN we can alter it t...
Definition: SpecialLog.php:177
SpecialLog\$linkBatchFactory
LinkBatchFactory $linkBatchFactory
Definition: SpecialLog.php:39
SpecialPage\msg
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
Definition: SpecialPage.php:912
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:395
LogPage\validTypes
static validTypes()
Get the list of valid log types.
Definition: LogPage.php:206
SpecialLog\parseParams
parseParams(FormOptions $opts, $par)
Set options based on the subpage title parts:
Definition: SpecialLog.php:213
SpecialPage\getOutput
getOutput()
Get the OutputPage being used for this instance.
Definition: SpecialPage.php:790
FormOptions\getValue
getValue( $name)
Get the value for the given option name.
Definition: FormOptions.php:182
FormOptions\INTNULL
const INTNULL
Integer type or null, maps to WebRequest::getIntOrNull() This is useful for the namespace selector.
Definition: FormOptions.php:55
SpecialLog\getActionButtons
getActionButtons( $formcontents)
Definition: SpecialLog.php:298
LogPager
Definition: LogPager.php:35
SpecialPage\getSkin
getSkin()
Shortcut to get the skin being used for this instance.
Definition: SpecialPage.php:820
SpecialPage\getAuthority
getAuthority()
Shortcut to get the Authority executing this instance.
Definition: SpecialPage.php:810
PermissionsError
Show an error when a user tries to do something they do not have the necessary permissions for.
Definition: PermissionsError.php:32
SpecialLog\$actorNormalization
ActorNormalization $actorNormalization
Definition: SpecialLog.php:45
LogEventsList\USE_CHECKBOXES
const USE_CHECKBOXES
Definition: LogEventsList.php:36
NS_MAIN
const NS_MAIN
Definition: Defines.php:64
$dbr
$dbr
Definition: testCompression.php:54
FormOptions\add
add( $name, $default, $type=self::AUTO)
Add an option to be handled by this FormOptions instance.
Definition: FormOptions.php:83
Html\closeElement
static closeElement( $element)
Returns "</$element>".
Definition: Html.php:316
SpecialPage\addHelpLink
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition: SpecialPage.php:948
SpecialPage\getHookRunner
getHookRunner()
Definition: SpecialPage.php:1095
SpecialPage\getConfig
getConfig()
Shortcut to get main config object.
Definition: SpecialPage.php:878
wfScript
wfScript( $script='index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
Definition: GlobalFunctions.php:2282
ListToggle
Class for generating clickable toggle links for a list of checkboxes.
Definition: ListToggle.php:31
LogPage\isLogType
static isLogType( $type)
Is $type a valid log type.
Definition: LogPage.php:218
MediaWiki\Cache\LinkBatchFactory
Definition: LinkBatchFactory.php:39
LogPage
Class to simplify the use of log pages.
Definition: LogPage.php:38
MWTimestamp\getInstance
static getInstance( $ts=false)
Get a timestamp instance in GMT.
Definition: MWTimestamp.php:45
SpecialPage\setHeaders
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
Definition: SpecialPage.php:618
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
LogEventsList
Definition: LogEventsList.php:33
SpecialPage\getContext
getContext()
Gets the context this SpecialPage is executed in.
Definition: SpecialPage.php:764
FormOptions\setValue
setValue( $name, $value, $force=false)
Use to set the value of an option.
Definition: FormOptions.php:165
Html\hidden
static hidden( $name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:831
MediaWiki\User\UserIdentityLookup
Definition: UserIdentityLookup.php:33
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:689
$s
foreach( $mmfl['setupFiles'] as $fileName) if( $queue) if(empty( $mmfl['quiet'])) $s
Definition: mergeMessageFileList.php:206
SpecialPage
Parent class for all special pages.
Definition: SpecialPage.php:43
Hooks\runner
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
Definition: Hooks.php:173
SpecialLog\getGroupName
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
Definition: SpecialLog.php:374
SpecialPage\getRequest
getRequest()
Get the WebRequest being used for this instance.
Definition: SpecialPage.php:780
SpecialLog\show
show(FormOptions $opts, array $extraConds)
Definition: SpecialLog.php:230
NS_USER
const NS_USER
Definition: Defines.php:66
SpecialPage\getLinkRenderer
getLinkRenderer()
Definition: SpecialPage.php:1028
SpecialLog
A special page that lists log entries.
Definition: SpecialLog.php:36
SpecialLog\$loadBalancer
ILoadBalancer $loadBalancer
Definition: SpecialLog.php:42
Html\openElement
static openElement( $element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing '/'...
Definition: Html.php:252
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:554
FormOptions
Helper class to keep track of options when mixing links and form elements.
Definition: FormOptions.php:35
SpecialLog\__construct
__construct(LinkBatchFactory $linkBatchFactory, ILoadBalancer $loadBalancer, ActorNormalization $actorNormalization, UserIdentityLookup $userIdentityLookup)
Definition: SpecialLog.php:56
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:232
ChangeTags\showTagEditingUI
static showTagEditingUI(Authority $performer)
Indicate whether change tag editing UI is relevant.
Definition: ChangeTags.php:1738
SpecialLog\execute
execute( $par)
Default execute method Checks user permissions.
Definition: SpecialLog.php:69
SpecialPage\outputHeader
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
Definition: SpecialPage.php:709
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
SpecialLog\getSubpagesForPrefixSearch
getSubpagesForPrefixSearch()
Return an array of subpages that this special page will accept.
Definition: SpecialLog.php:197
SpecialLog\$userIdentityLookup
UserIdentityLookup $userIdentityLookup
Definition: SpecialLog.php:48
SpecialLog\addHeader
addHeader( $type)
Set page title and show header for this log type.
Definition: SpecialLog.php:367
MediaWiki\User\ActorNormalization
Service for dealing with the actor table.
Definition: ActorNormalization.php:32
$type
$type
Definition: testCompression.php:52