55 private $linkRenderer;
71 $this->linkRenderer = $linkRenderer;
73 $this->hookRunner =
new HookRunner( MediaWikiServices::getInstance()->getHookContainer() );
81 if ( $this->linkRenderer !==
null ) {
82 return $this->linkRenderer;
84 return MediaWikiServices::getInstance()->getLinkRenderer();
99 public function showOptions( $type =
'', $year = 0, $month = 0, $day = 0 ) {
100 $formDescriptor = [];
103 $formDescriptor[
'type'] = $this->getTypeMenuDesc();
104 $formDescriptor[
'user'] = [
105 'class' => HTMLUserTextField::class,
106 'label-message' =>
'specialloguserlabel',
112 $formDescriptor[
'page'] = [
113 'class' => HTMLTitleTextField::class,
114 'label-message' =>
'speciallogtitlelabel',
120 if ( !$this->
getConfig()->
get( MainConfigNames::MiserMode ) ) {
121 $formDescriptor[
'pattern'] = [
123 'label-message' =>
'log-title-wildcard',
129 $extraInputsDescriptor = $this->getExtraInputsDesc( $type );
130 if ( $extraInputsDescriptor ) {
131 $formDescriptor[
'extra' ] = $extraInputsDescriptor;
135 $formDescriptor[
'date'] = [
137 'label-message' =>
'date',
138 'default' => $year && $month && $day ? sprintf(
"%04d-%02d-%02d", $year, $month, $day ) :
'',
142 $formDescriptor[
'tagfilter'] = [
143 'type' =>
'tagfilter',
144 'name' =>
'tagfilter',
145 'label-message' =>
'tag-filter',
147 $formDescriptor[
'tagInvert'] = [
149 'name' =>
'tagInvert',
150 'label-message' =>
'invert',
151 'hide-if' => [
'===',
'tagfilter',
'' ],
155 if ( $type ===
'' ) {
156 $formDescriptor[
'filters'] = $this->getFiltersDesc();
160 $allowedActions = $this->
getConfig()->get( MainConfigNames::ActionFilteredLogs );
161 if ( isset( $allowedActions[$type] ) ) {
162 $formDescriptor[
'subtype'] = $this->getActionSelectorDesc( $type, $allowedActions[$type] );
167 ->setTitle( SpecialPage::getTitleFor(
'Log' ) )
168 ->setSubmitTextMsg(
'logeventslist-submit' )
170 ->setWrapperLegendMsg(
'log' )
171 ->setFormIdentifier(
'logeventslist',
true )
173 ->setSubmitCallback(
static function ( $formData, $form ) {
175 (
new LogPage( $formData[
'type'] ) )->getDescription()
176 ->
setContext( $form->getContext() )->parseAsBlock()
181 $result = $htmlForm->prepareForm()->trySubmit();
182 $htmlForm->displayForm( $result );
183 return $result ===
true || ( $result instanceof
Status && $result->
isGood() );
189 private function getFiltersDesc() {
191 $filters = $this->
getConfig()->get( MainConfigNames::FilterLogTypes );
192 foreach ( $filters as $type => $val ) {
193 $optionsMsg[
"logeventslist-{$type}-log"] = $type;
196 'class' => HTMLMultiSelectField::class,
197 'label-message' =>
'logeventslist-more-filters',
199 'options-messages' => $optionsMsg,
200 'default' => array_keys( array_intersect( $filters, [
false ] ) ),
207 private function getTypeMenuDesc() {
212 if ( $this->
getAuthority()->isAllowed( $page->getRestriction() ) ) {
213 $typesByName[$type] = $page->getName()->text();
217 asort( $typesByName );
220 $public = $typesByName[
''];
221 unset( $typesByName[
''] );
222 $typesByName = [
'' => $public ] + $typesByName;
225 'class' => HTMLSelectField::class,
227 'options' => array_flip( $typesByName ),
235 private function getExtraInputsDesc( $type ) {
236 if ( $type ===
'suppress' ) {
239 'label-message' =>
'revdelete-offender',
240 'name' =>
'offender',
245 $formDescriptor = [];
246 $this->hookRunner->onLogEventsListGetExtraInputs( $type, $this, $unused, $formDescriptor );
248 return $formDescriptor;
258 private function getActionSelectorDesc( $type, $actions ) {
259 $actionOptions = [
'log-action-filter-all' =>
'' ];
261 foreach ( $actions as $value => $_ ) {
262 $msgKey =
"log-action-filter-$type-$value";
263 $actionOptions[ $msgKey ] = $value;
267 'class' => HTMLSelectField::class,
269 'options-messages' => $actionOptions,
270 'label-message' =>
'log-action-filter-' . $type,
278 return "<ul class='mw-logevent-loglines'>\n";
295 $formatter->setContext( $this->
getContext() );
297 $formatter->setShowUserToolLinks( !( $this->flags & self::NO_EXTRA_USER_LINKS ) );
300 $entry->getTimestamp(),
305 SpecialPage::getTitleValueFor(
'Log' ),
308 [
'logid' => $entry->getId() ]
311 $action = $formatter->getActionText();
313 if ( $this->flags & self::NO_ACTION_LINK ) {
316 $revert = $formatter->getActionLinks();
317 if ( $revert !=
'' ) {
318 $revert =
'<span class="mw-logevent-actionlink">' . $revert .
'</span>';
322 $comment = $formatter->getComment();
325 $del = $this->getShowHideLinks( $row );
333 $classes = array_merge(
334 [
'mw-logline-' . $entry->getType() ],
338 'data-mw-logid' => $entry->getId(),
339 'data-mw-logaction' => $entry->getFullType(),
341 $ret =
"$del $timeLink $action $comment $revert $tagDisplay";
344 $this->hookRunner->onLogEventsListLineEnding( $this, $ret, $entry, $classes, $attribs );
345 $attribs = array_filter( $attribs,
346 [ Sanitizer::class,
'isReservedDataAttribute' ],
349 $attribs[
'class'] = $classes;
351 return Html::rawElement(
'li', $attribs, $ret ) .
"\n";
358 private function getShowHideLinks( $row ) {
360 if ( $this->flags == self::NO_ACTION_LINK ) {
365 if ( $this->flags & self::USE_CHECKBOXES && $this->showTagEditUI ) {
369 [
'name' =>
'ids[' . $row->log_id .
']' ]
374 if ( $row->log_type ==
'suppress' ) {
381 if ( $authority->isAllowed(
'deletedhistory' ) ) {
382 $canHide = $authority->isAllowed(
'deletelogentry' );
383 $canViewSuppressedOnly = $authority->isAllowed(
'viewsuppressed' ) &&
384 !$authority->isAllowed(
'suppressrevision' );
386 $canViewThisSuppressedEntry = $canViewSuppressedOnly && $entryIsSuppressed;
387 if ( $row->log_deleted || $canHide ) {
389 if ( $canHide && $this->flags & self::USE_CHECKBOXES && !$canViewThisSuppressedEntry ) {
392 $del =
Xml::check(
'deleterevisions',
false, [
'disabled' =>
'disabled' ] );
397 [
'name' =>
'ids[' . $row->log_id .
']' ]
403 $del = Linker::revDeleteLinkDisabled( $canHide );
406 'target' => SpecialPage::getTitleFor(
'Log', $row->log_type )->getPrefixedDBkey(),
408 'ids' => $row->log_id,
410 $del = Linker::revDeleteLink(
413 $canHide && !$canViewThisSuppressedEntry
430 $match = is_array( $type ) ?
431 in_array( $row->log_type, $type ) : $row->log_type == $type;
433 $match = is_array( $action ) ?
434 in_array( $row->log_action, $action ) : $row->log_action == $action;
464 if ( $bitfield & $field ) {
466 return $performer->
isAllowedAny(
'suppressrevision',
'viewsuppressed' );
468 return $performer->
isAllowed(
'deletedhistory' );
483 $logRestrictions = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::LogRestrictions );
484 if ( isset( $logRestrictions[$type] ) && !$performer->
isAllowed( $logRestrictions[$type] ) ) {
496 return ( $row->log_deleted & $field ) == $field;
525 &$out, $types = [], $page =
'', $user =
'', $param = []
527 $defaultParameters = [
530 'showIfEmpty' =>
true,
534 'useRequestParams' =>
false,
535 'useMaster' =>
false,
536 'extraUrlParams' =>
false,
538 # The + operator appends elements of remaining keys from the right
539 # handed array to the left handed, whereas duplicated keys are NOT overwritten.
540 $param += $defaultParameters;
541 # Convert $param array to individual variables
542 $lim = $param[
'lim'];
543 $conds = $param[
'conds'];
544 $showIfEmpty = $param[
'showIfEmpty'];
545 $msgKey = $param[
'msgKey'];
546 $wrap = $param[
'wrap'];
548 $extraUrlParams = $param[
'extraUrlParams'];
550 $useRequestParams = $param[
'useRequestParams'];
552 if ( !is_array( $msgKey ) ) {
553 $msgKey = [ $msgKey ];
557 $context = $out->getContext();
562 $services = MediaWikiServices::getInstance();
564 $linkRenderer = $services->getLinkRenderer();
566 # Insert list of top 50 (or top $lim) items
581 $services->getLinkBatchFactory(),
582 $services->getActorNormalization()
585 if ( !$useRequestParams ) {
586 # Reset vars that may have been taken from the request
588 $pager->mDefaultLimit = 50;
589 $pager->mOffset =
"";
590 $pager->mIsBackwards =
false;
594 if ( $param[
'useMaster'] ) {
595 $pager->mDb = $services->getDBLoadBalancerFactory()->getPrimaryDatabase();
598 if ( isset( $param[
'offset'] ) ) { # Tell pager to ignore WebRequest offset
599 $pager->setOffset( $param[
'offset'] );
604 $pager->mLimit = $lim;
607 $logBody = $pager->getBody();
608 $numRows = $pager->getNumRows();
615 $msg = $context->msg( ...$msgKey );
619 $s .= $msg->parseAsBlock();
621 $s .= $loglist->beginLogEventsList() .
623 $loglist->endLogEventsList();
625 $context->getOutput()->addModuleStyles(
'mediawiki.interface.helpers.styles' );
627 } elseif ( $showIfEmpty ) {
628 $s = Html::rawElement(
'div', [
'class' =>
'mw-warning-logempty' ],
629 $context->msg(
'logempty' )->parse() );
633 $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
634 $pageName = $titleFormatter->getPrefixedDBkey( $page );
635 } elseif ( $page !=
'' ) {
641 if ( $numRows > $pager->mLimit ) { # Show
"Full log" link
644 $urlParam[
'page'] = $pageName;
648 $urlParam[
'user'] = $user;
651 if ( !is_array( $types ) ) { # Make it an array,
if it isn
't
655 # If there is exactly one log type, we can link to Special:Log?type=foo
656 if ( count( $types ) == 1 ) {
657 $urlParam['type
'] = $types[0];
660 // @phan-suppress-next-line PhanSuspiciousValueComparison
661 if ( $extraUrlParams !== false ) {
662 $urlParam = array_merge( $urlParam, $extraUrlParams );
665 $s .= $linkRenderer->makeKnownLink(
666 SpecialPage::getTitleFor( 'Log
' ),
667 $context->msg( 'log-fulllog
' )->text(),
673 if ( $logBody && $msgKey[0] ) {
674 // TODO: The condition above is weird. Should this be done in any other cases?
675 // Or is it always true in practice?
677 // Mark as interface language (T60685)
678 $dir = $context->getLanguage()->getDir();
679 $lang = $context->getLanguage()->getHtmlCode();
680 $s = Html::rawElement( 'div
', [
681 'class' => "mw-content-$dir",
686 // Wrap in warning box
687 $s = Html::warningBox(
689 'mw-warning-with-logexcerpt
'
693 // @phan-suppress-next-line PhanSuspiciousValueComparison, PhanRedundantCondition
694 if ( $wrap != '' ) { // Wrap message in html
695 $s = str_replace( '$1
', $s, $wrap );
698 /* hook can return false, if we don't want the message to be emitted (Wikia BugId:7093) */
699 $hookRunner =
new HookRunner( $services->getHookContainer() );
722 $logRestrictions = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::LogRestrictions );
724 if ( $audience !=
'public' && $performer ===
null ) {
725 throw new InvalidArgumentException(
726 'A User object must be given when checking for a user audience.'
734 foreach ( $logRestrictions as $logType => $right ) {
735 if ( $audience ==
'public' || !$performer->isAllowed( $right ) ) {
736 $hiddenLogs[] = $logType;
739 if ( count( $hiddenLogs ) == 1 ) {
740 return 'log_type != ' . $db->addQuotes( $hiddenLogs[0] );
741 } elseif ( $hiddenLogs ) {
742 return 'log_type NOT IN (' . $db->makeList( $hiddenLogs ) .
')';
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
getContext()
Get the base IContextSource object.
setContext(IContextSource $context)
static newFromRow( $row)
Constructs new LogEntry from database result row.
const NO_EXTRA_USER_LINKS
static typeAction( $row, $type, $action)
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
static getExcludeClause( $db, $audience='public', Authority $performer=null)
SQL clause to skip forbidden log types for this user.
showOptions( $type='', $year=0, $month=0, $day=0)
Show options for the log list.
static userCan( $row, $field, Authority $performer)
Determine if the current user is allowed to view a particular field of this log row,...
static userCanBitfield( $bitfield, $field, Authority $performer)
Determine if the current user is allowed to view a particular field of this log row,...
__construct( $context, $linkRenderer=null, $flags=0)
static userCanViewLogType( $type, Authority $performer)
Determine if the current user is allowed to view a particular field of this log row,...
static isDeleted( $row, $field)
Class to simplify the use of log pages.
static validTypes()
Get the list of valid log types.
A class containing constants representing the names of configuration variables.
This is one of the Core classes and should be read at least once by any new developers.
Parent class for all special pages.
static getMain()
Get the RequestContext object associated with the main request.
isGood()
Returns whether the operation completed and didn't have any error or warnings.
static check( $name, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox.