MediaWiki  REL1_22
LinksUpdate.php
Go to the documentation of this file.
00001 <?php
00028 class LinksUpdate extends SqlDataUpdate {
00029 
00030     // @todo make members protected, but make sure extensions don't break
00031 
00032     public $mId,         
00033         $mTitle,         
00034         $mParserOutput,  
00035         $mLinks,         
00036         $mImages,        
00037         $mTemplates,     
00038         $mExternals,     
00039         $mCategories,    
00040         $mInterlangs,    
00041         $mProperties,    
00042         $mDb,            
00043         $mOptions,       
00044         $mRecursive;     
00045 
00049     private $linkInsertions = null;
00050 
00054     private $linkDeletions = null;
00055 
00064     function __construct( $title, $parserOutput, $recursive = true ) {
00065         parent::__construct( false ); // no implicit transaction
00066 
00067         if ( !( $title instanceof Title ) ) {
00068             throw new MWException( "The calling convention to LinksUpdate::LinksUpdate() has changed. " .
00069                 "Please see Article::editUpdates() for an invocation example.\n" );
00070         }
00071 
00072         if ( !( $parserOutput instanceof ParserOutput ) ) {
00073             throw new MWException( "The calling convention to LinksUpdate::__construct() has changed. " .
00074                 "Please see WikiPage::doEditUpdates() for an invocation example.\n" );
00075         }
00076 
00077         $this->mTitle = $title;
00078         $this->mId = $title->getArticleID();
00079 
00080         if ( !$this->mId ) {
00081             throw new MWException( "The Title object did not provide an article ID. Perhaps the page doesn't exist?" );
00082         }
00083 
00084         $this->mParserOutput = $parserOutput;
00085 
00086         $this->mLinks = $parserOutput->getLinks();
00087         $this->mImages = $parserOutput->getImages();
00088         $this->mTemplates = $parserOutput->getTemplates();
00089         $this->mExternals = $parserOutput->getExternalLinks();
00090         $this->mCategories = $parserOutput->getCategories();
00091         $this->mProperties = $parserOutput->getProperties();
00092         $this->mInterwikis = $parserOutput->getInterwikiLinks();
00093 
00094         # Convert the format of the interlanguage links
00095         # I didn't want to change it in the ParserOutput, because that array is passed all
00096         # the way back to the skin, so either a skin API break would be required, or an
00097         # inefficient back-conversion.
00098         $ill = $parserOutput->getLanguageLinks();
00099         $this->mInterlangs = array();
00100         foreach ( $ill as $link ) {
00101             list( $key, $title ) = explode( ':', $link, 2 );
00102             $this->mInterlangs[$key] = $title;
00103         }
00104 
00105         foreach ( $this->mCategories as &$sortkey ) {
00106             # If the sortkey is longer then 255 bytes,
00107             # it truncated by DB, and then doesn't get
00108             # matched when comparing existing vs current
00109             # categories, causing bug 25254.
00110             # Also. substr behaves weird when given "".
00111             if ( $sortkey !== '' ) {
00112                 $sortkey = substr( $sortkey, 0, 255 );
00113             }
00114         }
00115 
00116         $this->mRecursive = $recursive;
00117 
00118         wfRunHooks( 'LinksUpdateConstructed', array( &$this ) );
00119     }
00120 
00124     public function doUpdate() {
00125         wfRunHooks( 'LinksUpdate', array( &$this ) );
00126         $this->doIncrementalUpdate();
00127         wfRunHooks( 'LinksUpdateComplete', array( &$this ) );
00128     }
00129 
00130     protected function doIncrementalUpdate() {
00131         wfProfileIn( __METHOD__ );
00132 
00133         # Page links
00134         $existing = $this->getExistingLinks();
00135         $this->linkDeletions = $this->getLinkDeletions( $existing );
00136         $this->linkInsertions = $this->getLinkInsertions( $existing );
00137         $this->incrTableUpdate( 'pagelinks', 'pl', $this->linkDeletions, $this->linkInsertions );
00138 
00139         # Image links
00140         $existing = $this->getExistingImages();
00141 
00142         $imageDeletes = $this->getImageDeletions( $existing );
00143         $this->incrTableUpdate( 'imagelinks', 'il', $imageDeletes,
00144             $this->getImageInsertions( $existing ) );
00145 
00146         # Invalidate all image description pages which had links added or removed
00147         $imageUpdates = $imageDeletes + array_diff_key( $this->mImages, $existing );
00148         $this->invalidateImageDescriptions( $imageUpdates );
00149 
00150         # External links
00151         $existing = $this->getExistingExternals();
00152         $this->incrTableUpdate( 'externallinks', 'el', $this->getExternalDeletions( $existing ),
00153             $this->getExternalInsertions( $existing ) );
00154 
00155         # Language links
00156         $existing = $this->getExistingInterlangs();
00157         $this->incrTableUpdate( 'langlinks', 'll', $this->getInterlangDeletions( $existing ),
00158             $this->getInterlangInsertions( $existing ) );
00159 
00160         # Inline interwiki links
00161         $existing = $this->getExistingInterwikis();
00162         $this->incrTableUpdate( 'iwlinks', 'iwl', $this->getInterwikiDeletions( $existing ),
00163             $this->getInterwikiInsertions( $existing ) );
00164 
00165         # Template links
00166         $existing = $this->getExistingTemplates();
00167         $this->incrTableUpdate( 'templatelinks', 'tl', $this->getTemplateDeletions( $existing ),
00168             $this->getTemplateInsertions( $existing ) );
00169 
00170         # Category links
00171         $existing = $this->getExistingCategories();
00172 
00173         $categoryDeletes = $this->getCategoryDeletions( $existing );
00174 
00175         $this->incrTableUpdate( 'categorylinks', 'cl', $categoryDeletes,
00176             $this->getCategoryInsertions( $existing ) );
00177 
00178         # Invalidate all categories which were added, deleted or changed (set symmetric difference)
00179         $categoryInserts = array_diff_assoc( $this->mCategories, $existing );
00180         $categoryUpdates = $categoryInserts + $categoryDeletes;
00181         $this->invalidateCategories( $categoryUpdates );
00182         $this->updateCategoryCounts( $categoryInserts, $categoryDeletes );
00183 
00184         # Page properties
00185         $existing = $this->getExistingProperties();
00186 
00187         $propertiesDeletes = $this->getPropertyDeletions( $existing );
00188 
00189         $this->incrTableUpdate( 'page_props', 'pp', $propertiesDeletes,
00190             $this->getPropertyInsertions( $existing ) );
00191 
00192         # Invalidate the necessary pages
00193         $changed = $propertiesDeletes + array_diff_assoc( $this->mProperties, $existing );
00194         $this->invalidateProperties( $changed );
00195 
00196         # Refresh links of all pages including this page
00197         # This will be in a separate transaction
00198         if ( $this->mRecursive ) {
00199             $this->queueRecursiveJobs();
00200         }
00201 
00202         wfProfileOut( __METHOD__ );
00203     }
00204 
00211     function queueRecursiveJobs() {
00212         self::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' );
00213     }
00214 
00221     public static function queueRecursiveJobsForTable( Title $title, $table ) {
00222         wfProfileIn( __METHOD__ );
00223         if ( $title->getBacklinkCache()->hasLinks( $table ) ) {
00224             $job = new RefreshLinksJob2(
00225                 $title,
00226                 array(
00227                     'table' => $table,
00228                 ) + Job::newRootJobParams( // "overall" refresh links job info
00229                     "refreshlinks:{$table}:{$title->getPrefixedText()}"
00230                 )
00231             );
00232             JobQueueGroup::singleton()->push( $job );
00233             JobQueueGroup::singleton()->deduplicateRootJob( $job );
00234         }
00235         wfProfileOut( __METHOD__ );
00236     }
00237 
00241     function invalidateCategories( $cats ) {
00242         $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) );
00243     }
00244 
00250     function updateCategoryCounts( $added, $deleted ) {
00251         $a = WikiPage::factory( $this->mTitle );
00252         $a->updateCategoryCounts(
00253             array_keys( $added ), array_keys( $deleted )
00254         );
00255     }
00256 
00260     function invalidateImageDescriptions( $images ) {
00261         $this->invalidatePages( NS_FILE, array_keys( $images ) );
00262     }
00263 
00271     function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
00272         if ( $table == 'page_props' ) {
00273             $fromField = 'pp_page';
00274         } else {
00275             $fromField = "{$prefix}_from";
00276         }
00277         $where = array( $fromField => $this->mId );
00278         if ( $table == 'pagelinks' || $table == 'templatelinks' || $table == 'iwlinks' ) {
00279             if ( $table == 'iwlinks' ) {
00280                 $baseKey = 'iwl_prefix';
00281             } else {
00282                 $baseKey = "{$prefix}_namespace";
00283             }
00284             $clause = $this->mDb->makeWhereFrom2d( $deletions, $baseKey, "{$prefix}_title" );
00285             if ( $clause ) {
00286                 $where[] = $clause;
00287             } else {
00288                 $where = false;
00289             }
00290         } else {
00291             if ( $table == 'langlinks' ) {
00292                 $toField = 'll_lang';
00293             } elseif ( $table == 'page_props' ) {
00294                 $toField = 'pp_propname';
00295             } else {
00296                 $toField = $prefix . '_to';
00297             }
00298             if ( count( $deletions ) ) {
00299                 $where[] = "$toField IN (" . $this->mDb->makeList( array_keys( $deletions ) ) . ')';
00300             } else {
00301                 $where = false;
00302             }
00303         }
00304         if ( $where ) {
00305             $this->mDb->delete( $table, $where, __METHOD__ );
00306         }
00307         if ( count( $insertions ) ) {
00308             $this->mDb->insert( $table, $insertions, __METHOD__, 'IGNORE' );
00309             wfRunHooks( 'LinksUpdateAfterInsert', array( $this, $table, $insertions ) );
00310         }
00311     }
00312 
00319     private function getLinkInsertions( $existing = array() ) {
00320         $arr = array();
00321         foreach ( $this->mLinks as $ns => $dbkeys ) {
00322             $diffs = isset( $existing[$ns] )
00323                 ? array_diff_key( $dbkeys, $existing[$ns] )
00324                 : $dbkeys;
00325             foreach ( $diffs as $dbk => $id ) {
00326                 $arr[] = array(
00327                     'pl_from' => $this->mId,
00328                     'pl_namespace' => $ns,
00329                     'pl_title' => $dbk
00330                 );
00331             }
00332         }
00333         return $arr;
00334     }
00335 
00341     private function getTemplateInsertions( $existing = array() ) {
00342         $arr = array();
00343         foreach ( $this->mTemplates as $ns => $dbkeys ) {
00344             $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys;
00345             foreach ( $diffs as $dbk => $id ) {
00346                 $arr[] = array(
00347                     'tl_from' => $this->mId,
00348                     'tl_namespace' => $ns,
00349                     'tl_title' => $dbk
00350                 );
00351             }
00352         }
00353         return $arr;
00354     }
00355 
00362     private function getImageInsertions( $existing = array() ) {
00363         $arr = array();
00364         $diffs = array_diff_key( $this->mImages, $existing );
00365         foreach ( $diffs as $iname => $dummy ) {
00366             $arr[] = array(
00367                 'il_from' => $this->mId,
00368                 'il_to' => $iname
00369             );
00370         }
00371         return $arr;
00372     }
00373 
00379     private function getExternalInsertions( $existing = array() ) {
00380         $arr = array();
00381         $diffs = array_diff_key( $this->mExternals, $existing );
00382         foreach ( $diffs as $url => $dummy ) {
00383             foreach ( wfMakeUrlIndexes( $url ) as $index ) {
00384                 $arr[] = array(
00385                     'el_id' => $this->mDb->nextSequenceValue( 'externallinks_el_id_seq' ),
00386                     'el_from' => $this->mId,
00387                     'el_to' => $url,
00388                     'el_index' => $index,
00389                 );
00390             }
00391         }
00392         return $arr;
00393     }
00394 
00403     private function getCategoryInsertions( $existing = array() ) {
00404         global $wgContLang, $wgCategoryCollation;
00405         $diffs = array_diff_assoc( $this->mCategories, $existing );
00406         $arr = array();
00407         foreach ( $diffs as $name => $prefix ) {
00408             $nt = Title::makeTitleSafe( NS_CATEGORY, $name );
00409             $wgContLang->findVariantLink( $name, $nt, true );
00410 
00411             if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
00412                 $type = 'subcat';
00413             } elseif ( $this->mTitle->getNamespace() == NS_FILE ) {
00414                 $type = 'file';
00415             } else {
00416                 $type = 'page';
00417             }
00418 
00419             # Treat custom sortkeys as a prefix, so that if multiple
00420             # things are forced to sort as '*' or something, they'll
00421             # sort properly in the category rather than in page_id
00422             # order or such.
00423             $sortkey = Collation::singleton()->getSortKey(
00424                 $this->mTitle->getCategorySortkey( $prefix ) );
00425 
00426             $arr[] = array(
00427                 'cl_from' => $this->mId,
00428                 'cl_to' => $name,
00429                 'cl_sortkey' => $sortkey,
00430                 'cl_timestamp' => $this->mDb->timestamp(),
00431                 'cl_sortkey_prefix' => $prefix,
00432                 'cl_collation' => $wgCategoryCollation,
00433                 'cl_type' => $type,
00434             );
00435         }
00436         return $arr;
00437     }
00438 
00446     private function getInterlangInsertions( $existing = array() ) {
00447         $diffs = array_diff_assoc( $this->mInterlangs, $existing );
00448         $arr = array();
00449         foreach ( $diffs as $lang => $title ) {
00450             $arr[] = array(
00451                 'll_from' => $this->mId,
00452                 'll_lang' => $lang,
00453                 'll_title' => $title
00454             );
00455         }
00456         return $arr;
00457     }
00458 
00464     function getPropertyInsertions( $existing = array() ) {
00465         $diffs = array_diff_assoc( $this->mProperties, $existing );
00466         $arr = array();
00467         foreach ( $diffs as $name => $value ) {
00468             $arr[] = array(
00469                 'pp_page' => $this->mId,
00470                 'pp_propname' => $name,
00471                 'pp_value' => $value,
00472             );
00473         }
00474         return $arr;
00475     }
00476 
00483     private function getInterwikiInsertions( $existing = array() ) {
00484         $arr = array();
00485         foreach ( $this->mInterwikis as $prefix => $dbkeys ) {
00486             $diffs = isset( $existing[$prefix] ) ? array_diff_key( $dbkeys, $existing[$prefix] ) : $dbkeys;
00487             foreach ( $diffs as $dbk => $id ) {
00488                 $arr[] = array(
00489                     'iwl_from' => $this->mId,
00490                     'iwl_prefix' => $prefix,
00491                     'iwl_title' => $dbk
00492                 );
00493             }
00494         }
00495         return $arr;
00496     }
00497 
00504     private function getLinkDeletions( $existing ) {
00505         $del = array();
00506         foreach ( $existing as $ns => $dbkeys ) {
00507             if ( isset( $this->mLinks[$ns] ) ) {
00508                 $del[$ns] = array_diff_key( $existing[$ns], $this->mLinks[$ns] );
00509             } else {
00510                 $del[$ns] = $existing[$ns];
00511             }
00512         }
00513         return $del;
00514     }
00515 
00522     private function getTemplateDeletions( $existing ) {
00523         $del = array();
00524         foreach ( $existing as $ns => $dbkeys ) {
00525             if ( isset( $this->mTemplates[$ns] ) ) {
00526                 $del[$ns] = array_diff_key( $existing[$ns], $this->mTemplates[$ns] );
00527             } else {
00528                 $del[$ns] = $existing[$ns];
00529             }
00530         }
00531         return $del;
00532     }
00533 
00540     private function getImageDeletions( $existing ) {
00541         return array_diff_key( $existing, $this->mImages );
00542     }
00543 
00550     private function getExternalDeletions( $existing ) {
00551         return array_diff_key( $existing, $this->mExternals );
00552     }
00553 
00560     private function getCategoryDeletions( $existing ) {
00561         return array_diff_assoc( $existing, $this->mCategories );
00562     }
00563 
00570     private function getInterlangDeletions( $existing ) {
00571         return array_diff_assoc( $existing, $this->mInterlangs );
00572     }
00573 
00579     function getPropertyDeletions( $existing ) {
00580         return array_diff_assoc( $existing, $this->mProperties );
00581     }
00582 
00589     private function getInterwikiDeletions( $existing ) {
00590         $del = array();
00591         foreach ( $existing as $prefix => $dbkeys ) {
00592             if ( isset( $this->mInterwikis[$prefix] ) ) {
00593                 $del[$prefix] = array_diff_key( $existing[$prefix], $this->mInterwikis[$prefix] );
00594             } else {
00595                 $del[$prefix] = $existing[$prefix];
00596             }
00597         }
00598         return $del;
00599     }
00600 
00606     private function getExistingLinks() {
00607         $res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ),
00608             array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions );
00609         $arr = array();
00610         foreach ( $res as $row ) {
00611             if ( !isset( $arr[$row->pl_namespace] ) ) {
00612                 $arr[$row->pl_namespace] = array();
00613             }
00614             $arr[$row->pl_namespace][$row->pl_title] = 1;
00615         }
00616         return $arr;
00617     }
00618 
00624     private function getExistingTemplates() {
00625         $res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ),
00626             array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions );
00627         $arr = array();
00628         foreach ( $res as $row ) {
00629             if ( !isset( $arr[$row->tl_namespace] ) ) {
00630                 $arr[$row->tl_namespace] = array();
00631             }
00632             $arr[$row->tl_namespace][$row->tl_title] = 1;
00633         }
00634         return $arr;
00635     }
00636 
00642     private function getExistingImages() {
00643         $res = $this->mDb->select( 'imagelinks', array( 'il_to' ),
00644             array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions );
00645         $arr = array();
00646         foreach ( $res as $row ) {
00647             $arr[$row->il_to] = 1;
00648         }
00649         return $arr;
00650     }
00651 
00657     private function getExistingExternals() {
00658         $res = $this->mDb->select( 'externallinks', array( 'el_to' ),
00659             array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions );
00660         $arr = array();
00661         foreach ( $res as $row ) {
00662             $arr[$row->el_to] = 1;
00663         }
00664         return $arr;
00665     }
00666 
00672     private function getExistingCategories() {
00673         $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey_prefix' ),
00674             array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions );
00675         $arr = array();
00676         foreach ( $res as $row ) {
00677             $arr[$row->cl_to] = $row->cl_sortkey_prefix;
00678         }
00679         return $arr;
00680     }
00681 
00688     private function getExistingInterlangs() {
00689         $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ),
00690             array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions );
00691         $arr = array();
00692         foreach ( $res as $row ) {
00693             $arr[$row->ll_lang] = $row->ll_title;
00694         }
00695         return $arr;
00696     }
00697 
00702     protected function getExistingInterwikis() {
00703         $res = $this->mDb->select( 'iwlinks', array( 'iwl_prefix', 'iwl_title' ),
00704             array( 'iwl_from' => $this->mId ), __METHOD__, $this->mOptions );
00705         $arr = array();
00706         foreach ( $res as $row ) {
00707             if ( !isset( $arr[$row->iwl_prefix] ) ) {
00708                 $arr[$row->iwl_prefix] = array();
00709             }
00710             $arr[$row->iwl_prefix][$row->iwl_title] = 1;
00711         }
00712         return $arr;
00713     }
00714 
00720     private function getExistingProperties() {
00721         $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ),
00722             array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions );
00723         $arr = array();
00724         foreach ( $res as $row ) {
00725             $arr[$row->pp_propname] = $row->pp_value;
00726         }
00727         return $arr;
00728     }
00729 
00734     public function getTitle() {
00735         return $this->mTitle;
00736     }
00737 
00743     public function getParserOutput() {
00744         return $this->mParserOutput;
00745     }
00746 
00751     public function getImages() {
00752         return $this->mImages;
00753     }
00754 
00759     private function invalidateProperties( $changed ) {
00760         global $wgPagePropLinkInvalidations;
00761 
00762         foreach ( $changed as $name => $value ) {
00763             if ( isset( $wgPagePropLinkInvalidations[$name] ) ) {
00764                 $inv = $wgPagePropLinkInvalidations[$name];
00765                 if ( !is_array( $inv ) ) {
00766                     $inv = array( $inv );
00767                 }
00768                 foreach ( $inv as $table ) {
00769                     $update = new HTMLCacheUpdate( $this->mTitle, $table );
00770                     $update->doUpdate();
00771                 }
00772             }
00773         }
00774     }
00775 
00781     public function getAddedLinks() {
00782         if ( $this->linkInsertions === null ) {
00783             return null;
00784         }
00785         $result = array();
00786         foreach ( $this->linkInsertions as $insertion ) {
00787             $result[] = Title::makeTitle( $insertion[ 'pl_namespace' ], $insertion[ 'pl_title' ] );
00788         }
00789         return $result;
00790     }
00791 
00797     public function getRemovedLinks() {
00798         if ( $this->linkDeletions === null ) {
00799             return null;
00800         }
00801         $result = array();
00802         foreach ( $this->linkDeletions as $ns => $titles ) {
00803             foreach ( $titles as $title => $unused ) {
00804                 $result[] = Title::makeTitle( $ns, $title );
00805             }
00806         }
00807         return $result;
00808     }
00809 }
00810 
00814 class LinksDeletionUpdate extends SqlDataUpdate {
00815 
00816     protected $mPage;     
00817 
00824     function __construct( WikiPage $page ) {
00825         parent::__construct( false ); // no implicit transaction
00826 
00827         $this->mPage = $page;
00828 
00829         if ( !$page->exists() ) {
00830             throw new MWException( "Page ID not known, perhaps the page doesn't exist?" );
00831         }
00832     }
00833 
00837     public function doUpdate() {
00838         $title = $this->mPage->getTitle();
00839         $id = $this->mPage->getId();
00840 
00841         # Delete restrictions for it
00842         $this->mDb->delete( 'page_restrictions', array( 'pr_page' => $id ), __METHOD__ );
00843 
00844         # Fix category table counts
00845         $cats = array();
00846         $res = $this->mDb->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ );
00847 
00848         foreach ( $res as $row ) {
00849             $cats[] = $row->cl_to;
00850         }
00851 
00852         $this->mPage->updateCategoryCounts( array(), $cats );
00853 
00854         # If using cascading deletes, we can skip some explicit deletes
00855         if ( !$this->mDb->cascadingDeletes() ) {
00856             # Delete outgoing links
00857             $this->mDb->delete( 'pagelinks', array( 'pl_from' => $id ), __METHOD__ );
00858             $this->mDb->delete( 'imagelinks', array( 'il_from' => $id ), __METHOD__ );
00859             $this->mDb->delete( 'categorylinks', array( 'cl_from' => $id ), __METHOD__ );
00860             $this->mDb->delete( 'templatelinks', array( 'tl_from' => $id ), __METHOD__ );
00861             $this->mDb->delete( 'externallinks', array( 'el_from' => $id ), __METHOD__ );
00862             $this->mDb->delete( 'langlinks', array( 'll_from' => $id ), __METHOD__ );
00863             $this->mDb->delete( 'iwlinks', array( 'iwl_from' => $id ), __METHOD__ );
00864             $this->mDb->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ );
00865             $this->mDb->delete( 'page_props', array( 'pp_page' => $id ), __METHOD__ );
00866         }
00867 
00868         # If using cleanup triggers, we can skip some manual deletes
00869         if ( !$this->mDb->cleanupTriggers() ) {
00870             # Clean up recentchanges entries...
00871             $this->mDb->delete( 'recentchanges',
00872                 array( 'rc_type != ' . RC_LOG,
00873                     'rc_namespace' => $title->getNamespace(),
00874                     'rc_title' => $title->getDBkey() ),
00875                 __METHOD__ );
00876             $this->mDb->delete( 'recentchanges',
00877                 array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ),
00878                 __METHOD__ );
00879         }
00880     }
00881 
00887     function updateCategoryCounts( $added, $deleted ) {
00888         $a = WikiPage::factory( $this->mTitle );
00889         $a->updateCategoryCounts(
00890             array_keys( $added ), array_keys( $deleted )
00891         );
00892     }
00893 }