00001 <?php
00015 global $wgQueryPages;
00016 $wgQueryPages = array(
00017
00018
00019 array( 'AncientPagesPage', 'Ancientpages' ),
00020 array( 'BrokenRedirectsPage', 'BrokenRedirects' ),
00021 array( 'DeadendPagesPage', 'Deadendpages' ),
00022 array( 'DisambiguationsPage', 'Disambiguations' ),
00023 array( 'DoubleRedirectsPage', 'DoubleRedirects' ),
00024 array( 'LinkSearchPage', 'LinkSearch' ),
00025 array( 'ListredirectsPage', 'Listredirects' ),
00026 array( 'LonelyPagesPage', 'Lonelypages' ),
00027 array( 'LongPagesPage', 'Longpages' ),
00028 array( 'MostcategoriesPage', 'Mostcategories' ),
00029 array( 'MostimagesPage', 'Mostimages' ),
00030 array( 'MostlinkedCategoriesPage', 'Mostlinkedcategories' ),
00031 array( 'SpecialMostlinkedtemplates', 'Mostlinkedtemplates' ),
00032 array( 'MostlinkedPage', 'Mostlinked' ),
00033 array( 'MostrevisionsPage', 'Mostrevisions' ),
00034 array( 'FewestrevisionsPage', 'Fewestrevisions' ),
00035 array( 'ShortPagesPage', 'Shortpages' ),
00036 array( 'UncategorizedCategoriesPage', 'Uncategorizedcategories' ),
00037 array( 'UncategorizedPagesPage', 'Uncategorizedpages' ),
00038 array( 'UncategorizedImagesPage', 'Uncategorizedimages' ),
00039 array( 'UncategorizedTemplatesPage', 'Uncategorizedtemplates' ),
00040 array( 'UnusedCategoriesPage', 'Unusedcategories' ),
00041 array( 'UnusedimagesPage', 'Unusedimages' ),
00042 array( 'WantedCategoriesPage', 'Wantedcategories' ),
00043 array( 'WantedFilesPage', 'Wantedfiles' ),
00044 array( 'WantedPagesPage', 'Wantedpages' ),
00045 array( 'WantedTemplatesPage', 'Wantedtemplates' ),
00046 array( 'UnwatchedPagesPage', 'Unwatchedpages' ),
00047 array( 'UnusedtemplatesPage', 'Unusedtemplates' ),
00048 array( 'WithoutInterwikiPage', 'Withoutinterwiki' ),
00049 );
00050 wfRunHooks( 'wgQueryPages', array( &$wgQueryPages ) );
00051
00052 global $wgDisableCounters;
00053 if ( !$wgDisableCounters )
00054 $wgQueryPages[] = array( 'PopularPagesPage', 'Popularpages' );
00055
00056
00063 class QueryPage {
00069 var $listoutput = false;
00070
00076 var $offset = 0;
00077 var $limit = 0;
00078
00084 function setListoutput( $bool ) {
00085 $this->listoutput = $bool;
00086 }
00087
00095 function getName() {
00096 return '';
00097 }
00098
00104 function getTitle() {
00105 return SpecialPage::getTitleFor( $this->getName() );
00106 }
00107
00123 function getSQL() {
00124 return "SELECT 'sample' as type, 0 as namespace, 'Sample result' as title, 42 as value";
00125 }
00126
00132 function sortDescending() {
00133 return true;
00134 }
00135
00136 function getOrder() {
00137 return ' ORDER BY value ' .
00138 ($this->sortDescending() ? 'DESC' : '');
00139 }
00140
00148 function isExpensive() {
00149 global $wgDisableQueryPages;
00150 return $wgDisableQueryPages;
00151 }
00152
00159 function isCached() {
00160 global $wgMiserMode;
00161
00162 return $this->isExpensive() && $wgMiserMode;
00163 }
00164
00170 function isSyndicated() {
00171 return true;
00172 }
00173
00183 function formatResult( $skin, $result ) {
00184 return '';
00185 }
00186
00192 function getPageHeader() {
00193 return '';
00194 }
00195
00203 function linkParameters() {
00204 return array();
00205 }
00206
00213 function tryLastResult() {
00214 return false;
00215 }
00216
00223 function recache( $limit, $ignoreErrors = true ) {
00224 $fname = get_class( $this ) . '::recache';
00225 $dbw = wfGetDB( DB_MASTER );
00226 $dbr = wfGetDB( DB_SLAVE, array( $this->getName(), __METHOD__, 'vslow' ) );
00227 if ( !$dbw || !$dbr ) {
00228 return false;
00229 }
00230
00231 $querycache = $dbr->tableName( 'querycache' );
00232
00233 if ( $ignoreErrors ) {
00234 $ignoreW = $dbw->ignoreErrors( true );
00235 $ignoreR = $dbr->ignoreErrors( true );
00236 }
00237
00238 # Clear out any old cached data
00239 $dbw->delete( 'querycache', array( 'qc_type' => $this->getName() ), $fname );
00240 # Do query
00241 $sql = $this->getSQL() . $this->getOrder();
00242 if ( $limit !== false )
00243 $sql = $dbr->limitResult( $sql, $limit, 0 );
00244 $res = $dbr->query( $sql, $fname );
00245 $num = false;
00246 if ( $res ) {
00247 $num = $dbr->numRows( $res );
00248 # Fetch results
00249 $vals = array();
00250 while ( $res && $row = $dbr->fetchObject( $res ) ) {
00251 if ( isset( $row->value ) ) {
00252 $value = intval( $row->value );
00253 } else {
00254 $value = 0;
00255 }
00256
00257 $vals[] = array('qc_type' => $row->type,
00258 'qc_namespace' => $row->namespace,
00259 'qc_title' => $row->title,
00260 'qc_value' => $value);
00261 }
00262
00263 # Save results into the querycache table on the master
00264 if ( count( $vals ) ) {
00265 if ( !$dbw->insert( 'querycache', $vals, __METHOD__ ) ) {
00266
00267 $dbr->freeResult( $res );
00268 $res = false;
00269 }
00270 }
00271 if ( $res ) {
00272 $dbr->freeResult( $res );
00273 }
00274 if ( $ignoreErrors ) {
00275 $dbw->ignoreErrors( $ignoreW );
00276 $dbr->ignoreErrors( $ignoreR );
00277 }
00278
00279 # Update the querycache_info record for the page
00280 $dbw->delete( 'querycache_info', array( 'qci_type' => $this->getName() ), $fname );
00281 $dbw->insert( 'querycache_info', array( 'qci_type' => $this->getName(), 'qci_timestamp' => $dbw->timestamp() ), $fname );
00282
00283 }
00284 return $num;
00285 }
00286
00295 function doQuery( $offset, $limit, $shownavigation=true ) {
00296 global $wgUser, $wgOut, $wgLang, $wgContLang;
00297
00298 $this->offset = $offset;
00299 $this->limit = $limit;
00300
00301 $sname = $this->getName();
00302 $fname = get_class($this) . '::doQuery';
00303 $dbr = wfGetDB( DB_SLAVE );
00304
00305 $wgOut->setSyndicated( $this->isSyndicated() );
00306
00307 if ( !$this->isCached() ) {
00308 $sql = $this->getSQL();
00309 } else {
00310 # Get the cached result
00311 $querycache = $dbr->tableName( 'querycache' );
00312 $type = $dbr->strencode( $sname );
00313 $sql =
00314 "SELECT qc_type as type, qc_namespace as namespace,qc_title as title, qc_value as value
00315 FROM $querycache WHERE qc_type='$type'";
00316
00317 if( !$this->listoutput ) {
00318
00319 # Fetch the timestamp of this update
00320 $tRes = $dbr->select( 'querycache_info', array( 'qci_timestamp' ), array( 'qci_type' => $type ), $fname );
00321 $tRow = $dbr->fetchObject( $tRes );
00322
00323 if( $tRow ) {
00324 $updated = $wgLang->timeanddate( $tRow->qci_timestamp, true, true );
00325 $updateddate = $wgLang->date( $tRow->qci_timestamp, true, true );
00326 $updatedtime = $wgLang->time( $tRow->qci_timestamp, true, true );
00327 $wgOut->addMeta( 'Data-Cache-Time', $tRow->qci_timestamp );
00328 $wgOut->addInlineScript( "var dataCacheTime = '{$tRow->qci_timestamp}';" );
00329 $wgOut->addWikiMsg( 'perfcachedts', $updated, $updateddate, $updatedtime );
00330 } else {
00331 $wgOut->addWikiMsg( 'perfcached' );
00332 }
00333
00334 # If updates on this page have been disabled, let the user know
00335 # that the data set won't be refreshed for now
00336 global $wgDisableQueryPageUpdate;
00337 if( is_array( $wgDisableQueryPageUpdate ) && in_array( $this->getName(), $wgDisableQueryPageUpdate ) ) {
00338 $wgOut->addWikiMsg( 'querypage-no-updates' );
00339 }
00340
00341 }
00342
00343 }
00344
00345 $sql .= $this->getOrder();
00346 $sql = $dbr->limitResult($sql, $limit, $offset);
00347 $res = $dbr->query( $sql );
00348 $num = $dbr->numRows($res);
00349
00350 $this->preprocessResults( $dbr, $res );
00351
00352 $wgOut->addHTML( XML::openElement( 'div', array('class' => 'mw-spcontent') ) );
00353
00354 # Top header and navigation
00355 if( $shownavigation ) {
00356 $wgOut->addHTML( $this->getPageHeader() );
00357 if( $num > 0 ) {
00358 $wgOut->addHTML( '<p>' . wfShowingResults( $offset, $num ) . '</p>' );
00359 # Disable the "next" link when we reach the end
00360 $paging = wfViewPrevNext( $offset, $limit, $wgContLang->specialPage( $sname ),
00361 wfArrayToCGI( $this->linkParameters() ), ( $num < $limit ) );
00362 $wgOut->addHTML( '<p>' . $paging . '</p>' );
00363 } else {
00364 # No results to show, so don't bother with "showing X of Y" etc.
00365 # -- just let the user know and give up now
00366 $wgOut->addHTML( '<p>' . wfMsgHtml( 'specialpage-empty' ) . '</p>' );
00367 $wgOut->addHTML( XML::closeElement( 'div' ) );
00368 return;
00369 }
00370 }
00371
00372 # The actual results; specialist subclasses will want to handle this
00373 # with more than a straight list, so we hand them the info, plus
00374 # an OutputPage, and let them get on with it
00375 $this->outputResults( $wgOut,
00376 $wgUser->getSkin(),
00377 $dbr, # Should use a ResultWrapper for this
00378 $res,
00379 $dbr->numRows( $res ),
00380 $offset );
00381
00382 # Repeat the paging links at the bottom
00383 if( $shownavigation ) {
00384 $wgOut->addHTML( '<p>' . $paging . '</p>' );
00385 }
00386
00387 $wgOut->addHTML( XML::closeElement( 'div' ) );
00388
00389 return $num;
00390 }
00391
00403 protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
00404 global $wgContLang;
00405
00406 if( $num > 0 ) {
00407 $html = array();
00408 if( !$this->listoutput )
00409 $html[] = $this->openList( $offset );
00410
00411 # $res might contain the whole 1,000 rows, so we read up to
00412 # $num [should update this to use a Pager]
00413 for( $i = 0; $i < $num && $row = $dbr->fetchObject( $res ); $i++ ) {
00414 $line = $this->formatResult( $skin, $row );
00415 if( $line ) {
00416 $attr = ( isset( $row->usepatrol ) && $row->usepatrol && $row->patrolled == 0 )
00417 ? ' class="not-patrolled"'
00418 : '';
00419 $html[] = $this->listoutput
00420 ? $line
00421 : "<li{$attr}>{$line}</li>\n";
00422 }
00423 }
00424
00425 # Flush the final result
00426 if( $this->tryLastResult() ) {
00427 $row = null;
00428 $line = $this->formatResult( $skin, $row );
00429 if( $line ) {
00430 $attr = ( isset( $row->usepatrol ) && $row->usepatrol && $row->patrolled == 0 )
00431 ? ' class="not-patrolled"'
00432 : '';
00433 $html[] = $this->listoutput
00434 ? $line
00435 : "<li{$attr}>{$line}</li>\n";
00436 }
00437 }
00438
00439 if( !$this->listoutput )
00440 $html[] = $this->closeList();
00441
00442 $html = $this->listoutput
00443 ? $wgContLang->listToText( $html )
00444 : implode( '', $html );
00445
00446 $out->addHTML( $html );
00447 }
00448 }
00449
00450 function openList( $offset ) {
00451 return "\n<ol start='" . ( $offset + 1 ) . "' class='special'>\n";
00452 }
00453
00454 function closeList() {
00455 return "</ol>\n";
00456 }
00457
00461 function preprocessResults( $db, $res ) {}
00462
00466 function doFeed( $class = '', $limit = 50 ) {
00467 global $wgFeed, $wgFeedClasses;
00468
00469 if ( !$wgFeed ) {
00470 global $wgOut;
00471 $wgOut->addWikiMsg( 'feed-unavailable' );
00472 return;
00473 }
00474
00475 global $wgFeedLimit;
00476 if( $limit > $wgFeedLimit ) {
00477 $limit = $wgFeedLimit;
00478 }
00479
00480 if( isset($wgFeedClasses[$class]) ) {
00481 $feed = new $wgFeedClasses[$class](
00482 $this->feedTitle(),
00483 $this->feedDesc(),
00484 $this->feedUrl() );
00485 $feed->outHeader();
00486
00487 $dbr = wfGetDB( DB_SLAVE );
00488 $sql = $this->getSQL() . $this->getOrder();
00489 $sql = $dbr->limitResult( $sql, $limit, 0 );
00490 $res = $dbr->query( $sql, 'QueryPage::doFeed' );
00491 while( $obj = $dbr->fetchObject( $res ) ) {
00492 $item = $this->feedResult( $obj );
00493 if( $item ) $feed->outItem( $item );
00494 }
00495 $dbr->freeResult( $res );
00496
00497 $feed->outFooter();
00498 return true;
00499 } else {
00500 return false;
00501 }
00502 }
00503
00508 function feedResult( $row ) {
00509 if( !isset( $row->title ) ) {
00510 return null;
00511 }
00512 $title = Title::MakeTitle( intval( $row->namespace ), $row->title );
00513 if( $title ) {
00514 $date = isset( $row->timestamp ) ? $row->timestamp : '';
00515 $comments = '';
00516 if( $title ) {
00517 $talkpage = $title->getTalkPage();
00518 $comments = $talkpage->getFullURL();
00519 }
00520
00521 return new FeedItem(
00522 $title->getPrefixedText(),
00523 $this->feedItemDesc( $row ),
00524 $title->getFullURL(),
00525 $date,
00526 $this->feedItemAuthor( $row ),
00527 $comments);
00528 } else {
00529 return null;
00530 }
00531 }
00532
00533 function feedItemDesc( $row ) {
00534 return isset( $row->comment ) ? htmlspecialchars( $row->comment ) : '';
00535 }
00536
00537 function feedItemAuthor( $row ) {
00538 return isset( $row->user_text ) ? $row->user_text : '';
00539 }
00540
00541 function feedTitle() {
00542 global $wgContLanguageCode, $wgSitename;
00543 $page = SpecialPage::getPage( $this->getName() );
00544 $desc = $page->getDescription();
00545 return "$wgSitename - $desc [$wgContLanguageCode]";
00546 }
00547
00548 function feedDesc() {
00549 return wfMsgExt( 'tagline', 'parsemag' );
00550 }
00551
00552 function feedUrl() {
00553 $title = SpecialPage::getTitleFor( $this->getName() );
00554 return $title->getFullURL();
00555 }
00556 }
00557
00562 abstract class WantedQueryPage extends QueryPage {
00563
00564 function isExpensive() {
00565 return true;
00566 }
00567
00568 function isSyndicated() {
00569 return false;
00570 }
00571
00575 function preprocessResults( $db, $res ) {
00576 $batch = new LinkBatch;
00577 while ( $row = $db->fetchObject( $res ) )
00578 $batch->add( $row->namespace, $row->title );
00579 $batch->execute();
00580
00581
00582 if ( $db->numRows( $res ) > 0 )
00583
00584 $db->dataSeek( $res, 0 );
00585 }
00586
00594 public function formatResult( $skin, $result ) {
00595 $title = Title::makeTitleSafe( $result->namespace, $result->title );
00596 if( $title instanceof Title ) {
00597 if( $this->isCached() ) {
00598 $pageLink = $title->exists()
00599 ? '<s>' . $skin->link( $title ) . '</s>'
00600 : $skin->link(
00601 $title,
00602 null,
00603 array(),
00604 array(),
00605 array( 'broken' )
00606 );
00607 } else {
00608 $pageLink = $skin->link(
00609 $title,
00610 null,
00611 array(),
00612 array(),
00613 array( 'broken' )
00614 );
00615 }
00616 return wfSpecialList( $pageLink, $this->makeWlhLink( $title, $skin, $result ) );
00617 } else {
00618 $tsafe = htmlspecialchars( $result->title );
00619 return wfMsgHtml( 'wantedpages-badtitle', $tsafe );
00620 }
00621 }
00622
00631 private function makeWlhLink( $title, $skin, $result ) {
00632 global $wgLang;
00633 $wlh = SpecialPage::getTitleFor( 'Whatlinkshere' );
00634 $label = wfMsgExt( 'nlinks', array( 'parsemag', 'escape' ),
00635 $wgLang->formatNum( $result->value ) );
00636 return $skin->link( $wlh, $label, array(), array( 'target' => $title->getPrefixedText() ) );
00637 }
00638 }