r69082 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r69081‎ | r69082 | r69083 >
Date:05:47, 6 July 2010
Author:aaron
Status:deferred
Tags:
Comment:
* Split off inclusion handling code into FRInclusionManager class. $wgParser->fr_isStable removed.
* Replaced insane fr_ImageSHA1Keys with fr_fileSHA1Keys (new format).
* Removed maybeUpdateMainCache() and reparse when needed by form.
* Defer stable-only cache dependancy updates to cache regeneration.
* Renamed FRLinksUpdate -> FRDependencyUpdate.
* Fix for filetimestamp=x params not showing after autoreview.
* Removed stable categories feature for performance/caching reasons.
* Removed code duplication in galleryFindStableFileTime.
* Removed unused FlaggedRevs::getLocalFile.
* parseStableText():
** Take $title instead of $article
** Query the template/file versions up-front rather then one at a time in hooks
* FlaggedRevsion:
* Split off code from newFromStable() into determineStable() function
* Consolidated inclusion INSERT code duplication into insertOn()
* Removed fr_ prefix from FlaggedRevision via array construction
Modified paths:
  • /trunk/extensions/FlaggedRevs/FRCacheUpdate.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/FRDependencyUpdate.php (added) (history)
  • /trunk/extensions/FlaggedRevs/FRInclusionManager.php (added) (history)
  • /trunk/extensions/FlaggedRevs/FRLinksUpdate.php (deleted) (history)
  • /trunk/extensions/FlaggedRevs/FlaggedArticleView.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/FlaggedRevision.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/FlaggedRevs.class.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/FlaggedRevs.hooks.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/FlaggedRevs.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/api/ApiReview.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/forms/PageStabilityForm.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/forms/RevisionReviewForm.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/maintenance/reviewAllPages.inc (modified) (history)
  • /trunk/extensions/FlaggedRevs/maintenance/updateTracking.inc (modified) (history)

Diff [purge]

Index: trunk/extensions/FlaggedRevs/FRLinksUpdate.php
@@ -1,194 +0,0 @@
2 -<?php
3 -/**
4 - * Class containing link update methods and job construction
5 - * for the special case of refreshing page links due to templates
6 - * contained only in the stable version of pages
7 - *
8 - * @TODO: have flaggedrevs_templatelinks table for stable versions
9 - * to be more specific in what pages to effect
10 - */
11 -class FRLinksUpdate {
12 - protected $title;
13 - protected $sLinks, $cLinks;
14 - protected $sTemplates, $cTemplates;
15 - protected $sImages, $cImages;
16 - protected $sCategories, $cCategories;
17 -
18 - // @TODO: replace raw $linksUpdate field accesses
19 - public function __construct( LinksUpdate $linksUpdate, ParserOutput $stableOutput ) {
20 - $this->title = $linksUpdate->mTitle;
21 - # Stable version links
22 - $this->sLinks = $stableOutput->getLinks();
23 - $this->sTemplates = $stableOutput->getTemplates();
24 - $this->sImages = $stableOutput->getImages();
25 - $this->sCategories = $stableOutput->getCategories();
26 - # Current version links
27 - $this->cLinks = $linksUpdate->mLinks;
28 - $this->cTemplates = $linksUpdate->mTemplates;
29 - $this->cImages = $linksUpdate->mImages;
30 - $this->cCategories = $linksUpdate->mCategories;
31 - }
32 -
33 - public function doUpdate() {
34 - $links = array();
35 - # Get any links that are only in the stable version...
36 - foreach ( $this->sLinks as $ns => $titles ) {
37 - foreach ( $titles as $title => $id ) {
38 - if ( !isset( $this->cLinks[$ns] )
39 - || !isset( $this->cLinks[$ns][$title] ) )
40 - {
41 - self::addLink( $links, $ns, $title );
42 - }
43 - }
44 - }
45 - # Get any images that are only in the stable version...
46 - foreach ( $this->sImages as $image => $n ) {
47 - if ( !isset( $this->cImages[$image] ) ) {
48 - self::addLink( $links, NS_FILE, $image );
49 - }
50 - }
51 - # Get any templates that are only in the stable version...
52 - foreach ( $this->sTemplates as $ns => $titles ) {
53 - foreach ( $titles as $title => $id ) {
54 - if ( !isset( $this->cTemplates[$ns] )
55 - || !isset( $this->cTemplates[$ns][$title] ) )
56 - {
57 - self::addLink( $links, $ns, $title );
58 - }
59 - }
60 - }
61 - # Get any categories that are only in the stable version...
62 - foreach ( $this->sCategories as $category => $sort ) {
63 - if ( !isset( $this->cCategories[$category] ) ) {
64 - self::addLink( $links, NS_CATEGORY, $category );
65 - }
66 - }
67 - $pageId = $this->title->getArticleId();
68 - # Get any link tracking changes
69 - $existing = self::getExistingLinks( $pageId );
70 - $insertions = self::getLinkInsertions( $existing, $links, $pageId );
71 - $deletions = self::getLinkDeletions( $existing, $links );
72 - # Delete removed links
73 - $dbw = wfGetDB( DB_MASTER );
74 - if ( $clause = self::makeWhereFrom2d( $deletions ) ) {
75 - $where = array( 'ftr_from' => $pageId );
76 - $where[] = $clause;
77 - $dbw->delete( 'flaggedrevs_tracking', $where, __METHOD__ );
78 - }
79 - # Add any new links
80 - if ( count( $insertions ) ) {
81 - $dbw->insert( 'flaggedrevs_tracking', $insertions, __METHOD__, 'IGNORE' );
82 - }
83 - }
84 -
85 - protected static function addLink( array &$links, $ns, $dbKey ) {
86 - if ( !isset( $links[$ns] ) ) {
87 - $links[$ns] = array();
88 - }
89 - $links[$ns][$dbKey] = 1;
90 - }
91 -
92 - protected static function getExistingLinks( $pageId ) {
93 - $dbr = wfGetDB( DB_SLAVE );
94 - $res = $dbr->select( 'flaggedrevs_tracking',
95 - array( 'ftr_namespace', 'ftr_title' ),
96 - array( 'ftr_from' => $pageId ),
97 - __METHOD__ );
98 - $arr = array();
99 - while ( $row = $dbr->fetchObject( $res ) ) {
100 - if ( !isset( $arr[$row->ftr_namespace] ) ) {
101 - $arr[$row->ftr_namespace] = array();
102 - }
103 - $arr[$row->ftr_namespace][$row->ftr_title] = 1;
104 - }
105 - return $arr;
106 - }
107 -
108 - protected static function makeWhereFrom2d( &$arr ) {
109 - $lb = new LinkBatch();
110 - $lb->setArray( $arr );
111 - return $lb->constructSet( 'ftr', wfGetDB( DB_SLAVE ) );
112 - }
113 -
114 - protected static function getLinkInsertions( $existing, $new, $pageId ) {
115 - $arr = array();
116 - foreach ( $new as $ns => $dbkeys ) {
117 - $diffs = isset( $existing[$ns] ) ?
118 - array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys;
119 - foreach ( $diffs as $dbk => $id ) {
120 - $arr[] = array(
121 - 'ftr_from' => $pageId,
122 - 'ftr_namespace' => $ns,
123 - 'ftr_title' => $dbk
124 - );
125 - }
126 - }
127 - return $arr;
128 - }
129 -
130 - protected static function getLinkDeletions( $existing, $new ) {
131 - $del = array();
132 - foreach ( $existing as $ns => $dbkeys ) {
133 - if ( isset( $new[$ns] ) ) {
134 - $del[$ns] = array_diff_key( $existing[$ns], $new[$ns] );
135 - } else {
136 - $del[$ns] = $existing[$ns];
137 - }
138 - }
139 - return $del;
140 - }
141 -
142 - /*
143 - * Refresh links of all pages with only the stable version
144 - * including this page. This will be in a separate transaction.
145 - * @param Title
146 - */
147 - public static function queueRefreshLinksJobs( Title $title ) {
148 - global $wgUpdateRowsPerJob;
149 - wfProfileIn( __METHOD__ );
150 - # Fetch the IDs
151 - $dbr = wfGetDB( DB_SLAVE );
152 - $res = $dbr->select( 'flaggedrevs_tracking',
153 - 'ftr_from',
154 - array( 'ftr_namespace' => $title->getNamespace(),
155 - 'ftr_title' => $title->getDBkey() ),
156 - __METHOD__
157 - );
158 - $numRows = $res->numRows();
159 - if ( !$numRows ) {
160 - wfProfileOut( __METHOD__ );
161 - return; // sanity check
162 - }
163 - $numBatches = ceil( $numRows / $wgUpdateRowsPerJob );
164 - $realBatchSize = ceil( $numRows / $numBatches );
165 - $start = false;
166 - $jobs = array();
167 - do {
168 - $first = $last = false; // first/last page_id of this batch
169 - # Get $realBatchSize items (or less if not enough)...
170 - for ( $i = 0; $i < $realBatchSize; $i++ ) {
171 - $row = $res->fetchRow();
172 - # Is there another row?
173 - if ( $row ) {
174 - $id = $row[0];
175 - $last = $id; // $id is the last page_id of this batch
176 - if ( $first === false ) {
177 - $first = $id; // set first page_id of this batch
178 - }
179 - # Out of rows?
180 - } else {
181 - $id = false;
182 - break;
183 - }
184 - }
185 - # Insert batch into the queue if there is anything there
186 - if ( $first ) {
187 - $params = array( 'start' => $first, 'end' => $last, );
188 - $jobs[] = new RefreshLinksJob2( $title, $params );
189 - }
190 - $start = $id; // Where the last ID left off
191 - } while ( $start );
192 - Job::batchInsert( $jobs );
193 - wfProfileOut( __METHOD__ );
194 - }
195 -}
Index: trunk/extensions/FlaggedRevs/FlaggedRevs.php
@@ -302,11 +302,13 @@
303303
304304 $wgAutoloadClasses['FlaggedRevs'] = $dir . 'FlaggedRevs.class.php';
305305 $wgAutoloadClasses['FRUserCounters'] = $dir . 'FRUserCounters.php';
 306+$wgAutoloadClasses['FRInclusionManager'] = $dir . 'FRInclusionManager.php';
306307 $wgAutoloadClasses['FlaggedRevsHooks'] = $dir . 'FlaggedRevs.hooks.php';
307308 $wgAutoloadClasses['FlaggedRevsLogs'] = $dir . 'FlaggedRevsLogs.php';
308309 $wgAutoloadClasses['FRCacheUpdate'] = $dir . 'FRCacheUpdate.php';
309310 $wgAutoloadClasses['FRCacheUpdateJob'] = $dir . 'FRCacheUpdate.php';
310 -$wgAutoloadClasses['FRLinksUpdate'] = $dir . 'FRLinksUpdate.php';
 311+$wgAutoloadClasses['FRSquidUpdate'] = $dir . 'FRCacheUpdate.php';
 312+$wgAutoloadClasses['FRDependencyUpdate'] = $dir . 'FRDependencyUpdate.php';
311313
312314 # Special case cache invalidations
313315 $wgJobClasses['flaggedrevs_CacheUpdate'] = 'FRCacheUpdateJob';
@@ -463,8 +465,8 @@
464466 # Parser hooks, selects the desired images/templates
465467 $wgHooks['ParserClearState'][] = 'FlaggedRevsHooks::parserAddFields';
466468 $wgHooks['BeforeParserFetchTemplateAndtitle'][] = 'FlaggedRevsHooks::parserFetchStableTemplate';
467 -$wgHooks['BeforeParserMakeImageLinkObj'][] = 'FlaggedRevsHooks::parserMakeStableFileLink';
468 -$wgHooks['BeforeGalleryFindFile'][] = 'FlaggedRevsHooks::galleryFindStableFileTime';
 469+$wgHooks['BeforeParserMakeImageLinkObj'][] = 'FlaggedRevsHooks::parserFetchStableFile';
 470+$wgHooks['BeforeGalleryFindFile'][] = 'FlaggedRevsHooks::galleryFetchStableFile';
469471 # Additional parser versioning
470472 $wgHooks['ParserAfterTidy'][] = 'FlaggedRevsHooks::parserInjectTimestamps';
471473 $wgHooks['OutputPageParserOutput'][] = 'FlaggedRevsHooks::outputInjectTimestamps';
@@ -487,7 +489,7 @@
488490 $wgHooks['HTMLCacheUpdate::doUpdate'][] = 'FlaggedRevsHooks::doCacheUpdate';
489491 # Updates stable version tracking data
490492 $wgHooks['LinksUpdate'][] = 'FlaggedRevsHooks::onLinksUpdate';
491 -# Clear dead config rows
 493+# Clear/update config rows
492494 $wgHooks['ArticleDeleteComplete'][] = 'FlaggedRevsHooks::onArticleDelete';
493495 $wgHooks['ArticleRevisionVisibilitySet'][] = 'FlaggedRevsHooks::onRevisionDelete';
494496 $wgHooks['ArticleRevisionVisiblitySet'][] = 'FlaggedRevsHooks::onRevisionDelete'; // B/C for now
Index: trunk/extensions/FlaggedRevs/maintenance/reviewAllPages.inc
@@ -35,13 +35,15 @@
3636 # Is it already reviewed?
3737 $frev = FlaggedRevision::newFromTitle( $title, $row->page_latest, FR_MASTER );
3838 # Should exist, but to be safe...
39 - if( !$frev && $rev && FlaggedRevs::inReviewNamespace($title) ) {
 39+ if( !$frev && $rev && FlaggedRevs::inReviewNamespace( $title ) ) {
4040 $text = $rev->getText();
4141 $article = new Article( $title );
42 - FlaggedRevs::autoReviewEdit( $article, $user, $text, $rev, $flags, true );
 42+ $db->begin();
 43+ FlaggedRevs::autoReviewEdit( $article, $user, $rev, $flags, true );
 44+ # Update page and tracking tables and clear cache
 45+ FlaggedRevs::stableVersionUpdates( $title );
 46+ $dbw->commit();
4347 $changed++;
44 - # Purge squid for this page only
45 - $article->getTitle()->purgeSquid();
4648 }
4749 $count++;
4850 }
Index: trunk/extensions/FlaggedRevs/maintenance/updateTracking.inc
@@ -121,17 +121,19 @@
122122 $title = Title::newFromRow( $row );
123123 $article = new Article( $title );
124124 # Replaces new fields into flaggedpages
125 - $frev = FlaggedRevision::newFromStable( $title, FR_FOR_UPDATE );
 125+ $frev = FlaggedRevision::determineStable( $title, FR_MASTER );
