MediaWiki  REL1_24
SpecialRecentchangeslinked.php
Go to the documentation of this file.
00001 <?php
00029 class SpecialRecentChangesLinked extends SpecialRecentChanges {
00031     protected $rclTargetTitle;
00032 
00033     function __construct() {
00034         parent::__construct( 'Recentchangeslinked' );
00035     }
00036 
00037     public function getDefaultOptions() {
00038         $opts = parent::getDefaultOptions();
00039         $opts->add( 'target', '' );
00040         $opts->add( 'showlinkedto', false );
00041 
00042         return $opts;
00043     }
00044 
00045     public function parseParameters( $par, FormOptions $opts ) {
00046         $opts['target'] = $par;
00047     }
00048 
00049     public function doMainQuery( $conds, $opts ) {
00050         $target = $opts['target'];
00051         $showlinkedto = $opts['showlinkedto'];
00052         $limit = $opts['limit'];
00053 
00054         if ( $target === '' ) {
00055             return false;
00056         }
00057         $outputPage = $this->getOutput();
00058         $title = Title::newFromURL( $target );
00059         if ( !$title || $title->isExternal() ) {
00060             $outputPage->addHtml( '<div class="errorbox">' . $this->msg( 'allpagesbadtitle' )
00061                     ->parse() . '</div>' );
00062 
00063             return false;
00064         }
00065 
00066         $outputPage->setPageTitle( $this->msg( 'recentchangeslinked-title', $title->getPrefixedText() ) );
00067 
00068         /*
00069          * Ordinary links are in the pagelinks table, while transclusions are
00070          * in the templatelinks table, categorizations in categorylinks and
00071          * image use in imagelinks.  We need to somehow combine all these.
00072          * Special:Whatlinkshere does this by firing multiple queries and
00073          * merging the results, but the code we inherit from our parent class
00074          * expects only one result set so we use UNION instead.
00075          */
00076 
00077         $dbr = wfGetDB( DB_SLAVE, 'recentchangeslinked' );
00078         $id = $title->getArticleID();
00079         $ns = $title->getNamespace();
00080         $dbkey = $title->getDBkey();
00081 
00082         $tables = array( 'recentchanges' );
00083         $select = RecentChange::selectFields();
00084         $join_conds = array();
00085         $query_options = array();
00086 
00087         // left join with watchlist table to highlight watched rows
00088         $uid = $this->getUser()->getId();
00089         if ( $uid && $this->getUser()->isAllowed( 'viewmywatchlist' ) ) {
00090             $tables[] = 'watchlist';
00091             $select[] = 'wl_user';
00092             $join_conds['watchlist'] = array( 'LEFT JOIN', array(
00093                 'wl_user' => $uid,
00094                 'wl_title=rc_title',
00095                 'wl_namespace=rc_namespace'
00096             ) );
00097         }
00098         if ( $this->getUser()->isAllowed( 'rollback' ) ) {
00099             $tables[] = 'page';
00100             $join_conds['page'] = array( 'LEFT JOIN', 'rc_cur_id=page_id' );
00101             $select[] = 'page_latest';
00102         }
00103         ChangeTags::modifyDisplayQuery(
00104             $tables,
00105             $select,
00106             $conds,
00107             $join_conds,
00108             $query_options,
00109             $opts['tagfilter']
00110         );
00111 
00112         if ( !$this->runMainQueryHook( $tables, $select, $conds, $query_options, $join_conds,
00113             $opts )
00114         ) {
00115             return false;
00116         }
00117 
00118         if ( $ns == NS_CATEGORY && !$showlinkedto ) {
00119             // special handling for categories
00120             // XXX: should try to make this less kludgy
00121             $link_tables = array( 'categorylinks' );
00122             $showlinkedto = true;
00123         } else {
00124             // for now, always join on these tables; really should be configurable as in whatlinkshere
00125             $link_tables = array( 'pagelinks', 'templatelinks' );
00126             // imagelinks only contains links to pages in NS_FILE
00127             if ( $ns == NS_FILE || !$showlinkedto ) {
00128                 $link_tables[] = 'imagelinks';
00129             }
00130         }
00131 
00132         if ( $id == 0 && !$showlinkedto ) {
00133             return false; // nonexistent pages can't link to any pages
00134         }
00135 
00136         // field name prefixes for all the various tables we might want to join with
00137         $prefix = array(
00138             'pagelinks' => 'pl',
00139             'templatelinks' => 'tl',
00140             'categorylinks' => 'cl',
00141             'imagelinks' => 'il'
00142         );
00143 
00144         $subsql = array(); // SELECT statements to combine with UNION
00145 
00146         foreach ( $link_tables as $link_table ) {
00147             $pfx = $prefix[$link_table];
00148 
00149             // imagelinks and categorylinks tables have no xx_namespace field,
00150             // and have xx_to instead of xx_title
00151             if ( $link_table == 'imagelinks' ) {
00152                 $link_ns = NS_FILE;
00153             } elseif ( $link_table == 'categorylinks' ) {
00154                 $link_ns = NS_CATEGORY;
00155             } else {
00156                 $link_ns = 0;
00157             }
00158 
00159             if ( $showlinkedto ) {
00160                 // find changes to pages linking to this page
00161                 if ( $link_ns ) {
00162                     if ( $ns != $link_ns ) {
00163                         continue;
00164                     } // should never happen, but check anyway
00165                     $subconds = array( "{$pfx}_to" => $dbkey );
00166                 } else {
00167                     $subconds = array( "{$pfx}_namespace" => $ns, "{$pfx}_title" => $dbkey );
00168                 }
00169                 $subjoin = "rc_cur_id = {$pfx}_from";
00170             } else {
00171                 // find changes to pages linked from this page
00172                 $subconds = array( "{$pfx}_from" => $id );
00173                 if ( $link_table == 'imagelinks' || $link_table == 'categorylinks' ) {
00174                     $subconds["rc_namespace"] = $link_ns;
00175                     $subjoin = "rc_title = {$pfx}_to";
00176                 } else {
00177                     $subjoin = array( "rc_namespace = {$pfx}_namespace", "rc_title = {$pfx}_title" );
00178                 }
00179             }
00180 
00181             if ( $dbr->unionSupportsOrderAndLimit() ) {
00182                 $order = array( 'ORDER BY' => 'rc_timestamp DESC' );
00183             } else {
00184                 $order = array();
00185             }
00186 
00187             $query = $dbr->selectSQLText(
00188                 array_merge( $tables, array( $link_table ) ),
00189                 $select,
00190                 $conds + $subconds,
00191                 __METHOD__,
00192                 $order + $query_options,
00193                 $join_conds + array( $link_table => array( 'INNER JOIN', $subjoin ) )
00194             );
00195 
00196             if ( $dbr->unionSupportsOrderAndLimit() ) {
00197                 $query = $dbr->limitResult( $query, $limit );
00198             }
00199 
00200             $subsql[] = $query;
00201         }
00202 
00203         if ( count( $subsql ) == 0 ) {
00204             return false; // should never happen
00205         }
00206         if ( count( $subsql ) == 1 && $dbr->unionSupportsOrderAndLimit() ) {
00207             $sql = $subsql[0];
00208         } else {
00209             // need to resort and relimit after union
00210             $sql = $dbr->unionQueries( $subsql, false ) . ' ORDER BY rc_timestamp DESC';
00211             $sql = $dbr->limitResult( $sql, $limit, false );
00212         }
00213 
00214         $res = $dbr->query( $sql, __METHOD__ );
00215 
00216         if ( $res->numRows() == 0 ) {
00217             $this->mResultEmpty = true;
00218         }
00219 
00220         return $res;
00221     }
00222 
00223     function setTopText( FormOptions $opts ) {
00224         $target = $this->getTargetTitle();
00225         if ( $target ) {
00226             $this->getOutput()->addBacklinkSubtitle( $target );
00227             $this->getSkin()->setRelevantTitle( $target );
00228         }
00229     }
00230 
00237     function getExtraOptions( $opts ) {
00238         $extraOpts = parent::getExtraOptions( $opts );
00239 
00240         $opts->consumeValues( array( 'showlinkedto', 'target' ) );
00241 
00242         $extraOpts['target'] = array( $this->msg( 'recentchangeslinked-page' )->escaped(),
00243             Xml::input( 'target', 40, str_replace( '_', ' ', $opts['target'] ) ) .
00244             Xml::check( 'showlinkedto', $opts['showlinkedto'], array( 'id' => 'showlinkedto' ) ) . ' ' .
00245             Xml::label( $this->msg( 'recentchangeslinked-to' )->text(), 'showlinkedto' ) );
00246 
00247         return $extraOpts;
00248     }
00249 
00253     function getTargetTitle() {
00254         if ( $this->rclTargetTitle === null ) {
00255             $opts = $this->getOptions();
00256             if ( isset( $opts['target'] ) && $opts['target'] !== '' ) {
00257                 $this->rclTargetTitle = Title::newFromText( $opts['target'] );
00258             } else {
00259                 $this->rclTargetTitle = false;
00260             }
00261         }
00262 
00263         return $this->rclTargetTitle;
00264     }
00265 }