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
00093 function getName() {
00094 return '';
00095 }
00096
00102 function getTitle() {
00103 return SpecialPage::getTitleFor( $this->getName() );
00104 }
00105
00121 function getSQL() {
00122 return "SELECT 'sample' as type, 0 as namespace, 'Sample result' as title, 42 as value";
00123 }
00124
00128 function sortDescending() {
00129 return true;
00130 }
00131
00132 function getOrder() {
00133 return ' ORDER BY value ' .
00134 ($this->sortDescending() ? 'DESC' : '');
00135 }
00136
00142 function isExpensive( ) {
00143 global $wgDisableQueryPages;
00144 return $wgDisableQueryPages;
00145 }
00146
00153 function isCached() {
00154 global $wgMiserMode;
00155
00156 return $this->isExpensive() && $wgMiserMode;
00157 }
00158
00162 function isSyndicated() {
00163 return true;
00164 }
00165
00172 function formatResult( $skin, $result ) {
00173 return '';
00174 }
00175
00179 function getPageHeader( ) {
00180 return '';
00181 }
00182
00189 function linkParameters() {
00190 return array();
00191 }
00192
00199 function tryLastResult( ) {
00200 return false;
00201 }
00202
00206 function recache( $limit, $ignoreErrors = true ) {
00207 $fname = get_class( $this ) . '::recache';
00208 $dbw = wfGetDB( DB_MASTER );
00209 $dbr = wfGetDB( DB_SLAVE, array( $this->getName(), 'QueryPage::recache', 'vslow' ) );
00210 if ( !$dbw || !$dbr ) {
00211 return false;
00212 }
00213
00214 $querycache = $dbr->tableName( 'querycache' );
00215
00216 if ( $ignoreErrors ) {
00217 $ignoreW = $dbw->ignoreErrors( true );
00218 $ignoreR = $dbr->ignoreErrors( true );
00219 }
00220
00221 # Clear out any old cached data
00222 $dbw->delete( 'querycache', array( 'qc_type' => $this->getName() ), $fname );
00223 # Do query
00224 $sql = $this->getSQL() . $this->getOrder();
00225 if ( $limit !== false )
00226 $sql = $dbr->limitResult( $sql, $limit, 0 );
00227 $res = $dbr->query( $sql, $fname );
00228 $num = false;
00229 if ( $res ) {
00230 $num = $dbr->numRows( $res );
00231 # Fetch results
00232 $insertSql = "INSERT INTO $querycache (qc_type,qc_namespace,qc_title,qc_value) VALUES ";
00233 $first = true;
00234 while ( $res && $row = $dbr->fetchObject( $res ) ) {
00235 if ( $first ) {
00236 $first = false;
00237 } else {
00238 $insertSql .= ',';
00239 }
00240 if ( isset( $row->value ) ) {
00241 $value = intval( $row->value );
00242 } else {
00243 $value = 0;
00244 }
00245
00246 $insertSql .= '(' .
00247 $dbw->addQuotes( $row->type ) . ',' .
00248 $dbw->addQuotes( $row->namespace ) . ',' .
00249 $dbw->addQuotes( $row->title ) . ',' .
00250 $dbw->addQuotes( $value ) . ')';
00251 }
00252
00253 # Save results into the querycache table on the master
00254 if ( !$first ) {
00255 if ( !$dbw->query( $insertSql, $fname ) ) {
00256
00257 $dbr->freeResult( $res );
00258 $res = false;
00259 }
00260 }
00261 if ( $res ) {
00262 $dbr->freeResult( $res );
00263 }
00264 if ( $ignoreErrors ) {
00265 $dbw->ignoreErrors( $ignoreW );
00266 $dbr->ignoreErrors( $ignoreR );
00267 }
00268
00269 # Update the querycache_info record for the page
00270 $dbw->delete( 'querycache_info', array( 'qci_type' => $this->getName() ), $fname );
00271 $dbw->insert( 'querycache_info', array( 'qci_type' => $this->getName(), 'qci_timestamp' => $dbw->timestamp() ), $fname );
00272
00273 }
00274 return $num;
00275 }
00276
00285 function doQuery( $offset, $limit, $shownavigation=true ) {
00286 global $wgUser, $wgOut, $wgLang, $wgContLang;
00287
00288 $this->offset = $offset;
00289 $this->limit = $limit;
00290
00291 $sname = $this->getName();
00292 $fname = get_class($this) . '::doQuery';
00293 $dbr = wfGetDB( DB_SLAVE );
00294
00295 $wgOut->setSyndicated( $this->isSyndicated() );
00296
00297 if ( !$this->isCached() ) {
00298 $sql = $this->getSQL();
00299 } else {
00300 # Get the cached result
00301 $querycache = $dbr->tableName( 'querycache' );
00302 $type = $dbr->strencode( $sname );
00303 $sql =
00304 "SELECT qc_type as type, qc_namespace as namespace,qc_title as title, qc_value as value
00305 FROM $querycache WHERE qc_type='$type'";
00306
00307 if( !$this->listoutput ) {
00308
00309 # Fetch the timestamp of this update
00310 $tRes = $dbr->select( 'querycache_info', array( 'qci_timestamp' ), array( 'qci_type' => $type ), $fname );
00311 $tRow = $dbr->fetchObject( $tRes );
00312
00313 if( $tRow ) {
00314 $updated = $wgLang->timeAndDate( $tRow->qci_timestamp, true, true );
00315 $wgOut->addMeta( 'Data-Cache-Time', $tRow->qci_timestamp );
00316 $wgOut->addInlineScript( "var dataCacheTime = '{$tRow->qci_timestamp}';" );
00317 $wgOut->addWikiMsg( 'perfcachedts', $updated );
00318 } else {
00319 $wgOut->addWikiMsg( 'perfcached' );
00320 }
00321
00322 # If updates on this page have been disabled, let the user know
00323 # that the data set won't be refreshed for now
00324 global $wgDisableQueryPageUpdate;
00325 if( is_array( $wgDisableQueryPageUpdate ) && in_array( $this->getName(), $wgDisableQueryPageUpdate ) ) {
00326 $wgOut->addWikiMsg( 'querypage-no-updates' );
00327 }
00328
00329 }
00330
00331 }
00332
00333 $sql .= $this->getOrder();
00334 $sql = $dbr->limitResult($sql, $limit, $offset);
00335 $res = $dbr->query( $sql );
00336 $num = $dbr->numRows($res);
00337
00338 $this->preprocessResults( $dbr, $res );
00339
00340 $wgOut->addHTML( XML::openElement( 'div', array('class' => 'mw-spcontent') ) );
00341
00342 # Top header and navigation
00343 if( $shownavigation ) {
00344 $wgOut->addHTML( $this->getPageHeader() );
00345 if( $num > 0 ) {
00346 $wgOut->addHTML( '<p>' . wfShowingResults( $offset, $num ) . '</p>' );
00347 # Disable the "next" link when we reach the end
00348 $paging = wfViewPrevNext( $offset, $limit, $wgContLang->specialPage( $sname ),
00349 wfArrayToCGI( $this->linkParameters() ), ( $num < $limit ) );
00350 $wgOut->addHTML( '<p>' . $paging . '</p>' );
00351 } else {
00352 # No results to show, so don't bother with "showing X of Y" etc.
00353 # -- just let the user know and give up now
00354 $wgOut->addHTML( '<p>' . wfMsgHtml( 'specialpage-empty' ) . '</p>' );
00355 $wgOut->addHTML( XML::closeElement( 'div' ) );
00356 return;
00357 }
00358 }
00359
00360 # The actual results; specialist subclasses will want to handle this
00361 # with more than a straight list, so we hand them the info, plus
00362 # an OutputPage, and let them get on with it
00363 $this->outputResults( $wgOut,
00364 $wgUser->getSkin(),
00365 $dbr, # Should use a ResultWrapper for this
00366 $res,
00367 $dbr->numRows( $res ),
00368 $offset );
00369
00370 # Repeat the paging links at the bottom
00371 if( $shownavigation ) {
00372 $wgOut->addHTML( '<p>' . $paging . '</p>' );
00373 }
00374
00375 $wgOut->addHTML( XML::closeElement( 'div' ) );
00376
00377 return $num;
00378 }
00379
00391 protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
00392 global $wgContLang;
00393
00394 if( $num > 0 ) {
00395 $html = array();
00396 if( !$this->listoutput )
00397 $html[] = $this->openList( $offset );
00398
00399 # $res might contain the whole 1,000 rows, so we read up to
00400 # $num [should update this to use a Pager]
00401 for( $i = 0; $i < $num && $row = $dbr->fetchObject( $res ); $i++ ) {
00402 $line = $this->formatResult( $skin, $row );
00403 if( $line ) {
00404 $attr = ( isset( $row->usepatrol ) && $row->usepatrol && $row->patrolled == 0 )
00405 ? ' class="not-patrolled"'
00406 : '';
00407 $html[] = $this->listoutput
00408 ? $line
00409 : "<li{$attr}>{$line}</li>\n";
00410 }
00411 }
00412
00413 # Flush the final result
00414 if( $this->tryLastResult() ) {
00415 $row = null;
00416 $line = $this->formatResult( $skin, $row );
00417 if( $line ) {
00418 $attr = ( isset( $row->usepatrol ) && $row->usepatrol && $row->patrolled == 0 )
00419 ? ' class="not-patrolled"'
00420 : '';
00421 $html[] = $this->listoutput
00422 ? $line
00423 : "<li{$attr}>{$line}</li>\n";
00424 }
00425 }
00426
00427 if( !$this->listoutput )
00428 $html[] = $this->closeList();
00429
00430 $html = $this->listoutput
00431 ? $wgContLang->listToText( $html )
00432 : implode( '', $html );
00433
00434 $out->addHTML( $html );
00435 }
00436 }
00437
00438 function openList( $offset ) {
00439 return "\n<ol start='" . ( $offset + 1 ) . "' class='special'>\n";
00440 }
00441
00442 function closeList() {
00443 return "</ol>\n";
00444 }
00445
00449 function preprocessResults( $db, $res ) {}
00450
00454 function doFeed( $class = '', $limit = 50 ) {
00455 global $wgFeed, $wgFeedClasses;
00456
00457 if ( !$wgFeed ) {
00458 global $wgOut;
00459 $wgOut->addWikiMsg( 'feed-unavailable' );
00460 return;
00461 }
00462
00463 global $wgFeedLimit;
00464 if( $limit > $wgFeedLimit ) {
00465 $limit = $wgFeedLimit;
00466 }
00467
00468 if( isset($wgFeedClasses[$class]) ) {
00469 $feed = new $wgFeedClasses[$class](
00470 $this->feedTitle(),
00471 $this->feedDesc(),
00472 $this->feedUrl() );
00473 $feed->outHeader();
00474
00475 $dbr = wfGetDB( DB_SLAVE );
00476 $sql = $this->getSQL() . $this->getOrder();
00477 $sql = $dbr->limitResult( $sql, $limit, 0 );
00478 $res = $dbr->query( $sql, 'QueryPage::doFeed' );
00479 while( $obj = $dbr->fetchObject( $res ) ) {
00480 $item = $this->feedResult( $obj );
00481 if( $item ) $feed->outItem( $item );
00482 }
00483 $dbr->freeResult( $res );
00484
00485 $feed->outFooter();
00486 return true;
00487 } else {
00488 return false;
00489 }
00490 }
00491
00496 function feedResult( $row ) {
00497 if( !isset( $row->title ) ) {
00498 return NULL;
00499 }
00500 $title = Title::MakeTitle( intval( $row->namespace ), $row->title );
00501 if( $title ) {
00502 $date = isset( $row->timestamp ) ? $row->timestamp : '';
00503 $comments = '';
00504 if( $title ) {
00505 $talkpage = $title->getTalkPage();
00506 $comments = $talkpage->getFullURL();
00507 }
00508
00509 return new FeedItem(
00510 $title->getPrefixedText(),
00511 $this->feedItemDesc( $row ),
00512 $title->getFullURL(),
00513 $date,
00514 $this->feedItemAuthor( $row ),
00515 $comments);
00516 } else {
00517 return NULL;
00518 }
00519 }
00520
00521 function feedItemDesc( $row ) {
00522 return isset( $row->comment ) ? htmlspecialchars( $row->comment ) : '';
00523 }
00524
00525 function feedItemAuthor( $row ) {
00526 return isset( $row->user_text ) ? $row->user_text : '';
00527 }
00528
00529 function feedTitle() {
00530 global $wgContLanguageCode, $wgSitename;
00531 $page = SpecialPage::getPage( $this->getName() );
00532 $desc = $page->getDescription();
00533 return "$wgSitename - $desc [$wgContLanguageCode]";
00534 }
00535
00536 function feedDesc() {
00537 return wfMsgExt( 'tagline', 'parsemag' );
00538 }
00539
00540 function feedUrl() {
00541 $title = SpecialPage::getTitleFor( $this->getName() );
00542 return $title->getFullURL();
00543 }
00544 }