[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Base code for "query" special pages. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * http://www.gnu.org/copyleft/gpl.html 19 * 20 * @file 21 * @ingroup SpecialPage 22 */ 23 24 /** 25 * This is a class for doing query pages; since they're almost all the same, 26 * we factor out some of the functionality into a superclass, and let 27 * subclasses derive from it. 28 * @ingroup SpecialPage 29 */ 30 abstract class QueryPage extends SpecialPage { 31 /** @var bool Whether or not we want plain listoutput rather than an ordered list */ 32 protected $listoutput = false; 33 34 /** @var int The offset and limit in use, as passed to the query() function */ 35 protected $offset = 0; 36 37 /** @var int */ 38 protected $limit = 0; 39 40 /** 41 * The number of rows returned by the query. Reading this variable 42 * only makes sense in functions that are run after the query has been 43 * done, such as preprocessResults() and formatRow(). 44 */ 45 protected $numRows; 46 47 protected $cachedTimestamp = null; 48 49 /** 50 * Whether to show prev/next links 51 */ 52 protected $shownavigation = true; 53 54 /** 55 * Get a list of query page classes and their associated special pages, 56 * for periodic updates. 57 * 58 * DO NOT CHANGE THIS LIST without testing that 59 * maintenance/updateSpecialPages.php still works. 60 * @return array 61 */ 62 public static function getPages() { 63 global $wgDisableCounters; 64 static $qp = null; 65 66 if ( $qp === null ) { 67 // QueryPage subclass, Special page name 68 $qp = array( 69 array( 'AncientPagesPage', 'Ancientpages' ), 70 array( 'BrokenRedirectsPage', 'BrokenRedirects' ), 71 array( 'DeadendPagesPage', 'Deadendpages' ), 72 array( 'DoubleRedirectsPage', 'DoubleRedirects' ), 73 array( 'FileDuplicateSearchPage', 'FileDuplicateSearch' ), 74 array( 'ListDuplicatedFilesPage', 'ListDuplicatedFiles'), 75 array( 'LinkSearchPage', 'LinkSearch' ), 76 array( 'ListredirectsPage', 'Listredirects' ), 77 array( 'LonelyPagesPage', 'Lonelypages' ), 78 array( 'LongPagesPage', 'Longpages' ), 79 array( 'MediaStatisticsPage', 'MediaStatistics' ), 80 array( 'MIMEsearchPage', 'MIMEsearch' ), 81 array( 'MostcategoriesPage', 'Mostcategories' ), 82 array( 'MostimagesPage', 'Mostimages' ), 83 array( 'MostinterwikisPage', 'Mostinterwikis' ), 84 array( 'MostlinkedCategoriesPage', 'Mostlinkedcategories' ), 85 array( 'MostlinkedtemplatesPage', 'Mostlinkedtemplates' ), 86 array( 'MostlinkedPage', 'Mostlinked' ), 87 array( 'MostrevisionsPage', 'Mostrevisions' ), 88 array( 'FewestrevisionsPage', 'Fewestrevisions' ), 89 array( 'ShortPagesPage', 'Shortpages' ), 90 array( 'UncategorizedCategoriesPage', 'Uncategorizedcategories' ), 91 array( 'UncategorizedPagesPage', 'Uncategorizedpages' ), 92 array( 'UncategorizedImagesPage', 'Uncategorizedimages' ), 93 array( 'UncategorizedTemplatesPage', 'Uncategorizedtemplates' ), 94 array( 'UnusedCategoriesPage', 'Unusedcategories' ), 95 array( 'UnusedimagesPage', 'Unusedimages' ), 96 array( 'WantedCategoriesPage', 'Wantedcategories' ), 97 array( 'WantedFilesPage', 'Wantedfiles' ), 98 array( 'WantedPagesPage', 'Wantedpages' ), 99 array( 'WantedTemplatesPage', 'Wantedtemplates' ), 100 array( 'UnwatchedPagesPage', 'Unwatchedpages' ), 101 array( 'UnusedtemplatesPage', 'Unusedtemplates' ), 102 array( 'WithoutInterwikiPage', 'Withoutinterwiki' ), 103 ); 104 wfRunHooks( 'wgQueryPages', array( &$qp ) ); 105 106 if ( !$wgDisableCounters ) { 107 $qp[] = array( 'PopularPagesPage', 'Popularpages' ); 108 } 109 } 110 111 return $qp; 112 } 113 114 /** 115 * A mutator for $this->listoutput; 116 * 117 * @param bool $bool 118 */ 119 function setListoutput( $bool ) { 120 $this->listoutput = $bool; 121 } 122 123 /** 124 * Subclasses return an SQL query here, formatted as an array with the 125 * following keys: 126 * tables => Table(s) for passing to Database::select() 127 * fields => Field(s) for passing to Database::select(), may be * 128 * conds => WHERE conditions 129 * options => options 130 * join_conds => JOIN conditions 131 * 132 * Note that the query itself should return the following three columns: 133 * 'namespace', 'title', and 'value'. 'value' is used for sorting. 134 * 135 * These may be stored in the querycache table for expensive queries, 136 * and that cached data will be returned sometimes, so the presence of 137 * extra fields can't be relied upon. The cached 'value' column will be 138 * an integer; non-numeric values are useful only for sorting the 139 * initial query (except if they're timestamps, see usesTimestamps()). 140 * 141 * Don't include an ORDER or LIMIT clause, they will be added. 142 * 143 * If this function is not overridden or returns something other than 144 * an array, getSQL() will be used instead. This is for backwards 145 * compatibility only and is strongly deprecated. 146 * @return array 147 * @since 1.18 148 */ 149 function getQueryInfo() { 150 return null; 151 } 152 153 /** 154 * For back-compat, subclasses may return a raw SQL query here, as a string. 155 * This is strongly deprecated; getQueryInfo() should be overridden instead. 156 * @throws MWException 157 * @return string 158 */ 159 function getSQL() { 160 /* Implement getQueryInfo() instead */ 161 throw new MWException( "Bug in a QueryPage: doesn't implement getQueryInfo() nor " 162 . "getQuery() properly" ); 163 } 164 165 /** 166 * Subclasses return an array of fields to order by here. Don't append 167 * DESC to the field names, that'll be done automatically if 168 * sortDescending() returns true. 169 * @return array 170 * @since 1.18 171 */ 172 function getOrderFields() { 173 return array( 'value' ); 174 } 175 176 /** 177 * Does this query return timestamps rather than integers in its 178 * 'value' field? If true, this class will convert 'value' to a 179 * UNIX timestamp for caching. 180 * NOTE: formatRow() may get timestamps in TS_MW (mysql), TS_DB (pgsql) 181 * or TS_UNIX (querycache) format, so be sure to always run them 182 * through wfTimestamp() 183 * @return bool 184 * @since 1.18 185 */ 186 function usesTimestamps() { 187 return false; 188 } 189 190 /** 191 * Override to sort by increasing values 192 * 193 * @return bool 194 */ 195 function sortDescending() { 196 return true; 197 } 198 199 /** 200 * Is this query expensive (for some definition of expensive)? Then we 201 * don't let it run in miser mode. $wgDisableQueryPages causes all query 202 * pages to be declared expensive. Some query pages are always expensive. 203 * 204 * @return bool 205 */ 206 function isExpensive() { 207 return $this->getConfig()->get( 'DisableQueryPages' ); 208 } 209 210 /** 211 * Is the output of this query cacheable? Non-cacheable expensive pages 212 * will be disabled in miser mode and will not have their results written 213 * to the querycache table. 214 * @return bool 215 * @since 1.18 216 */ 217 public function isCacheable() { 218 return true; 219 } 220 221 /** 222 * Whether or not the output of the page in question is retrieved from 223 * the database cache. 224 * 225 * @return bool 226 */ 227 function isCached() { 228 return $this->isExpensive() && $this->getConfig()->get( 'MiserMode' ); 229 } 230 231 /** 232 * Sometime we don't want to build rss / atom feeds. 233 * 234 * @return bool 235 */ 236 function isSyndicated() { 237 return true; 238 } 239 240 /** 241 * Formats the results of the query for display. The skin is the current 242 * skin; you can use it for making links. The result is a single row of 243 * result data. You should be able to grab SQL results off of it. 244 * If the function returns false, the line output will be skipped. 245 * @param Skin $skin 246 * @param object $result Result row 247 * @return string|bool String or false to skip 248 */ 249 abstract function formatResult( $skin, $result ); 250 251 /** 252 * The content returned by this function will be output before any result 253 * 254 * @return string 255 */ 256 function getPageHeader() { 257 return ''; 258 } 259 260 /** 261 * If using extra form wheely-dealies, return a set of parameters here 262 * as an associative array. They will be encoded and added to the paging 263 * links (prev/next/lengths). 264 * 265 * @return array 266 */ 267 function linkParameters() { 268 return array(); 269 } 270 271 /** 272 * Some special pages (for example SpecialListusers) might not return the 273 * current object formatted, but return the previous one instead. 274 * Setting this to return true will ensure formatResult() is called 275 * one more time to make sure that the very last result is formatted 276 * as well. 277 * @return bool 278 */ 279 function tryLastResult() { 280 return false; 281 } 282 283 /** 284 * Clear the cache and save new results 285 * 286 * @param int|bool $limit Limit for SQL statement 287 * @param bool $ignoreErrors Whether to ignore database errors 288 * @throws DBError|Exception 289 * @return bool|int 290 */ 291 function recache( $limit, $ignoreErrors = true ) { 292 if ( !$this->isCacheable() ) { 293 return 0; 294 } 295 296 $fname = get_class( $this ) . '::recache'; 297 $dbw = wfGetDB( DB_MASTER ); 298 if ( !$dbw ) { 299 return false; 300 } 301 302 try { 303 # Do query 304 $res = $this->reallyDoQuery( $limit, false ); 305 $num = false; 306 if ( $res ) { 307 $num = $res->numRows(); 308 # Fetch results 309 $vals = array(); 310 foreach ( $res as $row ) { 311 if ( isset( $row->value ) ) { 312 if ( $this->usesTimestamps() ) { 313 $value = wfTimestamp( TS_UNIX, 314 $row->value ); 315 } else { 316 $value = intval( $row->value ); // @bug 14414 317 } 318 } else { 319 $value = 0; 320 } 321 322 $vals[] = array( 'qc_type' => $this->getName(), 323 'qc_namespace' => $row->namespace, 324 'qc_title' => $row->title, 325 'qc_value' => $value ); 326 } 327 328 $dbw->startAtomic( __METHOD__ ); 329 # Clear out any old cached data 330 $dbw->delete( 'querycache', array( 'qc_type' => $this->getName() ), $fname ); 331 # Save results into the querycache table on the master 332 if ( count( $vals ) ) { 333 $dbw->insert( 'querycache', $vals, __METHOD__ ); 334 } 335 # Update the querycache_info record for the page 336 $dbw->delete( 'querycache_info', array( 'qci_type' => $this->getName() ), $fname ); 337 $dbw->insert( 'querycache_info', 338 array( 'qci_type' => $this->getName(), 'qci_timestamp' => $dbw->timestamp() ), 339 $fname ); 340 $dbw->endAtomic( __METHOD__ ); 341 } 342 } catch ( DBError $e ) { 343 if ( !$ignoreErrors ) { 344 throw $e; // report query error 345 } 346 $num = false; // set result to false to indicate error 347 } 348 349 return $num; 350 } 351 352 /** 353 * Get a DB connection to be used for slow recache queries 354 * @return DatabaseBase 355 */ 356 function getRecacheDB() { 357 return wfGetDB( DB_SLAVE, array( $this->getName(), 'QueryPage::recache', 'vslow' ) ); 358 } 359 360 /** 361 * Run the query and return the result 362 * @param int|bool $limit Numerical limit or false for no limit 363 * @param int|bool $offset Numerical offset or false for no offset 364 * @return ResultWrapper 365 * @since 1.18 366 */ 367 function reallyDoQuery( $limit, $offset = false ) { 368 $fname = get_class( $this ) . "::reallyDoQuery"; 369 $dbr = $this->getRecacheDB(); 370 $query = $this->getQueryInfo(); 371 $order = $this->getOrderFields(); 372 373 if ( $this->sortDescending() ) { 374 foreach ( $order as &$field ) { 375 $field .= ' DESC'; 376 } 377 } 378 379 if ( is_array( $query ) ) { 380 $tables = isset( $query['tables'] ) ? (array)$query['tables'] : array(); 381 $fields = isset( $query['fields'] ) ? (array)$query['fields'] : array(); 382 $conds = isset( $query['conds'] ) ? (array)$query['conds'] : array(); 383 $options = isset( $query['options'] ) ? (array)$query['options'] : array(); 384 $join_conds = isset( $query['join_conds'] ) ? (array)$query['join_conds'] : array(); 385 386 if ( count( $order ) ) { 387 $options['ORDER BY'] = $order; 388 } 389 390 if ( $limit !== false ) { 391 $options['LIMIT'] = intval( $limit ); 392 } 393 394 if ( $offset !== false ) { 395 $options['OFFSET'] = intval( $offset ); 396 } 397 398 $res = $dbr->select( $tables, $fields, $conds, $fname, 399 $options, $join_conds 400 ); 401 } else { 402 // Old-fashioned raw SQL style, deprecated 403 $sql = $this->getSQL(); 404 $sql .= ' ORDER BY ' . implode( ', ', $order ); 405 $sql = $dbr->limitResult( $sql, $limit, $offset ); 406 $res = $dbr->query( $sql, $fname ); 407 } 408 409 return $res; 410 } 411 412 /** 413 * Somewhat deprecated, you probably want to be using execute() 414 * @param int|bool $offset 415 * @param int|bool $limit 416 * @return ResultWrapper 417 */ 418 function doQuery( $offset = false, $limit = false ) { 419 if ( $this->isCached() && $this->isCacheable() ) { 420 return $this->fetchFromCache( $limit, $offset ); 421 } else { 422 return $this->reallyDoQuery( $limit, $offset ); 423 } 424 } 425 426 /** 427 * Fetch the query results from the query cache 428 * @param int|bool $limit Numerical limit or false for no limit 429 * @param int|bool $offset Numerical offset or false for no offset 430 * @return ResultWrapper 431 * @since 1.18 432 */ 433 function fetchFromCache( $limit, $offset = false ) { 434 $dbr = wfGetDB( DB_SLAVE ); 435 $options = array(); 436 if ( $limit !== false ) { 437 $options['LIMIT'] = intval( $limit ); 438 } 439 if ( $offset !== false ) { 440 $options['OFFSET'] = intval( $offset ); 441 } 442 if ( $this->sortDescending() ) { 443 $options['ORDER BY'] = 'qc_value DESC'; 444 } else { 445 $options['ORDER BY'] = 'qc_value ASC'; 446 } 447 $res = $dbr->select( 'querycache', array( 'qc_type', 448 'namespace' => 'qc_namespace', 449 'title' => 'qc_title', 450 'value' => 'qc_value' ), 451 array( 'qc_type' => $this->getName() ), 452 __METHOD__, $options 453 ); 454 return $dbr->resultObject( $res ); 455 } 456 457 public function getCachedTimestamp() { 458 if ( is_null( $this->cachedTimestamp ) ) { 459 $dbr = wfGetDB( DB_SLAVE ); 460 $fname = get_class( $this ) . '::getCachedTimestamp'; 461 $this->cachedTimestamp = $dbr->selectField( 'querycache_info', 'qci_timestamp', 462 array( 'qci_type' => $this->getName() ), $fname ); 463 } 464 return $this->cachedTimestamp; 465 } 466 467 /** 468 * This is the actual workhorse. It does everything needed to make a 469 * real, honest-to-gosh query page. 470 * @param string $par 471 */ 472 function execute( $par ) { 473 $user = $this->getUser(); 474 if ( !$this->userCanExecute( $user ) ) { 475 $this->displayRestrictionError(); 476 return; 477 } 478 479 $this->setHeaders(); 480 $this->outputHeader(); 481 482 $out = $this->getOutput(); 483 484 if ( $this->isCached() && !$this->isCacheable() ) { 485 $out->addWikiMsg( 'querypage-disabled' ); 486 return; 487 } 488 489 $out->setSyndicated( $this->isSyndicated() ); 490 491 if ( $this->limit == 0 && $this->offset == 0 ) { 492 list( $this->limit, $this->offset ) = $this->getRequest()->getLimitOffset(); 493 } 494 495 // @todo Use doQuery() 496 if ( !$this->isCached() ) { 497 # select one extra row for navigation 498 $res = $this->reallyDoQuery( $this->limit + 1, $this->offset ); 499 } else { 500 # Get the cached result, select one extra row for navigation 501 $res = $this->fetchFromCache( $this->limit + 1, $this->offset ); 502 if ( !$this->listoutput ) { 503 504 # Fetch the timestamp of this update 505 $ts = $this->getCachedTimestamp(); 506 $lang = $this->getLanguage(); 507 $maxResults = $lang->formatNum( $this->getConfig()->get( 'QueryCacheLimit' ) ); 508 509 if ( $ts ) { 510 $updated = $lang->userTimeAndDate( $ts, $user ); 511 $updateddate = $lang->userDate( $ts, $user ); 512 $updatedtime = $lang->userTime( $ts, $user ); 513 $out->addMeta( 'Data-Cache-Time', $ts ); 514 $out->addJsConfigVars( 'dataCacheTime', $ts ); 515 $out->addWikiMsg( 'perfcachedts', $updated, $updateddate, $updatedtime, $maxResults ); 516 } else { 517 $out->addWikiMsg( 'perfcached', $maxResults ); 518 } 519 520 # If updates on this page have been disabled, let the user know 521 # that the data set won't be refreshed for now 522 if ( is_array( $this->getConfig()->get( 'DisableQueryPageUpdate' ) ) 523 && in_array( $this->getName(), $this->getConfig()->get( 'DisableQueryPageUpdate' ) ) 524 ) { 525 $out->wrapWikiMsg( 526 "<div class=\"mw-querypage-no-updates\">\n$1\n</div>", 527 'querypage-no-updates' 528 ); 529 } 530 } 531 } 532 533 $this->numRows = $res->numRows(); 534 535 $dbr = wfGetDB( DB_SLAVE ); 536 $this->preprocessResults( $dbr, $res ); 537 538 $out->addHTML( Xml::openElement( 'div', array( 'class' => 'mw-spcontent' ) ) ); 539 540 # Top header and navigation 541 if ( $this->shownavigation ) { 542 $out->addHTML( $this->getPageHeader() ); 543 if ( $this->numRows > 0 ) { 544 $out->addHTML( $this->msg( 'showingresultsinrange' )->numParams( 545 min( $this->numRows, $this->limit ), # do not show the one extra row, if exist 546 $this->offset + 1, ( min( $this->numRows, $this->limit ) + $this->offset ) )->parseAsBlock() ); 547 # Disable the "next" link when we reach the end 548 $paging = $this->getLanguage()->viewPrevNext( $this->getPageTitle( $par ), $this->offset, 549 $this->limit, $this->linkParameters(), ( $this->numRows <= $this->limit ) ); 550 $out->addHTML( '<p>' . $paging . '</p>' ); 551 } else { 552 # No results to show, so don't bother with "showing X of Y" etc. 553 # -- just let the user know and give up now 554 $out->addWikiMsg( 'specialpage-empty' ); 555 $out->addHTML( Xml::closeElement( 'div' ) ); 556 return; 557 } 558 } 559 560 # The actual results; specialist subclasses will want to handle this 561 # with more than a straight list, so we hand them the info, plus 562 # an OutputPage, and let them get on with it 563 $this->outputResults( $out, 564 $this->getSkin(), 565 $dbr, # Should use a ResultWrapper for this 566 $res, 567 min( $this->numRows, $this->limit ), # do not format the one extra row, if exist 568 $this->offset ); 569 570 # Repeat the paging links at the bottom 571 if ( $this->shownavigation ) { 572 $out->addHTML( '<p>' . $paging . '</p>' ); 573 } 574 575 $out->addHTML( Xml::closeElement( 'div' ) ); 576 } 577 578 /** 579 * Format and output report results using the given information plus 580 * OutputPage 581 * 582 * @param OutputPage $out OutputPage to print to 583 * @param Skin $skin User skin to use 584 * @param DatabaseBase $dbr Database (read) connection to use 585 * @param ResultWrapper $res Result pointer 586 * @param int $num Number of available result rows 587 * @param int $offset Paging offset 588 */ 589 protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) { 590 global $wgContLang; 591 592 if ( $num > 0 ) { 593 $html = array(); 594 if ( !$this->listoutput ) { 595 $html[] = $this->openList( $offset ); 596 } 597 598 # $res might contain the whole 1,000 rows, so we read up to 599 # $num [should update this to use a Pager] 600 // @codingStandardsIgnoreStart Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed 601 for ( $i = 0; $i < $num && $row = $res->fetchObject(); $i++ ) { 602 // @codingStandardsIgnoreEnd 603 $line = $this->formatResult( $skin, $row ); 604 if ( $line ) { 605 $attr = ( isset( $row->usepatrol ) && $row->usepatrol && $row->patrolled == 0 ) 606 ? ' class="not-patrolled"' 607 : ''; 608 $html[] = $this->listoutput 609 ? $line 610 : "<li{$attr}>{$line}</li>\n"; 611 } 612 } 613 614 # Flush the final result 615 if ( $this->tryLastResult() ) { 616 $row = null; 617 $line = $this->formatResult( $skin, $row ); 618 if ( $line ) { 619 $attr = ( isset( $row->usepatrol ) && $row->usepatrol && $row->patrolled == 0 ) 620 ? ' class="not-patrolled"' 621 : ''; 622 $html[] = $this->listoutput 623 ? $line 624 : "<li{$attr}>{$line}</li>\n"; 625 } 626 } 627 628 if ( !$this->listoutput ) { 629 $html[] = $this->closeList(); 630 } 631 632 $html = $this->listoutput 633 ? $wgContLang->listToText( $html ) 634 : implode( '', $html ); 635 636 $out->addHTML( $html ); 637 } 638 } 639 640 /** 641 * @param int $offset 642 * @return string 643 */ 644 function openList( $offset ) { 645 return "\n<ol start='" . ( $offset + 1 ) . "' class='special'>\n"; 646 } 647 648 /** 649 * @return string 650 */ 651 function closeList() { 652 return "</ol>\n"; 653 } 654 655 /** 656 * Do any necessary preprocessing of the result object. 657 * @param DatabaseBase $db 658 * @param ResultWrapper $res 659 */ 660 function preprocessResults( $db, $res ) { 661 } 662 663 /** 664 * Similar to above, but packaging in a syndicated feed instead of a web page 665 * @param string $class 666 * @param int $limit 667 * @return bool 668 */ 669 function doFeed( $class = '', $limit = 50 ) { 670 if ( !$this->getConfig()->get( 'Feed' ) ) { 671 $this->getOutput()->addWikiMsg( 'feed-unavailable' ); 672 return false; 673 } 674 675 $limit = min( $limit, $this->getConfig()->get( 'FeedLimit' ) ); 676 677 $feedClasses = $this->getConfig()->get( 'FeedClasses' ); 678 if ( isset( $feedClasses[$class] ) ) { 679 /** @var RSSFeed|AtomFeed $feed */ 680 $feed = new $feedClasses[$class]( 681 $this->feedTitle(), 682 $this->feedDesc(), 683 $this->feedUrl() ); 684 $feed->outHeader(); 685 686 $res = $this->reallyDoQuery( $limit, 0 ); 687 foreach ( $res as $obj ) { 688 $item = $this->feedResult( $obj ); 689 if ( $item ) { 690 $feed->outItem( $item ); 691 } 692 } 693 694 $feed->outFooter(); 695 return true; 696 } else { 697 return false; 698 } 699 } 700 701 /** 702 * Override for custom handling. If the titles/links are ok, just do 703 * feedItemDesc() 704 * @param object $row 705 * @return FeedItem|null 706 */ 707 function feedResult( $row ) { 708 if ( !isset( $row->title ) ) { 709 return null; 710 } 711 $title = Title::makeTitle( intval( $row->namespace ), $row->title ); 712 if ( $title ) { 713 $date = isset( $row->timestamp ) ? $row->timestamp : ''; 714 $comments = ''; 715 if ( $title ) { 716 $talkpage = $title->getTalkPage(); 717 $comments = $talkpage->getFullURL(); 718 } 719 720 return new FeedItem( 721 $title->getPrefixedText(), 722 $this->feedItemDesc( $row ), 723 $title->getFullURL(), 724 $date, 725 $this->feedItemAuthor( $row ), 726 $comments ); 727 } else { 728 return null; 729 } 730 } 731 732 function feedItemDesc( $row ) { 733 return isset( $row->comment ) ? htmlspecialchars( $row->comment ) : ''; 734 } 735 736 function feedItemAuthor( $row ) { 737 return isset( $row->user_text ) ? $row->user_text : ''; 738 } 739 740 function feedTitle() { 741 $desc = $this->getDescription(); 742 $code = $this->getConfig()->get( 'LanguageCode' ); 743 $sitename = $this->getConfig()->get( 'Sitename' ); 744 return "$sitename - $desc [$code]"; 745 } 746 747 function feedDesc() { 748 return $this->msg( 'tagline' )->text(); 749 } 750 751 function feedUrl() { 752 return $this->getPageTitle()->getFullURL(); 753 } 754 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |