MediaWiki  REL1_23
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 ( !wfRunHooks( 'SpecialRecentChangesQuery',
00113             array( &$conds, &$tables, &$join_conds, $opts, &$query_options, &$select ),
00114             '1.23' )
00115         ) {
00116             return false;
00117         }
00118 
00119         if ( $ns == NS_CATEGORY && !$showlinkedto ) {
00120             // special handling for categories
00121             // XXX: should try to make this less kludgy
00122             $link_tables = array( 'categorylinks' );
00123             $showlinkedto = true;
00124         } else {
00125             // for now, always join on these tables; really should be configurable as in whatlinkshere
00126             $link_tables = array( 'pagelinks', 'templatelinks' );
00127             // imagelinks only contains links to pages in NS_FILE
00128             if ( $ns == NS_FILE || !$showlinkedto ) {
00129                 $link_tables[] = 'imagelinks';
00130             }
00131         }
00132 
00133         if ( $id == 0 && !$showlinkedto ) {
00134             return false; // nonexistent pages can't link to any pages
00135         }
00136 
00137         // field name prefixes for all the various tables we might want to join with
00138         $prefix = array(
00139             'pagelinks' => 'pl',
00140             'templatelinks' => 'tl',
00141             'categorylinks' => 'cl',
00142             'imagelinks' => 'il'
00143         );
00144 
00145         $subsql = array(); // SELECT statements to combine with UNION
00146 
00147         foreach ( $link_tables as $link_table ) {
00148             $pfx = $prefix[$link_table];
00149 
00150             // imagelinks and categorylinks tables have no xx_namespace field,
00151             // and have xx_to instead of xx_title
00152             if ( $link_table == 'imagelinks' ) {
00153                 $link_ns = NS_FILE;
00154             } elseif ( $link_table == 'categorylinks' ) {
00155                 $link_ns = NS_CATEGORY;
00156             } else {
00157                 $link_ns = 0;
00158             }
00159 
00160             if ( $showlinkedto ) {
00161                 // find changes to pages linking to this page
00162                 if ( $link_ns ) {
00163                     if ( $ns != $link_ns ) {
00164                         continue;
00165                     } // should never happen, but check anyway
00166                     $subconds = array( "{$pfx}_to" => $dbkey );
00167                 } else {
00168                     $subconds = array( "{$pfx}_namespace" => $ns, "{$pfx}_title" => $dbkey );
00169                 }
00170                 $subjoin = "rc_cur_id = {$pfx}_from";
00171             } else {
00172                 // find changes to pages linked from this page
00173                 $subconds = array( "{$pfx}_from" => $id );
00174                 if ( $link_table == 'imagelinks' || $link_table == 'categorylinks' ) {
00175                     $subconds["rc_namespace"] = $link_ns;
00176                     $subjoin = "rc_title = {$pfx}_to";
00177                 } else {
00178                     $subjoin = array( "rc_namespace = {$pfx}_namespace", "rc_title = {$pfx}_title" );
00179                 }
00180             }
00181 
00182             if ( $dbr->unionSupportsOrderAndLimit() ) {
00183                 $order = array( 'ORDER BY' => 'rc_timestamp DESC' );
00184             } else {
00185                 $order = array();
00186             }
00187 
00188             $query = $dbr->selectSQLText(
00189                 array_merge( $tables, array( $link_table ) ),
00190                 $select,
00191                 $conds + $subconds,
00192                 __METHOD__,
00193                 $order + $query_options,
00194                 $join_conds + array( $link_table => array( 'INNER JOIN', $subjoin ) )
00195             );
00196 
00197             if ( $dbr->unionSupportsOrderAndLimit() ) {
00198                 $query = $dbr->limitResult( $query, $limit );
00199             }
00200 
00201             $subsql[] = $query;
00202         }
00203 
00204         if ( count( $subsql ) == 0 ) {
00205             return false; // should never happen
00206         }
00207         if ( count( $subsql ) == 1 && $dbr->unionSupportsOrderAndLimit() ) {
00208             $sql = $subsql[0];
00209         } else {
00210             // need to resort and relimit after union
00211             $sql = $dbr->unionQueries( $subsql, false ) . ' ORDER BY rc_timestamp DESC';
00212             $sql = $dbr->limitResult( $sql, $limit, false );
00213         }
00214 
00215         $res = $dbr->query( $sql, __METHOD__ );
00216 
00217         if ( $res->numRows() == 0 ) {
00218             $this->mResultEmpty = true;
00219         }
00220 
00221         return $res;
00222     }
00223 
00224     function setTopText( FormOptions $opts ) {
00225         $target = $this->getTargetTitle();
00226         if ( $target ) {
00227             $this->getOutput()->addBacklinkSubtitle( $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 }