MediaWiki REL1_39
LogEventsList.php
Go to the documentation of this file.
1<?php
33
35 public const NO_ACTION_LINK = 1;
36 public const NO_EXTRA_USER_LINKS = 2;
37 public const USE_CHECKBOXES = 4;
38
39 public $flags;
40
44 protected $showTagEditUI;
45
49 protected $allowedActions = null;
50
54 private $linkRenderer;
55
57 private $hookRunner;
58
69 public function __construct( $context, $linkRenderer = null, $flags = 0 ) {
70 $this->setContext( $context );
71 $this->flags = $flags;
72 $this->showTagEditUI = ChangeTags::showTagEditingUI( $this->getAuthority() );
73 if ( $linkRenderer instanceof LinkRenderer ) {
74 $this->linkRenderer = $linkRenderer;
75 }
76 $this->hookRunner = Hooks::runner();
77 }
78
83 protected function getLinkRenderer() {
84 if ( $this->linkRenderer !== null ) {
85 return $this->linkRenderer;
86 } else {
87 return MediaWikiServices::getInstance()->getLinkRenderer();
88 }
89 }
90
107 public function showOptions( $types = [], $user = '', $page = '', $pattern = false, $year = 0,
108 $month = 0, $day = 0, $filter = null, $tagFilter = '', $action = null
109 ) {
110 // For B/C, we take strings, but make sure they are converted...
111 $types = ( $types === '' ) ? [] : (array)$types;
112
113 $formDescriptor = [];
114
115 // Basic selectors
116 $formDescriptor['type'] = $this->getTypeMenuDesc( $types );
117 $formDescriptor['user'] = $this->getUserInputDesc( $user );
118 $formDescriptor['page'] = $this->getTitleInputDesc( $page );
119
120 // Add extra inputs if any
121 // This could either be a form descriptor array or a string with raw HTML.
122 // We need it to work in both cases and show a deprecation warning if it
123 // is a string. See T199495.
124 $extraInputsDescriptor = $this->getExtraInputsDesc( $types );
125 if (
126 is_array( $extraInputsDescriptor ) &&
127 !empty( $extraInputsDescriptor )
128 ) {
129 $formDescriptor[ 'extra' ] = $extraInputsDescriptor;
130 } elseif (
131 is_string( $extraInputsDescriptor ) &&
132 $extraInputsDescriptor !== ''
133 ) {
134 // We'll add this to the footer of the form later
135 $extraInputsString = $extraInputsDescriptor;
136 wfDeprecated( '$input in LogEventsListGetExtraInputs hook', '1.32' );
137 }
138
139 // Title pattern, if allowed
140 if ( !$this->getConfig()->get( MainConfigNames::MiserMode ) ) {
141 $formDescriptor['pattern'] = $this->getTitlePatternDesc( $pattern );
142 }
143
144 // Date menu
145 $formDescriptor['date'] = [
146 'type' => 'date',
147 'label-message' => 'date',
148 'default' => $year && $month && $day ? sprintf( "%04d-%02d-%02d", $year, $month, $day ) : '',
149 ];
150
151 // Tag filter
152 $formDescriptor['tagfilter'] = [
153 'type' => 'tagfilter',
154 'name' => 'tagfilter',
155 'label-message' => 'tag-filter',
156 ];
157
158 // Filter links
159 if ( $filter ) {
160 $formDescriptor['filters'] = $this->getFiltersDesc( $filter );
161 }
162
163 // Action filter
164 if (
165 $action !== null &&
166 $this->allowedActions !== null &&
167 count( $this->allowedActions ) > 0
168 ) {
169 $formDescriptor['subtype'] = $this->getActionSelectorDesc( $types, $action );
170 }
171
172 $context = new DerivativeContext( $this->getContext() );
173 $context->setTitle( SpecialPage::getTitleFor( 'Log' ) ); // Remove subpage
174 $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $context );
175 $htmlForm
176 ->setSubmitTextMsg( 'logeventslist-submit' )
177 ->setMethod( 'get' )
178 ->setWrapperLegendMsg( 'log' )
179 // T321154
180 ->setFormIdentifier( 'logeventslist' );
181
182 // TODO This will should be removed at some point. See T199495.
183 if ( isset( $extraInputsString ) ) {
184 $htmlForm->addFooterText( Html::rawElement(
185 'div',
186 [],
187 $extraInputsString
188 ) );
189 }
190
191 $htmlForm->prepareForm()->displayForm( false );
192 }
193
198 private function getFiltersDesc( $filter ) {
199 $optionsMsg = [];
200 $default = [];
201 foreach ( $filter as $type => $val ) {
202 $optionsMsg["logeventslist-{$type}-log"] = $type;
203
204 if ( $val === false ) {
205 $default[] = $type;
206 }
207 }
208 return [
209 'class' => HTMLMultiSelectField::class,
210 'label-message' => 'logeventslist-more-filters',
211 'flatlist' => true,
212 'options-messages' => $optionsMsg,
213 'default' => $default,
214 ];
215 }
216
221 private function getTypeMenuDesc( $queryTypes ) {
222 $queryType = count( $queryTypes ) == 1 ? $queryTypes[0] : '';
223
224 $typesByName = []; // Temporary array
225 // First pass to load the log names
226 foreach ( LogPage::validTypes() as $type ) {
227 $page = new LogPage( $type );
228 $restriction = $page->getRestriction();
229 if ( $this->getAuthority()->isAllowed( $restriction ) ) {
230 $typesByName[$type] = $page->getName()->text();
231 }
232 }
233
234 // Second pass to sort by name
235 asort( $typesByName );
236
237 // Always put "All public logs" on top
238 $public = $typesByName[''];
239 unset( $typesByName[''] );
240 $typesByName = [ '' => $public ] + $typesByName;
241
242 return [
243 'class' => HTMLSelectField::class,
244 'name' => 'type',
245 'options' => array_flip( $typesByName ),
246 'default' => $queryType,
247 ];
248 }
249
254 private function getUserInputDesc( $user ) {
255 return [
256 'class' => HTMLUserTextField::class,
257 'label-message' => 'specialloguserlabel',
258 'name' => 'user',
259 'default' => $user,
260 ];
261 }
262
267 private function getTitleInputDesc( $page ) {
268 return [
269 'class' => HTMLTitleTextField::class,
270 'label-message' => 'speciallogtitlelabel',
271 'name' => 'page',
272 'required' => false
273 ];
274 }
275
280 private function getTitlePatternDesc( $pattern ) {
281 return [
282 'type' => 'check',
283 'label-message' => 'log-title-wildcard',
284 'name' => 'pattern',
285 ];
286 }
287
292 private function getExtraInputsDesc( $types ) {
293 if ( count( $types ) == 1 ) {
294 if ( $types[0] == 'suppress' ) {
295 return [
296 'type' => 'text',
297 'label-message' => 'revdelete-offender',
298 'name' => 'offender',
299 ];
300 } else {
301 // Allow extensions to add their own extra inputs
302 // This could be an array or string. See T199495.
303 $input = ''; // Deprecated
304 $formDescriptor = [];
305 $this->hookRunner->onLogEventsListGetExtraInputs( $types[0], $this, $input, $formDescriptor );
306
307 return empty( $formDescriptor ) ? $input : $formDescriptor;
308 }
309 }
310
311 return [];
312 }
313
320 private function getActionSelectorDesc( $types, $action ) {
321 $actionOptions = [];
322 $actionOptions[ 'log-action-filter-all' ] = '';
323
324 foreach ( $this->allowedActions as $value ) {
325 $msgKey = 'log-action-filter-' . $types[0] . '-' . $value;
326 $actionOptions[ $msgKey ] = $value;
327 }
328
329 return [
330 'class' => HTMLSelectField::class,
331 'name' => 'subtype',
332 'options-messages' => $actionOptions,
333 'default' => $action,
334 'label' => $this->msg( 'log-action-filter-' . $types[0] )->text(),
335 ];
336 }
337
344 public function setAllowedActions( $actions ) {
345 $this->allowedActions = $actions;
346 }
347
351 public function beginLogEventsList() {
352 return "<ul class='mw-logevent-loglines'>\n";
353 }
354
358 public function endLogEventsList() {
359 return "</ul>\n";
360 }
361
366 public function logLine( $row ) {
367 $entry = DatabaseLogEntry::newFromRow( $row );
368 $formatter = LogFormatter::newFromEntry( $entry );
369 $formatter->setContext( $this->getContext() );
370 $formatter->setLinkRenderer( $this->getLinkRenderer() );
371 $formatter->setShowUserToolLinks( !( $this->flags & self::NO_EXTRA_USER_LINKS ) );
372
373 $time = $this->getLanguage()->userTimeAndDate(
374 $entry->getTimestamp(),
375 $this->getUser()
376 );
377 // Link the time text to the specific log entry, see T207562
378 $timeLink = $this->getLinkRenderer()->makeKnownLink(
380 $time,
381 [],
382 [ 'logid' => $entry->getId() ]
383 );
384
385 $action = $formatter->getActionText();
386
387 if ( $this->flags & self::NO_ACTION_LINK ) {
388 $revert = '';
389 } else {
390 $revert = $formatter->getActionLinks();
391 if ( $revert != '' ) {
392 $revert = '<span class="mw-logevent-actionlink">' . $revert . '</span>';
393 }
394 }
395
396 $comment = $formatter->getComment();
397
398 // Some user can hide log items and have review links
399 $del = $this->getShowHideLinks( $row );
400
401 // Any tags...
402 list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow(
403 $row->ts_tags,
404 'logevent',
405 $this->getContext()
406 );
407 $classes = array_merge(
408 [ 'mw-logline-' . $entry->getType() ],
409 $newClasses
410 );
411 $attribs = [
412 'data-mw-logid' => $entry->getId(),
413 'data-mw-logaction' => $entry->getFullType(),
414 ];
415 $ret = "$del $timeLink $action $comment $revert $tagDisplay";
416
417 // Let extensions add data
418 $this->hookRunner->onLogEventsListLineEnding( $this, $ret, $entry, $classes, $attribs );
419 $attribs = array_filter( $attribs,
420 [ Sanitizer::class, 'isReservedDataAttribute' ],
421 ARRAY_FILTER_USE_KEY
422 );
423 $attribs['class'] = $classes;
424
425 return Html::rawElement( 'li', $attribs, $ret ) . "\n";
426 }
427
432 private function getShowHideLinks( $row ) {
433 // We don't want to see the links and
434 if ( $this->flags == self::NO_ACTION_LINK ) {
435 return '';
436 }
437
438 // If change tag editing is available to this user, return the checkbox
439 if ( $this->flags & self::USE_CHECKBOXES && $this->showTagEditUI ) {
440 return Xml::check(
441 'showhiderevisions',
442 false,
443 [ 'name' => 'ids[' . $row->log_id . ']' ]
444 );
445 }
446
447 // no one can hide items from the suppress log.
448 if ( $row->log_type == 'suppress' ) {
449 return '';
450 }
451
452 $del = '';
453 $authority = $this->getAuthority();
454 // Don't show useless checkbox to people who cannot hide log entries
455 if ( $authority->isAllowed( 'deletedhistory' ) ) {
456 $canHide = $authority->isAllowed( 'deletelogentry' );
457 $canViewSuppressedOnly = $authority->isAllowed( 'viewsuppressed' ) &&
458 !$authority->isAllowed( 'suppressrevision' );
459 $entryIsSuppressed = self::isDeleted( $row, LogPage::DELETED_RESTRICTED );
460 $canViewThisSuppressedEntry = $canViewSuppressedOnly && $entryIsSuppressed;
461 if ( $row->log_deleted || $canHide ) {
462 // Show checkboxes instead of links.
463 if ( $canHide && $this->flags & self::USE_CHECKBOXES && !$canViewThisSuppressedEntry ) {
464 // If event was hidden from sysops
465 if ( !self::userCan( $row, LogPage::DELETED_RESTRICTED, $authority ) ) {
466 $del = Xml::check( 'deleterevisions', false, [ 'disabled' => 'disabled' ] );
467 } else {
468 $del = Xml::check(
469 'showhiderevisions',
470 false,
471 [ 'name' => 'ids[' . $row->log_id . ']' ]
472 );
473 }
474 } else {
475 // If event was hidden from sysops
476 if ( !self::userCan( $row, LogPage::DELETED_RESTRICTED, $authority ) ) {
477 $del = Linker::revDeleteLinkDisabled( $canHide );
478 } else {
479 $query = [
480 'target' => SpecialPage::getTitleFor( 'Log', $row->log_type )->getPrefixedDBkey(),
481 'type' => 'logging',
482 'ids' => $row->log_id,
483 ];
485 $query,
486 $entryIsSuppressed,
487 $canHide && !$canViewThisSuppressedEntry
488 );
489 }
490 }
491 }
492 }
493
494 return $del;
495 }
496
503 public static function typeAction( $row, $type, $action ) {
504 $match = is_array( $type ) ?
505 in_array( $row->log_type, $type ) : $row->log_type == $type;
506 if ( $match ) {
507 $match = is_array( $action ) ?
508 in_array( $row->log_action, $action ) : $row->log_action == $action;
509 }
510
511 return $match;
512 }
513
523 public static function userCan( $row, $field, Authority $performer ) {
524 return self::userCanBitfield( $row->log_deleted, $field, $performer ) &&
525 self::userCanViewLogType( $row->log_type, $performer );
526 }
527
537 public static function userCanBitfield( $bitfield, $field, Authority $performer ) {
538 if ( $bitfield & $field ) {
539 if ( $bitfield & LogPage::DELETED_RESTRICTED ) {
540 return $performer->isAllowedAny( 'suppressrevision', 'viewsuppressed' );
541 } else {
542 return $performer->isAllowed( 'deletedhistory' );
543 }
544 }
545 return true;
546 }
547
556 public static function userCanViewLogType( $type, Authority $performer ) {
557 $logRestrictions = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::LogRestrictions );
558 if ( isset( $logRestrictions[$type] ) && !$performer->isAllowed( $logRestrictions[$type] )
559 ) {
560 return false;
561 }
562 return true;
563 }
564
570 public static function isDeleted( $row, $field ) {
571 return ( $row->log_deleted & $field ) == $field;
572 }
573
599 public static function showLogExtract(
600 &$out, $types = [], $page = '', $user = '', $param = []
601 ) {
602 $defaultParameters = [
603 'lim' => 25,
604 'conds' => [],
605 'showIfEmpty' => true,
606 'msgKey' => [ '' ],
607 'wrap' => "$1",
608 'flags' => 0,
609 'useRequestParams' => false,
610 'useMaster' => false,
611 'extraUrlParams' => false,
612 ];
613 # The + operator appends elements of remaining keys from the right
614 # handed array to the left handed, whereas duplicated keys are NOT overwritten.
615 $param += $defaultParameters;
616 # Convert $param array to individual variables
617 $lim = $param['lim'];
618 $conds = $param['conds'];
619 $showIfEmpty = $param['showIfEmpty'];
620 $msgKey = $param['msgKey'];
621 $wrap = $param['wrap'];
622 $flags = $param['flags'];
623 $extraUrlParams = $param['extraUrlParams'];
624
625 $useRequestParams = $param['useRequestParams'];
626 // @phan-suppress-next-line PhanRedundantCondition
627 if ( !is_array( $msgKey ) ) {
628 $msgKey = [ $msgKey ];
629 }
630
631 if ( $out instanceof OutputPage ) {
632 $context = $out->getContext();
633 } else {
634 $context = RequestContext::getMain();
635 }
636
637 $services = MediaWikiServices::getInstance();
638 // FIXME: Figure out how to inject this
639 $linkRenderer = $services->getLinkRenderer();
640
641 # Insert list of top 50 (or top $lim) items
642 $loglist = new LogEventsList( $context, $linkRenderer, $flags );
643 $pager = new LogPager(
644 $loglist,
645 $types,
646 $user,
647 $page,
648 false,
649 $conds,
650 false,
651 false,
652 false,
653 '',
654 '',
655 0,
656 $services->getLinkBatchFactory(),
657 $services->getDBLoadBalancer(),
658 $services->getActorNormalization()
659 );
660 if ( !$useRequestParams ) {
661 # Reset vars that may have been taken from the request
662 $pager->mLimit = 50;
663 $pager->mDefaultLimit = 50;
664 $pager->mOffset = "";
665 $pager->mIsBackwards = false;
666 }
667
668 if ( $param['useMaster'] ) {
669 $pager->mDb = wfGetDB( DB_PRIMARY );
670 }
671 // @phan-suppress-next-line PhanImpossibleCondition
672 if ( isset( $param['offset'] ) ) { # Tell pager to ignore WebRequest offset
673 $pager->setOffset( $param['offset'] );
674 }
675
676 // @phan-suppress-next-line PhanSuspiciousValueComparison
677 if ( $lim > 0 ) {
678 $pager->mLimit = $lim;
679 }
680 // Fetch the log rows and build the HTML if needed
681 $logBody = $pager->getBody();
682 $numRows = $pager->getNumRows();
683
684 $s = '';
685
686 if ( $logBody ) {
687 if ( $msgKey[0] ) {
688 // @phan-suppress-next-line PhanParamTooFewUnpack Non-emptiness checked above
689 $msg = $context->msg( ...$msgKey );
690 if ( $page instanceof PageReference ) {
691 $msg->page( $page );
692 }
693 $s .= $msg->parseAsBlock();
694 }
695 $s .= $loglist->beginLogEventsList() .
696 $logBody .
697 $loglist->endLogEventsList();
698 // add styles for change tags
699 $context->getOutput()->addModuleStyles( 'mediawiki.interface.helpers.styles' );
700 } elseif ( $showIfEmpty ) {
701 $s = Html::rawElement( 'div', [ 'class' => 'mw-warning-logempty' ],
702 $context->msg( 'logempty' )->parse() );
703 }
704
705 if ( $page instanceof PageReference ) {
706 $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
707 $pageName = $titleFormatter->getPrefixedDBkey( $page );
708 } elseif ( $page != '' ) {
709 $pageName = $page;
710 } else {
711 $pageName = null;
712 }
713
714 if ( $numRows > $pager->mLimit ) { # Show "Full log" link
715 $urlParam = [];
716 if ( $pageName ) {
717 $urlParam['page'] = $pageName;
718 }
719
720 if ( $user != '' ) {
721 $urlParam['user'] = $user;
722 }
723
724 if ( !is_array( $types ) ) { # Make it an array, if it isn't
725 $types = [ $types ];
726 }
727
728 # If there is exactly one log type, we can link to Special:Log?type=foo
729 if ( count( $types ) == 1 ) {
730 $urlParam['type'] = $types[0];
731 }
732
733 // @phan-suppress-next-line PhanSuspiciousValueComparison
734 if ( $extraUrlParams !== false ) {
735 $urlParam = array_merge( $urlParam, $extraUrlParams );
736 }
737
738 $s .= $linkRenderer->makeKnownLink(
739 SpecialPage::getTitleFor( 'Log' ),
740 $context->msg( 'log-fulllog' )->text(),
741 [],
742 $urlParam
743 );
744 }
745
746 if ( $logBody && $msgKey[0] ) {
747 // TODO: The condition above is weird. Should this be done in any other cases?
748 // Or is it always true in practice?
749
750 // Mark as interface language (T60685)
751 $dir = $context->getLanguage()->getDir();
752 $lang = $context->getLanguage()->getHtmlCode();
753 $s = Html::rawElement( 'div', [
754 'class' => "mw-content-$dir",
755 'dir' => $dir,
756 'lang' => $lang,
757 ], $s );
758
759 // Wrap in warning box
760 $s = Html::warningBox(
761 $s,
762 'mw-warning-with-logexcerpt'
763 );
764 }
765
766 // @phan-suppress-next-line PhanSuspiciousValueComparison
767 if ( $wrap != '' ) { // Wrap message in html
768 $s = str_replace( '$1', $s, $wrap );
769 }
770
771 /* hook can return false, if we don't want the message to be emitted (Wikia BugId:7093) */
772 if ( Hooks::runner()->onLogEventsListShowLogExtract( $s, $types, $pageName, $user, $param ) ) {
773 // $out can be either an OutputPage object or a String-by-reference
774 if ( $out instanceof OutputPage ) {
775 $out->addHTML( $s );
776 } else {
777 $out = $s;
778 }
779 }
780
781 return $numRows;
782 }
783
793 public static function getExcludeClause( $db, $audience = 'public', Authority $performer = null ) {
794 $logRestrictions = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::LogRestrictions );
795
796 if ( $audience != 'public' && $performer === null ) {
797 throw new InvalidArgumentException(
798 'A User object must be given when checking for a user audience.'
799 );
800 }
801
802 // Reset the array, clears extra "where" clauses when $par is used
803 $hiddenLogs = [];
804
805 // Don't show private logs to unprivileged users
806 foreach ( $logRestrictions as $logType => $right ) {
807 if ( $audience == 'public' || !$performer->isAllowed( $right )
808 ) {
809 $hiddenLogs[] = $logType;
810 }
811 }
812 if ( count( $hiddenLogs ) == 1 ) {
813 return 'log_type != ' . $db->addQuotes( $hiddenLogs[0] );
814 } elseif ( $hiddenLogs ) {
815 return 'log_type NOT IN (' . $db->makeList( $hiddenLogs ) . ')';
816 }
817
818 return false;
819 }
820}
getAuthority()
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
getContext()
static showTagEditingUI(Authority $performer)
Indicate whether change tag editing UI is relevant.
static formatSummaryRow( $tags, $page, MessageLocalizer $localizer=null)
Creates HTML for the given tags.
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
setContext(IContextSource $context)
An IContextSource implementation which will inherit context from another source but allow individual ...
static revDeleteLinkDisabled( $delete=true)
Creates a dead (show/hide) link for deleting revisions/log entries.
Definition Linker.php:2229
static revDeleteLink( $query=[], $restricted=false, $delete=true)
Creates a (show/hide) link for deleting revisions/log entries.
Definition Linker.php:2205
const NO_EXTRA_USER_LINKS
static typeAction( $row, $type, $action)
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
showOptions( $types=[], $user='', $page='', $pattern=false, $year=0, $month=0, $day=0, $filter=null, $tagFilter='', $action=null)
Show options for the log list.
static getExcludeClause( $db, $audience='public', Authority $performer=null)
SQL clause to skip forbidden log types for this user.
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)
setAllowedActions( $actions)
Sets the action types allowed for log filtering To one action type may correspond several log_actions...
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.
Definition LogPage.php:39
const DELETED_RESTRICTED
Definition LogPage.php:43
static validTypes()
Get the list of valid log types.
Definition LogPage.php:207
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Class that generates HTML anchor link elements for pages.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
This is one of the Core classes and should be read at least once by any new developers.
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,...
static getTitleValueFor( $name, $subpage=false, $fragment='')
Get a localised TitleValue object for a specified special page name.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
static check( $name, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox.
Definition Xml.php:332
Interface for objects (potentially) representing a page that can be viewable and linked to on a wiki.
This interface represents the authority associated the current execution context, such as a web reque...
Definition Authority.php:37
isAllowed(string $permission)
Checks whether this authority has the given permission in general.
isAllowedAny(... $permissions)
Checks whether this authority has any of the given permissions in general.
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:39
foreach( $mmfl['setupFiles'] as $fileName) if($queue) if(empty( $mmfl['quiet'])) $s
const DB_PRIMARY
Definition defines.php:28