126126 # Update fp_stable, fp_quality, and fp_reviewed
127127 if( $frev ) {
128 - FlaggedRevs::updateStableVersion( $article, $frev->getRevision(), $row->page_latest );
 128+ FlaggedRevs::updateStableVersion(
 129+ $article, $frev->getRevision(), $row->page_latest );
129130 # Somethings broke? Delete the row...
130131 } else {
131132 FlaggedRevs::clearTrackingRows( $row->page_id );
132133 if( $db->affectedRows() > 0 ) $deleted++;
133134 }
134135 # Get the latest revision
135 - $revRow = $db->selectRow( 'revision', '*', array('rev_page' => $row->page_id), __FUNCTION__,
 136+ $revRow = $db->selectRow( 'revision', '*',
 137+ array('rev_page' => $row->page_id), __FUNCTION__,
136138 array('ORDER BY' => 'rev_timestamp DESC') );
137139 # Correct page_latest if needed (import/files made plenty of bad rows)
138140 if( $revRow ) {
Index: trunk/extensions/FlaggedRevs/FlaggedRevs.class.php
@@ -18,8 +18,6 @@
1919 protected static $patrolNamespaces = array();
2020 # Restriction levels/config
2121 protected static $restrictionLevels = array();
22 - # Temporary process cache variable
23 - protected static $includeVersionCache = array();
2422
2523 protected static $loaded = false;
2624
@@ -210,8 +208,26 @@
211209 global $wgFlaggedRevsHandleIncludes;
212210 return $wgFlaggedRevsHandleIncludes;
213211 }
214 -
 212+
215213 /**
 214+ * Fallback to current templates when no version is specified?
 215+ * @returns bool
 216+ */
 217+ public static function fallbackToCurrentTemplates() {
 218+ global $wgUseCurrentTemplates;
 219+ return $wgUseCurrentTemplates;
 220+ }
 221+
 222+ /**
 223+ * Fallback to current files when no version is specified?
 224+ * @returns bool
 225+ */
 226+ public static function fallbackToCurrentFiles() {
 227+ global $wgUseCurrentImages;
 228+ return $wgUseCurrentImages;
 229+ }
 230+
 231+ /**
216232 * Should tags only be shown for unreviewed content for this user?
217233 * @returns bool
218234 */
@@ -458,51 +474,67 @@
459475
460476 # ################ Parsing functions #################
461477
462 - /**
463 - * @param string $text
 478+ /**
 479+ * All included pages/arguments are expanded out
464480 * @param Title $title
465 - * @param integer $id, revision id
 481+ * @param string $text
 482+ * @param int $id Source revision Id
466483 * @return array( string, array, array )
467 - * All included pages/arguments are expanded out
468484 */
469 - public static function expandText( $text = '', Title $title, $id ) {
 485+ public static function expandText( Title $title, $text, $id ) {
470486 global $wgParser;
471 - # Make our hooks trigger (force unstub so setting doesn't get lost)
472 - $wgParser->firstCallInit();
473 - # Begin stable parse
474 - $wgParser->fr_isStable = ( self::inclusionSetting() != FR_INCLUDES_CURRENT );
475 - # Parse with default options
476 - $options = self::makeParserOptions();
 487+ $options = self::makeParserOptions(); // default options
 488+ # Notify Parser if includes should be stabilized
 489+ $resetManager = false;
 490+ $incManager = FRInclusionManager::singleton();
 491+ if ( self::inclusionSetting() != FR_INCLUDES_CURRENT ) {
 492+ # Use FRInclusionManager to do the template/file version query
 493+ # up front unless the versions are already specified there...
 494+ if ( $id && !$incManager->parserOutputIsStabilized( $id ) ) {
 495+ $incManager->stabilizeParserOutput( $title, $id );
 496+ $resetManager = true; // need to reset when done
 497+ }
 498+ }
477499 $outputText = $wgParser->preprocess( $text, $title, $options, $id );
478 - $out =& $wgParser->mOutput;
 500+ $out = $wgParser->mOutput;
479501 # Stable parse done!
480 - $wgParser->fr_isStable = false;
 502+ if ( $resetManager ) {
 503+ $incManager->clear(); // reset the FRInclusionManager as needed
 504+ }
481505 # Return data array
482506 return array( $outputText, $out->mTemplateIds, $out->fr_includeErrors );
483507 }
484508
485509 /**
486510 * Get the HTML output of a revision based on $text.
487 - * @param Article $article
 511+ * @param Title $title
488512 * @param string $text
489 - * @param int $id
 513+ * @param int $id Source revision Id
490514 * @return ParserOutput
491515 */
492 - public static function parseStableText( Article $article, $text, $id ) {
 516+ public static function parseStableText( Title $title, $text, $id ) {
493517 global $wgParser;
494 - $title = $article->getTitle(); // avoid pass-by-reference error
495 - # Make our hooks trigger (force unstub so setting doesn't get lost)
496 - $wgParser->firstCallInit();
497 - $wgParser->fr_isStable = ( self::inclusionSetting() != FR_INCLUDES_CURRENT );
498 - # Don't show section-edit links, they can be old and misleading
499 - $options = self::makeParserOptions();
 518+ $options = self::makeParserOptions(); // default options
 519+ # Notify Parser if includes should be stabilized
 520+ $resetManager = false;
 521+ $incManager = FRInclusionManager::singleton();
 522+ if ( self::inclusionSetting() != FR_INCLUDES_CURRENT ) {
 523+ # Use FRInclusionManager to do the template/file version query
 524+ # up front unless the versions are already specified there...
 525+ if ( $id && !$incManager->parserOutputIsStabilized( $id ) ) {
 526+ $incManager->stabilizeParserOutput( $title, $id );
 527+ $resetManager = true; // need to reset when done
 528+ }
 529+ }
500530 # Parse the new body, wikitext -> html
501531 $parserOut = $wgParser->parse( $text, $title, $options, true, true, $id );
502 - # Done with parser!
503 - $wgParser->fr_isStable = false;
 532+ # Stable parse done!
 533+ if ( $resetManager ) {
 534+ $incManager->clear(); // reset the FRInclusionManager as needed
 535+ }
504536 return $parserOut;
505537 }
506 -
 538+
507539 /**
508540 * Get standard parser options
509541 * @param User $user (optional)
@@ -523,10 +555,9 @@
524556 * Get the page cache for the stable version of an article
525557 * @param Article $article
526558 * @param User $user
527 - * @param string $okStale set to 'okStale' to ignore expiration date
528559 * @return mixed (ParserOutput/false)
529560 */
530 - public static function getPageCache( Article $article, $user, $okStale = false ) {
 561+ public static function getPageCache( Article $article, $user ) {
531562 global $parserMemc, $wgCacheEpoch;
532563 wfProfileIn( __METHOD__ );
533564 # Make sure it is valid
@@ -545,7 +576,7 @@
546577 $canCache = $article->checkTouched();
547578 $cacheTime = $value->getCacheTime();
548579 $touched = $article->mTouched;
549 - if ( !$canCache || ( $value->expired( $touched ) && $okStale !== 'okStale' ) ) {
 580+ if ( !$canCache || $value->expired( $touched ) ) {
550581 if ( !$canCache ) {
551582 wfIncrStats( "pcache_miss_invalid" );
552583 wfDebug( "Invalid cached redirect, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" );
@@ -586,10 +617,12 @@
587618 Article $article, $user, ParserOutput $parserOut = null
588619 ) {
589620 global $parserMemc, $wgParserCacheExpireTime, $wgEnableParserCache;
 621+ wfProfileIn( __METHOD__ );
590622 # Make sure it is valid and $wgEnableParserCache is enabled
591 - if ( !$wgEnableParserCache || is_null( $parserOut ) )
 623+ if ( !$wgEnableParserCache || !$parserOut ) {
 624+ wfProfileOut( __METHOD__ );
592625 return false;
593 -
 626+ }
594627 $parserCache = ParserCache::singleton();
595628 $key = self::getCacheKey( $parserCache, $article, $user );
596629 # Add cache mark to HTML
@@ -606,85 +639,21 @@
607640 }
608641 # Save to objectcache
609642 $parserMemc->set( $key, $parserOut, $expire );
610 -
 643+ wfProfileOut( __METHOD__ );
611644 return true;
612645 }
613 -
614 - /**
615 - * @param int $revId
616 - * @param array $tmpParams (like ParserOutput template IDs)
617 - * @param array $imgParams (like ParserOutput image time->sha1 pairs)
618 - * Set the template/image versioning cache for parser
619 - */
620 - public static function setIncludeVersionCache( $revId, array $tmpParams, array $imgParams ) {
621 - self::load();
622 - self::$includeVersionCache[$revId] = array();
623 - self::$includeVersionCache[$revId]['templates'] = $tmpParams;
624 - self::$includeVersionCache[$revId]['files'] = $imgParams;
625 - }
626 -
627 - /**
628 - * Destroy the template/image versioning cache instance for parser
629 - */
630 - public static function clearIncludeVersionCache( $revId ) {
631 - self::load();
632 - if ( isset( self::$includeVersionCache[$revId] ) ) {
633 - unset( self::$includeVersionCache[$revId] );
634 - }
635 - }
636646
637647 /**
638 - * Should the params be in the process cache?
639 - */
640 - public static function useProcessCache( $revId ) {
641 - self::load();
642 - if ( isset( self::$includeVersionCache[$revId] ) ) {
643 - return true;
644 - }
645 - return false;
 648+ * @param Article $article
 649+ * @param parserOutput $parserOut
 650+ * Updates the stable-only cache dependancy table
 651+ */
 652+ public static function updateCacheTracking( Article $article, ParserOutput $stableOut ) {
 653+ wfProfileIn( __METHOD__ );
 654+ $frDepUpdate = new FRDependencyUpdate( $article->getTitle(), $stableOut );
 655+ $frDepUpdate->doUpdate();
 656+ wfProfileOut( __METHOD__ );
646657 }
647 -
648 - /**
649 - * Get template versioning cache for parser
650 - * @param int $revID
651 - * @param int $namespace
652 - * @param string $dbKey
653 - * @returns mixed (integer/null)
654 - */
655 - public static function getTemplateIdFromCache( $revId, $namespace, $dbKey ) {
656 - self::load();
657 - if ( isset( self::$includeVersionCache[$revId] ) ) {
658 - if ( isset( self::$includeVersionCache[$revId]['templates'][$namespace] ) ) {
659 - if ( isset( self::$includeVersionCache[$revId]['templates'][$namespace][$dbKey] ) ) {
660 - return self::$includeVersionCache[$revId]['templates'][$namespace][$dbKey];
661 - }
662 - }
663 - return false; // missing?
664 - }
665 - return null; // cache not found
666 - }
667 -
668 - /**
669 - * Get image versioning cache for parser
670 - * @param int $revID
671 - * @param string $dbKey
672 - * @returns mixed (array/null)
673 - */
674 - public static function getFileVersionFromCache( $revId, $dbKey ) {
675 - self::load();
676 - if ( isset( self::$includeVersionCache[$revId] ) ) {
677 - # All NS_FILE, no need to check namespace
678 - if ( isset( self::$includeVersionCache[$revId]['files'][$dbKey] ) ) {
679 - $time_SHA1 = self::$includeVersionCache[$revId]['files'][$dbKey];
680 - foreach ( $time_SHA1 as $time => $sha1 ) {
681 - // Should only be one, but this is an easy check
682 - return array( $time, $sha1 );
683 - }
684 - return array( false, "" ); // missing?
685 - }
686 - }
687 - return null; // cache not found
688 - }
689658
690659 # ################ Synchronization and link update functions #################
691660
@@ -709,16 +678,14 @@
710679 }
711680 }
712681 }
713 - if ( isset( $stableOutput->fr_ImageSHA1Keys ) // sanity check
714 - && isset( $currentOutput->fr_ImageSHA1Keys ) )
 682+ if ( isset( $stableOutput->fr_fileSHA1Keys ) // sanity check
 683+ && isset( $currentOutput->fr_fileSHA1Keys ) )
715684 {
716 - $sFiles = $stableOutput->fr_ImageSHA1Keys;
717 - $cFiles = $currentOutput->fr_ImageSHA1Keys;
 685+ $sFiles = $stableOutput->fr_fileSHA1Keys;
 686+ $cFiles = $currentOutput->fr_fileSHA1Keys;
718687 foreach ( $sFiles as $name => $timeKey ) {
719 - foreach ( $timeKey as $sTs => $sSha1 ) {
720 - if ( isset( $cFiles[$name] ) && !isset( $cFiles[$name][$sTs] ) ) {
721 - return false; // updated/created
722 - }
 688+ if ( isset( $cFiles[$name] ) && $cFiles[$name]['ts'] != $timeKey['ts'] ) {
 689+ return false; // updated/created
723690 }
724691 }
725692 }
@@ -751,6 +718,59 @@
752719 }
753720
754721 /**
 722+ * Update the page tables with a new stable version.
 723+ * @param Title $title
 724+ * @param mixed $sv, the new stable version (optional)
 725+ * @param mixed $oldSv, the old stable version (optional)
 726+ */
 727+ public static function stableVersionUpdates( Title $title, $sv = null, $oldSv = null ) {
 728+ $changed = false;
 729+ if ( $oldSv === null ) { // optional
 730+ $oldSv = FlaggedRevision::newFromStable( $title, FR_MASTER );
 731+ }
 732+ if ( $sv === null ) { // optional
 733+ $sv = FlaggedRevision::determineStable( $title, FR_MASTER );
 734+ }
 735+ if ( !$sv ) {
 736+ # Empty flaggedrevs data for this page if there is no stable version
 737+ self::clearTrackingRows( $title->getArticleID() );
 738+ # Check if pages using this need to be refreshed...
 739+ if ( FlaggedRevs::inclusionSetting() == FR_INCLUDES_STABLE ) {
 740+ $changed = (bool)$oldSv;
 741+ }
 742+ } else {
 743+ $article = new Article( $title );
 744+ # Update flagged page related fields
 745+ FlaggedRevs::updateStableVersion( $article, $sv->getRevision() );
 746+ # Lazily rebuild dependancies on next parse (we invalidate below)
 747+ FlaggedRevs::clearStableOnlyDeps( $title );
 748+ # Check if pages using this need to be invalidated/purged...
 749+ if ( FlaggedRevs::inclusionSetting() == FR_INCLUDES_STABLE ) {
 750+ $changed = (
 751+ !$oldSv ||
 752+ $sv->getRevId() != $oldSv->getRevId() ||
 753+ $sv->getFileTimestamp() != $oldSv->getFileTimestamp() ||
 754+ $sv->getFileSha1() != $oldSv->getFileSha1()
 755+ );
 756+ }
 757+ }
 758+ # Clear page cache
 759+ $title->invalidateCache();
 760+ # Purge squids as needed
 761+ FlaggedRevs::squidUpdates( $title, $changed );
 762+ }
 763+
 764+ /**
 765+ * @param Title $title
 766+ * @param bool $recursive invalidate/purge pages using this page
 767+ * Updates squid cache for a title. Defers till after main commit().
 768+ */
 769+ public static function squidUpdates( Title $title, $recursive ) {
 770+ global $wgDeferredUpdateList;
 771+ $wgDeferredUpdateList[] = new FRSquidUpdate( $title, $recursive );
 772+ }
 773+
 774+ /**
755775 * @param Article $article
756776 * @param Revision $rev, the new stable version
757777 * @param mixed $latest, the latest rev ID (optional)
@@ -810,7 +830,7 @@
811831 self::updatePendingList( $article, $latest );
812832 return true;
813833 }
814 -
 834+
815835 /**
816836 * @param Article $article
817837 * @param mixed $latest, the latest rev ID (optional)
@@ -879,35 +899,7 @@
880900 $dbw->insert( 'flaggedpage_pending', $data, __METHOD__ );
881901 return true;
882902 }
883 -
884 - /**
885 - * Resets links for a page when changed (other than edits)
886 - */
887 - public static function articleLinksUpdate( Article $article ) {
888 - global $wgUser, $wgParser;
889 - # Update the links tables as the stable version may now be the default page...
890 - $parserCache = ParserCache::singleton();
891 - $anon = new User(); // anon cache most likely to exist
892 - $poutput = $parserCache->get( $article, $anon );
893 - if ( $poutput == false && $wgUser->getId() )
894 - $poutput = $parserCache->get( $article, $wgUser );
895 - if ( $poutput == false ) {
896 - $text = $article->getContent();
897 - $options = self::makeParserOptions();
898 - $poutput = $wgParser->parse( $text, $article->getTitle(), $options );
899 - }
900 - $u = new LinksUpdate( $article->getTitle(), $poutput );
901 - $u->doUpdate(); // this will trigger our hook to add stable links too...
902 - return true;
903 - }
904903
905 - /**
906 - * Resets links for a page when changed (other than edits)
907 - */
908 - public static function titleLinksUpdate( Title $title ) {
909 - return self::articleLinksUpdate( new Article( $title ) );
910 - }
911 -
912904 # ################ Revision functions #################
913905
914906 /**
@@ -1000,7 +992,7 @@
1001993 }
1002994 return false;
1003995 }
1004 -
 996+
1005997 # ################ Page configuration functions #################
1006998
1007999 /**
@@ -1024,8 +1016,7 @@
10251017 if ( !$expiry || $expiry < wfTimestampNow() ) {
10261018 $row = null; // expired
10271019 self::purgeExpiredConfigurations();
1028 - self::titleLinksUpdate( $title ); // re-find stable version
1029 - $title->invalidateCache(); // purge squid/memcached
 1020+ self::stableVersionUpdates( $title ); // re-find stable version
10301021 }
10311022 }
10321023 // Is there a non-expired row?
@@ -1167,13 +1158,7 @@
11681159 foreach ( $pagesRetrack as $pageId ) {
11691160 $title = Title::newFromId( $pageId, GAID_FOR_UPDATE );
11701161 // Determine the new stable version and update the tracking tables...
1171 - $srev = FlaggedRevision::newFromStable( $title, FR_MASTER, $config );
1172 - if ( $srev ) {
1173 - $article = new Article( $title );
1174 - self::updateStableVersion( $article, $srev->getRevision(), $title->getArticleID() );
1175 - } else {
1176 - self::clearTrackingRows( $pageId ); // no stable version
1177 - }
 1162+ self::stableVersionUpdates( $title );
11781163 }
11791164 }
11801165
@@ -1186,7 +1171,7 @@
11871172 public static function isSighted( array $flags ) {
11881173 return self::tagsAtLevel( $flags, self::$minSL );
11891174 }
1190 -
 1175+
11911176 /**
11921177 * @param array $flags
11931178 * @return bool, is this revision at quality review condition?
@@ -1360,27 +1345,6 @@
13611346 }
13621347
13631348 /**
1364 - * Get a list of stable categories which go in categorylinks
1365 - * iff they're in the stable version of of the page (if there is one).
1366 - * Note: used for bug 20813
1367 - * @return array
1368 - */
1369 - public static function getStableCategories() {
1370 - $reviewedCats = array();
1371 - $msg = wfMsgForContent( 'flaggedrevs-stable-categories' );
1372 - if ( !wfEmptyMsg( 'flaggedrevs-stable-categories', $msg ) ) {
1373 - $list = explode( "\n*", "\n$msg" );
1374 - foreach ( $list as $category ) {
1375 - $category = trim( $category );
1376 - if ( $category != '' ) {
1377 - $reviewedCats[] = $category;
1378 - }
1379 - }
1380 - }
1381 - return $reviewedCats;
1382 - }
1383 -
1384 - /**
13851349 * Clear FlaggedRevs tracking tables for this page
13861350 * @param mixed $pageId (int or array)
13871351 */
@@ -1391,6 +1355,15 @@
13921356 $dbw->delete( 'flaggedpage_pending', array( 'fpp_page_id' => $pageId ), __METHOD__ );
13931357 }
13941358
 1359+ /**
 1360+ * Clear tracking table of stable-only links for this page
 1361+ * @param mixed $pageId (int or array)
 1362+ */
 1363+ public static function clearStableOnlyDeps( $pageId ) {
 1364+ $dbw = wfGetDB( DB_MASTER );
 1365+ $dbw->delete( 'flaggedrevs_tracking', array( 'ftr_from' => $pageId ), __METHOD__ );
 1366+ }
 1367+
13951368 # ################ Auto-review function #################
13961369
13971370 /**
@@ -1408,12 +1381,12 @@
14091382 * If no appropriate tags can be found, then the review will abort.
14101383 */
14111384 public static function autoReviewEdit(
1412 - Article $article, $user, $text, Revision $rev, array $flags = null, $auto = true
 1385+ Article $article, $user, Revision $rev, array $flags = null, $auto = true
14131386 ) {
14141387 wfProfileIn( __METHOD__ );
1415 - $title = $article->getTitle();
 1388+ $title = $article->getTitle(); // convenience
14161389 # Get current stable version ID (for logging)
1417 - $oldSv = FlaggedRevision::newFromStable( $title );
 1390+ $oldSv = FlaggedRevision::newFromStable( $title, FR_MASTER );
14181391 $oldSvId = $oldSv ? $oldSv->getRevId() : 0;
14191392 # Set the auto-review tags from the prior stable version.
14201393 # Normally, this should already be done and given here...
@@ -1423,7 +1396,8 @@
14241397 if ( $user->isAllowed( 'bot' ) ) {
14251398 $flags = $oldSv->getTags(); // no change for bot edits
14261399 } else {
1427 - $flags = self::getAutoReviewTags( $user, $oldSv->getTags() ); // account for perms
 1400+ # Account for perms/tags...
 1401+ $flags = self::getAutoReviewTags( $user, $oldSv->getTags() );
14281402 }
14291403 } else { // new page?
14301404 $flags = self::quickTags( FR_SIGHTED ); // use minimal level
@@ -1433,48 +1407,19 @@
14341408 return false; // can't auto-review this revision
14351409 }
14361410 }
 1411+ # Get quality tier from flags
14371412 $quality = 0;
14381413 if ( self::isQuality( $flags ) ) {
14391414 $quality = self::isPristine( $flags ) ? 2 : 1;
14401415 }
14411416
1442 - $tmpset = $imgset = array();
1443 - $poutput = false;
1444 -
14451417 # Rev ID is not put into parser on edit, so do the same here.
14461418 # Also, a second parse would be triggered otherwise.
1447 - $editInfo = $article->prepareTextForEdit( $text ); // Parse the revision HTML output
1448 - $poutput = $editInfo->output;
 1419+ $editInfo = $article->prepareTextForEdit( $rev->getText() );
 1420+ $poutput = $editInfo->output; // revision HTML output
14491421
1450 - $dbw = wfGetDB( DB_MASTER );
1451 -
1452 - # NS:title -> rev ID mapping
1453 - foreach ( $poutput->mTemplateIds as $namespace => $titleAndID ) {
1454 - foreach ( $titleAndID as $dbkey => $id ) {
1455 - $tmpset[] = array(
1456 - 'ft_rev_id' => $rev->getId(),
1457 - 'ft_namespace' => $namespace,
1458 - 'ft_title' => $dbkey,
1459 - 'ft_tmp_rev_id' => $id
1460 - );
1461 - }
1462 - }
1463 - # Image -> timestamp mapping
1464 - foreach ( $poutput->fr_ImageSHA1Keys as $dbkey => $timeAndSHA1 ) {
1465 - foreach ( $timeAndSHA1 as $time => $sha1 ) {
1466 - $fileIncludeData = array(
1467 - 'fi_rev_id' => $rev->getId(),
1468 - 'fi_name' => $dbkey,
1469 - 'fi_img_sha1' => $sha1,
1470 - // b/c: fi_img_timestamp DEFAULT either NULL (new) or '' (old)
1471 - 'fi_img_timestamp' => $time ? $dbw->timestamp( $time ) : ''
1472 - );
1473 - $imgset[] = $fileIncludeData;
1474 - }
1475 - }
1476 -
14771422 # If this is an image page, store corresponding file info
1478 - $fileData = array();
 1423+ $fileData = array( 'name' => null, 'timestamp' => null, 'sha1' => null );
14791424 if ( $title->getNamespace() == NS_FILE ) {
14801425 $file = $article instanceof ImagePage ?
14811426 $article->getFile() : wfFindFile( $title );
@@ -1487,50 +1432,30 @@
14881433
14891434 # Our review entry
14901435 $flaggedRevision = new FlaggedRevision( array(
1491 - 'fr_page_id' => $rev->getPage(),
1492 - 'fr_rev_id' => $rev->getId(),
1493 - 'fr_user' => $user->getId(),
1494 - 'fr_timestamp' => $rev->getTimestamp(),
1495 - 'fr_comment' => "",
1496 - 'fr_quality' => $quality,
1497 - 'fr_tags' => FlaggedRevision::flattenRevisionTags( $flags ),
1498 - 'fr_img_name' => $fileData ? $fileData['name'] : null,
1499 - 'fr_img_timestamp' => $fileData ? $fileData['timestamp'] : null,
1500 - 'fr_img_sha1' => $fileData ? $fileData['sha1'] : null
 1436+ 'page_id' => $rev->getPage(),
 1437+ 'rev_id' => $rev->getId(),
 1438+ 'user' => $user->getId(),
 1439+ 'timestamp' => $rev->getTimestamp(),
 1440+ 'comment' => "",
 1441+ 'quality' => $quality,
 1442+ 'tags' => FlaggedRevision::flattenRevisionTags( $flags ),
 1443+ 'img_name' => $fileData['name'],
 1444+ 'img_timestamp' => $fileData['timestamp'],
 1445+ 'img_sha1' => $fileData['sha1'],
 1446+ 'templateVersions' => $poutput->mTemplateIds,
 1447+ 'fileVersions' => $poutput->fr_fileSHA1Keys
15011448 ) );
1502 - $flaggedRevision->insertOn( $tmpset, $imgset, $auto );
 1449+ $flaggedRevision->insertOn( $auto );
15031450 # Update the article review log
1504 - FlaggedRevsLogs::updateLog( $title, $flags, array(), '', $rev->getId(),
1505 - $oldSvId, true, $auto );
 1451+ FlaggedRevsLogs::updateLog( $title,
 1452+ $flags, array(), '', $rev->getId(), $oldSvId, true, $auto );
15061453
1507 - # If we know that this is now the new stable version
1508 - # (which it probably is), save it to the cache...
1509 - $sv = FlaggedRevision::newFromStable( $article->getTitle(), FR_MASTER/*consistent*/ );
1510 - if ( $sv && $sv->getRevId() == $rev->getId() ) {
1511 - global $wgMemc;
1512 - # Update stable page cache. Don't cache redirects;
1513 - # it would go unused and complicate things.
1514 - if ( !Title::newFromRedirect( $text ) ) {
1515 - self::updatePageCache( $article, $user, $poutput );
1516 - }
1517 - # Update page tracking fields
1518 - self::updateStableVersion( $article, $rev, $rev->getId() );
1519 - # We can set the sync cache key already.
1520 - global $wgParserCacheExpireTime;
1521 - $key = wfMemcKey( 'flaggedrevs', 'includesSynced', $article->getId() );
1522 - $data = self::makeMemcObj( "true" );
1523 - $wgMemc->set( $key, $data, $wgParserCacheExpireTime );
1524 - } else if ( $sv ) {
1525 - # Update tracking table
1526 - self::updatePendingList( $article, $rev->getId() );
1527 - } else {
1528 - # Weird case: autoreview when flaggedrevs is deactivated for page
1529 - self::clearTrackingRows( $article->getId() );
1530 - }
 1454+ # Note: stableVersionUpdates() should be called after this.
 1455+ # This is done via LinksUpdate on page edits...
15311456 wfProfileOut( __METHOD__ );
15321457 return true;
15331458 }
1534 -
 1459+
15351460 /**
15361461 * Get JS script params
15371462 */
@@ -1547,38 +1472,4 @@
15481473 $params = array( 'tags' => (object)$tagsJS );
15491474 return (object)$params;
15501475 }
1551 -
1552 - /**
1553 - * Get template and image parameters from parser output
1554 - * @param FlaggedArticle $article
1555 - * @param array $templateIDs (from ParserOutput/OutputPage->mTemplateIds)
1556 - * @param array $imageSHA1Keys (from ParserOutput/OutputPage->fr_ImageSHA1Keys)
1557 - * @returns array( templateParams, imageParams, fileVersion )
1558 - */
1559 - public static function getIncludeParams(
1560 - FlaggedArticle $article, array $templateIDs, array $imageSHA1Keys
1561 - ) {
1562 - $templateParams = $imageParams = $fileVersion = '';
1563 - # NS -> title -> rev ID mapping
1564 - foreach ( $templateIDs as $namespace => $t ) {
1565 - foreach ( $t as $dbKey => $revId ) {
1566 - $temptitle = Title::makeTitle( $namespace, $dbKey );
1567 - $templateParams .= $temptitle->getPrefixedDBKey() . "|" . $revId . "#";
1568 - }
1569 - }
1570 - # Image -> timestamp -> sha1 mapping
1571 - foreach ( $imageSHA1Keys as $dbKey => $timeAndSHA1 ) {
1572 - foreach ( $timeAndSHA1 as $time => $sha1 ) {
1573 - $imageParams .= $dbKey . "|" . $time . "|" . $sha1 . "#";
1574 - }
1575 - }
1576 - # For image pages, note the displayed image version
1577 - if ( $article->getTitle()->getNamespace() == NS_FILE ) {
1578 - $file = $article->getDisplayedFile(); // File obj
1579 - if ( $file ) {
1580 - $fileVersion = $file->getTimestamp() . "#" . $file->getSha1();
1581 - }
1582 - }
1583 - return array( $templateParams, $imageParams, $fileVersion );
1584 - }
15851476 }
Index: trunk/extensions/FlaggedRevs/FRCacheUpdate.php
@@ -23,7 +23,7 @@
2424 }
2525 $key = $this->mTitle->getPrefixedDBKey();
2626 if ( isset( $wgFlaggedRevsCacheUpdates[$key] ) ) {
27 - return; // No duplicates...
 27+ return; // No duplicates (templates/redirects)...
2828 }
2929 # Fetch the IDs
3030 $dbr = wfGetDB( DB_SLAVE );
@@ -174,3 +174,28 @@
175175 return true;
176176 }
177177 }
 178+
 179+/**
 180+ * Class for handling deferred squid purges/invalidations
 181+ */
 182+class FRSquidUpdate {
 183+ protected $title;
 184+ protected $recursive;
 185+
 186+ function __construct( Title $title, $recursive ) {
 187+ $this->title = $title;
 188+ $this->recursive = $recursive;
 189+ }
 190+
 191+ function doUpdate() {
 192+ # Purge squid for this page only
 193+ $this->title->purgeSquid();
 194+ # Clear file cache for this page only
 195+ HTMLFileCache::clearFileCache( $this->title );
 196+ if ( $this->recursive ) {
 197+ # Invalidate caches of articles which include this page
 198+ $update = new HTMLCacheUpdate( $this->title, 'templatelinks' );
 199+ $update->doUpdate();
 200+ }
 201+ }
 202+}
