From fe2e53ca935874159491974f6e4c0d6fca41805b Mon Sep 17 00:00:00 2001 From: Bryan Tong Minh Date: Tue, 8 Jul 2008 15:02:07 +0000 Subject: [PATCH] (bug 13588) Experimentally track link changes if $wgTrackLinkChanges is set to true. Requires schema change. --- RELEASE-NOTES | 4 +- includes/DefaultSettings.php | 5 ++ includes/LinksUpdate.php | 166 +++++++++++++++++++++++++++++++++-- maintenance/tables.sql | 24 +++++ 4 files changed, 190 insertions(+), 9 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index d6f4af089a..ef9cce3447 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -185,7 +185,9 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN * Add a new hook NormalizeMessageKey to allow extensions to replace messages before the database is potentially queried * (bug 9736) Redirects on Special:Fewestrevisions are now marked as such. - +* (bug 13588) Experimentally track link changes if $wgTrackLinkChanges is set + to true. Requires schema change. + === Bug fixes in 1.13 === * (bug 10677) Add link to the file description page on the shared repository diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 537205bde3..70c28b3854 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -3262,3 +3262,8 @@ $wgSitemapNamespaces = false; * ting this variable false. */ $wgUseAutomaticEditSummaries = true; + +/* + * Track link changes + */ +$wgTrackLinkChanges = false; diff --git a/includes/LinksUpdate.php b/includes/LinksUpdate.php index bb192fb945..957cceb4c3 100644 --- a/includes/LinksUpdate.php +++ b/includes/LinksUpdate.php @@ -12,12 +12,19 @@ class LinksUpdate { var $mId, //!< Page ID of the article linked from $mTitle, //!< Title object of the article linked from $mLinks, //!< Map of title strings to IDs for the links in the document + $mExistingLinks, $mImages, //!< DB keys of the images used, in the array key only + $mExistingImages, $mTemplates, //!< Map of title strings to IDs for the template references, including broken ones + $mExistingTemplates, $mExternals, //!< URLs of external links, array key only + $mExistingExternals, $mCategories, //!< Map of category names to sort keys + $mExistingCategories, $mInterlangs, //!< Map of language codes to titles + $mExistingInterlangs, $mProperties, //!< Map of arbitrary name to value + $mExistingProperties, $mDb, //!< Database connection reference $mOptions, //!< SELECT options to be used (array) $mRecursive; //!< Whether to queue jobs for recursive updates @@ -75,7 +82,7 @@ class LinksUpdate { * Update link tables with outgoing links from an updated article */ function doUpdate() { - global $wgUseDumbLinkUpdate; + global $wgUseDumbLinkUpdate, $wgTrackLinkChanges; wfRunHooks( 'LinksUpdate', array( &$this ) ); if ( $wgUseDumbLinkUpdate ) { @@ -83,6 +90,8 @@ class LinksUpdate { } else { $this->doIncrementalUpdate(); } + if ( $wgTrackLinkChanges ) + $this->makeRecentlinkchanges(); wfRunHooks( 'LinksUpdateComplete', array( &$this ) ); } @@ -565,6 +574,9 @@ class LinksUpdate { * @private */ function getExistingLinks() { + if ( is_array( $this->mExistingLinks ) ) + return $this->mExistingLinks; + $res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ), array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); @@ -575,7 +587,7 @@ class LinksUpdate { $arr[$row->pl_namespace][$row->pl_title] = 1; } $this->mDb->freeResult( $res ); - return $arr; + return $this->mExistingLinks = $arr; } /** @@ -583,6 +595,9 @@ class LinksUpdate { * @private */ function getExistingTemplates() { + if ( is_array( $this->mExistingTemplates ) ) + return $this->mExistingTemplates; + $res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ), array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); @@ -593,7 +608,7 @@ class LinksUpdate { $arr[$row->tl_namespace][$row->tl_title] = 1; } $this->mDb->freeResult( $res ); - return $arr; + return $this->mExistingTemplates = $arr; } /** @@ -601,6 +616,9 @@ class LinksUpdate { * @private */ function getExistingImages() { + if ( is_array( $this->mExistingImages ) ) + return $this->mExistingImages; + $res = $this->mDb->select( 'imagelinks', array( 'il_to' ), array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); @@ -608,7 +626,7 @@ class LinksUpdate { $arr[$row->il_to] = 1; } $this->mDb->freeResult( $res ); - return $arr; + return $this->mExistingImages = $arr; } /** @@ -616,6 +634,9 @@ class LinksUpdate { * @private */ function getExistingExternals() { + if ( is_array( $this->mExistingExternals ) ) + return $this->mExistingExternals; + $res = $this->mDb->select( 'externallinks', array( 'el_to' ), array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); @@ -623,7 +644,7 @@ class LinksUpdate { $arr[$row->el_to] = 1; } $this->mDb->freeResult( $res ); - return $arr; + return $this->mExistingExternals = $arr; } /** @@ -631,6 +652,9 @@ class LinksUpdate { * @private */ function getExistingCategories() { + if ( is_array( $this->mExistingCategories ) ) + return $this->mExistingCategories; + $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey' ), array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); @@ -638,7 +662,7 @@ class LinksUpdate { $arr[$row->cl_to] = $row->cl_sortkey; } $this->mDb->freeResult( $res ); - return $arr; + return $this->mExistingCategories = $arr; } /** @@ -647,13 +671,16 @@ class LinksUpdate { * @private */ function getExistingInterlangs() { + if ( is_array( $this->mExistingInterlangs ) ) + return $this->mExistingInterlangs; + $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ), array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); while ( $row = $this->mDb->fetchObject( $res ) ) { $arr[$row->ll_lang] = $row->ll_title; } - return $arr; + return $this->mExistingInterlangs = $arr; } /** @@ -661,6 +688,9 @@ class LinksUpdate { * @private */ function getExistingProperties() { + if ( is_array( $this->mExistingProperties ) ) + return $this->mExistingProperties; + $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ), array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions ); $arr = array(); @@ -668,7 +698,7 @@ class LinksUpdate { $arr[$row->pp_propname] = $row->pp_value; } $this->mDb->freeResult( $res ); - return $arr; + return $this->mExistingProperties = $arr; } @@ -698,4 +728,124 @@ class LinksUpdate { } } } + + // Recentlinkchanges constants + const INSERTION = 1; + const DELETION = 2; + private static $rlcFields = array( + 'rlc_type', + 'rlc_timestamp', + 'rlc_action', + 'rlc_from', + 'rlc_to_namespace', + 'rlc_to_title', + 'rlc_to_blob' + ); + /* + * Insert items to recentlinkchanges + */ + function makeRecentlinkchanges() { + $insert = array(); + $now = $this->mDb->timestamp(); + + // Category changes + $existing = array_keys( $this->getExistingCategories() ); + $current = array_keys( $this->mCategories ); + $this->simpleAddToLinkchanges( $insert, 'category', $now, $existing, $current, NS_CATEGORY ); + + // External links + $existing = array_keys( $this->getExistingExternals() ); + $current = array_keys( $this->mExternals ); + $insertions = array_diff( $current, $existing ); + foreach ( $insertions as $item ) + $insert[] = array( + 'external', $now, self::INSERTION, + $this->mId, null, null, $item ); + $deletions = array_diff( $existing, $current ); + foreach ( $deletions as $item ) + $insert[] = array( + 'external', $now, self::DELETION, + $this->mId, null, null, $item ); + + // Image changes + $existing = array_keys( $this->getExistingImages() ); + $current = array_keys( $this->mImages ); + $this->simpleAddToLinkchanges( $insert, 'image', $now, $existing, $current, NS_IMAGE ); + + // Interlangs + $existing = $this->getExistingInterlangs(); + $current = $this->mInterlangs; + $this->assocAddToLinkchanges( $insert, 'interlang', $existing, $current ); + + // Page links + $existing = $this->getExistingLinks(); + $current = $this->mLinks; + $this->addToLinkChangesByNamespace( $insert, 'page', $now, $existing, $current); + + // Properties + $existing = $this->getExistingProperties(); + $current = $this->mProperties; + $this->assocAddToLinkchanges( $insert, 'property', $existing, $current ); + + // Templates + $existing = $this->getExistingTemplates(); + $current = $this->mTemplates; + $this->addToLinkChangesByNamespace( $insert, 'template', $now, $existing, $current); + + $this->mDb->insert( 'recentlinkchanges', $insert, __METHOD__ ); + + } + + /* + * Compute the difference for arrays of titles with namespace $ns and add + * them to $insert. + */ + private function simpleAddToLinkchanges( &$insert, $type, $now, $existing, $current, $ns ) { + + $insertions = array_diff( $current, $existing ); + foreach ( $insertions as $item ) + $insert[] = array_combine(self::$rlcFields, array( + $type, $now, self::INSERTION, + $this->mId, $ns, $item, null + ) ); + $deletions = array_diff( $existing, $current ); + foreach ( $deletions as $item ) + $insert[] = array_combine(self::$rlcFields, array( + $type, $now, self::DELETION, + $this->mId, $ns, $item, null + ) ); + + } + + /* + * Compute the difference for associative arrays and insert them to + * $insert as title => blob. + */ + function assocAddToLinkChanges( &$insert, $type, $now, $existing, $current ) { + $insertions = array_diff_assoc( $current, $existing ); + foreach ( $insertions as $key => $value ) + $insert[] = array_combine(self::$rlcFields, array( + $type, $now, self::INSERTION, + $this->mId, null, $key, $value + ) ); + $deletions = array_diff_assoc( $existing, $current ); + foreach ( $deletions as $key => $value ) + $insert[] = array_combine(self::$rlcFields, array( + $type, $now, self::DELETION, + $this->mId, null, $key, $value + ) ); + } + + /* + * Format arrays in the form $namespace => $titleArray for use in + * simpleAddLinkLinkChanges + */ + function addToLinkChangesByNamespace( &$insert, $type, $now, $existing, $current ) { + $namespaces = array_merge( array_keys( $existing ), array_keys( $current ) ); + foreach ( $namespaces as $ns ) + $this->simpleAddToLinkchanges( $insert, $type, $now, + isset( $existing[$ns] ) ? array_keys( $existing[$ns] ) : array(), + isset( $current[$ns] ) ? array_keys( $current[$ns] ) : array(), + $ns ); + } } diff --git a/maintenance/tables.sql b/maintenance/tables.sql index b6bfc0a00a..d994881ba8 100644 --- a/maintenance/tables.sql +++ b/maintenance/tables.sql @@ -1237,4 +1237,28 @@ CREATE TABLE /*$wgDBprefix*/updatelog ( PRIMARY KEY (ul_key) ) /*$wgDBTableOptions*/; +-- A table to track link changes +-- Experimental: enable using $wgTrackLinkChanges +CREATE TABLE /*$wgDBprefix*/recentlinkchanges ( + rlc_id int unsigned NOT NULL auto_increment, + + -- page, image, category, ... + rlc_type varchar(15) binary NOT NULL, + rlc_timestamp binary(14) NOT NULL default '', + -- 1: insert; 2: deletion; + -- should probably make this an enum... + rlc_action tinyint(1) NOT NULL default 0, + + -- page where the links are on + rlc_from int NOT NULL, + + rlc_to_namespace int, + rlc_to_title varchar(255) binary, + rlc_to_blob blob, + + PRIMARY KEY(rlc_id), + KEY from_timestamp (rlc_type, rlc_timestamp), + KEY to_timestamp (rlc_to_namespace, rlc_to_title, rlc_timestamp) +) /*$wgDBTableOptions*/; + -- vim: sw=2 sts=2 et -- 2.20.1