Index: trunk/extensions/CollabWatchlist/sql/patch-change_tag_id.sql |
— | — | @@ -1,5 +0,0 @@ |
2 | | - |
3 | | -ALTER TABLE /*$wgDBprefix*/change_tag |
4 | | - ADD ct_id integer unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT; |
\ No newline at end of file |
Index: trunk/extensions/CollabWatchlist/sql/collabwatchlistrevisiontag.sql |
— | — | @@ -7,8 +7,10 @@ |
8 | 8 | CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/collabwatchlistrevisiontag ( |
9 | 9 | -- The id of this entry |
10 | 10 | rrt_id integer unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT, |
11 | | - -- Foreign key to change_tag.ct_id |
12 | | - ct_id integer unsigned NOT NULL, |
| 11 | + -- See change_tag.ct_tag |
| 12 | + ct_tag varbinary(255) NOT NULL, |
| 13 | + -- See change_tag.ct_rc_id |
| 14 | + ct_rc_id int NOT NULL default 0, |
13 | 15 | -- Foreign key to collabwatchlist.rl_id |
14 | 16 | rl_id integer unsigned NOT NULL, |
15 | 17 | -- Foreign key to user.user_id |
— | — | @@ -17,5 +19,5 @@ |
18 | 20 | -- Comment for the tag |
19 | 21 | rrt_comment varchar(255), |
20 | 22 | |
21 | | - UNIQUE KEY (ct_id, rl_id) |
22 | | -) /*$wgDBTableOptions*/; |
\ No newline at end of file |
| 23 | + UNIQUE KEY (ct_tag, ct_rc_id, rl_id) |
| 24 | +) /*$wgDBTableOptions*/; |
Index: trunk/extensions/CollabWatchlist/sql/patch-collabwatchlist_noctid.sql |
— | — | @@ -0,0 +1,11 @@ |
| 2 | +-- Remove the ct_id column which was introduced in a previous revision |
| 3 | +-- This is intended to be used manually as there does not seem to be |
| 4 | +-- support |
| 5 | + |
| 6 | +ALTER TABLE /*$wgDBprefix*/collabwatchlistrevisiontag |
| 7 | + ADD ct_tag varbinary(255) NOT NULL; |
| 8 | +ALTER TABLE /*$wgDBprefix*/collabwatchlistrevisiontag |
| 9 | + ADD ct_rc_id int NOT NULL default 0; |
| 10 | +UPDATE /*$wgDBprefix*/collabwatchlistrevisiontag cwlrt set ct_rc_id = (select ct.ct_rc_id from change_tag ct where ct.ct_id = cwlrt.ct_id), ct_tag = (select ct.ct_tag from change_tag ct where ct.ct_id = cwlrt.ct_id); |
| 11 | +ALTER TABLE /*$wgDBprefix*/change_tag |
| 12 | + DROP ct_id; |
Property changes on: trunk/extensions/CollabWatchlist/sql/patch-collabwatchlist_noctid.sql |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 13 | + native |
Index: trunk/extensions/CollabWatchlist/tests/CollabWatchlistTest.php |
— | — | @@ -0,0 +1,11 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class CollabWatchlistTest extends PHPUnit_Framework_TestCase { |
| 5 | + public function testCategoryTree() { |
| 6 | + $catTree = new CategoryTreeManip(); |
| 7 | + $catNames = array( 'Test', 'Category:Root' ); |
| 8 | + $catTree->initialiseFromCategoryNames( $catNames ); |
| 9 | + //var_dump( $catTree->getEnabledCategoryNames() ); |
| 10 | + $catTree->printTree(); |
| 11 | + } |
| 12 | +} |
Property changes on: trunk/extensions/CollabWatchlist/tests/CollabWatchlistTest.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 13 | + native |
Index: trunk/extensions/CollabWatchlist/CollabWatchlist.php |
— | — | @@ -46,15 +46,16 @@ |
47 | 47 | $wgHooks['GetPreferences'][] = 'fnCollabWatchlistPreferences'; |
48 | 48 | |
49 | 49 | function fnCollabWatchlistDbSchema() { |
| 50 | + global $updater; |
50 | 51 | $wgSql = dirname(__FILE__) . '/sql/'; |
51 | 52 | if ( $updater === null ) { // <= 1.16 support |
52 | | - global $wgExtNewTables; |
| 53 | + global $wgExtNewTables, $wgExtNewFields; |
53 | 54 | $wgExtNewTables[] = array('collabwatchlist', $wgSql . 'collabwatchlist.sql'); |
54 | 55 | $wgExtNewTables[] = array('collabwatchlistuser', $wgSql . 'collabwatchlistuser.sql'); |
55 | 56 | $wgExtNewTables[] = array('collabwatchlistcategory', $wgSql . 'collabwatchlistcategory.sql'); |
56 | 57 | $wgExtNewTables[] = array('collabwatchlistrevisiontag', $wgSql . 'collabwatchlistrevisiontag.sql'); |
57 | 58 | $wgExtNewTables[] = array('collabwatchlisttag', $wgSql . 'collabwatchlisttag.sql'); |
58 | | - $wgExtNewFields[] = array('change_tag', 'ct_id', $wgSql . 'patch-change_tag_id.sql'); |
| 59 | + $wgExtNewFields[] = array('collabwatchlistrevisiontag', 'ct_rc_id', $wgSql . 'patch-collabwatchlist_noctid.sql'); |
59 | 60 | } else { // >= 1.17 support |
60 | 61 | $updater->addExtensionUpdate( array ( 'addTable', 'collabwatchlist', |
61 | 62 | $wgSql . 'collabwatchlist.sql', true ) ); |
— | — | @@ -66,8 +67,8 @@ |
67 | 68 | $wgSql . 'collabwatchlistrevisiontag.sql', true ) ); |
68 | 69 | $updater->addExtensionUpdate( array ( 'addTable', 'collabwatchlisttag', |
69 | 70 | $wgSql . 'collabwatchlisttag.sql', true ) ); |
70 | | - $updater->addExtensionUpdate( array( 'modifyField', 'change_tag', 'ct_id', |
71 | | - $wgSql . 'patch-change_tag_id.sql', true ) ); |
| 71 | + $updater->addExtensionUpdate( array( 'modifyField', 'collabwatchlistrevisiontag', 'ct_rc_id', |
| 72 | + $wgSql . 'patch-collabwatchlist_noctid.sql', true ) ); |
72 | 73 | } |
73 | 74 | return true; |
74 | 75 | } |
— | — | @@ -81,9 +82,23 @@ |
82 | 83 | return true; |
83 | 84 | } |
84 | 85 | |
| 86 | +// You might want to disable that, as it causes quite a bit of database |
| 87 | +// and (if enabled) cache load and size |
| 88 | +$wgCollabWatchlistRecursiveCatScan = true; |
| 89 | +// The depth of the category tree we are building. -1 means infinite |
| 90 | +// 0 fetches no child categories at all |
| 91 | +$wgCollabWatchlistRecursiveCatMaxDepth = -1; |
85 | 92 | $wgCollabWatchlistNSPrefix = 'CollabWatchlist'; |
86 | 93 | $wgCollabWatchlistPermissionDeniedPage = 'CollabWatchlistPermissionDenied'; |
87 | 94 | |
| 95 | +# Unit tests |
| 96 | +$wgHooks['UnitTestsList'][] = 'efCollabWatchlistUnitTests'; |
| 97 | + |
| 98 | +function efCollabWatchlistUnitTests( &$files ) { |
| 99 | + $files[] = dirname( __FILE__ ) . '/tests/CollabWatchlistTest.php'; |
| 100 | + return true; |
| 101 | +} |
| 102 | + |
88 | 103 | /**#@+ |
89 | 104 | * Collaborative watchlist user types |
90 | 105 | * This defines constants for the collabwatchlistuser.rlu_type |
Index: trunk/extensions/CollabWatchlist/includes/SpecialCollabWatchlist.php |
— | — | @@ -260,9 +260,8 @@ |
261 | 261 | } else { |
262 | 262 | $filter = 'NOT EXISTS '; |
263 | 263 | } |
264 | | - $filter .= '(select ct_rc_id from change_tag |
265 | | - JOIN collabwatchlistrevisiontag ON collabwatchlistrevisiontag.ct_id = change_tag.ct_id |
266 | | - WHERE ct_rc_id = recentchanges.rc_id AND ct_tag '; |
| 264 | + $filter .= '(SELECT cwlrt.ct_rc_id FROM collabwatchlistrevisiontag cwlrt |
| 265 | + WHERE cwlrt.ct_rc_id = recentchanges.rc_id AND cwlrt.ct_tag '; |
267 | 266 | if ( count( $tagFilter ) > 1 ) |
268 | 267 | $filter .= 'IN (' . $dbr->makeList( $tagFilter ) . '))'; |
269 | 268 | else |
— | — | @@ -582,11 +581,11 @@ |
583 | 582 | } |
584 | 583 | // $table, $vars, $conds='', $fname = 'Database::select', $options = array(), $join_conds = array() |
585 | 584 | $res = $dbr->select( array( 'change_tag', 'collabwatchlistrevisiontag', 'user' ), # Tables |
586 | | - array( 'rl_id', 'ct_tag', 'collabwatchlistrevisiontag.user_id', 'user_name', 'rrt_comment' ), # Fields |
587 | | - array( 'ct_rev_id' => $rev_id ) + $cond, # Conditions |
| 585 | + array( 'rl_id', 'collabwatchlistrevisiontag.ct_tag', 'collabwatchlistrevisiontag.user_id', 'user_name', 'rrt_comment' ), # Fields |
| 586 | + array( 'change_tag.ct_rev_id' => $rev_id ) + $cond, # Conditions |
588 | 587 | __METHOD__, array(), |
589 | 588 | # Join conditions |
590 | | - array( 'collabwatchlistrevisiontag' => array( 'JOIN', 'change_tag.ct_id = collabwatchlistrevisiontag.ct_id' ), |
| 589 | + array( 'collabwatchlistrevisiontag' => array( 'JOIN', 'change_tag.ct_rc_id = collabwatchlistrevisiontag.ct_rc_id AND change_tag.ct_tag = collabwatchlistrevisiontag.ct_tag' ), |
591 | 590 | 'user' => array( 'JOIN', 'collabwatchlistrevisiontag.user_id = user.user_id' ) |
592 | 591 | ) |
593 | 592 | ); |
— | — | @@ -608,6 +607,7 @@ |
609 | 608 | * @return String: An SQL clause usable in the conditions parameter of $db->select() |
610 | 609 | */ |
611 | 610 | function wlGetFilterClauseForCollabWatchlistIds( $rl_ids, $catNameCol, $pageIdCol ) { |
| 611 | + global $wgCollabWatchlistRecursiveCatScan; |
612 | 612 | $excludedCatPageIds = array(); |
613 | 613 | $includedCatPageIds = array(); |
614 | 614 | $includedPageIds = array(); |
— | — | @@ -632,8 +632,9 @@ |
633 | 633 | } |
634 | 634 | } |
635 | 635 | |
636 | | - if ( $includedCatPageIds ) { |
| 636 | + if ( $wgCollabWatchlistRecursiveCatScan && $includedCatPageIds ) { |
637 | 637 | $catTree = new CategoryTreeManip(); |
| 638 | + $catTree->setMaxDepth($wgCollabWatchlistRecursiveCatScan); |
638 | 639 | $catTree->initialiseFromCategoryNames( array_values( $includedCatPageIds ) ); |
639 | 640 | $catTree->disableCategoryIds( array_keys( $excludedCatPageIds ) ); |
640 | 641 | $enabledCategoryNames = $catTree->getEnabledCategoryNames(); |
Index: trunk/extensions/CollabWatchlist/includes/CategoryTreeManip.php |
— | — | @@ -10,19 +10,31 @@ |
11 | 11 | * @author fhackenberger |
12 | 12 | */ |
13 | 13 | class CategoryTreeManip { |
| 14 | + const CACHE_KEY = 'categorytree'; |
| 15 | + const CACHE_KEY_CHILDCATEGORIES = 'childcats'; |
| 16 | + const CACHE_KEY_CATEGORYPAGEID = 'catpageid'; |
| 17 | + // How long the cache for child categories and category page ids should last |
| 18 | + protected $cacheDurationSec; |
| 19 | + // Whether the cache should be used to speed up building the tree |
| 20 | + // We always fill the cache, though |
| 21 | + protected $cacheEnabled = true; |
| 22 | + // The depth of the tree we are building. -1 means infinite |
| 23 | + // 0 fetches no child categories at all |
| 24 | + protected $maxDepth = -1; |
| 25 | + protected $root; |
| 26 | + public $name; |
| 27 | + public $id; |
| 28 | + // Maps from the page id of a category to instances of this class |
| 29 | + protected $catPageIdToNode = array(); |
| 30 | + protected $parents = array(); |
| 31 | + protected $enabled = true; |
| 32 | + protected $children = array(); |
14 | 33 | |
15 | | - var $root; |
16 | | - var $name; |
17 | | - var $id; |
18 | | - var $catPageIdToNode = array(); |
19 | | - var $parents = array(); |
20 | | - var $enabled = true; |
21 | | - var $children = array(); |
22 | | - |
23 | 34 | /** |
24 | 35 | * Constructor |
25 | 36 | */ |
26 | 37 | function __construct( $id = null, $name = null, $root = null, $parents = array() ) { |
| 38 | + $this->cacheDurationSec = 15 * 60; |
27 | 39 | $this->id = $id; |
28 | 40 | $this->name = $name; |
29 | 41 | if ( !is_null( $root ) ) { |
— | — | @@ -32,10 +44,26 @@ |
33 | 45 | } |
34 | 46 | $this->parents = $parents; |
35 | 47 | } |
| 48 | + |
| 49 | + public function getCacheEnabled() { |
| 50 | + return $this->cacheEnabled; |
| 51 | + } |
| 52 | + |
| 53 | + public function setCacheEnabled( $enabled ) { |
| 54 | + $this->cacheEnabled = $enabled; |
| 55 | + } |
| 56 | + |
| 57 | + public function getMaxDepth() { |
| 58 | + return $this->maxDepth; |
| 59 | + } |
| 60 | + |
| 61 | + public function setMaxDepth( $maxDepth ) { |
| 62 | + $this->maxDepth = $maxDepth; |
| 63 | + } |
36 | 64 | |
37 | 65 | private function addChildren( $children ) { |
38 | 66 | if ( !is_array( $children ) ) |
39 | | - throw new Exception( 'Argument must be an array' ); |
| 67 | + throw new Exception( 'Argument must be an array' ); |
40 | 68 | foreach ( $children as $child ) { |
41 | 69 | $this->children[$child->id] = $child; |
42 | 70 | } |
— | — | @@ -43,7 +71,7 @@ |
44 | 72 | |
45 | 73 | private function addParents( $parents ) { |
46 | 74 | if ( !is_array( $parents ) ) |
47 | | - throw new Exception( 'Argument must be an array' ); |
| 75 | + throw new Exception( 'Argument must be an array' ); |
48 | 76 | foreach ( $parents as $parent ) { |
49 | 77 | $this->parents[$parent->id] = $parent; |
50 | 78 | } |
— | — | @@ -71,7 +99,7 @@ |
72 | 100 | |
73 | 101 | private function recursiveDisable( $visitedNodeIds = array() ) { |
74 | 102 | if ( !$this->enabled || array_key_exists( $this->id, $visitedNodeIds ) ) |
75 | | - return; # Break the recursion |
| 103 | + return; # Break the recursion |
76 | 104 | $this->enabled = false; |
77 | 105 | $visitedNodeIds[] = $this->id; |
78 | 106 | foreach ( $this->children as $cat ) { |
— | — | @@ -105,7 +133,7 @@ |
106 | 134 | private function recursiveGetEnabledNodeMap( &$foundNodes = array() ) { |
107 | 135 | if ( isset( $this->id ) ) { |
108 | 136 | if ( !$this->enabled || array_key_exists( $this->id, $foundNodes ) ) |
109 | | - return $foundNodes; # Break the recursion |
| 137 | + return $foundNodes; # Break the recursion |
110 | 138 | $foundNodes[$this->id] = $this; |
111 | 139 | } |
112 | 140 | foreach ( $this->children as $cat ) { |
— | — | @@ -121,12 +149,29 @@ |
122 | 150 | */ |
123 | 151 | public function getNodeForCatPageId( $catPageId ) { |
124 | 152 | if ( array_key_exists( $catPageId, $this->root->catPageIdToNode ) ) |
125 | | - return $this->root->catPageIdToNode[$catPageId]; |
| 153 | + return $this->root->catPageIdToNode[$catPageId]; |
126 | 154 | } |
127 | 155 | |
128 | 156 | private function addNode( $node ) { |
129 | 157 | $this->root->catPageIdToNode[$node->id] = $node; |
130 | 158 | } |
| 159 | + |
| 160 | + public function printTree() { |
| 161 | + print "all categories:\n"; |
| 162 | + foreach( $this->catPageIdToNode as $catPageId => $node ) { |
| 163 | + print $catPageId . ": " . $node->name . " enabled: " . $node->enabled . "\n"; |
| 164 | + } |
| 165 | + print "child categories:\n"; |
| 166 | + $this->printTreeRecursive( $this->root, '' ); |
| 167 | + } |
| 168 | + |
| 169 | + protected function printTreeRecursive( $node, $prefix ) { |
| 170 | + if( $node->id ) |
| 171 | + print $node->id . ": " . $node->name . " enabled: " . $node->enabled . "\n"; |
| 172 | + foreach( $node->children as $child ) { |
| 173 | + $this->printTreeRecursive( $child, $prefix . ' ' ); |
| 174 | + } |
| 175 | + } |
131 | 176 | |
132 | 177 | /** Build the category tree, given a list of category names. |
133 | 178 | * All categories and subcategories are enabled by default. |
— | — | @@ -135,47 +180,46 @@ |
136 | 181 | * @return |
137 | 182 | */ |
138 | 183 | public function initialiseFromCategoryNames( $catNames ) { |
139 | | - $dbr = wfGetDB( DB_SLAVE ); |
| 184 | + $currDepth = 0; |
| 185 | + // This method builds the category tree breadth-first in an |
| 186 | + // iterative fashion. |
140 | 187 | while ( $catNames ) { |
141 | | - $res = $dbr->select( array( 'categorylinks', 'page' ), # Tables |
142 | | - array( 'cl_to AS parName', 'cl_from AS childId', 'page_title AS childName' ), # Fields |
143 | | - array( 'cl_to' => $catNames, 'page_namespace' => NS_CATEGORY ), # Conditions |
144 | | - __METHOD__, array(), |
145 | | - # Join conditions |
146 | | - array( 'page' => array( 'JOIN', 'page_id = cl_from' ) ) |
147 | | - ); |
| 188 | + if($this->maxDepth > 0 && $currDepth > $this->maxDepth) |
| 189 | + break; |
| 190 | + // Get a list of child categories (array of hashmaps) |
| 191 | + $res = $this->getChildCategories( $catNames ); |
| 192 | + // Maps parent category name -> array ( array ( child id , child name ) ) |
148 | 193 | $parentList = array(); |
| 194 | + // Maps child category id -> array( child id, array ( parent name, ... ) ) |
149 | 195 | $childList = array(); |
| 196 | + // Build $parentList and $childList |
150 | 197 | foreach ( $res as $row ) { |
151 | | - $parentList[$row->parName][] = array( $row->childId, $row->childName ); |
152 | | - if ( array_key_exists( $row->childId, $childList ) ) { |
153 | | - $childEntry = $childList[$row->childId]; |
154 | | - $childEntry[1][] = $row->parName; |
| 198 | + $parentList[$row['parName']][] = array( $row['childId'], $row['childName'] ); |
| 199 | + if ( array_key_exists( $row['childId'], $childList ) ) { |
| 200 | + $childEntry = $childList[$row['childId']]; |
| 201 | + $childEntry[1][] = $row['parName']; |
155 | 202 | } else { |
156 | | - $childList[$row->childId] = array( $row->childName, array( $row->parName ) ); |
| 203 | + $childList[$row['childId']] = array( $row['childName'], array( $row['parName'] ) ); |
157 | 204 | } |
158 | 205 | } |
159 | 206 | |
| 207 | + // Fetch the page ids of the $catNames and build the node objects for them |
160 | 208 | if ( !isset( $parentNameToNode ) && !empty( $parentList ) ) { |
161 | | - // Fetch the page ids of the $catNames and add the parent categories if needed |
162 | | - $res = $dbr->select( array( 'page' ), # Tables |
163 | | - array( 'page_id, page_title' ), # Fields |
164 | | - array( 'page_title' => array_keys( $parentList ) ) # Conditions |
165 | | - ); |
| 209 | + $res = $this->getCategoryPageIds( array_keys( $parentList ) ); |
166 | 210 | $parentNameToNode = array(); |
167 | 211 | foreach ( $res as $row ) { |
168 | | - $node = $this->getNodeForCatPageId( $row->page_id ); |
| 212 | + $node = $this->getNodeForCatPageId( $row['page_id'] ); |
169 | 213 | if ( !isset( $node ) ) { |
170 | | - $node = new CategoryTreeManip( $row->page_id, $row->page_title, $this->root ); |
| 214 | + $node = new CategoryTreeManip( $row['page_id'], $row['page_title'], $this->root ); |
171 | 215 | $this->addNode( $node ); |
172 | 216 | $this->addChildren( array( $node ) ); |
173 | 217 | } |
174 | | - $parentNameToNode[$row->page_title] = $node; |
| 218 | + $parentNameToNode[$row['page_title']] = $node; |
175 | 219 | } |
176 | 220 | } |
177 | 221 | |
178 | 222 | $newChildNameToNode = array(); |
179 | | - // Add the new child nodes |
| 223 | + // Create and add the new node objects for all child categories |
180 | 224 | foreach ( $childList as $childPageId => $childInfo ) { |
181 | 225 | $childNode = $this->getNodeForCatPageId( $childPageId ); |
182 | 226 | if ( !isset( $childNode ) ) { |
— | — | @@ -193,6 +237,112 @@ |
194 | 238 | // Prepare for the next loop |
195 | 239 | $parentNameToNode = $newChildNameToNode; |
196 | 240 | $catNames = array_keys( $parentNameToNode ); |
| 241 | + $currDepth++; |
197 | 242 | } |
198 | 243 | } |
| 244 | + |
| 245 | + /** Retrieve a list of child categories for all the given category names |
| 246 | + * Uses the cache if enabled |
| 247 | + * @param $catNames Array: List of category names |
| 248 | + * @return Mixed: Array of Maps with keys: parName, childId, childName |
| 249 | + */ |
| 250 | + protected function getChildCategories( $catNames ) { |
| 251 | + global $wgMemc; |
| 252 | + $dbr = wfGetDB( DB_SLAVE ); |
| 253 | + $childList = array(); |
| 254 | + $nonCachedCatNames = array(); |
| 255 | + // Try cache first |
| 256 | + if( $this->cacheEnabled ) { |
| 257 | + foreach( $catNames as $catName ) { |
| 258 | + $key = wfMemcKey( self::CACHE_KEY, self::CACHE_KEY_CHILDCATEGORIES, $catName ); |
| 259 | + $res = $wgMemc->get( $key ); |
| 260 | + if( $res != '' ) { |
| 261 | + $childList = array_merge( $childList, $res ); |
| 262 | + } else { |
| 263 | + $nonCachedCatNames[$catName] = false; |
| 264 | + } |
| 265 | + } |
| 266 | + } else { |
| 267 | + $nonCachedCatNames = array_fill_keys( $catNames, false ); |
| 268 | + } |
| 269 | + |
| 270 | + // Select the child categories of all categories we have not found in the cache |
| 271 | + $res = array(); |
| 272 | + if( !empty( $nonCachedCatNames ) ) { |
| 273 | + // Select the direct child categories of all category names |
| 274 | + // I.e. category name, child category id and child category name |
| 275 | + $res = $dbr->select( array( 'categorylinks', 'page' ), # Tables |
| 276 | + array( 'cl_to AS parName', 'cl_from AS childId', 'page_title AS childName' ), # Fields |
| 277 | + array( 'cl_to' => array_keys($nonCachedCatNames), 'page_namespace' => NS_CATEGORY ), # Conditions |
| 278 | + __METHOD__, array( 'GROUP BY' => 'cl_to' ), # Options |
| 279 | + array( 'page' => array( 'JOIN', 'page_id = cl_from' ) ) # Join conditions |
| 280 | + ); |
| 281 | + } |
| 282 | + |
| 283 | + // Prepare and store cache objects |
| 284 | + $cacheObj = array(); |
| 285 | + foreach ( $res as $row ) { |
| 286 | + unset($nonCachedCatNames[$row->parName]); |
| 287 | + if( !isset( $currCatName ) ) |
| 288 | + $currCatName = $row->parName; |
| 289 | + if( $currCatName != $row->parName ) { |
| 290 | + $key = wfMemcKey( self::CACHE_KEY, self::CACHE_KEY_CHILDCATEGORIES, $currCatName ); |
| 291 | + $wgMemc->set( $key, $cacheObj, $this->cacheDurationSec ); |
| 292 | + $childList = array_merge( $childList, $cacheObj ); |
| 293 | + $cacheObj = array(); |
| 294 | + $currCatName = $row->parName; |
| 295 | + } |
| 296 | + $cacheObj[] = array( 'parName' => $row->parName, 'childId' => $row->childId, 'childName' => $row->childName ); |
| 297 | + } |
| 298 | + // Store the last bunch |
| 299 | + if( !empty( $cacheObj ) ) { |
| 300 | + $key = wfMemcKey( self::CACHE_KEY, self::CACHE_KEY_CHILDCATEGORIES, $currCatName ); |
| 301 | + $wgMemc->set( $key, $cacheObj, $this->cacheDurationSec ); |
| 302 | + $childList = array_merge( $childList, $cacheObj ); |
| 303 | + } |
| 304 | + // Store empty values for leaf categories, otherwise we would query the DB because we did not find a cache entry |
| 305 | + foreach( array_keys($nonCachedCatNames ) as $currCatName ) { |
| 306 | + $key = wfMemcKey( self::CACHE_KEY, self::CACHE_KEY_CHILDCATEGORIES, $currCatName ); |
| 307 | + $wgMemc->set( $key, array(), $this->cacheDurationSec ); |
| 308 | + } |
| 309 | + return $childList; |
| 310 | + } |
| 311 | + |
| 312 | + /** Retrieve the page ids of the category pages for the given categories |
| 313 | + * |
| 314 | + * @param $catNames Array: A list of category names you would like to query for |
| 315 | + * @return Array: An array of maps with keys: page_id, page_title |
| 316 | + */ |
| 317 | + protected function getCategoryPageIds( $catNames ) { |
| 318 | + global $wgMemc; |
| 319 | + $dbr = wfGetDB( DB_SLAVE ); |
| 320 | + $pageInfo = array(); |
| 321 | + $nonCachedCatNames = array(); |
| 322 | + // Try cache first |
| 323 | + if( $this->cacheEnabled ) { |
| 324 | + foreach( $catNames as $catName ) { |
| 325 | + $res = $wgMemc->get( wfMemcKey( self::CACHE_KEY, self::CACHE_KEY_CATEGORYPAGEID, $catName ) ); |
| 326 | + if( $res != '' ) { |
| 327 | + $pageInfo[] = array( 'page_id' => $res, 'page_title' => $catName ); |
| 328 | + } else { |
| 329 | + $nonCachedCatNames[] = $catName; |
| 330 | + } |
| 331 | + } |
| 332 | + } |
| 333 | + // Select the child categories of all categories we have not found in the cache |
| 334 | + $res = array(); |
| 335 | + if( !empty( $nonCachedCatNames ) ) { |
| 336 | + $res = $dbr->select( array( 'page' ), # Tables |
| 337 | + array( 'page_id, page_title' ), # Fields |
| 338 | + array( 'page_title' => $nonCachedCatNames ) # Conditions |
| 339 | + ); |
| 340 | + } |
| 341 | + // Prepare and store cache object |
| 342 | + foreach ( $res as $row ) { |
| 343 | + $pageInfo[] = array( 'page_id' => $row->page_id, 'page_title' => $row->page_title ); |
| 344 | + $wgMemc->set( wfMemcKey( self::CACHE_KEY, self::CACHE_KEY_CATEGORYPAGEID, $row->page_title ), $row->page_id, $this->cacheDurationSec ); |
| 345 | + } |
| 346 | + return $pageInfo; |
| 347 | + } |
| 348 | + |
199 | 349 | } |
Index: trunk/extensions/CollabWatchlist/includes/CollabWatchlistEditor.php |
— | — | @@ -789,8 +789,8 @@ |
790 | 790 | } |
791 | 791 | } |
792 | 792 | // Add the tagged revisions to the collaborative watchlist |
793 | | - $sql = 'INSERT IGNORE INTO collabwatchlistrevisiontag (ct_id, rl_id, user_id, rrt_comment) |
794 | | - SELECT ct_id, ' . $dbw->strencode( $rlId ) . ',' . |
| 793 | + $sql = 'INSERT IGNORE INTO collabwatchlistrevisiontag (ct_rc_id, ct_tag, rl_id, user_id, rrt_comment) |
| 794 | + SELECT ct_rc_id, ct_tag, ' . $dbw->strencode( $rlId ) . ',' . |
795 | 795 | $dbw->strencode( $userId ) . ',' . |
796 | 796 | $dbw->addQuotes( $comment ) . ' FROM change_tag WHERE ct_tag = ? AND ct_rc_id '; |
797 | 797 | if ( count( $rcIds ) > 1 ) { |
— | — | @@ -817,9 +817,7 @@ |
818 | 818 | $rcIds[] = $info['rc_id']; |
819 | 819 | } |
820 | 820 | // Remove the tag from the collaborative watchlist |
821 | | - $sql = 'delete collabwatchlistrevisiontag from collabwatchlistrevisiontag JOIN change_tag |
822 | | - ON change_tag.ct_id = collabwatchlistrevisiontag.ct_id |
823 | | - WHERE ct_tag = ? AND ct_rc_id '; |
| 821 | + $sql = 'DELETE FROM collabwatchlistrevisiontag WHERE ct_tag = ? AND ct_rc_id '; |
824 | 822 | if ( count( $rcIds ) > 1 ) { |
825 | 823 | $sql .= 'IN (' . $dbw->makeList( $rcIds ) . ')'; |
826 | 824 | $params = array( $tag ); |