Index: trunk/extensions/FlaggedRevs/forms/RevisionReviewForm.php
@@ -307,7 +307,9 @@
308308 }
309309 # Watch page if set to do so
310310 if ( $status === true ) {
311 - if ( $this->user->getOption( 'flaggedrevswatch' ) && !$this->page->userIsWatching() ) {
 311+ if ( $this->user->getOption( 'flaggedrevswatch' )
 312+ && !$this->page->userIsWatching() )
 313+ {
312314 $this->user->addWatch( $this->page );
313315 }
314316 }
@@ -319,80 +321,23 @@
320322 * @param Revision $rev
321323 * @returns true on success, array of errors on failure
322324 */
323 - private function approveRevision( $rev ) {
324 - global $wgMemc, $wgParser, $wgEnableParserCache;
 325+ private function approveRevision( Revision $rev ) {
 326+ global $wgMemc, $wgParser;
325327 wfProfileIn( __METHOD__ );
326 -
327 - $dbw = wfGetDB( DB_MASTER );
328 - $article = new Article( $this->page );
329 -
330 - $quality = 0;
331 - if ( FlaggedRevs::isQuality( $this->dims ) ) {
332 - $quality = FlaggedRevs::isPristine( $this->dims ) ? 2 : 1;
333 - }
334 - # Our flags
 328+ # Revision rating flags
335329 $flags = $this->dims;
336 - # Our template version pointers
337 - $tmpset = $tmpParams = array();
338 - $templateMap = explode( '#', trim( $this->templateParams ) );
339 - foreach ( $templateMap as $template ) {
340 - if ( !$template ) {
341 - continue;
342 - }
343 - $m = explode( '|', $template, 2 );
344 - if ( !isset( $m[0] ) || !isset( $m[1] ) || !$m[0] ) {
345 - continue;
346 - }
347 - list( $prefixed_text, $rev_id ) = $m;
348 - # Get the template title
349 - $tmp_title = Title::newFromText( $prefixed_text ); // Normalize this to be sure...
350 - if ( is_null( $tmp_title ) ) {
351 - continue; // Page must be valid!
352 - }
353 - $tmpset[] = array(
354 - 'ft_rev_id' => $rev->getId(),
355 - 'ft_namespace' => $tmp_title->getNamespace(),
356 - 'ft_title' => $tmp_title->getDBkey(),
357 - 'ft_tmp_rev_id' => $rev_id
358 - );
359 - if ( !isset( $tmpParams[$tmp_title->getNamespace()] ) ) {
360 - $tmpParams[$tmp_title->getNamespace()] = array();
361 - }
362 - $tmpParams[$tmp_title->getNamespace()][$tmp_title->getDBkey()] = $rev_id;
 330+ $quality = 0; // quality tier from flags
 331+ if ( FlaggedRevs::isQuality( $flags ) ) {
 332+ $quality = FlaggedRevs::isPristine( $flags ) ? 2 : 1;
363333 }
364 - # Our image version pointers
365 - $imgset = $imgParams = array();
366 - $imageMap = explode( '#', trim( $this->imageParams ) );
367 - foreach ( $imageMap as $image ) {
368 - if ( !$image ) {
369 - continue;
370 - }
371 - $m = explode( '|', $image, 3 );
372 - # Expand our parameters ... <name>#<timestamp>#<key>
373 - if ( !isset( $m[0] ) || !isset( $m[1] ) || !isset( $m[2] ) || !$m[0] ) {
374 - continue;
375 - }
376 - list( $dbkey, $timestamp, $key ) = $m;
377 - # Get the file title
378 - $img_title = Title::makeTitle( NS_IMAGE, $dbkey ); // Normalize
379 - if ( is_null( $img_title ) ) {
380 - continue; // Page must be valid!
381 - }
382 - $imgset[] = array(
383 - 'fi_rev_id' => $rev->getId(),
384 - 'fi_name' => $img_title->getDBkey(),
385 - 'fi_img_sha1' => $key,
386 - // b/c: fi_img_timestamp DEFAULT either NULL (new) or '' (old)
387 - 'fi_img_timestamp' => $timestamp ? $dbw->timestamp( $timestamp ) : ''
388 - );
389 - if ( !isset( $imgParams[$img_title->getDBkey()] ) ) {
390 - $imgParams[$img_title->getDBkey()] = array();
391 - }
392 - $imgParams[$img_title->getDBkey()][$timestamp] = $key;
393 - }
 334+ # Our template/file version pointers
 335+ list( $tmpVersions, $fileVersions ) = self::getIncludeVersions(
 336+ $this->templateParams, $this->imageParams
 337+ );
394338 # If this is an image page, store corresponding file info
395 - $fileData = array();
396 - if ( $this->page->getNamespace() == NS_IMAGE && $this->fileVersion ) {
 339+ $fileData = array( 'name' => null, 'timestamp' => null, 'sha1' => null );
 340+ if ( $this->page->getNamespace() == NS_FILE && $this->fileVersion ) {
 341+ # Stable upload version for file pages...
397342 $data = explode( '#', $this->fileVersion, 2 );
398343 if ( count( $data ) == 2 ) {
399344 $fileData['name'] = $this->page->getDBkey();
@@ -400,135 +345,62 @@
401346 $fileData['sha1'] = $data[1];
402347 }
403348 }
404 -
 349+
405350 # Get current stable version ID (for logging)
406351 $oldSv = FlaggedRevision::newFromStable( $this->page, FR_MASTER );
407 -
 352+
408353 # Is this rev already flagged? (re-review)
409354 $oldFrev = null;
410355 if ( $oldSv ) { // stable rev exists
411356 if ( $rev->getId() == $oldSv->getRevId() ) {
412357 $oldFrev = $oldSv; // save a query
413358 } else {
414 - $oldFrev = FlaggedRevision::newFromTitle( $this->page, $rev->getId(), FR_MASTER );
 359+ $oldFrev = FlaggedRevision::newFromTitle(
 360+ $this->page, $rev->getId(), FR_MASTER );
415361 }
416362 }
417 -
418 - # Be loose on templates that includes other files/templates dynamically.
419 - # Strict checking breaks randomized images/metatemplates...(bug 14580)
420 - global $wgUseCurrentTemplates, $wgUseCurrentImages;
421 - $mustMatch = !( $wgUseCurrentTemplates && $wgUseCurrentImages );
422 -
423 - # Set our versioning params cache
424 - FlaggedRevs::setIncludeVersionCache( $rev->getId(), $tmpParams, $imgParams );
425 - # Parse the text and check if all templates/files match up
426 - $text = $rev->getText();
427 - $stableOutput = FlaggedRevs::parseStableText( $article, $text, $rev->getId() );
428 - $err =& $stableOutput->fr_includeErrors;
429 - if ( $mustMatch ) { // if template/files must all be specified...
430 - if ( !empty( $err ) ) {
431 - wfProfileOut( __METHOD__ );
432 - return $err; // return templates/files with no version specified
433 - }
434 - }
435 - # Clear our versioning params cache
436 - FlaggedRevs::clearIncludeVersionCache( $rev->getId() );
437 -
438363 # Is this a duplicate review?
439 - if ( $oldFrev ) {
440 - // stable upload version for file pages
441 - $fileSha1 = $fileData ? $fileData['sha1'] : null;
442 - $synced = (
443 - $oldFrev->getTags() == $flags && // tags => quality
444 - $oldFrev->getFileSha1() == $fileSha1 &&
445 - $oldFrev->getComment() == $this->notes &&
446 - $oldFrev->getTemplateVersions() == $tmpParams &&
447 - $oldFrev->getFileVersions() == $imgParams
448 - );
449 - # Don't review if the same
450 - if ( $synced ) {
451 - wfProfileOut( __METHOD__ );
452 - return true;
453 - }
 364+ if ( $oldFrev &&
 365+ $oldFrev->getTags() == $flags && // tags => quality
 366+ $oldFrev->getFileSha1() == $fileData['sha1'] &&
 367+ $oldFrev->getFileTimestamp() == $fileData['timestamp'] &&
 368+ $oldFrev->getComment() == $this->notes &&
 369+ $oldFrev->getTemplateVersions( FR_MASTER ) == $tmpVersions &&
 370+ $oldFrev->getFileVersions( FR_MASTER ) == $fileVersions )
 371+ {
 372+ wfProfileOut( __METHOD__ );
 373+ return true; // don't record if the same
454374 }
455375
456 - # Our review entry
 376+ # Insert the review entry...
457377 $flaggedRevision = new FlaggedRevision( array(
458 - 'fr_rev_id' => $rev->getId(),
459 - 'fr_page_id' => $rev->getPage(),
460 - 'fr_user' => $this->user->getId(),
461 - 'fr_timestamp' => wfTimestampNow(),
462 - 'fr_comment' => $this->notes,
463 - 'fr_quality' => $quality,
464 - 'fr_tags' => FlaggedRevision::flattenRevisionTags( $flags ),
465 - 'fr_img_name' => $fileData ? $fileData['name'] : null,
466 - 'fr_img_timestamp' => $fileData ? $fileData['timestamp'] : null,
467 - 'fr_img_sha1' => $fileData ? $fileData['sha1'] : null
 378+ 'rev_id' => $rev->getId(),
 379+ 'page_id' => $rev->getPage(),
 380+ 'user' => $this->user->getId(),
 381+ 'timestamp' => wfTimestampNow(),
 382+ 'comment' => $this->notes,
 383+ 'quality' => $quality,
 384+ 'tags' => FlaggedRevision::flattenRevisionTags( $flags ),
 385+ 'img_name' => $fileData['name'],
 386+ 'img_timestamp' => $fileData['timestamp'],
 387+ 'img_sha1' => $fileData['sha1'],
 388+ 'templateVersions' => $tmpVersions,
 389+ 'fileVersions' => $fileVersions,
468390 ) );
469 -
470 - $dbw->begin();
471 - $flaggedRevision->insertOn( $tmpset, $imgset );
472 - # Avoid any lag issues
473 - $this->page->resetArticleId( $rev->getPage() );
474 - # Update recent changes
 391+ $flaggedRevision->insertOn();
 392+ # Update recent changes...
475393 self::updateRecentChanges( $this->page, $rev->getId(), $this->rcid, true );
476 - # Update the article review log
 394+
 395+ # Update the article review log...
477396 $oldSvId = $oldSv ? $oldSv->getRevId() : 0;
478397 FlaggedRevsLogs::updateLog( $this->page, $this->dims, $this->oflags,
479398 $this->comment, $this->oldid, $oldSvId, true );
480399
481 - # Update the links tables as the stable version may now be the default page.
482 - # Try using the parser cache first since we didn't actually edit the current version.
483 - $parserCache = ParserCache::singleton();
484 - $poutput = $parserCache->get( $article, $this->user );
485 - if ( !$poutput
486 - || !isset( $poutput->fr_ImageSHA1Keys )
487 - || !isset( $poutput->mTemplateIds ) )
488 - {
489 - $source = $article->getContent();
490 - $options = FlaggedRevs::makeParserOptions();
491 - $poutput = $wgParser->parse( $source, $article->getTitle(), $options,
492 - /*$lineStart*/true, /*$clearState*/true, $article->getLatest() );
493 - }
494 - # Prepare for a link tracking update
495 - $u = new LinksUpdate( $this->page, $poutput );
496 - # If we know that this is now the new stable version
497 - # (which it probably is), save it to the stable cache...
498 - $sv = FlaggedRevision::newFromStable( $this->page, FR_MASTER/*consistent*/ );
499 - if ( $sv && $sv->getRevId() == $rev->getId() ) {
500 - global $wgParserCacheExpireTime;
501 - $this->page->invalidateCache();
502 - # Update stable cache with the revision we reviewed.
503 - # Don't cache redirects; it would go unused and complicate things.
504 - if ( !Title::newFromRedirect( $text ) ) {
505 - FlaggedRevs::updatePageCache( $article, $this->user, $stableOutput );
506 - }
507 - $u->fr_stableRev = $sv; // no need to re-fetch this!
508 - $u->fr_stableParserOut = $stableOutput; // no need to re-fetch this!
509 - # We can set the sync cache key already...
510 - if ( $rev->isCurrent() ) {
511 - $includesSynced = FlaggedRevs::includesAreSynced( $stableOutput, $poutput );
512 - $key = wfMemcKey( 'flaggedrevs', 'includesSynced', $article->getId() );
513 - $data = FlaggedRevs::makeMemcObj( $includesSynced ? "true" : "false" );
514 - $wgMemc->set( $key, $data, $wgParserCacheExpireTime );
515 - }
516 - } else {
517 - # Get the old stable cache
518 - $stableOutput = FlaggedRevs::getPageCache( $article, $this->user );
519 - # Clear the cache...(for page histories)
520 - $this->page->invalidateCache();
521 - if ( $stableOutput !== false ) {
522 - # Reset stable cache if it existed, since we know it is the same.
523 - FlaggedRevs::updatePageCache( $article, $this->user, $stableOutput );
524 - }
525 - }
526 - # Update link tracking. This will trigger extraLinksUpdate()...
527 - $u->doUpdate();
 400+ # Get the new stable version as of now
 401+ $sv = FlaggedRevision::determineStable( $this->page, FR_MASTER/*consistent*/ );
 402+ # Update page and tracking tables and clear cache
 403+ FlaggedRevs::stableVersionUpdates( $this->page, $sv, $oldSv );
528404
529 - $dbw->commit();
530 - # Purge cache/squids for this page and any page that uses it
531 - Article::onArticleEdit( $this->page );
532 -
533405 wfProfileOut( __METHOD__ );
534406 return true;
535407 }
@@ -537,12 +409,13 @@
538410 * @param FlaggedRevision $frev
539411 * Removes flagged revision data for this page/id set
540412 */
541 - private function unapproveRevision( $frev ) {
542 - global $wgParser, $wgMemc;
 413+ private function unapproveRevision( FlaggedRevision $frev ) {
543414 wfProfileIn( __METHOD__ );
544 -
 415+
 416+ # Get current stable version ID (for logging)
 417+ $oldSv = FlaggedRevision::newFromStable( $this->page, FR_MASTER );
 418+
545419 $dbw = wfGetDB( DB_MASTER );
546 - $dbw->begin();
547420 # Delete from flaggedrevs table
548421 $dbw->delete( 'flaggedrevs',
549422 array( 'fr_page_id' => $frev->getPage(), 'fr_rev_id' => $frev->getRevId() ) );
@@ -552,33 +425,16 @@
553426 # Update recent changes
554427 self::updateRecentChanges( $this->page, $frev->getRevId(), false, false );
555428
556 - # Get current stable version ID (for logging)
557 - $oldSv = FlaggedRevision::newFromStable( $this->page, FR_MASTER );
558 - $oldSvId = $oldSv ? $oldSv->getRevId() : 0;
559 -
560429 # Update the article review log
 430+ $oldSvId = $oldSv ? $oldSv->getRevId() : 0;
561431 FlaggedRevsLogs::updateLog( $this->page, $this->dims, $this->oflags,
562432 $this->comment, $this->oldid, $oldSvId, false );
563433
564 - $article = new Article( $this->page );
565 - # Update the links tables as a new stable version
566 - # may now be the default page.
567 - $parserCache = ParserCache::singleton();
568 - $poutput = $parserCache->get( $article, $this->user );
569 - if ( $poutput == false ) {
570 - $text = $article->getContent();
571 - $options = FlaggedRevs::makeParserOptions();
572 - $poutput = $wgParser->parse( $text, $article->mTitle, $options );
573 - }
574 - $u = new LinksUpdate( $this->page, $poutput );
575 - $u->doUpdate();
 434+ # Get the new stable version as of now
 435+ $sv = FlaggedRevision::determineStable( $this->page, FR_MASTER/*consistent*/ );
 436+ # Update page and tracking tables and clear cache
 437+ FlaggedRevs::stableVersionUpdates( $this->page, $sv, $oldSv );
576438
577 - # Clear the cache...
578 - $this->page->invalidateCache();
579 - # Purge cache/squids for this page and any page that uses it
580 - $dbw->commit();
581 - Article::onArticleEdit( $article->getTitle() );
582 -
583439 wfProfileOut( __METHOD__ );
584440 return true;
585441 }
@@ -626,8 +482,97 @@
627483 }
628484 wfProfileOut( __METHOD__ );
629485 }
630 -
 486+
 487+ /**
 488+ * Get template and image parameters from parser output to use on forms.
 489+ * @param FlaggedArticle $article
 490+ * @param array $templateIDs (from ParserOutput/OutputPage->mTemplateIds)
 491+ * @param array $imageSHA1Keys (from ParserOutput/OutputPage->fr_fileSHA1Keys)
 492+ * @returns array( templateParams, imageParams, fileVersion )
 493+ */
 494+ public static function getIncludeParams(
 495+ FlaggedArticle $article, array $templateIDs, array $imageSHA1Keys
 496+ ) {
 497+ $templateParams = $imageParams = $fileVersion = '';
 498+ # NS -> title -> rev ID mapping
 499+ foreach ( $templateIDs as $namespace => $t ) {
 500+ foreach ( $t as $dbKey => $revId ) {
 501+ $temptitle = Title::makeTitle( $namespace, $dbKey );
 502+ $templateParams .= $temptitle->getPrefixedDBKey() . "|" . $revId . "#";
 503+ }
 504+ }
 505+ # Image -> timestamp -> sha1 mapping
 506+ foreach ( $imageSHA1Keys as $dbKey => $timeAndSHA1 ) {
 507+ $imageParams .= $dbKey . "|" . $timeAndSHA1['ts'];
 508+ $imageParams .= "|" . $timeAndSHA1['sha1'] . "#";
 509+ }
 510+ # For image pages, note the displayed image version
 511+ if ( $article->getTitle()->getNamespace() == NS_FILE ) {
 512+ $file = $article->getDisplayedFile(); // File obj
 513+ if ( $file ) {
 514+ $fileVersion = $file->getTimestamp() . "#" . $file->getSha1();
 515+ }
 516+ }
 517+ return array( $templateParams, $imageParams, $fileVersion );
 518+ }
 519+
 520+ /**
 521+ * Get template and image versions from form value for parser output.
 522+ * @param string $templateParams
 523+ * @param string $imageParams
 524+ * @returns array( templateIds, fileSHA1Keys )
 525+ * templateIds like ParserOutput->mTemplateIds
 526+ * fileSHA1Keys like ParserOutput->fr_fileSHA1Keys
 527+ */
 528+ public static function getIncludeVersions( $templateParams, $imageParams ) {
 529+ $templateIds = array();
 530+ $templateMap = explode( '#', trim( $templateParams ) );
 531+ foreach ( $templateMap as $template ) {
 532+ if ( !$template ) {
 533+ continue;
 534+ }
 535+ $m = explode( '|', $template, 2 );
 536+ if ( !isset( $m[0] ) || !isset( $m[1] ) || !$m[0] ) {
 537+ continue;
 538+ }
 539+ list( $prefixed_text, $rev_id ) = $m;
 540+ # Get the template title
 541+ $tmp_title = Title::newFromText( $prefixed_text ); // Normalize this to be sure...
 542+ if ( is_null( $tmp_title ) ) {
 543+ continue; // Page must be valid!
 544+ }
 545+ if ( !isset( $templateIds[$tmp_title->getNamespace()] ) ) {
 546+ $templateIds[$tmp_title->getNamespace()] = array();
 547+ }
 548+ $templateIds[$tmp_title->getNamespace()][$tmp_title->getDBkey()] = $rev_id;
 549+ }
 550+ # Our image version pointers
 551+ $fileSHA1Keys = array();
 552+ $imageMap = explode( '#', trim( $imageParams ) );
 553+ foreach ( $imageMap as $image ) {
 554+ if ( !$image ) {
 555+ continue;
 556+ }
 557+ $m = explode( '|', $image, 3 );
 558+ # Expand our parameters ... <name>#<timestamp>#<key>
 559+ if ( !isset( $m[0] ) || !isset( $m[1] ) || !isset( $m[2] ) || !$m[0] ) {
 560+ continue;
 561+ }
 562+ list( $dbkey, $timestamp, $key ) = $m;
 563+ # Get the file title
 564+ $img_title = Title::makeTitle( NS_IMAGE, $dbkey ); // Normalize
 565+ if ( is_null( $img_title ) ) {
 566+ continue; // Page must be valid!
 567+ }
 568+ $fileSHA1Keys[$img_title->getDBkey()] = array();
 569+ $fileSHA1Keys[$img_title->getDBkey()]['ts'] = $timestamp;
 570+ $fileSHA1Keys[$img_title->getDBkey()]['sha1'] = $key;
 571+ }
 572+ return array( $templateIds, $fileSHA1Keys );
 573+ }
 574+
631575 ########## Common form & elements ##########
 576+ // @TODO: move to some other class
632577
633578 /**
634579 * Generates a brief review form for a page.
@@ -739,7 +684,7 @@
740685 $pOutput = $parserCache->get( $article, $user );
741686 }
742687 # Otherwise (or on cache miss), parse the rev text...
743 - if ( $pOutput == false ) {
 688+ if ( !$pOutput || !isset( $pOutput->fr_fileSHA1Keys ) ) {
744689 global $wgParser, $wgEnableParserCache;
745690 $text = $rev->getText();
746691 $title = $article->getTitle();
@@ -751,10 +696,10 @@
752697 }
753698 }
754699 $templateIDs = $pOutput->mTemplateIds;
755 - $imageSHA1Keys = $pOutput->fr_ImageSHA1Keys;
 700+ $imageSHA1Keys = $pOutput->fr_fileSHA1Keys;
756701 }
757702 list( $templateParams, $imageParams, $fileVersion ) =
758 - FlaggedRevs::getIncludeParams( $article, $templateIDs, $imageSHA1Keys );
 703+ RevisionReviewForm::getIncludeParams( $article, $templateIDs, $imageSHA1Keys );
759704
760705 $form .= Xml::openElement( 'span', array( 'style' => 'white-space: nowrap;' ) );
761706 # Hide comment input if needed
Index: trunk/extensions/FlaggedRevs/forms/PageStabilityForm.php
@@ -255,27 +255,24 @@
256256 if ( $changed ) {
257257 # Update logs and make a null edit
258258 $nullRev = $this->updateLogsAndHistory( $reset );
259 - # Null edit may have been autoreviewed already
260 - $frev = FlaggedRevision::newFromTitle( $this->page, $nullRev->getId(), FR_MASTER );
261 - # We may need to invalidate the page links after changing the stable version.
262 - # Only do so if not already done, such as by an auto-review of the null edit.
263 - $invalidate = !$frev;
264 - # Check if this null edit is to be reviewed...
265 - if ( !$frev && $this->reviewThis ) {
266 - $flags = null;
267 - $article = new Article( $this->page );
268 - # Review this revision of the page...
269 - $ok = FlaggedRevs::autoReviewEdit(
270 - $article, $this->user, $nullRev->getText(), $nullRev, $flags, true );
271 - if( $ok ) {
272 - FlaggedRevs::markRevisionPatrolled( $nullRev ); // reviewed -> patrolled
273 - $invalidate = false; // links invalidated (with auto-reviewed)
 259+ if ( $this->reviewThis ) {
 260+ # Null edit may have been auto-reviewed already
 261+ $frev = FlaggedRevision::newFromTitle(
 262+ $this->page, $nullRev->getId(), FR_MASTER );
 263+ # Check if this null edit is to be reviewed...
 264+ if ( !$frev ) {
 265+ $flags = null;
 266+ $article = new Article( $this->page );
 267+ # Review this revision of the page...
 268+ $ok = FlaggedRevs::autoReviewEdit(
 269+ $article, $this->user, $nullRev, $flags, true );
 270+ if ( $ok ) {
 271+ FlaggedRevs::markRevisionPatrolled( $nullRev ); // reviewed -> patrolled
 272+ }
274273 }
275274 }
276 - # Update the links tables as the stable version may now be the default page...
277 - if ( $invalidate ) {
278 - FlaggedRevs::titleLinksUpdate( $this->page );
279 - }
 275+ # Update page and tracking tables and clear cache
 276+ FlaggedRevs::stableVersionUpdates( $this->page );
280277 }
281278 # Apply watchlist checkbox value (may be NULL)
282279 $this->updateWatchlist();
Index: trunk/extensions/FlaggedRevs/FlaggedRevision.php
@@ -23,6 +23,8 @@
2424 private $mTitle;
2525 private $mPageId;
2626 private $mRevId;
 27+ private $mStableTemplates;
 28+ private $mStableFiles;
2729
2830 /**
2931 * @param mixed $row (DB row or array)
@@ -49,21 +51,25 @@
5052 $this->mFlags = isset( $row->fr_flags ) ?
5153 explode( ',', $row->fr_flags ) : null;
5254 } elseif ( is_array( $row ) ) {
53 - $this->mRevId = intval( $row['fr_rev_id'] );
54 - $this->mPageId = intval( $row['fr_page_id'] );
55 - $this->mTimestamp = $row['fr_timestamp'];
56 - $this->mComment = $row['fr_comment'];
57 - $this->mQuality = intval( $row['fr_quality'] );
58 - $this->mTags = self::expandRevisionTags( strval( $row['fr_tags'] ) );
 55+ $this->mRevId = intval( $row['rev_id'] );
 56+ $this->mPageId = intval( $row['page_id'] );
 57+ $this->mTimestamp = $row['timestamp'];
 58+ $this->mComment = $row['comment'];
 59+ $this->mQuality = intval( $row['quality'] );
 60+ $this->mTags = self::expandRevisionTags( strval( $row['tags'] ) );
5961 # Image page revision relevant params
60 - $this->mFileName = $row['fr_img_name'] ? $row['fr_img_name'] : null;
61 - $this->mFileSha1 = $row['fr_img_sha1'] ? $row['fr_img_sha1'] : null;
62 - $this->mFileTimestamp = $row['fr_img_timestamp'] ?
63 - $row['fr_img_timestamp'] : null;
64 - $this->mUser = intval( $row['fr_user'] );
 62+ $this->mFileName = $row['img_name'] ? $row['img_name'] : null;
 63+ $this->mFileSha1 = $row['img_sha1'] ? $row['img_sha1'] : null;
 64+ $this->mFileTimestamp = $row['img_timestamp'] ?
 65+ $row['img_timestamp'] : null;
 66+ $this->mUser = intval( $row['user'] );
6567 # Optional fields
66 - $this->mFlags = isset( $row['fr_flags'] ) ?
67 - explode( ',', $row['fr_flags'] ) : null;
 68+ $this->mFlags = isset( $row['flags'] ) ?
 69+ explode( ',', $row['flags'] ) : null;
 70+ $this->mTemplates = isset( $row['templateVersions'] ) ?
 71+ $row['templateVersions'] : null;
 72+ $this->mFiles = isset( $row['fileVersions'] ) ?
 73+ $row['fileVersions'] : null;
6874 } else {
6975 throw new MWException( 'FlaggedRevision constructor passed invalid row format.' );
7076 }
@@ -115,7 +121,7 @@
116122 }
117123 return null;
118124 }
119 -
 125+
120126 /**
121127 * Get a FlaggedRevision of the stable version of a title.
122128 * @param Title $title, page title
@@ -129,93 +135,128 @@
130136 }
131137 $columns = self::selectFields();
132138 $options = array();
133 - # Short-circuit query
134 - $pageId = $title->getArticleID( $flags & FR_FOR_UPDATE ? GAID_FOR_UPDATE : 0 );
135 - # Short-circuit query
 139+ $pageId = $title->getArticleID( $flags & FR_MASTER ? GAID_FOR_UPDATE : 0 );
136140 if ( !$pageId ) {
 141+ return null; // short-circuit query
 142+ }
 143+ # User master/slave as appropriate
 144+ if ( $flags & FR_FOR_UPDATE || $flags & FR_MASTER ) {
 145+ $db = wfGetDB( DB_MASTER );
 146+ if ( $flags & FR_FOR_UPDATE ) $options[] = 'FOR UPDATE';
 147+ } else {
 148+ $db = wfGetDB( DB_SLAVE );
 149+ }
 150+ # Check tracking tables
 151+ $row = $db->selectRow(
 152+ array( 'flaggedpages', 'flaggedrevs' ),
 153+ $columns,
 154+ array( 'fp_page_id' => $pageId,
 155+ 'fr_page_id = fp_page_id',
 156+ 'fr_rev_id = fp_stable'
 157+ ),
 158+ __METHOD__,
 159+ $options
 160+ );
 161+ if ( !$row ) {
137162 return null;
138163 }
139 - # Quick slave queries...
140 - if ( !( $flags & FR_FOR_UPDATE ) && !( $flags & FR_MASTER ) ) {
141 - $dbr = wfGetDB( DB_SLAVE );
142 - $row = $dbr->selectRow( array( 'flaggedpages', 'flaggedrevs' ),
 164+ $frev = new self( $row );
 165+ $frev->mTitle = $title;
 166+ return $frev;
 167+ }
 168+
 169+ /**
 170+ * Get a FlaggedRevision of the stable version of a title.
 171+ * Skips tracking tables to figure out new stable version.
 172+ * @param Title $title, page title
 173+ * @param int $flags FR_MASTER
 174+ * @param array $config, optional page config (use to skip queries)
 175+ * @return mixed FlaggedRevision (null on failure)
 176+ */
 177+ public static function determineStable( Title $title, $flags = 0, $config = array() ) {
 178+ if ( !FlaggedRevs::inReviewNamespace( $title ) ) {
 179+ return null; // short-circuit
 180+ }
 181+ $columns = self::selectFields();
 182+ $options = array();
 183+ $pageId = $title->getArticleID( $flags & FR_FOR_UPDATE ? GAID_FOR_UPDATE : 0 );
 184+ if ( !$pageId ) {
 185+ return null; // short-circuit query
 186+ }
 187+ # User master/slave as appropriate
 188+ if ( $flags & FR_FOR_UPDATE || $flags & FR_MASTER ) {
 189+ $db = wfGetDB( DB_MASTER );
 190+ if ( $flags & FR_FOR_UPDATE ) $options[] = 'FOR UPDATE';
 191+ } else {
 192+ $db = wfGetDB( DB_SLAVE );
 193+ }
 194+ # Get visiblity settings...
 195+ if ( empty( $config ) ) {
 196+ $config = FlaggedRevs::getPageVisibilitySettings( $title, $flags );
 197+ }
 198+ if ( !$config['override'] && FlaggedRevs::useOnlyIfProtected() ) {
 199+ return null; // page is not reviewable; no stable version
 200+ }
 201+ $row = null;
 202+ $options['ORDER BY'] = 'fr_rev_id DESC';
 203+ # Look for the latest pristine revision...
 204+ if ( FlaggedRevs::pristineVersions() && $config['select'] != FLAGGED_VIS_LATEST ) {
 205+ $prow = $db->selectRow(
 206+ array( 'flaggedrevs', 'revision' ),
143207 $columns,
144 - array( 'fp_page_id' => $pageId,
145 - 'fr_page_id = fp_page_id',
146 - 'fr_rev_id = fp_stable'
 208+ array( 'fr_page_id' => $pageId,
 209+ 'fr_quality = ' . FR_PRISTINE,
 210+ 'rev_id = fr_rev_id',
 211+ 'rev_page = fr_page_id',
 212+ 'rev_deleted & ' . Revision::DELETED_TEXT => 0
147213 ),
148 - __METHOD__
 214+ __METHOD__,
 215+ $options
149216 );
 217+ # Looks like a plausible revision
 218+ $row = $prow ? $prow : $row;
 219+ }
 220+ if ( $row && $config['select'] == FLAGGED_VIS_PRISTINE ) {
 221+ // we have what we want already
 222+ # Look for the latest quality revision...
 223+ } elseif ( FlaggedRevs::qualityVersions() && $config['select'] != FLAGGED_VIS_LATEST ) {
 224+ // If we found a pristine rev above, this one must be newer...
 225+ $newerClause = $row ? "fr_rev_id > {$row->fr_rev_id}" : "1 = 1";
 226+ $qrow = $db->selectRow(
 227+ array( 'flaggedrevs', 'revision' ),
 228+ $columns,
 229+ array( 'fr_page_id' => $pageId,
 230+ 'fr_quality = ' . FR_QUALITY,
 231+ $newerClause,
 232+ 'rev_id = fr_rev_id',
 233+ 'rev_page = fr_page_id',
 234+ 'rev_deleted & ' . Revision::DELETED_TEXT => 0
 235+ ),
 236+ __METHOD__,
 237+ $options
 238+ );
 239+ $row = $qrow ? $qrow : $row;
 240+ }
 241+ # Do we have one? If not, try the latest reviewed revision...
 242+ if ( !$row ) {
 243+ $row = $db->selectRow(
 244+ array( 'flaggedrevs', 'revision' ),
 245+ $columns,
 246+ array( 'fr_page_id' => $pageId,
 247+ 'rev_id = fr_rev_id',
 248+ 'rev_page = fr_page_id',
 249+ 'rev_deleted & ' . Revision::DELETED_TEXT => 0
 250+ ),
 251+ __METHOD__,
 252+ $options
 253+ );
150254 if ( !$row ) return null;
151 - # Master queries that skip the tracking table...
152 - } else {
153 - $row = null;
154 - # Get visiblity settings...
155 - if ( empty( $config ) ) {
156 - $config = FlaggedRevs::getPageVisibilitySettings( $title, $flags );
157 - }
158 - if ( !$config['override'] && FlaggedRevs::useOnlyIfProtected() ) {
159 - return $row; // page is not reviewable; no stable version
160 - }
161 - $dbw = wfGetDB( DB_MASTER );
162 - $options['ORDER BY'] = 'fr_rev_id DESC';
163 - if ( $flags & FR_FOR_UPDATE ) $options[] = 'FOR UPDATE';
164 - # Look for the latest pristine revision...
165 - if ( FlaggedRevs::pristineVersions() && $config['select'] != FLAGGED_VIS_LATEST ) {
166 - $prow = $dbw->selectRow( array( 'flaggedrevs', 'revision' ),
167 - $columns,
168 - array( 'fr_page_id' => $pageId,
169 - 'fr_quality = ' . FR_PRISTINE,
170 - 'rev_id = fr_rev_id',
171 - 'rev_page = fr_page_id',
172 - 'rev_deleted & ' . Revision::DELETED_TEXT => 0
173 - ),
174 - __METHOD__,
175 - $options
176 - );
177 - # Looks like a plausible revision
178 - $row = $prow ? $prow : $row;
179 - }
180 - if ( $row && $config['select'] == FLAGGED_VIS_PRISTINE ) {
181 - // we have what we want already
182 - # Look for the latest quality revision...
183 - } elseif ( FlaggedRevs::qualityVersions() && $config['select'] != FLAGGED_VIS_LATEST ) {
184 - // If we found a pristine rev above, this one must be newer...
185 - $newerClause = $row ? "fr_rev_id > {$row->fr_rev_id}" : "1 = 1";
186 - $qrow = $dbw->selectRow( array( 'flaggedrevs', 'revision' ),
187 - $columns,
188 - array( 'fr_page_id' => $pageId,
189 - 'fr_quality = ' . FR_QUALITY,
190 - $newerClause,
191 - 'rev_id = fr_rev_id',
192 - 'rev_page = fr_page_id',
193 - 'rev_deleted & ' . Revision::DELETED_TEXT => 0
194 - ),
195 - __METHOD__,
196 - $options
197 - );
198 - $row = $qrow ? $qrow : $row;
199 - }
200 - # Do we have one? If not, try the latest reviewed revision...
201 - if ( !$row ) {
202 - $row = $dbw->selectRow( array( 'flaggedrevs', 'revision' ),
203 - $columns,
204 - array( 'fr_page_id' => $pageId,
205 - 'rev_id = fr_rev_id',
206 - 'rev_page = fr_page_id',
207 - 'rev_deleted & ' . Revision::DELETED_TEXT => 0
208 - ),
209 - __METHOD__,
210 - $options
211 - );
212 - if ( !$row ) return null;
213 - }
214255 }
215256 $frev = new self( $row );
216257 $frev->mTitle = $title;
217258 return $frev;
218259 }
219 -
 260+
220261 /*
221262 * Insert a FlaggedRevision object into the database
222263 *
@@ -224,11 +265,34 @@
225266 * @param bool $auto autopatrolled
226267 * @return bool success
227268 */
228 - public function insertOn( array $tmpRows, array $fileRows, $auto = false ) {
229 - $textFlags = 'dynamic';
 269+ public function insertOn( $auto = false ) {
 270+ $dbw = wfGetDB( DB_MASTER );
 271+ # Set any text flags
 272+ $textFlags = 'dynamic';
230273 if ( $auto ) $textFlags .= ',auto';
231274 $this->mFlags = explode( ',', $textFlags );
232 - $dbw = wfGetDB( DB_MASTER );
 275+ # Build the inclusion data chunks
 276+ $tmpInsertRows = array();
 277+ foreach ( $this->getTemplateVersions() as $namespace => $titleAndID ) {
 278+ foreach ( $titleAndID as $dbkey => $id ) {
 279+ $tmpInsertRows[] = array(
 280+ 'ft_rev_id' => $this->getRevId(),
 281+ 'ft_namespace' => (int)$namespace,
 282+ 'ft_title' => $dbkey,
 283+ 'ft_tmp_rev_id' => (int)$id
 284+ );
 285+ }
 286+ }
 287+ $fileInsertRows = array();
 288+ foreach ( $this->getFileVersions() as $dbkey => $timeSHA1 ) {
 289+ $fileInsertRows[] = array(
 290+ 'fi_rev_id' => $this->getRevId(),
 291+ 'fi_name' => $dbkey,
 292+ 'fi_img_sha1' => strval( $timeSHA1['sha1'] ),
 293+ // b/c: fi_img_timestamp DEFAULT either NULL (new) or '' (old)
 294+ 'fi_img_timestamp' => $timeSHA1['ts'] ? $dbw->timestamp( $timeSHA1['ts'] ) : ''
 295+ );
 296+ }
233297 # Our review entry
234298 $revRow = array(
235299 'fr_page_id' => $this->getPage(),
@@ -254,11 +318,11 @@
255319 $dbw->delete( 'flaggedimages',
256320 array( 'fi_rev_id' => $this->getRevId() ), __METHOD__ );
257321 # Update our versioning params
258 - if ( !empty( $tmpRows ) ) {
259 - $dbw->insert( 'flaggedtemplates', $tmpRows, __METHOD__, 'IGNORE' );
 322+ if ( $tmpInsertRows ) {
 323+ $dbw->insert( 'flaggedtemplates', $tmpInsertRows, __METHOD__, 'IGNORE' );
260324 }
261 - if ( !empty( $fileRows ) ) {
262 - $dbw->insert( 'flaggedimages', $fileRows, __METHOD__, 'IGNORE' );
 325+ if ( $fileInsertRows ) {
 326+ $dbw->insert( 'flaggedimages', $fileInsertRows, __METHOD__, 'IGNORE' );
263327 }
264328 return true;
265329 }
@@ -392,33 +456,18 @@
393457 }
394458
395459 /**
396 - * Set template versions array
397 - * @param array template versions (ns -> dbKey -> rev id)
398 - * @return void
399 - */
400 - public function setTemplateVersions( array $templateVersions ) {
401 - $this->mTemplates = $templateVersions;
402 - }
403 -
404 - /**
405 - * Set file versions array
406 - * @param array file versions (dbKey -> sha1)
407 - * @return void
408 - */
409 - public function setFileVersions( array $fileVersions ) {
410 - $this->mFiles = $fileVersions;
411 - }
412 -
413 - /**
414460 * Get original template versions at time of review
 461+ * @param int $flags FR_MASTER
415462 * @return Array template versions (ns -> dbKey -> rev Id)
416463 * Note: 0 used for template rev Id if it didn't exist
417464 */
418 - public function getTemplateVersions() {
 465+ public function getTemplateVersions( $flags = 0 ) {
419466 if ( $this->mTemplates == null ) {
420467 $this->mTemplates = array();
421 - $dbr = wfGetDB( DB_SLAVE );
422 - $res = $dbr->select( 'flaggedtemplates', '*',
 468+ $db = ( $flags & FR_MASTER ) ?
 469+ wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
 470+ $res = $db->select( 'flaggedtemplates',
 471+ array( 'ft_namespace', 'ft_title', 'ft_tmp_rev_id' ),
423472 array( 'ft_rev_id' => $this->getRevId() ),
424473 __METHOD__
425474 );
@@ -434,14 +483,17 @@
435484
436485 /**
437486 * Get original template versions at time of review
438 - * @return Array file versions (dbKey -> MW timestamp -> sha1)
439 - * Note: '0' used for file timestamp if it didn't exist
 487+ * @param int $flags FR_MASTER
 488+ * @return Array file versions (dbKey => array('ts' => MW timestamp,'sha1' => sha1) )
 489+ * Note: '0' used for file timestamp if it didn't exist ('' for sha1)
440490 */
441 - public function getFileVersions() {
 491+ public function getFileVersions( $flags = 0 ) {
442492 if ( $this->mFiles == null ) {
443493 $this->mFiles = array();
444 - $dbr = wfGetDB( DB_SLAVE );
445 - $res = $dbr->select( 'flaggedimages', '*',
 494+ $db = ( $flags & FR_MASTER ) ?
 495+ wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
 496+ $res = $db->select( 'flaggedimages',
 497+ array( 'fi_name', 'fi_img_timestamp', 'fi_img_sha1' ),
446498 array( 'fi_rev_id' => $this->getRevId() ),
447499 __METHOD__
448500 );
@@ -449,12 +501,87 @@
450502 $reviewedTS = trim( $row->fi_img_timestamp ); // may be ''/NULL
451503 $reviewedTS = $reviewedTS ? wfTimestamp( TS_MW, $reviewedTS ) : '0';
452504 $this->mFiles[$row->fi_name] = array();
453 - $this->mFiles[$row->fi_name][$reviewedTS] = $row->fi_img_sha1;
 505+ $this->mFiles[$row->fi_name]['ts'] = $reviewedTS;
 506+ $this->mFiles[$row->fi_name]['sha1'] = $row->fi_img_sha1;
454507 }
455508 }
456509 return $this->mFiles;
457510 }
458511
 512+ /**
 513+ * Get the current stable version of the templates used at time of review
 514+ * @param int $flags FR_MASTER
 515+ * @return Array template versions (ns -> dbKey -> rev Id)
 516+ * Note: 0 used for template rev Id if it doesn't exist
 517+ */
 518+ public function getStableTemplateVersions( $flags = 0 ) {
 519+ if ( $this->mStableTemplates == null ) {
 520+ $this->mStableTemplates = array();
 521+ $db = ( $flags & FR_MASTER ) ?
 522+ wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
 523+ $res = $db->select(
 524+ array( 'flaggedtemplates', 'page', 'flaggedpages' ),
 525+ array( 'ft_namespace', 'ft_title', 'fp_stable' ),
 526+ array( 'ft_rev_id' => $this->getRevId() ),
 527+ __METHOD__,
 528+ array(),
 529+ array(
 530+ 'page' => array( 'LEFT JOIN',
 531+ 'page_namespace = ft_namespace AND page_title = ft_title'),
 532+ 'flaggedpages' => array( 'LEFT JOIN', 'fp_page_id = page_id' )
 533+ )
 534+ );
 535+ while ( $row = $res->fetchObject() ) {
 536+ if ( !isset( $this->mStableTemplates[$row->ft_namespace] ) ) {
 537+ $this->mStableTemplates[$row->ft_namespace] = array();
 538+ }
 539+ $revId = (int)$row->fp_stable; // 0 => none
 540+ $this->mStableTemplates[$row->ft_namespace][$row->ft_title] = $revId;
 541+ }
 542+ }
 543+ return $this->mStableTemplates;
 544+ }
 545+
 546+ /**
 547+ * Get the current stable version of the files used at time of review
 548+ * @param int $flags FR_MASTER
 549+ * @return Array file versions (dbKey => array('ts' => MW timestamp,'sha1' => sha1) )
 550+ * Note: '0' used for file timestamp if it doesn't exist ('' for sha1)
 551+ */
 552+ public function getStableFileVersions( $flags = 0 ) {
 553+ if ( $this->mStableFiles == null ) {
 554+ $this->mStableFiles = array();
 555+ $db = ( $flags & FR_MASTER ) ?
 556+ wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
 557+ $res = $db->select(
 558+ array( 'flaggedimages', 'page', 'flaggedpages', 'flaggedrevs' ),
 559+ array( 'fi_name', 'fr_img_timestamp', 'fr_img_sha1' ),
 560+ array( 'fi_rev_id' => $this->getRevId() ),
 561+ __METHOD__,
 562+ array(),
 563+ array(
 564+ 'page' => array( 'LEFT JOIN',
 565+ 'page_namespace = ' . NS_FILE . ' AND page_title = fi_name' ),
 566+ 'flaggedpages' => array( 'LEFT JOIN', 'fp_page_id = page_id' ),
 567+ 'flaggedrevs' => array( 'LEFT JOIN',
 568+ 'fr_page_id = fp_page_id AND fr_rev_id = fp_stable' )
 569+ )
 570+ );
 571+ while ( $row = $res->fetchObject() ) {
 572+ $reviewedTS = '0';
 573+ $reviewedSha1 = '';
 574+ if ( $row->fr_img_timestamp ) {
 575+ $reviewedTS = wfTimestamp( TS_MW, $reviewedTS );
 576+ $reviewedSha1 = strval( $row->fr_img_sha1 );
 577+ }
 578+ $this->mStableFiles[$row->fi_name] = array();
 579+ $this->mStableFiles[$row->fi_name]['ts'] = $reviewedTS;
 580+ $this->mStableFiles[$row->fi_name]['sha1'] = $reviewedSha1;
 581+ }
 582+ }
 583+ return $this->mStableFiles;
 584+ }
 585+
459586 /*
460587 * Fetch pending template changes for this reviewed page version.
461588 * For each template, the "version used" is:
Index: trunk/extensions/FlaggedRevs/FRDependencyUpdate.php
@@ -0,0 +1,231 @@
 2+<?php
 3+/**
 4+ * Class containing update methods for tracking links that
 5+ * are only in the stable version of pages. Used only for caching.
 6+ */
 7+class FRDependencyUpdate {
 8+ protected $title;
 9+ protected $sLinks;
 10+ protected $sTemplates;
 11+ protected $sImages;
 12+ protected $sCategories;
 13+ protected $dbw;
 14+
 15+ public function __construct( Title $title, ParserOutput $stableOutput ) {
 16+ $this->title = $title;
 17+ # Stable version links
 18+ $this->sLinks = $stableOutput->getLinks();
 19+ $this->sTemplates = $stableOutput->getTemplates();
 20+ $this->sImages = $stableOutput->getImages();
 21+ $this->sCategories = $stableOutput->getCategories();
 22+ $this->dbw = wfGetDB( DB_MASTER );
 23+ }
 24+
 25+ public function doUpdate() {
 26+ $deps = array();
 27+ # Get any links that are only in the stable version...
 28+ $cLinks = $this->getCurrentVersionLinks();
 29+ foreach ( $this->sLinks as $ns => $titles ) {
 30+ foreach ( $titles as $title => $pageId ) {
 31+ # Only track broken links. These will require this page to
 32+ # be invalidated if the title is created (bluelink -> redlink).
 33+ if ( !$pageId && !isset( $cLinks[$ns][$title] ) ) {
 34+ self::addDependency( $deps, $ns, $title );
 35+ }
 36+ }
 37+ }
 38+ # Get any images that are only in the stable version...
 39+ $cImages = $this->getCurrentVersionImages();
 40+ foreach ( $this->sImages as $image => $n ) {
 41+ if ( !isset( $cImages[$image] ) ) {
 42+ self::addDependency( $deps, NS_FILE, $image );
 43+ }
 44+ }
 45+ # Get any templates that are only in the stable version...
 46+ $cTemplates = $this->getCurrentVersionTemplates();
 47+ foreach ( $this->sTemplates as $ns => $titles ) {
 48+ foreach ( $titles as $title => $id ) {
 49+ if ( !isset( $cTemplates[$ns][$title] ) ) {
 50+ self::addDependency( $deps, $ns, $title );
 51+ }
 52+ }
 53+ }
 54+ # Get any categories that are only in the stable version...
 55+ $cCategories = $this->getCurrentVersionCategories();
 56+ foreach ( $this->sCategories as $category => $sort ) {
 57+ if ( !isset( $cCategories[$category] ) ) {
 58+ self::addDependency( $deps, NS_CATEGORY, $category );
 59+ }
 60+ }
 61+ # Get any dependency tracking changes
 62+ $existing = $this->getExistingDeps();
 63+ $insertions = $this->getDepInsertions( $existing, $deps );
 64+ $deletions = $this->getDepDeletions( $existing, $deps );
 65+ # Delete removed links
 66+ if ( $deletions ) {
 67+ $this->dbw->delete( 'flaggedrevs_tracking', $deletions, __METHOD__ );
 68+ }
 69+ # Add any new links
 70+ if ( $insertions ) {
 71+ $this->dbw->insert( 'flaggedrevs_tracking', $insertions, __METHOD__, 'IGNORE' );
 72+ }
 73+ }
 74+
 75+ /*
 76+ * Get existing cache dependancies
 77+ * @return array (ns => dbKey => 1)
 78+ */
 79+ protected function getExistingDeps() {
 80+ $dbr = wfGetDB( DB_SLAVE );
 81+ $res = $dbr->select( 'flaggedrevs_tracking',
 82+ array( 'ftr_namespace', 'ftr_title' ),
 83+ array( 'ftr_from' => $this->title->getArticleId() ),
 84+ __METHOD__
 85+ );
 86+ $arr = array();
 87+ while ( $row = $res->fetchObject() ) {
 88+ if ( !isset( $arr[$row->ftr_namespace] ) ) {
 89+ $arr[$row->ftr_namespace] = array();
 90+ }
 91+ $arr[$row->ftr_namespace][$row->ftr_title] = 1;
 92+ }
 93+ return $arr;
 94+ }
 95+
 96+ /*
 97+ * Get INSERT rows for cache dependancies in $new but not in $existing
 98+ * @return array
 99+ */
 100+ protected function getDepInsertions( array $existing, array $new ) {
 101+ $arr = array();
 102+ foreach ( $new as $ns => $dbkeys ) {
 103+ if ( isset( $existing[$ns] ) ) {
 104+ $diffs = array_diff_key( $dbkeys, $existing[$ns] );
 105+ } else {
 106+ $diffs = $dbkeys;
 107+ }
 108+ foreach ( $diffs as $dbk => $id ) {
 109+ $arr[] = array(
 110+ 'ftr_from' => $this->title->getArticleId(),
 111+ 'ftr_namespace' => $ns,
 112+ 'ftr_title' => $dbk
 113+ );
 114+ }
 115+ }
 116+ return $arr;
 117+ }
 118+
 119+ /*
 120+ * Get WHERE clause to delete items in $existing but not in $new
 121+ * @return mixed (array/false)
 122+ */
 123+ protected function getDepDeletions( array $existing, array $new ) {
 124+ $del = array();
 125+ foreach ( $existing as $ns => $dbkeys ) {
 126+ if ( isset( $new[$ns] ) ) {
 127+ $del[$ns] = array_diff_key( $existing[$ns], $new[$ns] );
 128+ } else {
 129+ $del[$ns] = $existing[$ns];
 130+ }
 131+ }
 132+ if ( $del ) {
 133+ $clause = self::makeWhereFrom2d( $del, $this->dbw );
 134+ $where = array( $clause, 'ftr_from' => $this->title->getArticleId() );
 135+ } else {
 136+ $where = false;
 137+ }
 138+ return $where;
 139+ }
 140+
 141+ // Make WHERE clause to match $arr titles
 142+ protected static function makeWhereFrom2d( &$arr, $db ) {
 143+ $lb = new LinkBatch();
 144+ $lb->setArray( $arr );
 145+ return $lb->constructSet( 'ftr', $db );
 146+ }
 147+
 148+ protected static function addDependency( array &$deps, $ns, $dbKey ) {
 149+ if ( !isset( $deps[$ns] ) ) {
 150+ $deps[$ns] = array();
 151+ }
 152+ $deps[$ns][$dbKey] = 1;
 153+ }
 154+
 155+ /**
 156+ * Get an array of existing links, as a 2-D array
 157+ * @return array (ns => dbKey => 1)
 158+ */
 159+ protected function getCurrentVersionLinks() {
 160+ $dbr = wfGetDB( DB_SLAVE );
 161+ $res = $dbr->select( 'pagelinks',
 162+ array( 'pl_namespace', 'pl_title' ),
 163+ array( 'pl_from' => $this->title->getArticleId() ),
 164+ __METHOD__
 165+ );
 166+ $arr = array();
 167+ while ( $row = $res->fetchObject() ) {
 168+ if ( !isset( $arr[$row->pl_namespace] ) ) {
 169+ $arr[$row->pl_namespace] = array();
 170+ }
 171+ $arr[$row->pl_namespace][$row->pl_title] = 1;
 172+ }
 173+ return $arr;
 174+ }
 175+
 176+ /**
 177+ * Get an array of existing templates, as a 2-D array
 178+ * @return array (ns => dbKey => 1)
 179+ */
 180+ protected function getCurrentVersionTemplates() {
 181+ $dbr = wfGetDB( DB_SLAVE );
 182+ $res = $dbr->select( 'templatelinks',
 183+ array( 'tl_namespace', 'tl_title' ),
 184+ array( 'tl_from' => $this->title->getArticleId() ),
 185+ __METHOD__
 186+ );
 187+ $arr = array();
 188+ while ( $row = $res->fetchObject() ) {
 189+ if ( !isset( $arr[$row->tl_namespace] ) ) {
 190+ $arr[$row->tl_namespace] = array();
 191+ }
 192+ $arr[$row->tl_namespace][$row->tl_title] = 1;
 193+ }
 194+ return $arr;
 195+ }
 196+
 197+ /**
 198+ * Get an array of existing images, image names in the keys
 199+ * @return array (dbKey => 1)
 200+ */
 201+ protected function getCurrentVersionImages() {
 202+ $dbr = wfGetDB( DB_SLAVE );
 203+ $res = $dbr->select( 'imagelinks',
 204+ array( 'il_to' ),
 205+ array( 'il_from' => $this->title->getArticleId() ),
 206+ __METHOD__
 207+ );
 208+ $arr = array();
 209+ while ( $row = $res->fetchObject() ) {
 210+ $arr[$row->il_to] = 1;
 211+ }
 212+ return $arr;
 213+ }
 214+
 215+ /**
 216+ * Get an array of existing categories, with the name in the key and sort key in the value.
 217+ * @return array (category => sortkey)
 218+ */
 219+ protected function getCurrentVersionCategories() {
 220+ $dbr = wfGetDB( DB_SLAVE );
 221+ $res = $dbr->select( 'categorylinks',
 222+ array( 'cl_to', 'cl_sortkey' ),
 223+ array( 'cl_from' => $this->title->getArticleId() ),
 224+ __METHOD__
 225+ );
 226+ $arr = array();
 227+ while ( $row = $res->fetchObject() ) {
 228+ $arr[$row->cl_to] = $row->cl_sortkey;
 229+ }
 230+ return $arr;
 231+ }
 232+}
Property changes on: trunk/extensions/FlaggedRevs/FRDependencyUpdate.php
___________________________________________________________________
Added: svn:eol-style
1233 + native
Index: trunk/extensions/FlaggedRevs/FlaggedRevs.hooks.php
@@ -205,19 +205,21 @@
206206 }
207207
208208 /**
209 - * Update flaggedrevs table on article history merge
 209+ * Update flaggedrevs page/tracking tables (revision moving)
210210 */
211211 public static function updateFromMerge( Title $sourceTitle, Title $destTitle ) {
212212 $oldPageID = $sourceTitle->getArticleID();
213213 $newPageID = $destTitle->getArticleID();
214214 # Get flagged revisions from old page id that point to destination page
215215 $dbw = wfGetDB( DB_MASTER );
216 - $result = $dbw->select( array( 'flaggedrevs', 'revision' ),
 216+ $result = $dbw->select(
 217+ array( 'flaggedrevs', 'revision' ),
217218 array( 'fr_rev_id' ),
218219 array( 'fr_page_id' => $oldPageID,
219220 'fr_rev_id = rev_id',
220221 'rev_page' => $newPageID ),
221 - __METHOD__ );
 222+ __METHOD__
 223+ );
222224 # Update these rows
223225 $revIDs = array();
224226 while ( $row = $dbw->fetchObject( $result ) ) {
@@ -230,32 +232,31 @@
231233 'fr_rev_id' => $revIDs ),
232234 __METHOD__ );
233235 }
234 - # Update pages..stable version possibly lost to another page
235 - FlaggedRevs::titleLinksUpdate( $sourceTitle );
236 - FlaggedRevs::titleLinksUpdate( $destTitle );
237 -
 236+ # Update pages..stable versions possibly lost to another page
 237+ FlaggedRevs::stableVersionUpdates( $sourceTitle );
 238+ FlaggedRevs::stableVersionUpdates( $destTitle );
238239 return true;
239240 }
240241
241242 /**
242 - * Update flaggedrevs tracking tables
 243+ * Update flaggedrevs page/tracking tables
243244 */
244 - public static function onArticleDelete( &$article, &$user, $reason, $id ) {
 245+ public static function onArticleDelete( Article $article, $user, $reason, $id ) {
245246 FlaggedRevs::clearTrackingRows( $id );
246247 return true;
247248 }
248249
249250 /**
250 - * Update stable version selection
 251+ * Update flaggedrevs page/tracking tables
251252 */
252 - public static function onRevisionDelete( Title &$title ) {
253 - FlaggedRevs::titleLinksUpdate( $title );
 253+ public static function onRevisionDelete( Title $title ) {
 254+ FlaggedRevs::stableVersionUpdates( $title );
254255 return true;
255256 }
256257
257258 /**
258 - * Update pending revision table
259 - * Autoreview pages moved into content NS
 259+ * (a) Update flaggedrevs page/tracking tables
 260+ * (b) Autoreview pages moved into content NS
260261 */
261262 public static function onTitleMoveComplete(
262263 Title $otitle, Title $ntitle, $user, $pageId
@@ -264,99 +265,32 @@
265266 // Re-validate NS/config (new title may not be reviewable)
266267 if ( $fa->isReviewable( FR_MASTER ) ) {
267268 // Moved from non-reviewable to reviewable NS?
 269+ // Auto-review such edits like new pages...
268270 if ( !FlaggedRevs::inReviewNamespace( $otitle )
269271 && FlaggedRevs::autoReviewNewPages()
270272 && $ntitle->userCan( 'autoreview' ) )
271273 {
272274 $rev = Revision::newFromTitle( $ntitle );
273 - // Treat this kind of like a new page...
274 - FlaggedRevs::autoReviewEdit( $fa, $user, $rev->getText(), $rev );
275 - return true; // pending list handled
276 - } else if ( $fa->getStableRev( FR_MASTER ) ) {
277 - return true; // nothing to do
 275+ if ( $rev ) { // sanity
 276+ FlaggedRevs::autoReviewEdit( $fa, $user, $rev );
 277+ }
278278 }
279279 }
280 - FlaggedRevs::clearTrackingRows( $pageId );
 280+ # Update page and tracking tables and clear cache
 281+ FlaggedRevs::stableVersionUpdates( $otitle );
 282+ FlaggedRevs::stableVersionUpdates( $ntitle );
281283 return true;
282284 }
283285
284286 // @TODO: replace raw $linksUpdate field accesses
285287 public static function onLinksUpdate( LinksUpdate $linksUpdate ) {
286 - global $wgUser;
287288 wfProfileIn( __METHOD__ );
288 - $fa = FlaggedArticle::getTitleInstance( $linksUpdate->mTitle );
289 - # Check if this page has a stable version...
290 - $sv = null;
291 - if ( isset( $u->fr_stableRev ) ) {
292 - $sv = $u->fr_stableRev; // Try the process cache...
293 - } elseif ( $fa->isReviewable( FR_MASTER ) ) {
294 - $sv = $fa->getStableRev( FR_MASTER ); // re-validate NS/config
295 - }
296 - if ( $sv ) {
297 - $stableCats = FlaggedRevs::getStableCategories();
298 - // Short-circuit things that need stable version output
299 - if ( $stableCats || FlaggedRevs::inclusionSetting() != FR_INCLUDES_CURRENT ) {
300 - # Get the parsed stable version...
301 - if ( isset( $linksUpdate->fr_stableParserOut ) ) {
302 - $stableOut = $linksUpdate->fr_stableParserOut; // process cache
303 - } else {
304 - # Try stable version cache, which should be up-to-date now.
305 - # Hack: use 'okStale' to ignore any previous invalidate() calls.
306 - $anon = new User(); // anon cache most likely to exist
307 - $stableOut = FlaggedRevs::getPageCache( $fa, $anon, 'okStale' );
308 - if ( $stableOut == false && $wgUser->getId() ) {
309 - $stableOut = FlaggedRevs::getPageCache( $fa, $wgUser, 'okStale' );
310 - }
311 - if ( $stableOut == false ) { // cache miss
312 - $text = $sv->getRevText();
313 - $stableOut = FlaggedRevs::parseStableText( $fa, $text, $sv->getRevId() );
314 - }
315 - }
316 - # Tracking for certain categories depends only on the stable version
317 - self::stabilizeCategories( $linksUpdate, $stableOut, $stableCats );
318 - # Update flaggedrevs link tracking tables
319 - $frLinksUpdate = new FRLinksUpdate( $linksUpdate, $stableOut );
320 - $frLinksUpdate->doUpdate();
321 - }
322 - # Update flagged page related fields
323 - FlaggedRevs::updateStableVersion( $fa, $sv->getRevision() );
324 - } else {
325 - # Empty flaggedrevs data for this page if there is no stable version
326 - FlaggedRevs::clearTrackingRows( $fa->getId() );
327 - }
328 - # Refresh links for pages were only the stable version includes this page
329 - if ( $linksUpdate->mRecursive ) {
330 - FRLinksUpdate::queueRefreshLinksJobs( $fa->getTitle() );
331 - }
 289+ # Update page and tracking tables and clear cache
 290+ FlaggedRevs::stableVersionUpdates( $linksUpdate->mTitle );
332291 wfProfileOut( __METHOD__ );
333292 return true;
334293 }
335294
336 - /**
337 - * Make "stable categories" appear in categorylinks for a page
338 - * iff they are currently in the stable version of the page (if there is one)
339 - * @TODO: replace raw $linksUpdate field accesses
340 - */
341 - protected static function stabilizeCategories(
342 - LinksUpdate $linksUpdate, ParserOutput $stableOut, array $stableCats
343 - ) {
344 - $sCategories = $stableOut->getCategories(); // assoc array (name => sortkey)
345 - foreach ( $stableCats as $category ) {
346 - $category = str_replace( ' ', '_', $category ); // ' ' -> underscore
347 - // Stable categories cannot be added until added to the stable version
348 - if ( isset( $linksUpdate->mCategories[$category] ) // in current
349 - && !isset( $sCategories[$category] ) ) // not in stable
350 - {
351 - unset( $linksUpdate->mCategories[$category] );
352 - // Stable categories must remain until removed from the stable version
353 - } elseif ( !isset( $linksUpdate->mCategories[$category] ) // not in current
354 - && isset( $sCategories[$category] ) ) // in stable
355 - {
356 - $linksUpdate->mCategories[$category] = $sCategories[$category];
357 - }
358 - }
359 - }
360 -
361295 /*
362296 * Update pages where only the stable version links to a page
363297 * that was just changed in some way.
@@ -371,7 +305,7 @@
372306 * Add special fields to parser.
373307 */
374308 public static function parserAddFields( Parser $parser ) {
375 - $parser->mOutput->fr_ImageSHA1Keys = array();
 309+ $parser->mOutput->fr_fileSHA1Keys = array();
376310 $parser->mOutput->fr_includeErrors = array();
377311 return true;
378312 }
@@ -381,71 +315,54 @@
382316 * Note: $parser can be false
383317 */
384318 public static function parserFetchStableTemplate( $parser, Title $title, &$skip, &$id ) {
385 - # Trigger for stable version parsing only
386 - if ( !( $parser instanceof Parser ) || empty( $parser->fr_isStable ) ) {
387 - return true;
388 - } elseif ( $title->getNamespace() < 0 ) {
 319+ if ( !( $parser instanceof Parser ) || $title->getNamespace() < 0 ) {
389320 return true; // nothing to do
390321 }
391 - $dbr = wfGetDB( DB_SLAVE );
392 - # Check for stable version of template if this feature is enabled.
393 - # Should be in reviewable namespace, this saves unneeded DB checks as
394 - # well as enforce site settings if they are later changed.
395 - if ( FlaggedRevs::inclusionSetting() == FR_INCLUDES_STABLE
396 - && FlaggedRevs::inReviewNamespace( $title ) && $title->getArticleId() )
397 - {
398 - $id = $dbr->selectField( 'flaggedpages', 'fp_stable',
399 - array( 'fp_page_id' => $title->getArticleId() ),
400 - __METHOD__ );
 322+ $incManager = FRInclusionManager::singleton();
 323+ if ( !$incManager->parserOutputIsStabilized() ) {
 324+ return true; // trigger for stable version parsing only
401325 }
402 - # Check cache before doing another DB hit...
403 - $idP = FlaggedRevs::getTemplateIdFromCache( $parser->getRevisionId(),
404 - $title->getNamespace(), $title->getDBkey() );
405 - # Use the process cache key if it's newer or we have none yet
406 - if ( !is_null( $idP ) && ( !$id || $idP > $id ) ) {
407 - $id = $idP;
 326+ $id = false; // current
 327+ # Check for the version of this template used when reviewed.
 328+ $maybeId = $incManager->getReviewedTemplateVersion( $title );
 329+ if ( $maybeId !== null ) {
 330+ $id = (int)$maybeId; // use if specified (even 0)
408331 }
409 - # If there is no stable version (or that feature is not enabled), use
410 - # the template revision during review time. If both, use the newest one.
411 - $revId = $parser->getRevisionId();
412 - if ( $revId && !FlaggedRevs::useProcessCache( $revId ) ) {
413 - $idP = $dbr->selectField( 'flaggedtemplates',
414 - 'ft_tmp_rev_id',
415 - array( 'ft_rev_id' => $revId,
416 - 'ft_namespace' => $title->getNamespace(),
417 - 'ft_title' => $title->getDBkey() ),
418 - __METHOD__
419 - );
420 - # Take the newest (or only available) of the two
421 - $id = ( $id === false || $idP > $id ) ? $idP : $id;
 332+ # Check for stable version of template if this feature is enabled.
 333+ if ( FlaggedRevs::inclusionSetting() == FR_INCLUDES_STABLE ) {
 334+ $maybeId = $incManager->getStableTemplateVersion( $title );
 335+ # Take the newest of these two...
 336+ if ( $maybeId && $maybeId > $id ) {
 337+ $id = (int)$maybeId;
 338+ }
422339 }
423 - # If none specified, see if we are allowed to use the current revision
424 - if ( !$id ) {
425 - global $wgUseCurrentTemplates;
426 - if ( $id === false ) {
427 - // May want to give an error
428 - $parser->mOutput->fr_includeErrors[] = $title->getPrefixedDBKey();
429 - if ( !$wgUseCurrentTemplates ) {
430 - $skip = true;
431 - }
432 - } else {
433 - $skip = true; // If ID is zero, don't load it
 340+ # If $id not specified, see if we are allowed to use the current revision
 341+ if ( $id === false ) {
 342+ $parser->mOutput->fr_includeErrors[] = $title->getPrefixedDBKey(); // unspecified
 343+ if ( !FlaggedRevs::fallbackToCurrentTemplates() ) {
 344+ $skip = true; // broken link
434345 }
 346+ # If $id is zero, don't bother loading it
 347+ } elseif ( !$id ) {
 348+ $skip = true;
435349 }
436350 return true;
437351 }
438352
439353 /**
440 - * Select the desired images based on the selected stable revision times/SHA-1s
 354+ * (a) Select the desired images based on the selected stable version time/SHA-1
 355+ * (b) Set specified versions in fr_fileSHA1Keys
441356 */
442 - public static function parserMakeStableFileLink(
 357+ public static function parserFetchStableFile(
443358 $parser, Title $nt, &$skip, &$time, &$query = false
444359 ) {
445 - # Trigger for stable version parsing only
446 - if ( !( $parser instanceof Parser ) || empty( $parser->fr_isStable ) ) {
447 - return true;
 360+ if ( !( $parser instanceof Parser ) ) {
 361+ return true; // nothing to do
448362 }
449 - $file = null;
 363+ $incManager = FRInclusionManager::singleton();
 364+ if ( !$incManager->parserOutputIsStabilized() ) {
 365+ return true; // trigger for stable version parsing only
 366+ }
450367 # Normalize NS_MEDIA to NS_FILE
451368 if ( $nt->getNamespace() == NS_MEDIA ) {
452369 $title = Title::makeTitle( NS_FILE, $nt->getDBkey() );
@@ -453,143 +370,78 @@
454371 } else {
455372 $title =& $nt;
456373 }
457 - # Check for stable version of image if this feature is enabled.
458 - # Should be in reviewable namespace, this saves unneeded DB checks as
459 - # well as enforce site settings if they are later changed.
460 - $sha1 = '';
461 - if ( FlaggedRevs::inclusionSetting() == FR_INCLUDES_STABLE
462 - && FlaggedRevs::inReviewNamespace( $title ) )
463 - {
464 - $srev = FlaggedRevision::newFromStable( $title );
465 - if ( $srev && $srev->getFileTimestamp() ) {
466 - $time = $srev->getFileTimestamp(); // TS or null
467 - $sha1 = $srev->getFileSha1();
468 - }
 374+ # Get version, update fr_fileSHA1Keys...
 375+ list( $time, $sha1 ) = self::parserFindStableFile( $parser, $title );
 376+ # Stabilize the file link
 377+ if ( $time ) {
 378+ if ( $query != '' ) $query .= '&';
 379+ $query = "filetimestamp=" . urlencode( wfTimestamp( TS_MW, $time ) );
469380 }
470 - # Check cache before doing another DB hit...
471 - $params = FlaggedRevs::getFileVersionFromCache(
472 - $parser->getRevisionId(), $title->getDBkey() );
473 - if ( is_array( $params ) ) {
474 - list( $timeP, $sha1P ) = $params;
475 - // Take the newest one...
476 - if ( !$time || $timeP > $time ) {
477 - $time = $timeP;
478 - $sha1 = $sha1P;
479 - }
 381+ return true;
 382+ }
 383+
 384+ /**
 385+ * (a) Select the desired images based on the selected stable version time/SHA-1
 386+ * (b) Set specified versions in fr_fileSHA1Keys
 387+ */
 388+ public static function galleryFetchStableFile( $ig, Title $nt, &$time, &$query = false ) {
 389+ $parser =& $ig->mParser; // convenience
 390+ if ( !( $parser instanceof Parser ) || $nt->getNamespace() != NS_FILE ) {
 391+ return true; // nothing to do
480392 }
481 - # If there is no stable version (or that feature is not enabled), use
482 - # the image revision during review time. If both, use the newest one.
483 - $revId = $parser->getRevisionId();
484 - if ( $revId && !FlaggedRevs::useProcessCache( $revId ) ) {
485 - $dbr = wfGetDB( DB_SLAVE );
486 - $row = $dbr->selectRow( 'flaggedimages',
487 - array( 'fi_img_timestamp', 'fi_img_sha1' ),
488 - array( 'fi_rev_id' => $parser->getRevisionId(), 'fi_name' => $title->getDBkey() ),
489 - __METHOD__
490 - );
491 - $reviewedTS = trim( $row->fi_img_timestamp ); // remove garbage
492 - # Only the one picked at review time exists OR it is the newest...use it!
493 - if ( $row && ( $time === false || $reviewedTS > $time ) ) {
494 - $time = $reviewedTS;
495 - $sha1 = $row->fi_img_sha1;
496 - }
 393+ $incManager = FRInclusionManager::singleton();
 394+ if ( !$incManager->parserOutputIsStabilized() ) {
 395+ return true; // trigger for stable version parsing only
497396 }
498 - $query = $time ? "filetimestamp=" . urlencode( wfTimestamp( TS_MW, $time ) ) : "";
499 - # If none specified, see if we are allowed to use the current revision
500 - if ( !$time ) {
501 - global $wgUseCurrentImages;
502 - # If the DB found nothing...
503 - if ( $time === false ) {
504 - # May want to give an error, so track these...
505 - $parser->mOutput->fr_includeErrors[] = $title->getPrefixedDBKey();
506 - if ( !$wgUseCurrentImages ) {
507 - $time = "0"; // no image
508 - } else {
509 - $file = wfFindFile( $title );
510 - $time = $file ? $file->getTimestamp() : "0"; // Use current
511 - }
512 - } else {
513 - $time = "0"; // no image (may trigger on review)
514 - }
 397+ # Get version, update fr_fileSHA1Keys...
 398+ list( $time, $sha1 ) = self::parserFindStableFile( $parser, $nt );
 399+ # Stabilize the file link
 400+ if ( $time ) {
 401+ if ( $query != '' ) $query .= '&';
 402+ $query = "filetimestamp=" . urlencode( wfTimestamp( TS_MW, $time ) );
515403 }
516 - # Add image metadata to parser output
517 - $parser->mOutput->fr_ImageSHA1Keys[$title->getDBkey()] = array();
518 - $parser->mOutput->fr_ImageSHA1Keys[$title->getDBkey()][$time] = $sha1;
519404 return true;
520405 }
521406
522407 /**
523 - * Select the desired images based on the selected stable revision times/SHA-1s
 408+ * (a) Select the desired images based on the selected stable version time/SHA-1
 409+ * (b) Set specified versions in fr_fileSHA1Keys
524410 */
525 - public static function galleryFindStableFileTime( $ig, Title $nt, &$time, &$query = false ) {
526 - $parser = $ig->mParser; // convenience
527 - # Trigger for stable version parsing only
528 - if ( !( $parser instanceof Parser ) || empty( $parser->fr_isStable ) ) {
529 - return true;
530 - } elseif ( $nt->getNamespace() != NS_FILE ) {
531 - return true; // nothing to do
 411+ protected static function parserFindStableFile( Parser $parser, Title $title ) {
 412+ $time = false; // current version
 413+ $sha1 = null; // corresponds to $time
 414+ # Check for the version of this file used when reviewed.
 415+ $incManager = FRInclusionManager::singleton();
 416+ list( $maybeTS, $maybeSha1 ) = $incManager->getReviewedFileVersion( $title );
 417+ if ( $maybeTS !== null ) {
 418+ $time = $maybeTS; // use if specified (even "0")
 419+ $sha1 = $maybeSha1;
532420 }
533 - $file = null;
534 - # Check for stable version of image if this feature is enabled.
535 - # Should be in reviewable namespace, this saves unneeded DB checks as
536 - # well as enforce site settings if they are later changed.
537 - $sha1 = "";
538 - if ( FlaggedRevs::inclusionSetting() == FR_INCLUDES_STABLE
539 - && FlaggedRevs::inReviewNamespace( $nt ) )
540 - {
541 - $srev = FlaggedRevision::newFromStable( $nt );
542 - if ( $srev && $srev->getFileTimestamp() ) {
543 - $time = $srev->getFileTimestamp(); // TS or null
544 - $sha1 = $srev->getFileSha1();
 421+ # Check for stable version of file if this feature is enabled.
 422+ if ( FlaggedRevs::inclusionSetting() == FR_INCLUDES_STABLE ) {
 423+ list( $maybeTS, $maybeSha1 ) = $incManager->getStableFileVersion( $title );
 424+ # Take the newest of these two...
 425+ if ( $maybeTS && $maybeTS > $time ) {
 426+ $time = $maybeTS;
 427+ $sha1 = $maybeSha1;
545428 }
546429 }
547 - # Check cache before doing another DB hit...
548 - $params = FlaggedRevs::getFileVersionFromCache( $ig->mRevisionId, $nt->getDBkey() );
549 - if ( is_array( $params ) ) {
550 - list( $timeP, $sha1P ) = $params;
551 - // Take the newest one...
552 - if ( !$time || $timeP > $time ) {
553 - $time = $timeP;
554 - $sha1 = $sha1P;
 430+ # If $time not specified, see if we are allowed to use the current revision
 431+ if ( $time === false ) {
 432+ # May want to give an error, so track these...
 433+ $parser->mOutput->fr_includeErrors[] = $title->getPrefixedDBKey();
 434+ if ( !FlaggedRevs::fallbackToCurrentFiles() ) {
 435+ $time = "0"; // no image
555436 }
 437+ } elseif ( !$time ) {
 438+ $time = "0"; // make sure this the string '0'
556439 }
557 - # If there is no stable version (or that feature is not enabled), use
558 - # the image revision during review time. If both, use the newest one.
559 - if ( $ig->mRevisionId && !FlaggedRevs::useProcessCache( $ig->mRevisionId ) ) {
560 - $dbr = wfGetDB( DB_SLAVE );
561 - $row = $dbr->selectRow( 'flaggedimages',
562 - array( 'fi_img_timestamp', 'fi_img_sha1' ),
563 - array( 'fi_rev_id' => $ig->mRevisionId, 'fi_name' => $nt->getDBkey() ),
564 - __METHOD__
565 - );
566 - $reviewedTS = trim( $row->fi_img_timestamp ); // remove garbage
567 - # Only the one picked at review time exists OR it is the newest...use it!
568 - if ( $row && ( $time === false || $reviewedTS > $time ) ) {
569 - $time = $reviewedTS;
570 - $sha1 = $row->fi_img_sha1;
571 - }
 440+ # Add specified image metadata to parser output
 441+ if ( $time !== false ) {
 442+ $parser->mOutput->fr_fileSHA1Keys[$title->getDBkey()] = array();
 443+ $parser->mOutput->fr_fileSHA1Keys[$title->getDBkey()]['ts'] = $time;
 444+ $parser->mOutput->fr_fileSHA1Keys[$title->getDBkey()]['sha1'] = $sha1;
572445 }
573 - $query = $time ? "filetimestamp=" . urlencode( wfTimestamp( TS_MW, $time ) ) : "";
574 - # If none specified, see if we are allowed to use the current revision
575 - if ( !$time ) {
576 - global $wgUseCurrentImages;
577 - # If the DB found nothing...
578 - if ( $time === false ) {
579 - # May want to give an error, so track these...
580 - $parser->mOutput->fr_includeErrors[] = $nt->getPrefixedDBKey();
581 - if ( !$wgUseCurrentImages ) {
582 - $time = "0"; // no image
583 - } else {
584 - $file = wfFindFile( $nt );
585 - $time = $file ? $file->getTimestamp() : "0";
586 - }
587 - } else {
588 - $time = "0"; // no image (may trigger on review)
589 - }
590 - }
591 - # Add image metadata to parser output
592 - $parser->mOutput->fr_ImageSHA1Keys[$nt->getDBkey()] = array();
593 - $parser->mOutput->fr_ImageSHA1Keys[$nt->getDBkey()][$time] = $sha1;
594446 return true;
595447 }
596448
@@ -601,23 +453,25 @@
602454 if ( !isset( $pOutput->mImages ) ) {
603455 return true; // sanity check
604456 }
605 - $stableOut = !empty( $parser->fr_isStable );
606457 # Fetch the current timestamps of the images.
607458 foreach ( $pOutput->mImages as $filename => $x ) {
608459 # FIXME: it would be nice not to double fetch these!
609460 $time = false; // current
610 - if ( $stableOut && isset( $pOutput->fr_ImageSHA1Keys[$filename] ) ) {
611 - foreach( $pOutput->fr_ImageSHA1Keys[$filename] as $time => $sha1 ) {
612 - // Fetch file with $time to confirm the specified version exists
613 - }
 461+ # Stable output with versions specified
 462+ if ( isset( $pOutput->fr_fileSHA1Keys[$filename] ) ) {
 463+ // Fetch file with $time to confirm the specified version exists
 464+ $time = $pOutput->fr_fileSHA1Keys[$filename]['ts'];
 465+ $sha1 = $pOutput->fr_fileSHA1Keys[$filename]['sha1'];
614466 }
615467 $title = Title::makeTitleSafe( NS_FILE, $filename );
616468 $file = wfFindFile( $title, array( 'time' => $time ) );
617 - $pOutput->fr_ImageSHA1Keys[$filename] = array();
 469+ $pOutput->fr_fileSHA1Keys[$filename] = array();
618470 if ( $file ) {
619 - $pOutput->fr_ImageSHA1Keys[$filename][$file->getTimestamp()] = $file->getSha1();
 471+ $pOutput->fr_fileSHA1Keys[$filename]['ts'] = $file->getTimestamp();
 472+ $pOutput->fr_fileSHA1Keys[$filename]['sha1'] = $file->getSha1();
620473 } else {
621 - $pOutput->fr_ImageSHA1Keys[$filename]["0"] = '';
 474+ $pOutput->fr_fileSHA1Keys[$filename]['ts'] = '0';
 475+ $pOutput->fr_fileSHA1Keys[$filename]['sha1'] = '';
622476 }
623477 }
624478 return true;
@@ -657,21 +511,19 @@
658512
659513 public static function parserPagesUsingPendingChanges( &$parser, $ns = '' ) {
660514 $nsList = FlaggedRevs::getReviewNamespaces();
661 -
662 -
663 - if( !$nsList ) {
 515+ if ( !$nsList ) {
664516 return 0;
665517 }
666518
667 - if( $ns !== '' ) {
 519+ if ( $ns !== '' ) {
668520 $ns = intval( $ns );
669 - if( !in_array( $ns, $nsList ) ) {
 521+ if ( !in_array( $ns, $nsList ) ) {
670522 return 0;
671523 }
672524 }
673525
674526 static $pcCounts = null;
675 - if( !$pcCounts ) {
 527+ if ( !$pcCounts ) {
676528 $dbr = wfGetDB( DB_SLAVE );
677529 $res = $dbr->select( 'flaggedrevs_stats', '*', array(), __METHOD__ );
678530 $totalCount = 0;
@@ -682,7 +534,7 @@
683535 $nsList[ 'all' ] = $totalCount;
684536 }
685537
686 - if( $ns === '' ) {
 538+ if ( $ns === '' ) {
687539 return $nsList['all'];
688540 } else {
689541 return $nsList[ "ns-$ns" ];
@@ -694,21 +546,18 @@
695547 */
696548 public static function outputInjectTimestamps( OutputPage $out, ParserOutput $parserOut ) {
697549 # Set first time
698 - $out->fr_ImageSHA1Keys = isset( $out->fr_ImageSHA1Keys ) ?
699 - $out->fr_ImageSHA1Keys : array();
 550+ if ( !isset( $out->fr_fileSHA1Keys ) ) {
 551+ $out->fr_fileSHA1Keys = array();
 552+ }
700553 # Leave as defaults if missing. Relevant things will be updated only when needed.
701554 # We don't want to go around resetting caches all over the place if avoidable...
702 - $imageSHA1Keys = isset( $parserOut->fr_ImageSHA1Keys ) ?
703 - $parserOut->fr_ImageSHA1Keys : array();
 555+ $fileSHA1Keys = isset( $parserOut->fr_fileSHA1Keys ) ?
 556+ $parserOut->fr_fileSHA1Keys : array();
704557 # Add on any new items
705 - $out->fr_ImageSHA1Keys = wfArrayMerge( $out->fr_ImageSHA1Keys, $imageSHA1Keys );
 558+ $out->fr_fileSHA1Keys = wfArrayMerge( $out->fr_fileSHA1Keys, $fileSHA1Keys );
706559 return true;
707560 }
708561
709 - protected static function getLocalFile( Title $title, $time ) {
710 - return RepoGroup::singleton()->getLocalRepo()->findFile( $title, $time );
711 - }
712 -
713562 /**
714563 * Check page move and patrol permissions for FlaggedRevs
715564 */
@@ -862,7 +711,7 @@
863712 if ( $srev && self::isSelfRevertToStable( $rev, $srev, $baseRevId, $user ) ) {
864713 $flags = $srev->getTags(); // use old tags
865714 # Review this revision of the page...
866 - FlaggedRevs::autoReviewEdit( $article, $user, $rev->getText(), $rev, $flags );
 715+ FlaggedRevs::autoReviewEdit( $article, $user, $rev, $flags );
867716 return true; // done!
868717 }
869718 # Can this user auto-review this page?
@@ -887,7 +736,7 @@
888737 $flags = $frev->getTags(); // Dummy edits always keep previous tags
889738 }
890739 # Review this revision of the page...
891 - FlaggedRevs::autoReviewEdit( $article, $user, $rev->getText(), $rev, $flags );
 740+ FlaggedRevs::autoReviewEdit( $article, $user, $rev, $flags );
892741 }
893742 return true;
894743 }
@@ -919,7 +768,7 @@
920769 }
921770 # Review this revision of the page...
922771 return FlaggedRevs::autoReviewEdit(
923 - $article, $user, $rev->getText(), $rev, $flags, false /* manual */ );
 772+ $article, $user, $rev, $flags, false /* manual */ );
924773 }
925774
926775 /**
@@ -963,6 +812,7 @@
964813 * When an user makes a null-edit we sometimes want to review it...
965814 * (a) Null undo or rollback
966815 * (b) Null edit with review box checked
 816+ * Note: called after edit ops are finished
967817 */
968818 public static function maybeNullEditReview(
969819 Article $article, $user, $text, $s, $m, $a, $b, $flags, $rev, &$status, $baseId
@@ -989,9 +839,10 @@
990840 # Was the edit that we tried to revert to reviewed?
991841 if ( $frev ) {
992842 # Review this revision of the page...
993 - $ok = FlaggedRevs::autoReviewEdit( $article, $user, $rev->getText(), $rev, $flags );
 843+ $ok = FlaggedRevs::autoReviewEdit( $article, $user, $rev, $flags );
994844 if ( $ok ) {
995845 FlaggedRevs::markRevisionPatrolled( $rev ); // reviewed -> patrolled
 846+ FlaggedRevs::stableVersionUpdates( $title ); // update page tables
996847 return true;
997848 }
998849 }
@@ -1014,10 +865,10 @@
1015866 }
1016867 }
1017868 # Review this revision of the page...
1018 - $ok = FlaggedRevs::autoReviewEdit(
1019 - $article, $user, $rev->getText(), $rev, $flags, false );
 869+ $ok = FlaggedRevs::autoReviewEdit( $article, $user, $rev, $flags, false );
1020870 if ( $ok ) {
1021871 FlaggedRevs::markRevisionPatrolled( $rev ); // reviewed -> patrolled
 872+ FlaggedRevs::stableVersionUpdates( $title ); // update page tables
1022873 }
1023874 }
1024875 return true;
@@ -1610,7 +1461,6 @@
16111462
16121463 public static function onArticleViewHeader( &$article, &$outputDone, &$pcache ) {
16131464 $view = FlaggedArticleView::singleton();
1614 - $view->maybeUpdateMainCache( $outputDone, $pcache );
16151465 $view->addStableLink( $outputDone, $pcache );
16161466 $view->setPageContent( $outputDone, $pcache );
16171467 return true;
Index: trunk/extensions/FlaggedRevs/api/ApiReview.php
@@ -66,6 +66,7 @@
6767 $form->setDim( $tag, intval( $params['flag_' . $tag] ) );
6868 }
6969 if ( $form->isApproval() ) {
 70+ $parserOutput = null;
7071 // Now get the template and image parameters needed
7172 // If it is the current revision, try the parser cache first
7273 $article = new FlaggedArticle( $title, $revid );
@@ -73,7 +74,7 @@
7475 $parserCache = ParserCache::singleton();
7576 $parserOutput = $parserCache->get( $article, $wgUser );
7677 }
77 - if ( empty( $parserOutput ) ) {
 78+ if ( !$parserOutput || !isset( $parserOutput->fr_fileSHA1Keys ) ) {
7879 // Miss, we have to reparse the page
7980 global $wgParser;
8081 $text = $article->getContent();
@@ -82,8 +83,8 @@
8384 }
8485 // Set version parameters for review submission
8586 list( $templateParams, $imageParams, $fileVersion ) =
86 - FlaggedRevs::getIncludeParams( $article,
87 - $parserOutput->mTemplateIds, $parserOutput->fr_ImageSHA1Keys );
 87+ RevisionReviewForm::getIncludeParams( $article,
 88+ $parserOutput->mTemplateIds, $parserOutput->fr_fileSHA1Keys );
8889 $form->setTemplateParams( $templateParams );
8990 $form->setFileParams( $imageParams );
9091 $form->setFileVersion( $fileVersion );
Index: trunk/extensions/FlaggedRevs/FRInclusionManager.php
@@ -0,0 +1,183 @@
 2+<?php
 3+/**
 4+ * Class containing template/file version usage requirements for
 5+ * Parser based on the source text (being parsed) revision ID.
 6+ * If no requirements are set, the page is parsed as normal.
 7+ *
 8+ * Parser hooks check this to determine what template/file version to use.
 9+ *
 10+ */
 11+class FRInclusionManager {
 12+ protected $reviewedVersions = null; // files/templates at review time
 13+ protected $stableVersions = array(); // stable versions of files/templates
 14+
 15+ protected static $instance = null;
 16+
 17+ /*
 18+ * Get the FlaggedArticleView for this request
 19+ */
 20+ public static function singleton() {
 21+ if ( self::$instance == null ) {
 22+ self::$instance = new self();
 23+ }
 24+ return self::$instance;
 25+ }
 26+ protected function __clone() { }
 27+
 28+ protected function __construct() {
 29+ $this->stableVersions['templates'] = array();
 30+ $this->stableVersions['files'] = array();
 31+ }
 32+
 33+ /**
 34+ * Reset all template/image version data
 35+ * @return void
 36+ */
 37+ public function clear() {
 38+ $this->reviewedVersions = null;
 39+ $this->stableVersions['templates'] = array();
 40+ $this->stableVersions['files'] = array();
 41+ }
 42+
 43+ /**
 44+ * (a) Stabilize inclusions in Parser output
 45+ * (b) Set the template/image versions used in the flagged version of a revision
 46+ * @param array $tmpParams (ns => dbKey => revId )
 47+ * @param array $imgParams (dbKey => array('ts' => MW timestamp,'sha1' => sha1) )
 48+ */
 49+ public function setReviewedVersions( array $tmpParams, array $imgParams ) {
 50+ $this->reviewedVersions = array();
 51+ $this->reviewedVersions['templates'] = $tmpParams;
 52+ $this->reviewedVersions['files'] = $imgParams;
 53+ }
 54+
 55+ /**
 56+ * Set the stable versions of some template/images
 57+ * @param array $tmpParams (ns => dbKey => revId )
 58+ * @param array $imgParams (dbKey => array('ts' => MW timestamp,'sha1' => sha1) )
 59+ */
 60+ public function setStableVersionCache( array $tmpParams, array $imgParams ) {
 61+ $this->stableVersions['templates'] = $tmpParams;
 62+ $this->stableVersions['files'] = $imgParams;
 63+ }
 64+
 65+ /**
 66+ * (a) Stabilize inclusions in Parser output
 67+ * (b) Load all of the "at review time" versions of template/files
 68+ * (c) Load their stable version counterparts (avoids DB hits)
 69+ * Note: Used when calling FlaggedRevs::parseStableText().
 70+ * @param Title $title
 71+ * @param int $revId
 72+ * @return void
 73+ */
 74+ public function stabilizeParserOutput( Title $title, $revId ) {
 75+ $tRevVersions = $fRevVersions = array(); // review time versions
 76+ $tStbVersions = $fStbVersions = array(); // stable versions
 77+ $frev = FlaggedRevision::newFromTitle( $title, $revId );
 78+ if ( $frev ) {
 79+ $tRevVersions = $frev->getTemplateVersions();
 80+ $fRevVersions = $frev->getFileVersions();
 81+ # We can preload *most* of the stable version IDs the parser will need...
 82+ if ( FlaggedRevs::inclusionSetting() == FR_INCLUDES_STABLE ) {
 83+ $tStbVersions = $frev->getStableTemplateVersions();
 84+ $fStbVersions = $frev->getStableFileVersions();
 85+ }
 86+ }
 87+ $this->setReviewedVersions( $tRevVersions, $fRevVersions );
 88+ $this->setStableVersionCache( $tStbVersions, $fStbVersions );
 89+ }
 90+
 91+ /**
 92+ * Should Parser stabilize includes?
 93+ * @return bool
 94+ */
 95+ public function parserOutputIsStabilized() {
 96+ return is_array( $this->reviewedVersions );
 97+ }
 98+
 99+ /**
 100+ * Get the "review time" template version for parser
 101+ * @param Title $title
 102+ * @returns mixed (int/null)
 103+ */
 104+ public function getReviewedTemplateVersion( Title $title ) {
 105+ if ( !is_array( $this->reviewedVersions ) ) {
 106+ throw new MWException( "prepareForParse() nor setReviewedVersions() called yet" );
 107+ }
 108+ $dbKey = $title->getDBkey();
 109+ $namespace = $title->getNamespace();
 110+ if ( isset( $this->reviewedVersions['templates'][$namespace] ) ) {
 111+ if ( isset( $this->reviewedVersions['templates'][$namespace][$dbKey] ) ) {
 112+ return $this->reviewedVersions['templates'][$namespace][$dbKey];
 113+ }
 114+ }
 115+ return null; // missing version
 116+ }
 117+
 118+ /**
 119+ * Get the "review time" file version for parser
 120+ * @param Title $title
 121+ * @returns array (MW timestamp/'0'/null, sha1/''/null )
 122+ */
 123+ public function getReviewedFileVersion( Title $title ) {
 124+ if ( !is_array( $this->reviewedVersions ) ) {
 125+ throw new MWException( "prepareForParse() nor setReviewedVersions() called yet" );
 126+ }
 127+ $dbKey = $title->getDBkey();
 128+ # All NS_FILE, no need to check namespace
 129+ if ( isset( $this->reviewedVersions['files'][$dbKey] ) ) {
 130+ $time = $this->reviewedVersions['files'][$dbKey]['ts'];
 131+ $sha1 = $this->reviewedVersions['files'][$dbKey]['sha1'];
 132+ return array( $time, $sha1 );
 133+ }
 134+ return array( null, null ); // missing version
 135+ }
 136+
 137+ /**
 138+ * Get the stable version of a template
 139+ * @param Title $title
 140+ * @returns int (0 if none)
 141+ */
 142+ public function getStableTemplateVersion( Title $title ) {
 143+ $dbKey = $title->getDBkey();
 144+ $namespace = $title->getNamespace();
 145+ $id = null;
 146+ if ( isset( $this->stableVersions['templates'][$namespace] ) ) {
 147+ if ( isset( $this->stableVersions['templates'][$namespace][$dbKey] ) ) {
 148+ $id = $this->stableVersions['templates'][$namespace][$dbKey];
 149+ }
 150+ }
 151+ if ( $id === null ) { // cache miss
 152+ $srev = FlaggedRevision::newFromStable( $title );
 153+ $id = $srev ? $srev->getRevId() : 0;
 154+ }
 155+ $this->stableVersions['templates'][$namespace][$dbKey] = $id; // cache
 156+ return $id;
 157+ }
 158+
 159+ /**
 160+ * Get the stable version of a file
 161+ * @param Title $title
 162+ * @returns array (MW timestamp/'0', sha1/'')
 163+ */
 164+ public function getStableFileVersion( Title $title ) {
 165+ $dbKey = $title->getDBkey();
 166+ $time = '0';
 167+ $sha1 = '';
 168+ # All NS_FILE, no need to check namespace
 169+ if ( isset( $this->stableVersions['files'][$dbKey] ) ) {
 170+ $time = $this->stableVersions['files'][$dbKey]['ts'];
 171+ $sha1 = $this->stableVersions['files'][$dbKey]['sha1'];
 172+ return array( $time, $sha1 );
 173+ }
 174+ $srev = FlaggedRevision::newFromStable( $title );
 175+ if ( $srev && $srev->getFileTimestamp() ) {
 176+ $time = $srev->getFileTimestamp(); // TS or null
 177+ $sha1 = $srev->getFileSha1();
 178+ }
 179+ $this->stableVersions['files'][$dbKey] = array();
 180+ $this->stableVersions['files'][$dbKey]['ts'] = $time;
 181+ $this->stableVersions['files'][$dbKey]['sha1'] = $sha1;
 182+ return array( $time, $sha1 );
 183+ }
 184+}
Property changes on: trunk/extensions/FlaggedRevs/FRInclusionManager.php
___________________________________________________________________
Added: svn:eol-style
1185 + native
Index: trunk/extensions/FlaggedRevs/FlaggedArticleView.php
@@ -496,7 +496,8 @@
497497 # Check if this is a redirect...
498498 $redirHtml = $this->getRedirectHtml( $text );
499499 if ( $redirHtml == '' ) {
500 - $parserOut = FlaggedRevs::parseStableText( $this->article, $text, $frev->getRevId() );
 500+ $parserOut = FlaggedRevs::parseStableText(
 501+ $this->article->getTitle(), $text, $frev->getRevId() );
501502 }
502503 # Construct some tagging for non-printable outputs. Note that the pending
503504 # notice has all this info already, so don't do this if we added that already.
@@ -566,15 +567,18 @@
567568 # Get parsed stable version
568569 $redirHtml = '';
569570 $parserOut = FlaggedRevs::getPageCache( $this->article, $wgUser );
570 - if ( $parserOut == false ) {
 571+ if ( !$parserOut ) {
571572 $text = $srev->getRevText();
572573 # Check if this is a redirect...
573574 $redirHtml = $this->getRedirectHtml( $text );
 575+ # Don't parse redirects, use separate handling...
574576 if ( $redirHtml == '' ) {
 577+ # Get the new stable output
575578 $parserOut = FlaggedRevs::parseStableText(
576 - $this->article, $text, $srev->getRevId() );
577 - # Update the stable version cache
 579+ $this->article->getTitle(), $text, $srev->getRevId() );
 580+ # Update the stable version cache & dependancies
578581 FlaggedRevs::updatePageCache( $this->article, $wgUser, $parserOut );
 582+ FlaggedRevs::updateCacheTracking( $this->article, $parserOut );
579583 } else {
580584 $parserOut = null;
581585 }
@@ -1035,12 +1039,13 @@
10361040 if ( $rev ) {
10371041 $templateIDs = $fileSHA1Keys = null;
10381042 # $wgOut may not already have the inclusion IDs, such as for diffonly=1.
 1043+ # RevisionReviewForm will fetch them as needed however.
10391044 if ( $wgOut->getRevisionId() == $rev->getId()
10401045 && isset( $wgOut->mTemplateIds )
1041 - && isset( $wgOut->fr_ImageSHA1Keys ) )
 1046+ && isset( $wgOut->fr_fileSHA1Keys ) )
10421047 {
10431048 $templateIDs = $wgOut->mTemplateIds;
1044 - $fileSHA1Keys = $wgOut->fr_ImageSHA1Keys;
 1049+ $fileSHA1Keys = $wgOut->fr_fileSHA1Keys;
10451050 }
10461051 $form = RevisionReviewForm::buildQuickReview( $wgUser, $this->article,
10471052 $rev, $templateIDs, $fileSHA1Keys, $this->isDiffFromStable );
@@ -1789,35 +1794,4 @@
17901795 }
17911796 return true;
17921797 }
1793 -
1794 - /**
1795 - * Updates parser cache output to included needed versioning params.
1796 - */
1797 - public function maybeUpdateMainCache( &$outputDone, &$pcache ) {
1798 - global $wgUser, $wgRequest;
1799 - $this->load();
1800 - $action = $wgRequest->getVal( 'action', 'view' );
1801 - if ( $action == 'purge' ) {
1802 - return true; // already purging!
1803 - }
1804 - # Only trigger on article view for content pages, not for protect/delete/hist
1805 - if ( !self::isViewAction( $action ) || !$wgUser->isAllowed( 'review' ) ) {
1806 - return true;
1807 - }
1808 - if ( !$this->article->exists() || !$this->article->isReviewable() ) {
1809 - return true;
1810 - }
1811 - $parserCache = ParserCache::singleton();
1812 - $parserOut = $parserCache->get( $this->article, $wgUser );
1813 - if ( $parserOut ) {
1814 - # Clear older, incomplete, cached versions
1815 - # We need the IDs of templates and timestamps of images used
1816 - if ( !isset( $parserOut->fr_ImageSHA1Keys )
1817 - || !isset( $parserOut->mTemplateIds ) )
1818 - {
1819 - $this->article->getTitle()->invalidateCache();
1820 - }
1821 - }
1822 - return true;
1823 - }
18241798 }

Follow-up revisions

RevisionCommit summaryAuthorDate
r69083* Format getFileTimestamp() correctly with TS_MW...aaron06:29, 6 July 2010
r69132Follow up r69082:...aaron06:59, 7 July 2010
r69249* Renamed FRCacheUpdate -> FRExtraCacheUpdate....aaron07:44, 11 July 2010

Status & tagging log