MediaWiki
master
|
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 00054 function __construct( $title, $parserOutput, $recursive = true ) { 00055 parent::__construct( false ); // no implicit transaction 00056 00057 if ( !( $title instanceof Title ) ) { 00058 throw new MWException( "The calling convention to LinksUpdate::LinksUpdate() has changed. " . 00059 "Please see Article::editUpdates() for an invocation example.\n" ); 00060 } 00061 00062 if ( !( $parserOutput instanceof ParserOutput ) ) { 00063 throw new MWException( "The calling convention to LinksUpdate::__construct() has changed. " . 00064 "Please see WikiPage::doEditUpdates() for an invocation example.\n" ); 00065 } 00066 00067 $this->mTitle = $title; 00068 $this->mId = $title->getArticleID(); 00069 00070 if ( !$this->mId ) { 00071 throw new MWException( "The Title object did not provide an article ID. Perhaps the page doesn't exist?" ); 00072 } 00073 00074 $this->mParserOutput = $parserOutput; 00075 00076 $this->mLinks = $parserOutput->getLinks(); 00077 $this->mImages = $parserOutput->getImages(); 00078 $this->mTemplates = $parserOutput->getTemplates(); 00079 $this->mExternals = $parserOutput->getExternalLinks(); 00080 $this->mCategories = $parserOutput->getCategories(); 00081 $this->mProperties = $parserOutput->getProperties(); 00082 $this->mInterwikis = $parserOutput->getInterwikiLinks(); 00083 00084 # Convert the format of the interlanguage links 00085 # I didn't want to change it in the ParserOutput, because that array is passed all 00086 # the way back to the skin, so either a skin API break would be required, or an 00087 # inefficient back-conversion. 00088 $ill = $parserOutput->getLanguageLinks(); 00089 $this->mInterlangs = array(); 00090 foreach ( $ill as $link ) { 00091 list( $key, $title ) = explode( ':', $link, 2 ); 00092 $this->mInterlangs[$key] = $title; 00093 } 00094 00095 foreach ( $this->mCategories as &$sortkey ) { 00096 # If the sortkey is longer then 255 bytes, 00097 # it truncated by DB, and then doesn't get 00098 # matched when comparing existing vs current 00099 # categories, causing bug 25254. 00100 # Also. substr behaves weird when given "". 00101 if ( $sortkey !== '' ) { 00102 $sortkey = substr( $sortkey, 0, 255 ); 00103 } 00104 } 00105 00106 $this->mRecursive = $recursive; 00107 00108 wfRunHooks( 'LinksUpdateConstructed', array( &$this ) ); 00109 } 00110 00114 public function doUpdate() { 00115 global $wgUseDumbLinkUpdate; 00116 00117 wfRunHooks( 'LinksUpdate', array( &$this ) ); 00118 if ( $wgUseDumbLinkUpdate ) { 00119 $this->doDumbUpdate(); 00120 } else { 00121 $this->doIncrementalUpdate(); 00122 } 00123 wfRunHooks( 'LinksUpdateComplete', array( &$this ) ); 00124 } 00125 00126 protected function doIncrementalUpdate() { 00127 wfProfileIn( __METHOD__ ); 00128 00129 # Page links 00130 $existing = $this->getExistingLinks(); 00131 $this->incrTableUpdate( 'pagelinks', 'pl', $this->getLinkDeletions( $existing ), 00132 $this->getLinkInsertions( $existing ) ); 00133 00134 # Image links 00135 $existing = $this->getExistingImages(); 00136 00137 $imageDeletes = $this->getImageDeletions( $existing ); 00138 $this->incrTableUpdate( 'imagelinks', 'il', $imageDeletes, 00139 $this->getImageInsertions( $existing ) ); 00140 00141 # Invalidate all image description pages which had links added or removed 00142 $imageUpdates = $imageDeletes + array_diff_key( $this->mImages, $existing ); 00143 $this->invalidateImageDescriptions( $imageUpdates ); 00144 00145 # External links 00146 $existing = $this->getExistingExternals(); 00147 $this->incrTableUpdate( 'externallinks', 'el', $this->getExternalDeletions( $existing ), 00148 $this->getExternalInsertions( $existing ) ); 00149 00150 # Language links 00151 $existing = $this->getExistingInterlangs(); 00152 $this->incrTableUpdate( 'langlinks', 'll', $this->getInterlangDeletions( $existing ), 00153 $this->getInterlangInsertions( $existing ) ); 00154 00155 # Inline interwiki links 00156 $existing = $this->getExistingInterwikis(); 00157 $this->incrTableUpdate( 'iwlinks', 'iwl', $this->getInterwikiDeletions( $existing ), 00158 $this->getInterwikiInsertions( $existing ) ); 00159 00160 # Template links 00161 $existing = $this->getExistingTemplates(); 00162 $this->incrTableUpdate( 'templatelinks', 'tl', $this->getTemplateDeletions( $existing ), 00163 $this->getTemplateInsertions( $existing ) ); 00164 00165 # Category links 00166 $existing = $this->getExistingCategories(); 00167 00168 $categoryDeletes = $this->getCategoryDeletions( $existing ); 00169 00170 $this->incrTableUpdate( 'categorylinks', 'cl', $categoryDeletes, 00171 $this->getCategoryInsertions( $existing ) ); 00172 00173 # Invalidate all categories which were added, deleted or changed (set symmetric difference) 00174 $categoryInserts = array_diff_assoc( $this->mCategories, $existing ); 00175 $categoryUpdates = $categoryInserts + $categoryDeletes; 00176 $this->invalidateCategories( $categoryUpdates ); 00177 $this->updateCategoryCounts( $categoryInserts, $categoryDeletes ); 00178 00179 # Page properties 00180 $existing = $this->getExistingProperties(); 00181 00182 $propertiesDeletes = $this->getPropertyDeletions( $existing ); 00183 00184 $this->incrTableUpdate( 'page_props', 'pp', $propertiesDeletes, 00185 $this->getPropertyInsertions( $existing ) ); 00186 00187 # Invalidate the necessary pages 00188 $changed = $propertiesDeletes + array_diff_assoc( $this->mProperties, $existing ); 00189 $this->invalidateProperties( $changed ); 00190 00191 # Refresh links of all pages including this page 00192 # This will be in a separate transaction 00193 if ( $this->mRecursive ) { 00194 $this->queueRecursiveJobs(); 00195 } 00196 00197 wfProfileOut( __METHOD__ ); 00198 } 00199 00205 protected function doDumbUpdate() { 00206 wfProfileIn( __METHOD__ ); 00207 00208 # Refresh category pages and image description pages 00209 $existing = $this->getExistingCategories(); 00210 $categoryInserts = array_diff_assoc( $this->mCategories, $existing ); 00211 $categoryDeletes = array_diff_assoc( $existing, $this->mCategories ); 00212 $categoryUpdates = $categoryInserts + $categoryDeletes; 00213 $existing = $this->getExistingImages(); 00214 $imageUpdates = array_diff_key( $existing, $this->mImages ) + array_diff_key( $this->mImages, $existing ); 00215 00216 $this->dumbTableUpdate( 'pagelinks', $this->getLinkInsertions(), 'pl_from' ); 00217 $this->dumbTableUpdate( 'imagelinks', $this->getImageInsertions(), 'il_from' ); 00218 $this->dumbTableUpdate( 'categorylinks', $this->getCategoryInsertions(), 'cl_from' ); 00219 $this->dumbTableUpdate( 'templatelinks', $this->getTemplateInsertions(), 'tl_from' ); 00220 $this->dumbTableUpdate( 'externallinks', $this->getExternalInsertions(), 'el_from' ); 00221 $this->dumbTableUpdate( 'langlinks', $this->getInterlangInsertions(),'ll_from' ); 00222 $this->dumbTableUpdate( 'iwlinks', $this->getInterwikiInsertions(),'iwl_from' ); 00223 $this->dumbTableUpdate( 'page_props', $this->getPropertyInsertions(), 'pp_page' ); 00224 00225 # Update the cache of all the category pages and image description 00226 # pages which were changed, and fix the category table count 00227 $this->invalidateCategories( $categoryUpdates ); 00228 $this->updateCategoryCounts( $categoryInserts, $categoryDeletes ); 00229 $this->invalidateImageDescriptions( $imageUpdates ); 00230 00231 # Refresh links of all pages including this page 00232 # This will be in a separate transaction 00233 if ( $this->mRecursive ) { 00234 $this->queueRecursiveJobs(); 00235 } 00236 00237 wfProfileOut( __METHOD__ ); 00238 } 00239 00240 function queueRecursiveJobs() { 00241 global $wgUpdateRowsPerJob; 00242 wfProfileIn( __METHOD__ ); 00243 00244 $cache = $this->mTitle->getBacklinkCache(); 00245 $batches = $cache->partition( 'templatelinks', $wgUpdateRowsPerJob ); 00246 if ( !$batches ) { 00247 wfProfileOut( __METHOD__ ); 00248 return; 00249 } 00250 $jobs = array(); 00251 foreach ( $batches as $batch ) { 00252 list( $start, $end ) = $batch; 00253 $params = array( 00254 'table' => 'templatelinks', 00255 'start' => $start, 00256 'end' => $end, 00257 ); 00258 $jobs[] = new RefreshLinksJob2( $this->mTitle, $params ); 00259 } 00260 Job::batchInsert( $jobs ); 00261 00262 wfProfileOut( __METHOD__ ); 00263 } 00264 00268 function invalidateCategories( $cats ) { 00269 $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) ); 00270 } 00271 00277 function updateCategoryCounts( $added, $deleted ) { 00278 $a = WikiPage::factory( $this->mTitle ); 00279 $a->updateCategoryCounts( 00280 array_keys( $added ), array_keys( $deleted ) 00281 ); 00282 } 00283 00287 function invalidateImageDescriptions( $images ) { 00288 $this->invalidatePages( NS_FILE, array_keys( $images ) ); 00289 } 00290 00296 private function dumbTableUpdate( $table, $insertions, $fromField ) { 00297 $this->mDb->delete( $table, array( $fromField => $this->mId ), __METHOD__ ); 00298 if ( count( $insertions ) ) { 00299 # The link array was constructed without FOR UPDATE, so there may 00300 # be collisions. This may cause minor link table inconsistencies, 00301 # which is better than crippling the site with lock contention. 00302 $this->mDb->insert( $table, $insertions, __METHOD__, array( 'IGNORE' ) ); 00303 } 00304 } 00305 00313 function incrTableUpdate( $table, $prefix, $deletions, $insertions ) { 00314 if ( $table == 'page_props' ) { 00315 $fromField = 'pp_page'; 00316 } else { 00317 $fromField = "{$prefix}_from"; 00318 } 00319 $where = array( $fromField => $this->mId ); 00320 if ( $table == 'pagelinks' || $table == 'templatelinks' || $table == 'iwlinks' ) { 00321 if ( $table == 'iwlinks' ) { 00322 $baseKey = 'iwl_prefix'; 00323 } else { 00324 $baseKey = "{$prefix}_namespace"; 00325 } 00326 $clause = $this->mDb->makeWhereFrom2d( $deletions, $baseKey, "{$prefix}_title" ); 00327 if ( $clause ) { 00328 $where[] = $clause; 00329 } else { 00330 $where = false; 00331 } 00332 } else { 00333 if ( $table == 'langlinks' ) { 00334 $toField = 'll_lang'; 00335 } elseif ( $table == 'page_props' ) { 00336 $toField = 'pp_propname'; 00337 } else { 00338 $toField = $prefix . '_to'; 00339 } 00340 if ( count( $deletions ) ) { 00341 $where[] = "$toField IN (" . $this->mDb->makeList( array_keys( $deletions ) ) . ')'; 00342 } else { 00343 $where = false; 00344 } 00345 } 00346 if ( $where ) { 00347 $this->mDb->delete( $table, $where, __METHOD__ ); 00348 } 00349 if ( count( $insertions ) ) { 00350 $this->mDb->insert( $table, $insertions, __METHOD__, 'IGNORE' ); 00351 } 00352 } 00353 00360 private function getLinkInsertions( $existing = array() ) { 00361 $arr = array(); 00362 foreach( $this->mLinks as $ns => $dbkeys ) { 00363 $diffs = isset( $existing[$ns] ) 00364 ? array_diff_key( $dbkeys, $existing[$ns] ) 00365 : $dbkeys; 00366 foreach ( $diffs as $dbk => $id ) { 00367 $arr[] = array( 00368 'pl_from' => $this->mId, 00369 'pl_namespace' => $ns, 00370 'pl_title' => $dbk 00371 ); 00372 } 00373 } 00374 return $arr; 00375 } 00376 00382 private function getTemplateInsertions( $existing = array() ) { 00383 $arr = array(); 00384 foreach( $this->mTemplates as $ns => $dbkeys ) { 00385 $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys; 00386 foreach ( $diffs as $dbk => $id ) { 00387 $arr[] = array( 00388 'tl_from' => $this->mId, 00389 'tl_namespace' => $ns, 00390 'tl_title' => $dbk 00391 ); 00392 } 00393 } 00394 return $arr; 00395 } 00396 00403 private function getImageInsertions( $existing = array() ) { 00404 $arr = array(); 00405 $diffs = array_diff_key( $this->mImages, $existing ); 00406 foreach( $diffs as $iname => $dummy ) { 00407 $arr[] = array( 00408 'il_from' => $this->mId, 00409 'il_to' => $iname 00410 ); 00411 } 00412 return $arr; 00413 } 00414 00420 private function getExternalInsertions( $existing = array() ) { 00421 $arr = array(); 00422 $diffs = array_diff_key( $this->mExternals, $existing ); 00423 foreach( $diffs as $url => $dummy ) { 00424 foreach( wfMakeUrlIndexes( $url ) as $index ) { 00425 $arr[] = array( 00426 'el_from' => $this->mId, 00427 'el_to' => $url, 00428 'el_index' => $index, 00429 ); 00430 } 00431 } 00432 return $arr; 00433 } 00434 00443 private function getCategoryInsertions( $existing = array() ) { 00444 global $wgContLang, $wgCategoryCollation; 00445 $diffs = array_diff_assoc( $this->mCategories, $existing ); 00446 $arr = array(); 00447 foreach ( $diffs as $name => $prefix ) { 00448 $nt = Title::makeTitleSafe( NS_CATEGORY, $name ); 00449 $wgContLang->findVariantLink( $name, $nt, true ); 00450 00451 if ( $this->mTitle->getNamespace() == NS_CATEGORY ) { 00452 $type = 'subcat'; 00453 } elseif ( $this->mTitle->getNamespace() == NS_FILE ) { 00454 $type = 'file'; 00455 } else { 00456 $type = 'page'; 00457 } 00458 00459 # Treat custom sortkeys as a prefix, so that if multiple 00460 # things are forced to sort as '*' or something, they'll 00461 # sort properly in the category rather than in page_id 00462 # order or such. 00463 $sortkey = Collation::singleton()->getSortKey( 00464 $this->mTitle->getCategorySortkey( $prefix ) ); 00465 00466 $arr[] = array( 00467 'cl_from' => $this->mId, 00468 'cl_to' => $name, 00469 'cl_sortkey' => $sortkey, 00470 'cl_timestamp' => $this->mDb->timestamp(), 00471 'cl_sortkey_prefix' => $prefix, 00472 'cl_collation' => $wgCategoryCollation, 00473 'cl_type' => $type, 00474 ); 00475 } 00476 return $arr; 00477 } 00478 00486 private function getInterlangInsertions( $existing = array() ) { 00487 $diffs = array_diff_assoc( $this->mInterlangs, $existing ); 00488 $arr = array(); 00489 foreach( $diffs as $lang => $title ) { 00490 $arr[] = array( 00491 'll_from' => $this->mId, 00492 'll_lang' => $lang, 00493 'll_title' => $title 00494 ); 00495 } 00496 return $arr; 00497 } 00498 00504 function getPropertyInsertions( $existing = array() ) { 00505 $diffs = array_diff_assoc( $this->mProperties, $existing ); 00506 $arr = array(); 00507 foreach ( $diffs as $name => $value ) { 00508 $arr[] = array( 00509 'pp_page' => $this->mId, 00510 'pp_propname' => $name, 00511 'pp_value' => $value, 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] ) ? array_diff_key( $dbkeys, $existing[$prefix] ) : $dbkeys; 00527 foreach ( $diffs as $dbk => $id ) { 00528 $arr[] = array( 00529 'iwl_from' => $this->mId, 00530 'iwl_prefix' => $prefix, 00531 'iwl_title' => $dbk 00532 ); 00533 } 00534 } 00535 return $arr; 00536 } 00537 00544 private function getLinkDeletions( $existing ) { 00545 $del = array(); 00546 foreach ( $existing as $ns => $dbkeys ) { 00547 if ( isset( $this->mLinks[$ns] ) ) { 00548 $del[$ns] = array_diff_key( $existing[$ns], $this->mLinks[$ns] ); 00549 } else { 00550 $del[$ns] = $existing[$ns]; 00551 } 00552 } 00553 return $del; 00554 } 00555 00562 private function getTemplateDeletions( $existing ) { 00563 $del = array(); 00564 foreach ( $existing as $ns => $dbkeys ) { 00565 if ( isset( $this->mTemplates[$ns] ) ) { 00566 $del[$ns] = array_diff_key( $existing[$ns], $this->mTemplates[$ns] ); 00567 } else { 00568 $del[$ns] = $existing[$ns]; 00569 } 00570 } 00571 return $del; 00572 } 00573 00580 private function getImageDeletions( $existing ) { 00581 return array_diff_key( $existing, $this->mImages ); 00582 } 00583 00590 private function getExternalDeletions( $existing ) { 00591 return array_diff_key( $existing, $this->mExternals ); 00592 } 00593 00600 private function getCategoryDeletions( $existing ) { 00601 return array_diff_assoc( $existing, $this->mCategories ); 00602 } 00603 00610 private function getInterlangDeletions( $existing ) { 00611 return array_diff_assoc( $existing, $this->mInterlangs ); 00612 } 00613 00619 function getPropertyDeletions( $existing ) { 00620 return array_diff_assoc( $existing, $this->mProperties ); 00621 } 00622 00629 private function getInterwikiDeletions( $existing ) { 00630 $del = array(); 00631 foreach ( $existing as $prefix => $dbkeys ) { 00632 if ( isset( $this->mInterwikis[$prefix] ) ) { 00633 $del[$prefix] = array_diff_key( $existing[$prefix], $this->mInterwikis[$prefix] ); 00634 } else { 00635 $del[$prefix] = $existing[$prefix]; 00636 } 00637 } 00638 return $del; 00639 } 00640 00646 private function getExistingLinks() { 00647 $res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ), 00648 array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions ); 00649 $arr = array(); 00650 foreach ( $res as $row ) { 00651 if ( !isset( $arr[$row->pl_namespace] ) ) { 00652 $arr[$row->pl_namespace] = array(); 00653 } 00654 $arr[$row->pl_namespace][$row->pl_title] = 1; 00655 } 00656 return $arr; 00657 } 00658 00664 private function getExistingTemplates() { 00665 $res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ), 00666 array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions ); 00667 $arr = array(); 00668 foreach ( $res as $row ) { 00669 if ( !isset( $arr[$row->tl_namespace] ) ) { 00670 $arr[$row->tl_namespace] = array(); 00671 } 00672 $arr[$row->tl_namespace][$row->tl_title] = 1; 00673 } 00674 return $arr; 00675 } 00676 00682 private function getExistingImages() { 00683 $res = $this->mDb->select( 'imagelinks', array( 'il_to' ), 00684 array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions ); 00685 $arr = array(); 00686 foreach ( $res as $row ) { 00687 $arr[$row->il_to] = 1; 00688 } 00689 return $arr; 00690 } 00691 00697 private function getExistingExternals() { 00698 $res = $this->mDb->select( 'externallinks', array( 'el_to' ), 00699 array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions ); 00700 $arr = array(); 00701 foreach ( $res as $row ) { 00702 $arr[$row->el_to] = 1; 00703 } 00704 return $arr; 00705 } 00706 00712 private function getExistingCategories() { 00713 $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey_prefix' ), 00714 array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions ); 00715 $arr = array(); 00716 foreach ( $res as $row ) { 00717 $arr[$row->cl_to] = $row->cl_sortkey_prefix; 00718 } 00719 return $arr; 00720 } 00721 00728 private function getExistingInterlangs() { 00729 $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ), 00730 array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions ); 00731 $arr = array(); 00732 foreach ( $res as $row ) { 00733 $arr[$row->ll_lang] = $row->ll_title; 00734 } 00735 return $arr; 00736 } 00737 00742 protected function getExistingInterwikis() { 00743 $res = $this->mDb->select( 'iwlinks', array( 'iwl_prefix', 'iwl_title' ), 00744 array( 'iwl_from' => $this->mId ), __METHOD__, $this->mOptions ); 00745 $arr = array(); 00746 foreach ( $res as $row ) { 00747 if ( !isset( $arr[$row->iwl_prefix] ) ) { 00748 $arr[$row->iwl_prefix] = array(); 00749 } 00750 $arr[$row->iwl_prefix][$row->iwl_title] = 1; 00751 } 00752 return $arr; 00753 } 00754 00760 private function getExistingProperties() { 00761 $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ), 00762 array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions ); 00763 $arr = array(); 00764 foreach ( $res as $row ) { 00765 $arr[$row->pp_propname] = $row->pp_value; 00766 } 00767 return $arr; 00768 } 00769 00774 public function getTitle() { 00775 return $this->mTitle; 00776 } 00777 00783 public function getParserOutput() { 00784 return $this->mParserOutput; 00785 } 00786 00791 public function getImages() { 00792 return $this->mImages; 00793 } 00794 00799 private function invalidateProperties( $changed ) { 00800 global $wgPagePropLinkInvalidations; 00801 00802 foreach ( $changed as $name => $value ) { 00803 if ( isset( $wgPagePropLinkInvalidations[$name] ) ) { 00804 $inv = $wgPagePropLinkInvalidations[$name]; 00805 if ( !is_array( $inv ) ) { 00806 $inv = array( $inv ); 00807 } 00808 foreach ( $inv as $table ) { 00809 $update = new HTMLCacheUpdate( $this->mTitle, $table ); 00810 $update->doUpdate(); 00811 } 00812 } 00813 } 00814 } 00815 } 00816 00820 class LinksDeletionUpdate extends SqlDataUpdate { 00821 00822 protected $mPage; 00823 00829 function __construct( WikiPage $page ) { 00830 parent::__construct( false ); // no implicit transaction 00831 00832 $this->mPage = $page; 00833 00834 if ( !$page->exists() ) { 00835 throw new MWException( "Page ID not known, perhaps the page doesn't exist?" ); 00836 } 00837 } 00838 00842 public function doUpdate() { 00843 $title = $this->mPage->getTitle(); 00844 $id = $this->mPage->getId(); 00845 00846 # Delete restrictions for it 00847 $this->mDb->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ ); 00848 00849 # Fix category table counts 00850 $cats = array(); 00851 $res = $this->mDb->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ ); 00852 00853 foreach ( $res as $row ) { 00854 $cats [] = $row->cl_to; 00855 } 00856 00857 $this->mPage->updateCategoryCounts( array(), $cats ); 00858 00859 # If using cascading deletes, we can skip some explicit deletes 00860 if ( !$this->mDb->cascadingDeletes() ) { 00861 $this->mDb->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ ); 00862 00863 # Delete outgoing links 00864 $this->mDb->delete( 'pagelinks', array( 'pl_from' => $id ), __METHOD__ ); 00865 $this->mDb->delete( 'imagelinks', array( 'il_from' => $id ), __METHOD__ ); 00866 $this->mDb->delete( 'categorylinks', array( 'cl_from' => $id ), __METHOD__ ); 00867 $this->mDb->delete( 'templatelinks', array( 'tl_from' => $id ), __METHOD__ ); 00868 $this->mDb->delete( 'externallinks', array( 'el_from' => $id ), __METHOD__ ); 00869 $this->mDb->delete( 'langlinks', array( 'll_from' => $id ), __METHOD__ ); 00870 $this->mDb->delete( 'iwlinks', array( 'iwl_from' => $id ), __METHOD__ ); 00871 $this->mDb->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ ); 00872 $this->mDb->delete( 'page_props', array( 'pp_page' => $id ), __METHOD__ ); 00873 } 00874 00875 # If using cleanup triggers, we can skip some manual deletes 00876 if ( !$this->mDb->cleanupTriggers() ) { 00877 # Clean up recentchanges entries... 00878 $this->mDb->delete( 'recentchanges', 00879 array( 'rc_type != ' . RC_LOG, 00880 'rc_namespace' => $title->getNamespace(), 00881 'rc_title' => $title->getDBkey() ), 00882 __METHOD__ ); 00883 $this->mDb->delete( 'recentchanges', 00884 array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ), 00885 __METHOD__ ); 00886 } 00887 } 00888 00894 function updateCategoryCounts( $added, $deleted ) { 00895 $a = WikiPage::factory( $this->mTitle ); 00896 $a->updateCategoryCounts( 00897 array_keys( $added ), array_keys( $deleted ) 00898 ); 00899 } 00900 }