MediaWiki  REL1_24
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_from_namespace' => $this->mTitle->getNamespace(),
00362                     'pl_namespace' => $ns,
00363                     'pl_title' => $dbk
00364                 );
00365             }
00366         }
00367 
00368         return $arr;
00369     }
00370 
00376     private function getTemplateInsertions( $existing = array() ) {
00377         $arr = array();
00378         foreach ( $this->mTemplates as $ns => $dbkeys ) {
00379             $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys;
00380             foreach ( $diffs as $dbk => $id ) {
00381                 $arr[] = array(
00382                     'tl_from' => $this->mId,
00383                     'tl_from_namespace' => $this->mTitle->getNamespace(),
00384                     'tl_namespace' => $ns,
00385                     'tl_title' => $dbk
00386                 );
00387             }
00388         }
00389 
00390         return $arr;
00391     }
00392 
00399     private function getImageInsertions( $existing = array() ) {
00400         $arr = array();
00401         $diffs = array_diff_key( $this->mImages, $existing );
00402         foreach ( $diffs as $iname => $dummy ) {
00403             $arr[] = array(
00404                 'il_from' => $this->mId,
00405                 'il_from_namespace' => $this->mTitle->getNamespace(),
00406                 'il_to' => $iname
00407             );
00408         }
00409 
00410         return $arr;
00411     }
00412 
00418     private function getExternalInsertions( $existing = array() ) {
00419         $arr = array();
00420         $diffs = array_diff_key( $this->mExternals, $existing );
00421         foreach ( $diffs as $url => $dummy ) {
00422             foreach ( wfMakeUrlIndexes( $url ) as $index ) {
00423                 $arr[] = array(
00424                     'el_id' => $this->mDb->nextSequenceValue( 'externallinks_el_id_seq' ),
00425                     'el_from' => $this->mId,
00426                     'el_to' => $url,
00427                     'el_index' => $index,
00428                 );
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 
00477         return $arr;
00478     }
00479 
00487     private function getInterlangInsertions( $existing = array() ) {
00488         $diffs = array_diff_assoc( $this->mInterlangs, $existing );
00489         $arr = array();
00490         foreach ( $diffs as $lang => $title ) {
00491             $arr[] = array(
00492                 'll_from' => $this->mId,
00493                 'll_lang' => $lang,
00494                 'll_title' => $title
00495             );
00496         }
00497 
00498         return $arr;
00499     }
00500 
00506     function getPropertyInsertions( $existing = array() ) {
00507         $diffs = array_diff_assoc( $this->mProperties, $existing );
00508 
00509         $arr = array();
00510         foreach ( array_keys( $diffs ) as $name ) {
00511             $arr[] = $this->getPagePropRowData( $name );
00512         }
00513 
00514         return $arr;
00515     }
00516 
00533     private function getPagePropRowData( $prop ) {
00534         global $wgPagePropsHaveSortkey;
00535 
00536         $value = $this->mProperties[$prop];
00537 
00538         $row = array(
00539             'pp_page' => $this->mId,
00540             'pp_propname' => $prop,
00541             'pp_value' => $value,
00542         );
00543 
00544         if ( $wgPagePropsHaveSortkey ) {
00545             $row['pp_sortkey'] = $this->getPropertySortKeyValue( $value );
00546         }
00547 
00548         return $row;
00549     }
00550 
00563     private function getPropertySortKeyValue( $value ) {
00564         if ( is_int( $value ) || is_float( $value ) || is_bool( $value ) ) {
00565             return floatval( $value );
00566         }
00567 
00568         return null;
00569     }
00570 
00577     private function getInterwikiInsertions( $existing = array() ) {
00578         $arr = array();
00579         foreach ( $this->mInterwikis as $prefix => $dbkeys ) {
00580             $diffs = isset( $existing[$prefix] )
00581                 ? array_diff_key( $dbkeys, $existing[$prefix] )
00582                 : $dbkeys;
00583 
00584             foreach ( $diffs as $dbk => $id ) {
00585                 $arr[] = array(
00586                     'iwl_from' => $this->mId,
00587                     'iwl_prefix' => $prefix,
00588                     'iwl_title' => $dbk
00589                 );
00590             }
00591         }
00592 
00593         return $arr;
00594     }
00595 
00602     private function getLinkDeletions( $existing ) {
00603         $del = array();
00604         foreach ( $existing as $ns => $dbkeys ) {
00605             if ( isset( $this->mLinks[$ns] ) ) {
00606                 $del[$ns] = array_diff_key( $existing[$ns], $this->mLinks[$ns] );
00607             } else {
00608                 $del[$ns] = $existing[$ns];
00609             }
00610         }
00611 
00612         return $del;
00613     }
00614 
00621     private function getTemplateDeletions( $existing ) {
00622         $del = array();
00623         foreach ( $existing as $ns => $dbkeys ) {
00624             if ( isset( $this->mTemplates[$ns] ) ) {
00625                 $del[$ns] = array_diff_key( $existing[$ns], $this->mTemplates[$ns] );
00626             } else {
00627                 $del[$ns] = $existing[$ns];
00628             }
00629         }
00630 
00631         return $del;
00632     }
00633 
00640     private function getImageDeletions( $existing ) {
00641         return array_diff_key( $existing, $this->mImages );
00642     }
00643 
00650     private function getExternalDeletions( $existing ) {
00651         return array_diff_key( $existing, $this->mExternals );
00652     }
00653 
00660     private function getCategoryDeletions( $existing ) {
00661         return array_diff_assoc( $existing, $this->mCategories );
00662     }
00663 
00670     private function getInterlangDeletions( $existing ) {
00671         return array_diff_assoc( $existing, $this->mInterlangs );
00672     }
00673 
00679     function getPropertyDeletions( $existing ) {
00680         return array_diff_assoc( $existing, $this->mProperties );
00681     }
00682 
00689     private function getInterwikiDeletions( $existing ) {
00690         $del = array();
00691         foreach ( $existing as $prefix => $dbkeys ) {
00692             if ( isset( $this->mInterwikis[$prefix] ) ) {
00693                 $del[$prefix] = array_diff_key( $existing[$prefix], $this->mInterwikis[$prefix] );
00694             } else {
00695                 $del[$prefix] = $existing[$prefix];
00696             }
00697         }
00698 
00699         return $del;
00700     }
00701 
00707     private function getExistingLinks() {
00708         $res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ),
00709             array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions );
00710         $arr = array();
00711         foreach ( $res as $row ) {
00712             if ( !isset( $arr[$row->pl_namespace] ) ) {
00713                 $arr[$row->pl_namespace] = array();
00714             }
00715             $arr[$row->pl_namespace][$row->pl_title] = 1;
00716         }
00717 
00718         return $arr;
00719     }
00720 
00726     private function getExistingTemplates() {
00727         $res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ),
00728             array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions );
00729         $arr = array();
00730         foreach ( $res as $row ) {
00731             if ( !isset( $arr[$row->tl_namespace] ) ) {
00732                 $arr[$row->tl_namespace] = array();
00733             }
00734             $arr[$row->tl_namespace][$row->tl_title] = 1;
00735         }
00736 
00737         return $arr;
00738     }
00739 
00745     private function getExistingImages() {
00746         $res = $this->mDb->select( 'imagelinks', array( 'il_to' ),
00747             array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions );
00748         $arr = array();
00749         foreach ( $res as $row ) {
00750             $arr[$row->il_to] = 1;
00751         }
00752 
00753         return $arr;
00754     }
00755 
00761     private function getExistingExternals() {
00762         $res = $this->mDb->select( 'externallinks', array( 'el_to' ),
00763             array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions );
00764         $arr = array();
00765         foreach ( $res as $row ) {
00766             $arr[$row->el_to] = 1;
00767         }
00768 
00769         return $arr;
00770     }
00771 
00777     private function getExistingCategories() {
00778         $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey_prefix' ),
00779             array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions );
00780         $arr = array();
00781         foreach ( $res as $row ) {
00782             $arr[$row->cl_to] = $row->cl_sortkey_prefix;
00783         }
00784 
00785         return $arr;
00786     }
00787 
00794     private function getExistingInterlangs() {
00795         $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ),
00796             array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions );
00797         $arr = array();
00798         foreach ( $res as $row ) {
00799             $arr[$row->ll_lang] = $row->ll_title;
00800         }
00801 
00802         return $arr;
00803     }
00804 
00809     protected function getExistingInterwikis() {
00810         $res = $this->mDb->select( 'iwlinks', array( 'iwl_prefix', 'iwl_title' ),
00811             array( 'iwl_from' => $this->mId ), __METHOD__, $this->mOptions );
00812         $arr = array();
00813         foreach ( $res as $row ) {
00814             if ( !isset( $arr[$row->iwl_prefix] ) ) {
00815                 $arr[$row->iwl_prefix] = array();
00816             }
00817             $arr[$row->iwl_prefix][$row->iwl_title] = 1;
00818         }
00819 
00820         return $arr;
00821     }
00822 
00828     private function getExistingProperties() {
00829         $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ),
00830             array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions );
00831         $arr = array();
00832         foreach ( $res as $row ) {
00833             $arr[$row->pp_propname] = $row->pp_value;
00834         }
00835 
00836         return $arr;
00837     }
00838 
00843     public function getTitle() {
00844         return $this->mTitle;
00845     }
00846 
00852     public function getParserOutput() {
00853         return $this->mParserOutput;
00854     }
00855 
00860     public function getImages() {
00861         return $this->mImages;
00862     }
00863 
00868     private function invalidateProperties( $changed ) {
00869         global $wgPagePropLinkInvalidations;
00870 
00871         foreach ( $changed as $name => $value ) {
00872             if ( isset( $wgPagePropLinkInvalidations[$name] ) ) {
00873                 $inv = $wgPagePropLinkInvalidations[$name];
00874                 if ( !is_array( $inv ) ) {
00875                     $inv = array( $inv );
00876                 }
00877                 foreach ( $inv as $table ) {
00878                     $update = new HTMLCacheUpdate( $this->mTitle, $table );
00879                     $update->doUpdate();
00880                 }
00881             }
00882         }
00883     }
00884 
00890     public function getAddedLinks() {
00891         if ( $this->linkInsertions === null ) {
00892             return null;
00893         }
00894         $result = array();
00895         foreach ( $this->linkInsertions as $insertion ) {
00896             $result[] = Title::makeTitle( $insertion['pl_namespace'], $insertion['pl_title'] );
00897         }
00898 
00899         return $result;
00900     }
00901 
00907     public function getRemovedLinks() {
00908         if ( $this->linkDeletions === null ) {
00909             return null;
00910         }
00911         $result = array();
00912         foreach ( $this->linkDeletions as $ns => $titles ) {
00913             foreach ( $titles as $title => $unused ) {
00914                 $result[] = Title::makeTitle( $ns, $title );
00915             }
00916         }
00917 
00918         return $result;
00919     }
00920 
00924     protected function updateLinksTimestamp() {
00925         if ( $this->mId ) {
00926             // The link updates made here only reflect the freshness of the parser output
00927             $timestamp = $this->mParserOutput->getCacheTime();
00928             $this->mDb->update( 'page',
00929                 array( 'page_links_updated' => $this->mDb->timestamp( $timestamp ) ),
00930                 array( 'page_id' => $this->mId ),
00931                 __METHOD__
00932             );
00933         }
00934     }
00935 }
00936 
00940 class LinksDeletionUpdate extends SqlDataUpdate {
00942     protected $mPage;
00943 
00950     function __construct( WikiPage $page ) {
00951         parent::__construct( false ); // no implicit transaction
00952 
00953         $this->mPage = $page;
00954 
00955         if ( !$page->exists() ) {
00956             throw new MWException( "Page ID not known, perhaps the page doesn't exist?" );
00957         }
00958     }
00959 
00963     public function doUpdate() {
00964         $title = $this->mPage->getTitle();
00965         $id = $this->mPage->getId();
00966 
00967         # Delete restrictions for it
00968         $this->mDb->delete( 'page_restrictions', array( 'pr_page' => $id ), __METHOD__ );
00969 
00970         # Fix category table counts
00971         $cats = array();
00972         $res = $this->mDb->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ );
00973 
00974         foreach ( $res as $row ) {
00975             $cats[] = $row->cl_to;
00976         }
00977 
00978         $this->mPage->updateCategoryCounts( array(), $cats );
00979 
00980         # If using cascading deletes, we can skip some explicit deletes
00981         if ( !$this->mDb->cascadingDeletes() ) {
00982             # Delete outgoing links
00983             $this->mDb->delete( 'pagelinks', array( 'pl_from' => $id ), __METHOD__ );
00984             $this->mDb->delete( 'imagelinks', array( 'il_from' => $id ), __METHOD__ );
00985             $this->mDb->delete( 'categorylinks', array( 'cl_from' => $id ), __METHOD__ );
00986             $this->mDb->delete( 'templatelinks', array( 'tl_from' => $id ), __METHOD__ );
00987             $this->mDb->delete( 'externallinks', array( 'el_from' => $id ), __METHOD__ );
00988             $this->mDb->delete( 'langlinks', array( 'll_from' => $id ), __METHOD__ );
00989             $this->mDb->delete( 'iwlinks', array( 'iwl_from' => $id ), __METHOD__ );
00990             $this->mDb->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ );
00991             $this->mDb->delete( 'page_props', array( 'pp_page' => $id ), __METHOD__ );
00992         }
00993 
00994         # If using cleanup triggers, we can skip some manual deletes
00995         if ( !$this->mDb->cleanupTriggers() ) {
00996             # Clean up recentchanges entries...
00997             $this->mDb->delete( 'recentchanges',
00998                 array( 'rc_type != ' . RC_LOG,
00999                     'rc_namespace' => $title->getNamespace(),
01000                     'rc_title' => $title->getDBkey() ),
01001                 __METHOD__ );
01002             $this->mDb->delete( 'recentchanges',
01003                 array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ),
01004                 __METHOD__ );
01005         }
01006     }
01007 
01013     function updateCategoryCounts( $added, $deleted ) {
01014         $a = WikiPage::factory( $this->mTitle );
01015         $a->updateCategoryCounts(
01016             array_keys( $added ), array_keys( $deleted )
01017         );
01018     }
01019 }