[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/specialpage/ -> QueryPage.php (source)

   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  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1