MediaWiki  REL1_23
LinksUpdate.php
Go to the documentation of this file.
00001 <?php
00028 class LinksUpdate extends SqlDataUpdate {
00029     // @todo make members protected, but make sure extensions don't break
00030 
00032     public $mId;
00033 
00035     public $mTitle;
00036 
00038     public $mParserOutput;
00039 
00041     public $mLinks;
00042 
00044     public $mImages;
00045 
00047     public $mTemplates;
00048 
00050     public $mExternals;
00051 
00053     public $mCategories;
00054 
00056     public $mInterlangs;
00057 
00059     public $mProperties;
00060 
00062     public $mDb;
00063 
00065     public $mOptions;
00066 
00068     public $mRecursive;
00069 
00073     private $linkInsertions = null;
00074 
00078     private $linkDeletions = null;
00079 
00088     function __construct( $title, $parserOutput, $recursive = true ) {
00089         parent::__construct( false ); // no implicit transaction
00090 
00091         if ( !( $title instanceof Title ) ) {
00092             throw new MWException( "The calling convention to LinksUpdate::LinksUpdate() has changed. " .
00093                 "Please see Article::editUpdates() for an invocation example.\n" );
00094         }
00095 
00096         if ( !( $parserOutput instanceof ParserOutput ) ) {
00097             throw new MWException( "The calling convention to LinksUpdate::__construct() has changed. " .
00098                 "Please see WikiPage::doEditUpdates() for an invocation example.\n" );
00099         }
00100 
00101         $this->mTitle = $title;
00102         $this->mId = $title->getArticleID();
00103 
00104         if ( !$this->mId ) {
00105             throw new MWException( "The Title object did not provide an article " .
00106                 "ID. Perhaps the page doesn't exist?" );
00107         }
00108 
00109         $this->mParserOutput = $parserOutput;
00110 
00111         $this->mLinks = $parserOutput->getLinks();
00112         $this->mImages = $parserOutput->getImages();
00113         $this->mTemplates = $parserOutput->getTemplates();
00114         $this->mExternals = $parserOutput->getExternalLinks();
00115         $this->mCategories = $parserOutput->getCategories();
00116         $this->mProperties = $parserOutput->getProperties();
00117         $this->mInterwikis = $parserOutput->getInterwikiLinks();
00118 
00119         # Convert the format of the interlanguage links
00120         # I didn't want to change it in the ParserOutput, because that array is passed all
00121         # the way back to the skin, so either a skin API break would be required, or an
00122         # inefficient back-conversion.
00123         $ill = $parserOutput->getLanguageLinks();
00124         $this->mInterlangs = array();
00125         foreach ( $ill as $link ) {
00126             list( $key, $title ) = explode( ':', $link, 2 );
00127             $this->mInterlangs[$key] = $title;
00128         }
00129 
00130         foreach ( $this->mCategories as &$sortkey ) {
00131             # If the sortkey is longer then 255 bytes,
00132             # it truncated by DB, and then doesn't get
00133             # matched when comparing existing vs current
00134             # categories, causing bug 25254.
00135             # Also. substr behaves weird when given "".
00136             if ( $sortkey !== '' ) {
00137                 $sortkey = substr( $sortkey, 0, 255 );
00138             }
00139         }
00140 
00141         $this->mRecursive = $recursive;
00142 
00143         wfRunHooks( 'LinksUpdateConstructed', array( &$this ) );
00144     }
00145 
00149     public function doUpdate() {
00150         wfRunHooks( 'LinksUpdate', array( &$this ) );
00151         $this->doIncrementalUpdate();
00152         wfRunHooks( 'LinksUpdateComplete', array( &$this ) );
00153     }
00154 
00155     protected function doIncrementalUpdate() {
00156         wfProfileIn( __METHOD__ );
00157 
00158         # Page links
00159         $existing = $this->getExistingLinks();
00160         $this->linkDeletions = $this->getLinkDeletions( $existing );
00161         $this->linkInsertions = $this->getLinkInsertions( $existing );
00162         $this->incrTableUpdate( 'pagelinks', 'pl', $this->linkDeletions, $this->linkInsertions );
00163 
00164         # Image links
00165         $existing = $this->getExistingImages();
00166 
00167         $imageDeletes = $this->getImageDeletions( $existing );
00168         $this->incrTableUpdate( 'imagelinks', 'il', $imageDeletes,
00169             $this->getImageInsertions( $existing ) );
00170 
00171         # Invalidate all image description pages which had links added or removed
00172         $imageUpdates = $imageDeletes + array_diff_key( $this->mImages, $existing );
00173         $this->invalidateImageDescriptions( $imageUpdates );
00174 
00175         # External links
00176         $existing = $this->getExistingExternals();
00177         $this->incrTableUpdate( 'externallinks', 'el', $this->getExternalDeletions( $existing ),
00178             $this->getExternalInsertions( $existing ) );
00179 
00180         # Language links
00181         $existing = $this->getExistingInterlangs();
00182         $this->incrTableUpdate( 'langlinks', 'll', $this->getInterlangDeletions( $existing ),
00183             $this->getInterlangInsertions( $existing ) );
00184 
00185         # Inline interwiki links
00186         $existing = $this->getExistingInterwikis();
00187         $this->incrTableUpdate( 'iwlinks', 'iwl', $this->getInterwikiDeletions( $existing ),
00188             $this->getInterwikiInsertions( $existing ) );
00189 
00190         # Template links
00191         $existing = $this->getExistingTemplates();
00192         $this->incrTableUpdate( 'templatelinks', 'tl', $this->getTemplateDeletions( $existing ),
00193             $this->getTemplateInsertions( $existing ) );
00194 
00195         # Category links
00196         $existing = $this->getExistingCategories();
00197 
00198         $categoryDeletes = $this->getCategoryDeletions( $existing );
00199 
00200         $this->incrTableUpdate( 'categorylinks', 'cl', $categoryDeletes,
00201             $this->getCategoryInsertions( $existing ) );
00202 
00203         # Invalidate all categories which were added, deleted or changed (set symmetric difference)
00204         $categoryInserts = array_diff_assoc( $this->mCategories, $existing );
00205         $categoryUpdates = $categoryInserts + $categoryDeletes;
00206         $this->invalidateCategories( $categoryUpdates );
00207         $this->updateCategoryCounts( $categoryInserts, $categoryDeletes );
00208 
00209         # Page properties
00210         $existing = $this->getExistingProperties();
00211 
00212         $propertiesDeletes = $this->getPropertyDeletions( $existing );
00213 
00214         $this->incrTableUpdate( 'page_props', 'pp', $propertiesDeletes,
00215             $this->getPropertyInsertions( $existing ) );
00216 
00217         # Invalidate the necessary pages
00218         $changed = $propertiesDeletes + array_diff_assoc( $this->mProperties, $existing );
00219         $this->invalidateProperties( $changed );
00220 
00221         # Update the links table freshness for this title
00222         $this->updateLinksTimestamp();
00223 
00224         # Refresh links of all pages including this page
00225         # This will be in a separate transaction
00226         if ( $this->mRecursive ) {
00227             $this->queueRecursiveJobs();
00228         }
00229 
00230         wfProfileOut( __METHOD__ );
00231     }
00232 
00239     function queueRecursiveJobs() {
00240         self::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' );
00241         if ( $this->mTitle->getNamespace() == NS_FILE ) {
00242             // Process imagelinks in case the title is or was a redirect
00243             self::queueRecursiveJobsForTable( $this->mTitle, 'imagelinks' );
00244         }
00245     }
00246 
00253     public static function queueRecursiveJobsForTable( Title $title, $table ) {
00254         wfProfileIn( __METHOD__ );
00255         if ( $title->getBacklinkCache()->hasLinks( $table ) ) {
00256             $job = new RefreshLinksJob(
00257                 $title,
00258                 array(
00259                     'table' => $table,
00260                     'recursive' => true,
00261                 ) + Job::newRootJobParams( // "overall" refresh links job info
00262                     "refreshlinks:{$table}:{$title->getPrefixedText()}"
00263                 )
00264             );
00265             JobQueueGroup::singleton()->push( $job );
00266             JobQueueGroup::singleton()->deduplicateRootJob( $job );
00267         }
00268         wfProfileOut( __METHOD__ );
00269     }
00270 
00274     function invalidateCategories( $cats ) {
00275         $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) );
00276     }
00277 
00283     function updateCategoryCounts( $added, $deleted ) {
00284         $a = WikiPage::factory( $this->mTitle );
00285         $a->updateCategoryCounts(
00286             array_keys( $added ), array_keys( $deleted )
00287         );
00288     }
00289 
00293     function invalidateImageDescriptions( $images ) {
00294         $this->invalidatePages( NS_FILE, array_keys( $images ) );
00295     }
00296 
00304     function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
00305         if ( $table == 'page_props' ) {
00306             $fromField = 'pp_page';
00307         } else {
00308             $fromField = "{$prefix}_from";
00309         }
00310         $where = array( $fromField => $this->mId );
00311         if ( $table == 'pagelinks' || $table == 'templatelinks' || $table == 'iwlinks' ) {
00312             if ( $table == 'iwlinks' ) {
00313                 $baseKey = 'iwl_prefix';
00314             } else {
00315                 $baseKey = "{$prefix}_namespace";
00316             }
00317             $clause = $this->mDb->makeWhereFrom2d( $deletions, $baseKey, "{$prefix}_title" );
00318             if ( $clause ) {
00319                 $where[] = $clause;
00320             } else {
00321                 $where = false;
00322             }
00323         } else {
00324             if ( $table == 'langlinks' ) {
00325                 $toField = 'll_lang';
00326             } elseif ( $table == 'page_props' ) {
00327                 $toField = 'pp_propname';
00328             } else {
00329                 $toField = $prefix . '_to';
00330             }
00331             if ( count( $deletions ) ) {
00332                 $where[$toField] = array_keys( $deletions );
00333             } else {
00334                 $where = false;
00335             }
00336         }
00337         if ( $where ) {
00338             $this->mDb->delete( $table, $where, __METHOD__ );
00339         }
00340         if ( count( $insertions ) ) {
00341             $this->mDb->insert( $table, $insertions, __METHOD__, 'IGNORE' );
00342             wfRunHooks( 'LinksUpdateAfterInsert', array( $this, $table, $insertions ) );
00343         }
00344     }
00345 
00352     private function getLinkInsertions( $existing = array() ) {
00353         $arr = array();
00354         foreach ( $this->mLinks as $ns => $dbkeys ) {
00355             $diffs = isset( $existing[$ns] )
00356                 ? array_diff_key( $dbkeys, $existing[$ns] )
00357                 : $dbkeys;
00358             foreach ( $diffs as $dbk => $id ) {
00359                 $arr[] = array(
00360                     'pl_from' => $this->mId,
00361                     'pl_namespace' => $ns,
00362                     'pl_title' => $dbk
00363                 );
00364             }
00365         }
00366 
00367         return $arr;
00368     }
00369 
00375     private function getTemplateInsertions( $existing = array() ) {
00376         $arr = array();
00377         foreach ( $this->mTemplates as $ns => $dbkeys ) {
00378             $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys;
00379             foreach ( $diffs as $dbk => $id ) {
00380                 $arr[] = array(
00381                     'tl_from' => $this->mId,
00382                     'tl_namespace' => $ns,
00383                     'tl_title' => $dbk
00384                 );
00385             }
00386         }
00387 
00388         return $arr;
00389     }
00390 
00397     private function getImageInsertions( $existing = array() ) {
00398         $arr = array();
00399         $diffs = array_diff_key( $this->mImages, $existing );
00400         foreach ( $diffs as $iname => $dummy ) {
00401             $arr[] = array(
00402                 'il_from' => $this->mId,
00403                 'il_to' => $iname
00404             );
00405         }
00406 
00407         return $arr;
00408     }
00409 
00415     private function getExternalInsertions( $existing = array() ) {
00416         $arr = array();
00417         $diffs = array_diff_key( $this->mExternals, $existing );
00418         foreach ( $diffs as $url => $dummy ) {
00419             foreach ( wfMakeUrlIndexes( $url ) as $index ) {
00420                 $arr[] = array(
00421                     'el_id' => $this->mDb->nextSequenceValue( 'externallinks_el_id_seq' ),
00422                     'el_from' => $this->mId,
00423                     'el_to' => $url,
00424                     'el_index' => $index,
00425                 );
00426             }
00427         }
00428 
00429         return $arr;
00430     }
00431 
00440     private function getCategoryInsertions( $existing = array() ) {
00441         global $wgContLang, $wgCategoryCollation;
00442         $diffs = array_diff_assoc( $this->mCategories, $existing );
00443         $arr = array();
00444         foreach ( $diffs as $name => $prefix ) {
00445             $nt = Title::makeTitleSafe( NS_CATEGORY, $name );
00446             $wgContLang->findVariantLink( $name, $nt, true );
00447 
00448             if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
00449                 $type = 'subcat';
00450             } elseif ( $this->mTitle->getNamespace() == NS_FILE ) {
00451                 $type = 'file';
00452             } else {
00453                 $type = 'page';
00454             }
00455 
00456             # Treat custom sortkeys as a prefix, so that if multiple
00457             # things are forced to sort as '*' or something, they'll
00458             # sort properly in the category rather than in page_id
00459             # order or such.
00460             $sortkey = Collation::singleton()->getSortKey(
00461                 $this->mTitle->getCategorySortkey( $prefix ) );
00462 
00463             $arr[] = array(
00464                 'cl_from' => $this->mId,
00465                 'cl_to' => $name,
00466                 'cl_sortkey' => $sortkey,
00467                 'cl_timestamp' => $this->mDb->timestamp(),
00468                 'cl_sortkey_prefix' => $prefix,
00469                 'cl_collation' => $wgCategoryCollation,
00470                 'cl_type' => $type,
00471             );
00472         }
00473 
00474         return $arr;
00475     }
00476 
00484     private function getInterlangInsertions( $existing = array() ) {
00485         $diffs = array_diff_assoc( $this->mInterlangs, $existing );
00486         $arr = array();
00487         foreach ( $diffs as $lang => $title ) {
00488             $arr[] = array(
00489                 'll_from' => $this->mId,
00490                 'll_lang' => $lang,
00491                 'll_title' => $title
00492             );
00493         }
00494 
00495         return $arr;
00496     }
00497 
00503     function getPropertyInsertions( $existing = array() ) {
00504         $diffs = array_diff_assoc( $this->mProperties, $existing );
00505         $arr = array();
00506         foreach ( $diffs as $name => $value ) {
00507             $arr[] = array(
00508                 'pp_page' => $this->mId,
00509                 'pp_propname' => $name,
00510                 'pp_value' => $value,
00511             );
00512         }
00513 
00514         return $arr;
00515     }
00516 
00523     private function getInterwikiInsertions( $existing = array() ) {
00524         $arr = array();
00525         foreach ( $this->mInterwikis as $prefix => $dbkeys ) {
00526             $diffs = isset( $existing[$prefix] )
00527                 ? array_diff_key( $dbkeys, $existing[$prefix] )
00528                 : $dbkeys;
00529 
00530             foreach ( $diffs as $dbk => $id ) {
00531                 $arr[] = array(
00532                     'iwl_from' => $this->mId,
00533                     'iwl_prefix' => $prefix,
00534                     'iwl_title' => $dbk
00535                 );
00536             }
00537         }
00538 
00539         return $arr;
00540     }
00541 
00548     private function getLinkDeletions( $existing ) {
00549         $del = array();
00550         foreach ( $existing as $ns => $dbkeys ) {
00551             if ( isset( $this->mLinks[$ns] ) ) {
00552                 $del[$ns] = array_diff_key( $existing[$ns], $this->mLinks[$ns] );
00553             } else {
00554                 $del[$ns] = $existing[$ns];
00555             }
00556         }
00557 
00558         return $del;
00559     }
00560 
00567     private function getTemplateDeletions( $existing ) {
00568         $del = array();
00569         foreach ( $existing as $ns => $dbkeys ) {
00570             if ( isset( $this->mTemplates[$ns] ) ) {
00571                 $del[$ns] = array_diff_key( $existing[$ns], $this->mTemplates[$ns] );
00572             } else {
00573                 $del[$ns] = $existing[$ns];
00574             }
00575         }
00576 
00577         return $del;
00578     }
00579 
00586     private function getImageDeletions( $existing ) {
00587         return array_diff_key( $existing, $this->mImages );
00588     }
00589 
00596     private function getExternalDeletions( $existing ) {
00597         return array_diff_key( $existing, $this->mExternals );
00598     }
00599 
00606     private function getCategoryDeletions( $existing ) {
00607         return array_diff_assoc( $existing, $this->mCategories );
00608     }
00609 
00616     private function getInterlangDeletions( $existing ) {
00617         return array_diff_assoc( $existing, $this->mInterlangs );
00618     }
00619 
00625     function getPropertyDeletions( $existing ) {
00626         return array_diff_assoc( $existing, $this->mProperties );
00627     }
00628 
00635     private function getInterwikiDeletions( $existing ) {
00636         $del = array();
00637         foreach ( $existing as $prefix => $dbkeys ) {
00638             if ( isset( $this->mInterwikis[$prefix] ) ) {
00639                 $del[$prefix] = array_diff_key( $existing[$prefix], $this->mInterwikis[$prefix] );
00640             } else {
00641                 $del[$prefix] = $existing[$prefix];
00642             }
00643         }
00644 
00645         return $del;
00646     }
00647 
00653     private function getExistingLinks() {
00654         $res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ),
00655             array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions );
00656         $arr = array();
00657         foreach ( $res as $row ) {
00658             if ( !isset( $arr[$row->pl_namespace] ) ) {
00659                 $arr[$row->pl_namespace] = array();
00660             }
00661             $arr[$row->pl_namespace][$row->pl_title] = 1;
00662         }
00663 
00664         return $arr;
00665     }
00666 
00672     private function getExistingTemplates() {
00673         $res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ),
00674             array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions );
00675         $arr = array();
00676         foreach ( $res as $row ) {
00677             if ( !isset( $arr[$row->tl_namespace] ) ) {
00678                 $arr[$row->tl_namespace] = array();
00679             }
00680             $arr[$row->tl_namespace][$row->tl_title] = 1;
00681         }
00682 
00683         return $arr;
00684     }
00685 
00691     private function getExistingImages() {
00692         $res = $this->mDb->select( 'imagelinks', array( 'il_to' ),
00693             array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions );
00694         $arr = array();
00695         foreach ( $res as $row ) {
00696             $arr[$row->il_to] = 1;
00697         }
00698 
00699         return $arr;
00700     }
00701 
00707     private function getExistingExternals() {
00708         $res = $this->mDb->select( 'externallinks', array( 'el_to' ),
00709             array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions );
00710         $arr = array();
00711         foreach ( $res as $row ) {
00712             $arr[$row->el_to] = 1;
00713         }
00714 
00715         return $arr;
00716     }
00717 
00723     private function getExistingCategories() {
00724         $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey_prefix' ),
00725             array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions );
00726         $arr = array();
00727         foreach ( $res as $row ) {
00728             $arr[$row->cl_to] = $row->cl_sortkey_prefix;
00729         }
00730 
00731         return $arr;
00732     }
00733 
00740     private function getExistingInterlangs() {
00741         $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ),
00742             array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions );
00743         $arr = array();
00744         foreach ( $res as $row ) {
00745             $arr[$row->ll_lang] = $row->ll_title;
00746         }
00747 
00748         return $arr;
00749     }
00750 
00755     protected function getExistingInterwikis() {
00756         $res = $this->mDb->select( 'iwlinks', array( 'iwl_prefix', 'iwl_title' ),
00757             array( 'iwl_from' => $this->mId ), __METHOD__, $this->mOptions );
00758         $arr = array();
00759         foreach ( $res as $row ) {
00760             if ( !isset( $arr[$row->iwl_prefix] ) ) {
00761                 $arr[$row->iwl_prefix] = array();
00762             }
00763             $arr[$row->iwl_prefix][$row->iwl_title] = 1;
00764         }
00765 
00766         return $arr;
00767     }
00768 
00774     private function getExistingProperties() {
00775         $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ),
00776             array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions );
00777         $arr = array();
00778         foreach ( $res as $row ) {
00779             $arr[$row->pp_propname] = $row->pp_value;
00780         }
00781 
00782         return $arr;
00783     }
00784 
00789     public function getTitle() {
00790         return $this->mTitle;
00791     }
00792 
00798     public function getParserOutput() {
00799         return $this->mParserOutput;
00800     }
00801 
00806     public function getImages() {
00807         return $this->mImages;
00808     }
00809 
00814     private function invalidateProperties( $changed ) {
00815         global $wgPagePropLinkInvalidations;
00816 
00817         foreach ( $changed as $name => $value ) {
00818             if ( isset( $wgPagePropLinkInvalidations[$name] ) ) {
00819                 $inv = $wgPagePropLinkInvalidations[$name];
00820                 if ( !is_array( $inv ) ) {
00821                     $inv = array( $inv );
00822                 }
00823                 foreach ( $inv as $table ) {
00824                     $update = new HTMLCacheUpdate( $this->mTitle, $table );
00825                     $update->doUpdate();
00826                 }
00827             }
00828         }
00829     }
00830 
00836     public function getAddedLinks() {
00837         if ( $this->linkInsertions === null ) {
00838             return null;
00839         }
00840         $result = array();
00841         foreach ( $this->linkInsertions as $insertion ) {
00842             $result[] = Title::makeTitle( $insertion['pl_namespace'], $insertion['pl_title'] );
00843         }
00844 
00845         return $result;
00846     }
00847 
00853     public function getRemovedLinks() {
00854         if ( $this->linkDeletions === null ) {
00855             return null;
00856         }
00857         $result = array();
00858         foreach ( $this->linkDeletions as $ns => $titles ) {
00859             foreach ( $titles as $title => $unused ) {
00860                 $result[] = Title::makeTitle( $ns, $title );
00861             }
00862         }
00863 
00864         return $result;
00865     }
00866 
00870     protected function updateLinksTimestamp() {
00871         if ( $this->mId ) {
00872             // The link updates made here only reflect the freshness of the parser output
00873             $timestamp = $this->mParserOutput->getCacheTime();
00874             $this->mDb->update( 'page',
00875                 array( 'page_links_updated' => $this->mDb->timestamp( $timestamp ) ),
00876                 array( 'page_id' => $this->mId ),
00877                 __METHOD__
00878             );
00879         }
00880     }
00881 }
00882 
00886 class LinksDeletionUpdate extends SqlDataUpdate {
00888     protected $mPage;
00889 
00896     function __construct( WikiPage $page ) {
00897         parent::__construct( false ); // no implicit transaction
00898 
00899         $this->mPage = $page;
00900 
00901         if ( !$page->exists() ) {
00902             throw new MWException( "Page ID not known, perhaps the page doesn't exist?" );
00903         }
00904     }
00905 
00909     public function doUpdate() {
00910         $title = $this->mPage->getTitle();
00911         $id = $this->mPage->getId();
00912 
00913         # Delete restrictions for it
00914         $this->mDb->delete( 'page_restrictions', array( 'pr_page' => $id ), __METHOD__ );
00915 
00916         # Fix category table counts
00917         $cats = array();
00918         $res = $this->mDb->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ );
00919 
00920         foreach ( $res as $row ) {
00921             $cats[] = $row->cl_to;
00922         }
00923 
00924         $this->mPage->updateCategoryCounts( array(), $cats );
00925 
00926         # If using cascading deletes, we can skip some explicit deletes
00927         if ( !$this->mDb->cascadingDeletes() ) {
00928             # Delete outgoing links
00929             $this->mDb->delete( 'pagelinks', array( 'pl_from' => $id ), __METHOD__ );
00930             $this->mDb->delete( 'imagelinks', array( 'il_from' => $id ), __METHOD__ );
00931             $this->mDb->delete( 'categorylinks', array( 'cl_from' => $id ), __METHOD__ );
00932             $this->mDb->delete( 'templatelinks', array( 'tl_from' => $id ), __METHOD__ );
00933             $this->mDb->delete( 'externallinks', array( 'el_from' => $id ), __METHOD__ );
00934             $this->mDb->delete( 'langlinks', array( 'll_from' => $id ), __METHOD__ );
00935             $this->mDb->delete( 'iwlinks', array( 'iwl_from' => $id ), __METHOD__ );
00936             $this->mDb->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ );
00937             $this->mDb->delete( 'page_props', array( 'pp_page' => $id ), __METHOD__ );
00938         }
00939 
00940         # If using cleanup triggers, we can skip some manual deletes
00941         if ( !$this->mDb->cleanupTriggers() ) {
00942             # Clean up recentchanges entries...
00943             $this->mDb->delete( 'recentchanges',
00944                 array( 'rc_type != ' . RC_LOG,
00945                     'rc_namespace' => $title->getNamespace(),
00946                     'rc_title' => $title->getDBkey() ),
00947                 __METHOD__ );
00948             $this->mDb->delete( 'recentchanges',
00949                 array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ),
00950                 __METHOD__ );
00951         }
00952     }
00953 
00959     function updateCategoryCounts( $added, $deleted ) {
00960         $a = WikiPage::factory( $this->mTitle );
00961         $a->updateCategoryCounts(
00962             array_keys( $added ), array_keys( $deleted )
00963         );
00964     }
00965 }