MediaWiki
REL1_22
|
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 }