80 [ SpecialAncientPages::class,
'Ancientpages' ],
81 [ SpecialBrokenRedirects::class,
'BrokenRedirects' ],
82 [ SpecialDeadendPages::class,
'Deadendpages' ],
83 [ SpecialDoubleRedirects::class,
'DoubleRedirects' ],
84 [ SpecialFileDuplicateSearch::class,
'FileDuplicateSearch' ],
85 [ SpecialListDuplicatedFiles::class,
'ListDuplicatedFiles' ],
86 [ SpecialLinkSearch::class,
'LinkSearch' ],
87 [ SpecialListRedirects::class,
'Listredirects' ],
88 [ SpecialLonelyPages::class,
'Lonelypages' ],
89 [ SpecialLongPages::class,
'Longpages' ],
90 [ SpecialMediaStatistics::class,
'MediaStatistics' ],
91 [ SpecialMIMESearch::class,
'MIMEsearch' ],
92 [ SpecialMostCategories::class,
'Mostcategories' ],
93 [ MostimagesPage::class,
'Mostimages' ],
94 [ SpecialMostInterwikis::class,
'Mostinterwikis' ],
95 [ SpecialMostLinkedCategories::class,
'Mostlinkedcategories' ],
96 [ SpecialMostLinkedTemplates::class,
'Mostlinkedtemplates' ],
97 [ SpecialMostLinked::class,
'Mostlinked' ],
98 [ SpecialMostRevisions::class,
'Mostrevisions' ],
99 [ SpecialFewestRevisions::class,
'Fewestrevisions' ],
100 [ SpecialShortPages::class,
'Shortpages' ],
101 [ SpecialUncategorizedCategories::class,
'Uncategorizedcategories' ],
102 [ SpecialUncategorizedPages::class,
'Uncategorizedpages' ],
103 [ SpecialUncategorizedImages::class,
'Uncategorizedimages' ],
104 [ SpecialUncategorizedTemplates::class,
'Uncategorizedtemplates' ],
105 [ SpecialUnusedCategories::class,
'Unusedcategories' ],
106 [ SpecialUnusedImages::class,
'Unusedimages' ],
107 [ SpecialWantedCategories::class,
'Wantedcategories' ],
108 [ WantedFilesPage::class,
'Wantedfiles' ],
109 [ WantedPagesPage::class,
'Wantedpages' ],
110 [ SpecialWantedTemplates::class,
'Wantedtemplates' ],
111 [ SpecialUnwatchedPages::class,
'Unwatchedpages' ],
112 [ SpecialUnusedTemplates::class,
'Unusedtemplates' ],
113 [ SpecialWithoutInterwiki::class,
'Withoutinterwiki' ],
127 $disableQueryPageUpdate = $config->
get(
'DisableQueryPageUpdate' );
129 if ( !is_array( $disableQueryPageUpdate ) ) {
134 foreach ( $disableQueryPageUpdate as $name =>
$runMode ) {
135 if ( is_int( $name ) ) {
151 $this->listoutput = $bool;
192 throw new MWException(
"Bug in a QueryPage: doesn't implement getQueryInfo() nor "
193 .
"getQuery() properly" );
238 return $this->
getConfig()->get(
'DisableQueryPages' );
299 $this->
getOutput()->addWikiMsg(
'specialpage-empty' );
326 $fname = static::class .
'::recache';
337 $num =
$res->numRows();
340 foreach (
$res as $i => $row ) {
341 if ( isset( $row->value ) ) {
346 $value = intval( $row->value );
354 'qc_namespace' => $row->namespace,
355 'qc_title' => $row->title,
360 $dbw->doAtomicSection(
362 function (
IDatabase $dbw, $fname ) use ( $vals ) {
363 # Clear out any old cached data
364 $dbw->
delete(
'querycache',
365 [
'qc_type' => $this->
getName() ],
368 # Save results into the querycache table on the master
369 if ( count( $vals ) ) {
370 $dbw->
insert(
'querycache', $vals, $fname );
372 # Update the querycache_info record for the page
373 $dbw->
delete(
'querycache_info',
374 [
'qci_type' => $this->
getName() ],
377 $dbw->
insert(
'querycache_info',
378 [
'qci_type' => $this->
getName(),
386 if ( !$ignoreErrors ) {
412 $dbw->delete(
'querycache', [
414 'qc_namespace' =>
$title->getNamespace(),
415 'qc_title' =>
$title->getDBkey(),
428 $fname = static::class .
'::reallyDoQuery';
434 foreach ( $order as &$field ) {
439 if ( is_array( $query ) ) {
440 $tables = isset( $query[
'tables'] ) ? (array)$query[
'tables'] : [];
441 $fields = isset( $query[
'fields'] ) ? (array)$query[
'fields'] : [];
442 $conds = isset( $query[
'conds'] ) ? (array)$query[
'conds'] : [];
443 $options = isset( $query[
'options'] ) ? (array)$query[
'options'] : [];
444 $join_conds = isset( $query[
'join_conds'] ) ? (array)$query[
'join_conds'] : [];
447 $options[
'ORDER BY'] = $order;
451 $options[
'LIMIT'] = intval(
$limit );
455 $options[
'OFFSET'] = intval(
$offset );
458 $res =
$dbr->select( $tables, $fields, $conds, $fname,
459 $options, $join_conds
464 $sql .=
' ORDER BY ' . implode(
', ', $order );
498 $options[
'LIMIT'] = intval(
$limit );
502 $options[
'OFFSET'] = intval(
$offset );
507 foreach ( $order as &$field ) {
512 $options[
'ORDER BY'] = $order;
515 return $dbr->select(
'querycache',
517 'namespace' =>
'qc_namespace',
518 'title' =>
'qc_title',
519 'value' =>
'qc_value' ],
520 [
'qc_type' => $this->
getName() ],
540 if ( is_null( $this->cachedTimestamp ) ) {
542 $fname = static::class .
'::getCachedTimestamp';
543 $this->cachedTimestamp =
$dbr->selectField(
'querycache_info',
'qci_timestamp',
544 [
'qci_type' => $this->
getName() ], $fname );
563 if ( $this->
getConfig()->
get(
'MiserMode' ) ) {
583 if ( $this->
getConfig()->
get(
'MiserMode' ) ) {
584 $limit = min( $uiLimit + 1, $maxResults - $uiOffset );
602 return max( $this->
getConfig()->
get(
'QueryCacheLimit' ), 10000 );
623 $out->addWikiMsg(
'querypage-disabled' );
629 if ( $this->limit == 0 && $this->offset == 0 ) {
632 $dbLimit = $this->
getDBLimit( $this->limit, $this->offset );
635 # select one extra row for navigation
638 # Get the cached result, select one extra row for navigation
640 if ( !$this->listoutput ) {
641 # Fetch the timestamp of this update
644 $maxResults =
$lang->formatNum( $this->
getConfig()->
get(
'QueryCacheLimit' ) );
647 $updated =
$lang->userTimeAndDate( $ts, $user );
648 $updateddate =
$lang->userDate( $ts, $user );
649 $updatedtime =
$lang->userTime( $ts, $user );
650 $out->addMeta(
'Data-Cache-Time', $ts );
651 $out->addJsConfigVars(
'dataCacheTime', $ts );
652 $out->addWikiMsg(
'perfcachedts', $updated, $updateddate, $updatedtime, $maxResults );
654 $out->addWikiMsg(
'perfcached', $maxResults );
657 # If updates on this page have been disabled, let the user know
658 # that the data set won't be refreshed for now
660 if ( isset( $disabledQueryPages[$this->
getName()] ) ) {
664 "<div class=\"mw-querypage-no-updates\">\n$1\n</div>",
665 'querypage-no-updates'
670 "<div class=\"mw-querypage-updates-" .
$runMode .
"\">\n$1\n</div>",
678 $this->numRows =
$res->numRows();
685 # Top header and navigation
686 if ( $this->shownavigation ) {
688 if ( $this->numRows > 0 ) {
689 $out->addHTML( $this->
msg(
'showingresultsinrange' )->numParams(
690 min( $this->numRows, $this->limit ), #
do not show the one extra row,
if exist
691 $this->offset + 1, ( min( $this->numRows, $this->limit ) + $this->offset ) )->parseAsBlock() );
692 # Disable the "next" link when we reach the end
693 $miserMaxResults = $this->
getConfig()->get(
'MiserMode' )
694 && ( $this->offset + $this->limit >= $this->
getMaxResults() );
695 $atEnd = ( $this->numRows <=
$this->limit ) || $miserMaxResults;
698 $out->addHTML(
'<p>' . $paging .
'</p>' );
700 # No results to show, so don't bother with "showing X of Y" etc.
701 # -- just let the user know and give up now
708 # The actual results; specialist subclasses will want to handle this
709 # with more than a straight list, so we hand them the info, plus
710 # an OutputPage, and let them get on with it
715 min( $this->numRows, $this->limit ), #
do not format the one extra row,
if exist
718 # Repeat the paging links at the bottom
719 if ( $this->shownavigation ) {
720 $out->addHTML(
'<p>' . $paging .
'</p>' );
740 if ( !$this->listoutput ) {
744 # $res might contain the whole 1,000 rows, so we read up to
745 # $num [should update this to use a Pager]
746 for ( $i = 0; $i < $num && $row =
$res->fetchObject(); $i++ ) {
749 $html[] = $this->listoutput
751 :
"<li>{$line}</li>\n";
755 if ( !$this->listoutput ) {
759 $html = $this->listoutput
760 ? MediaWikiServices::getInstance()->getContentLanguage()->listToText( $html )
761 : implode(
'', $html );
763 $out->addHTML( $html );
772 return "\n<ol start='" . (
$offset + 1 ) .
"' class='special'>\n";
801 if ( !
$res->numRows() ) {
806 foreach (
$res as $row ) {
807 $batch->
add( $ns ?? $row->namespace, $row->title );