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 @@ |
303 | 303 | |
304 | 304 | $wgAutoloadClasses['FlaggedRevs'] = $dir . 'FlaggedRevs.class.php'; |
305 | 305 | $wgAutoloadClasses['FRUserCounters'] = $dir . 'FRUserCounters.php'; |
| 306 | +$wgAutoloadClasses['FRInclusionManager'] = $dir . 'FRInclusionManager.php'; |
306 | 307 | $wgAutoloadClasses['FlaggedRevsHooks'] = $dir . 'FlaggedRevs.hooks.php'; |
307 | 308 | $wgAutoloadClasses['FlaggedRevsLogs'] = $dir . 'FlaggedRevsLogs.php'; |
308 | 309 | $wgAutoloadClasses['FRCacheUpdate'] = $dir . 'FRCacheUpdate.php'; |
309 | 310 | $wgAutoloadClasses['FRCacheUpdateJob'] = $dir . 'FRCacheUpdate.php'; |
310 | | -$wgAutoloadClasses['FRLinksUpdate'] = $dir . 'FRLinksUpdate.php'; |
| 311 | +$wgAutoloadClasses['FRSquidUpdate'] = $dir . 'FRCacheUpdate.php'; |
| 312 | +$wgAutoloadClasses['FRDependencyUpdate'] = $dir . 'FRDependencyUpdate.php'; |
311 | 313 | |
312 | 314 | # Special case cache invalidations |
313 | 315 | $wgJobClasses['flaggedrevs_CacheUpdate'] = 'FRCacheUpdateJob'; |
— | — | @@ -463,8 +465,8 @@ |
464 | 466 | # Parser hooks, selects the desired images/templates |
465 | 467 | $wgHooks['ParserClearState'][] = 'FlaggedRevsHooks::parserAddFields'; |
466 | 468 | $wgHooks['BeforeParserFetchTemplateAndtitle'][] = 'FlaggedRevsHooks::parserFetchStableTemplate'; |
467 | | -$wgHooks['BeforeParserMakeImageLinkObj'][] = 'FlaggedRevsHooks::parserMakeStableFileLink'; |
468 | | -$wgHooks['BeforeGalleryFindFile'][] = 'FlaggedRevsHooks::galleryFindStableFileTime'; |
| 469 | +$wgHooks['BeforeParserMakeImageLinkObj'][] = 'FlaggedRevsHooks::parserFetchStableFile'; |
| 470 | +$wgHooks['BeforeGalleryFindFile'][] = 'FlaggedRevsHooks::galleryFetchStableFile'; |
469 | 471 | # Additional parser versioning |
470 | 472 | $wgHooks['ParserAfterTidy'][] = 'FlaggedRevsHooks::parserInjectTimestamps'; |
471 | 473 | $wgHooks['OutputPageParserOutput'][] = 'FlaggedRevsHooks::outputInjectTimestamps'; |
— | — | @@ -487,7 +489,7 @@ |
488 | 490 | $wgHooks['HTMLCacheUpdate::doUpdate'][] = 'FlaggedRevsHooks::doCacheUpdate'; |
489 | 491 | # Updates stable version tracking data |
490 | 492 | $wgHooks['LinksUpdate'][] = 'FlaggedRevsHooks::onLinksUpdate'; |
491 | | -# Clear dead config rows |
| 493 | +# Clear/update config rows |
492 | 494 | $wgHooks['ArticleDeleteComplete'][] = 'FlaggedRevsHooks::onArticleDelete'; |
493 | 495 | $wgHooks['ArticleRevisionVisibilitySet'][] = 'FlaggedRevsHooks::onRevisionDelete'; |
494 | 496 | $wgHooks['ArticleRevisionVisiblitySet'][] = 'FlaggedRevsHooks::onRevisionDelete'; // B/C for now |
Index: trunk/extensions/FlaggedRevs/maintenance/reviewAllPages.inc |
— | — | @@ -35,13 +35,15 @@ |
36 | 36 | # Is it already reviewed? |
37 | 37 | $frev = FlaggedRevision::newFromTitle( $title, $row->page_latest, FR_MASTER ); |
38 | 38 | # Should exist, but to be safe... |
39 | | - if( !$frev && $rev && FlaggedRevs::inReviewNamespace($title) ) { |
| 39 | + if( !$frev && $rev && FlaggedRevs::inReviewNamespace( $title ) ) { |
40 | 40 | $text = $rev->getText(); |
41 | 41 | $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(); |
43 | 47 | $changed++; |
44 | | - # Purge squid for this page only |
45 | | - $article->getTitle()->purgeSquid(); |
46 | 48 | } |
47 | 49 | $count++; |
48 | 50 | } |
Index: trunk/extensions/FlaggedRevs/maintenance/updateTracking.inc |
— | — | @@ -121,17 +121,19 @@ |
122 | 122 | $title = Title::newFromRow( $row ); |
123 | 123 | $article = new Article( $title ); |
124 | 124 | # Replaces new fields into flaggedpages |
125 | | - $frev = FlaggedRevision::newFromStable( $title, FR_FOR_UPDATE ); |
| 125 | + $frev = FlaggedRevision::determineStable( $title, FR_MASTER ); |
126 | 126 | # Update fp_stable, fp_quality, and fp_reviewed |
127 | 127 | if( $frev ) { |
128 | | - FlaggedRevs::updateStableVersion( $article, $frev->getRevision(), $row->page_latest ); |
| 128 | + FlaggedRevs::updateStableVersion( |
| 129 | + $article, $frev->getRevision(), $row->page_latest ); |
129 | 130 | # Somethings broke? Delete the row... |
130 | 131 | } else { |
131 | 132 | FlaggedRevs::clearTrackingRows( $row->page_id ); |
132 | 133 | if( $db->affectedRows() > 0 ) $deleted++; |
133 | 134 | } |
134 | 135 | # 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__, |
136 | 138 | array('ORDER BY' => 'rev_timestamp DESC') ); |
137 | 139 | # Correct page_latest if needed (import/files made plenty of bad rows) |
138 | 140 | if( $revRow ) { |
Index: trunk/extensions/FlaggedRevs/FlaggedRevs.class.php |
— | — | @@ -18,8 +18,6 @@ |
19 | 19 | protected static $patrolNamespaces = array(); |
20 | 20 | # Restriction levels/config |
21 | 21 | protected static $restrictionLevels = array(); |
22 | | - # Temporary process cache variable |
23 | | - protected static $includeVersionCache = array(); |
24 | 22 | |
25 | 23 | protected static $loaded = false; |
26 | 24 | |
— | — | @@ -210,8 +208,26 @@ |
211 | 209 | global $wgFlaggedRevsHandleIncludes; |
212 | 210 | return $wgFlaggedRevsHandleIncludes; |
213 | 211 | } |
214 | | - |
| 212 | + |
215 | 213 | /** |
| 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 | + /** |
216 | 232 | * Should tags only be shown for unreviewed content for this user? |
217 | 233 | * @returns bool |
218 | 234 | */ |
— | — | @@ -458,51 +474,67 @@ |
459 | 475 | |
460 | 476 | # ################ Parsing functions ################# |
461 | 477 | |
462 | | - /** |
463 | | - * @param string $text |
| 478 | + /** |
| 479 | + * All included pages/arguments are expanded out |
464 | 480 | * @param Title $title |
465 | | - * @param integer $id, revision id |
| 481 | + * @param string $text |
| 482 | + * @param int $id Source revision Id |
466 | 483 | * @return array( string, array, array ) |
467 | | - * All included pages/arguments are expanded out |
468 | 484 | */ |
469 | | - public static function expandText( $text = '', Title $title, $id ) { |
| 485 | + public static function expandText( Title $title, $text, $id ) { |
470 | 486 | 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 | + } |
477 | 499 | $outputText = $wgParser->preprocess( $text, $title, $options, $id ); |
478 | | - $out =& $wgParser->mOutput; |
| 500 | + $out = $wgParser->mOutput; |
479 | 501 | # Stable parse done! |
480 | | - $wgParser->fr_isStable = false; |
| 502 | + if ( $resetManager ) { |
| 503 | + $incManager->clear(); // reset the FRInclusionManager as needed |
| 504 | + } |
481 | 505 | # Return data array |
482 | 506 | return array( $outputText, $out->mTemplateIds, $out->fr_includeErrors ); |
483 | 507 | } |
484 | 508 | |
485 | 509 | /** |
486 | 510 | * Get the HTML output of a revision based on $text. |
487 | | - * @param Article $article |
| 511 | + * @param Title $title |
488 | 512 | * @param string $text |
489 | | - * @param int $id |
| 513 | + * @param int $id Source revision Id |
490 | 514 | * @return ParserOutput |
491 | 515 | */ |
492 | | - public static function parseStableText( Article $article, $text, $id ) { |
| 516 | + public static function parseStableText( Title $title, $text, $id ) { |
493 | 517 | 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 | + } |
500 | 530 | # Parse the new body, wikitext -> html |
501 | 531 | $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 | + } |
504 | 536 | return $parserOut; |
505 | 537 | } |
506 | | - |
| 538 | + |
507 | 539 | /** |
508 | 540 | * Get standard parser options |
509 | 541 | * @param User $user (optional) |
— | — | @@ -523,10 +555,9 @@ |
524 | 556 | * Get the page cache for the stable version of an article |
525 | 557 | * @param Article $article |
526 | 558 | * @param User $user |
527 | | - * @param string $okStale set to 'okStale' to ignore expiration date |
528 | 559 | * @return mixed (ParserOutput/false) |
529 | 560 | */ |
530 | | - public static function getPageCache( Article $article, $user, $okStale = false ) { |
| 561 | + public static function getPageCache( Article $article, $user ) { |
531 | 562 | global $parserMemc, $wgCacheEpoch; |
532 | 563 | wfProfileIn( __METHOD__ ); |
533 | 564 | # Make sure it is valid |
— | — | @@ -545,7 +576,7 @@ |
546 | 577 | $canCache = $article->checkTouched(); |
547 | 578 | $cacheTime = $value->getCacheTime(); |
548 | 579 | $touched = $article->mTouched; |
549 | | - if ( !$canCache || ( $value->expired( $touched ) && $okStale !== 'okStale' ) ) { |
| 580 | + if ( !$canCache || $value->expired( $touched ) ) { |
550 | 581 | if ( !$canCache ) { |
551 | 582 | wfIncrStats( "pcache_miss_invalid" ); |
552 | 583 | wfDebug( "Invalid cached redirect, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" ); |
— | — | @@ -586,10 +617,12 @@ |
587 | 618 | Article $article, $user, ParserOutput $parserOut = null |
588 | 619 | ) { |
589 | 620 | global $parserMemc, $wgParserCacheExpireTime, $wgEnableParserCache; |
| 621 | + wfProfileIn( __METHOD__ ); |
590 | 622 | # Make sure it is valid and $wgEnableParserCache is enabled |
591 | | - if ( !$wgEnableParserCache || is_null( $parserOut ) ) |
| 623 | + if ( !$wgEnableParserCache || !$parserOut ) { |
| 624 | + wfProfileOut( __METHOD__ ); |
592 | 625 | return false; |
593 | | - |
| 626 | + } |
594 | 627 | $parserCache = ParserCache::singleton(); |
595 | 628 | $key = self::getCacheKey( $parserCache, $article, $user ); |
596 | 629 | # Add cache mark to HTML |
— | — | @@ -606,85 +639,21 @@ |
607 | 640 | } |
608 | 641 | # Save to objectcache |
609 | 642 | $parserMemc->set( $key, $parserOut, $expire ); |
610 | | - |
| 643 | + wfProfileOut( __METHOD__ ); |
611 | 644 | return true; |
612 | 645 | } |
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 | | - } |
636 | 646 | |
637 | 647 | /** |
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__ ); |
646 | 657 | } |
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 | | - } |
689 | 658 | |
690 | 659 | # ################ Synchronization and link update functions ################# |
691 | 660 | |
— | — | @@ -709,16 +678,14 @@ |
710 | 679 | } |
711 | 680 | } |
712 | 681 | } |
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 ) ) |
715 | 684 | { |
716 | | - $sFiles = $stableOutput->fr_ImageSHA1Keys; |
717 | | - $cFiles = $currentOutput->fr_ImageSHA1Keys; |
| 685 | + $sFiles = $stableOutput->fr_fileSHA1Keys; |
| 686 | + $cFiles = $currentOutput->fr_fileSHA1Keys; |
718 | 687 | 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 |
723 | 690 | } |
724 | 691 | } |
725 | 692 | } |
— | — | @@ -751,6 +718,59 @@ |
752 | 719 | } |
753 | 720 | |
754 | 721 | /** |
| 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 | + /** |
755 | 775 | * @param Article $article |
756 | 776 | * @param Revision $rev, the new stable version |
757 | 777 | * @param mixed $latest, the latest rev ID (optional) |
— | — | @@ -810,7 +830,7 @@ |
811 | 831 | self::updatePendingList( $article, $latest ); |
812 | 832 | return true; |
813 | 833 | } |
814 | | - |
| 834 | + |
815 | 835 | /** |
816 | 836 | * @param Article $article |
817 | 837 | * @param mixed $latest, the latest rev ID (optional) |
— | — | @@ -879,35 +899,7 @@ |
880 | 900 | $dbw->insert( 'flaggedpage_pending', $data, __METHOD__ ); |
881 | 901 | return true; |
882 | 902 | } |
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 | | - } |
904 | 903 | |
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 | | - |
912 | 904 | # ################ Revision functions ################# |
913 | 905 | |
914 | 906 | /** |
— | — | @@ -1000,7 +992,7 @@ |
1001 | 993 | } |
1002 | 994 | return false; |
1003 | 995 | } |
1004 | | - |
| 996 | + |
1005 | 997 | # ################ Page configuration functions ################# |
1006 | 998 | |
1007 | 999 | /** |
— | — | @@ -1024,8 +1016,7 @@ |
1025 | 1017 | if ( !$expiry || $expiry < wfTimestampNow() ) { |
1026 | 1018 | $row = null; // expired |
1027 | 1019 | self::purgeExpiredConfigurations(); |
1028 | | - self::titleLinksUpdate( $title ); // re-find stable version |
1029 | | - $title->invalidateCache(); // purge squid/memcached |
| 1020 | + self::stableVersionUpdates( $title ); // re-find stable version |
1030 | 1021 | } |
1031 | 1022 | } |
1032 | 1023 | // Is there a non-expired row? |
— | — | @@ -1167,13 +1158,7 @@ |
1168 | 1159 | foreach ( $pagesRetrack as $pageId ) { |
1169 | 1160 | $title = Title::newFromId( $pageId, GAID_FOR_UPDATE ); |
1170 | 1161 | // 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 ); |
1178 | 1163 | } |
1179 | 1164 | } |
1180 | 1165 | |
— | — | @@ -1186,7 +1171,7 @@ |
1187 | 1172 | public static function isSighted( array $flags ) { |
1188 | 1173 | return self::tagsAtLevel( $flags, self::$minSL ); |
1189 | 1174 | } |
1190 | | - |
| 1175 | + |
1191 | 1176 | /** |
1192 | 1177 | * @param array $flags |
1193 | 1178 | * @return bool, is this revision at quality review condition? |
— | — | @@ -1360,27 +1345,6 @@ |
1361 | 1346 | } |
1362 | 1347 | |
1363 | 1348 | /** |
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 | | - /** |
1385 | 1349 | * Clear FlaggedRevs tracking tables for this page |
1386 | 1350 | * @param mixed $pageId (int or array) |
1387 | 1351 | */ |
— | — | @@ -1391,6 +1355,15 @@ |
1392 | 1356 | $dbw->delete( 'flaggedpage_pending', array( 'fpp_page_id' => $pageId ), __METHOD__ ); |
1393 | 1357 | } |
1394 | 1358 | |
| 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 | + |
1395 | 1368 | # ################ Auto-review function ################# |
1396 | 1369 | |
1397 | 1370 | /** |
— | — | @@ -1408,12 +1381,12 @@ |
1409 | 1382 | * If no appropriate tags can be found, then the review will abort. |
1410 | 1383 | */ |
1411 | 1384 | 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 |
1413 | 1386 | ) { |
1414 | 1387 | wfProfileIn( __METHOD__ ); |
1415 | | - $title = $article->getTitle(); |
| 1388 | + $title = $article->getTitle(); // convenience |
1416 | 1389 | # Get current stable version ID (for logging) |
1417 | | - $oldSv = FlaggedRevision::newFromStable( $title ); |
| 1390 | + $oldSv = FlaggedRevision::newFromStable( $title, FR_MASTER ); |
1418 | 1391 | $oldSvId = $oldSv ? $oldSv->getRevId() : 0; |
1419 | 1392 | # Set the auto-review tags from the prior stable version. |
1420 | 1393 | # Normally, this should already be done and given here... |
— | — | @@ -1423,7 +1396,8 @@ |
1424 | 1397 | if ( $user->isAllowed( 'bot' ) ) { |
1425 | 1398 | $flags = $oldSv->getTags(); // no change for bot edits |
1426 | 1399 | } else { |
1427 | | - $flags = self::getAutoReviewTags( $user, $oldSv->getTags() ); // account for perms |
| 1400 | + # Account for perms/tags... |
| 1401 | + $flags = self::getAutoReviewTags( $user, $oldSv->getTags() ); |
1428 | 1402 | } |
1429 | 1403 | } else { // new page? |
1430 | 1404 | $flags = self::quickTags( FR_SIGHTED ); // use minimal level |
— | — | @@ -1433,48 +1407,19 @@ |
1434 | 1408 | return false; // can't auto-review this revision |
1435 | 1409 | } |
1436 | 1410 | } |
| 1411 | + # Get quality tier from flags |
1437 | 1412 | $quality = 0; |
1438 | 1413 | if ( self::isQuality( $flags ) ) { |
1439 | 1414 | $quality = self::isPristine( $flags ) ? 2 : 1; |
1440 | 1415 | } |
1441 | 1416 | |
1442 | | - $tmpset = $imgset = array(); |
1443 | | - $poutput = false; |
1444 | | - |
1445 | 1417 | # Rev ID is not put into parser on edit, so do the same here. |
1446 | 1418 | # 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 |
1449 | 1421 | |
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 | | - |
1477 | 1422 | # If this is an image page, store corresponding file info |
1478 | | - $fileData = array(); |
| 1423 | + $fileData = array( 'name' => null, 'timestamp' => null, 'sha1' => null ); |
1479 | 1424 | if ( $title->getNamespace() == NS_FILE ) { |
1480 | 1425 | $file = $article instanceof ImagePage ? |
1481 | 1426 | $article->getFile() : wfFindFile( $title ); |
— | — | @@ -1487,50 +1432,30 @@ |
1488 | 1433 | |
1489 | 1434 | # Our review entry |
1490 | 1435 | $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 |
1501 | 1448 | ) ); |
1502 | | - $flaggedRevision->insertOn( $tmpset, $imgset, $auto ); |
| 1449 | + $flaggedRevision->insertOn( $auto ); |
1503 | 1450 | # 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 ); |
1506 | 1453 | |
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... |
1531 | 1456 | wfProfileOut( __METHOD__ ); |
1532 | 1457 | return true; |
1533 | 1458 | } |
1534 | | - |
| 1459 | + |
1535 | 1460 | /** |
1536 | 1461 | * Get JS script params |
1537 | 1462 | */ |
— | — | @@ -1547,38 +1472,4 @@ |
1548 | 1473 | $params = array( 'tags' => (object)$tagsJS ); |
1549 | 1474 | return (object)$params; |
1550 | 1475 | } |
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 | | - } |
1585 | 1476 | } |
Index: trunk/extensions/FlaggedRevs/FRCacheUpdate.php |
— | — | @@ -23,7 +23,7 @@ |
24 | 24 | } |
25 | 25 | $key = $this->mTitle->getPrefixedDBKey(); |
26 | 26 | if ( isset( $wgFlaggedRevsCacheUpdates[$key] ) ) { |
27 | | - return; // No duplicates... |
| 27 | + return; // No duplicates (templates/redirects)... |
28 | 28 | } |
29 | 29 | # Fetch the IDs |
30 | 30 | $dbr = wfGetDB( DB_SLAVE ); |
— | — | @@ -174,3 +174,28 @@ |
175 | 175 | return true; |
176 | 176 | } |
177 | 177 | } |
| 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 @@ |
308 | 308 | } |
309 | 309 | # Watch page if set to do so |
310 | 310 | if ( $status === true ) { |
311 | | - if ( $this->user->getOption( 'flaggedrevswatch' ) && !$this->page->userIsWatching() ) { |
| 311 | + if ( $this->user->getOption( 'flaggedrevswatch' ) |
| 312 | + && !$this->page->userIsWatching() ) |
| 313 | + { |
312 | 314 | $this->user->addWatch( $this->page ); |
313 | 315 | } |
314 | 316 | } |
— | — | @@ -319,80 +321,23 @@ |
320 | 322 | * @param Revision $rev |
321 | 323 | * @returns true on success, array of errors on failure |
322 | 324 | */ |
323 | | - private function approveRevision( $rev ) { |
324 | | - global $wgMemc, $wgParser, $wgEnableParserCache; |
| 325 | + private function approveRevision( Revision $rev ) { |
| 326 | + global $wgMemc, $wgParser; |
325 | 327 | 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 |
335 | 329 | $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; |
363 | 333 | } |
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 | + ); |
394 | 338 | # 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... |
397 | 342 | $data = explode( '#', $this->fileVersion, 2 ); |
398 | 343 | if ( count( $data ) == 2 ) { |
399 | 344 | $fileData['name'] = $this->page->getDBkey(); |
— | — | @@ -400,135 +345,62 @@ |
401 | 346 | $fileData['sha1'] = $data[1]; |
402 | 347 | } |
403 | 348 | } |
404 | | - |
| 349 | + |
405 | 350 | # Get current stable version ID (for logging) |
406 | 351 | $oldSv = FlaggedRevision::newFromStable( $this->page, FR_MASTER ); |
407 | | - |
| 352 | + |
408 | 353 | # Is this rev already flagged? (re-review) |
409 | 354 | $oldFrev = null; |
410 | 355 | if ( $oldSv ) { // stable rev exists |
411 | 356 | if ( $rev->getId() == $oldSv->getRevId() ) { |
412 | 357 | $oldFrev = $oldSv; // save a query |
413 | 358 | } else { |
414 | | - $oldFrev = FlaggedRevision::newFromTitle( $this->page, $rev->getId(), FR_MASTER ); |
| 359 | + $oldFrev = FlaggedRevision::newFromTitle( |
| 360 | + $this->page, $rev->getId(), FR_MASTER ); |
415 | 361 | } |
416 | 362 | } |
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 | | - |
438 | 363 | # 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 |
454 | 374 | } |
455 | 375 | |
456 | | - # Our review entry |
| 376 | + # Insert the review entry... |
457 | 377 | $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, |
468 | 390 | ) ); |
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... |
475 | 393 | self::updateRecentChanges( $this->page, $rev->getId(), $this->rcid, true ); |
476 | | - # Update the article review log |
| 394 | + |
| 395 | + # Update the article review log... |
477 | 396 | $oldSvId = $oldSv ? $oldSv->getRevId() : 0; |
478 | 397 | FlaggedRevsLogs::updateLog( $this->page, $this->dims, $this->oflags, |
479 | 398 | $this->comment, $this->oldid, $oldSvId, true ); |
480 | 399 | |
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 ); |
528 | 404 | |
529 | | - $dbw->commit(); |
530 | | - # Purge cache/squids for this page and any page that uses it |
531 | | - Article::onArticleEdit( $this->page ); |
532 | | - |
533 | 405 | wfProfileOut( __METHOD__ ); |
534 | 406 | return true; |
535 | 407 | } |
— | — | @@ -537,12 +409,13 @@ |
538 | 410 | * @param FlaggedRevision $frev |
539 | 411 | * Removes flagged revision data for this page/id set |
540 | 412 | */ |
541 | | - private function unapproveRevision( $frev ) { |
542 | | - global $wgParser, $wgMemc; |
| 413 | + private function unapproveRevision( FlaggedRevision $frev ) { |
543 | 414 | wfProfileIn( __METHOD__ ); |
544 | | - |
| 415 | + |
| 416 | + # Get current stable version ID (for logging) |
| 417 | + $oldSv = FlaggedRevision::newFromStable( $this->page, FR_MASTER ); |
| 418 | + |
545 | 419 | $dbw = wfGetDB( DB_MASTER ); |
546 | | - $dbw->begin(); |
547 | 420 | # Delete from flaggedrevs table |
548 | 421 | $dbw->delete( 'flaggedrevs', |
549 | 422 | array( 'fr_page_id' => $frev->getPage(), 'fr_rev_id' => $frev->getRevId() ) ); |
— | — | @@ -552,33 +425,16 @@ |
553 | 426 | # Update recent changes |
554 | 427 | self::updateRecentChanges( $this->page, $frev->getRevId(), false, false ); |
555 | 428 | |
556 | | - # Get current stable version ID (for logging) |
557 | | - $oldSv = FlaggedRevision::newFromStable( $this->page, FR_MASTER ); |
558 | | - $oldSvId = $oldSv ? $oldSv->getRevId() : 0; |
559 | | - |
560 | 429 | # Update the article review log |
| 430 | + $oldSvId = $oldSv ? $oldSv->getRevId() : 0; |
561 | 431 | FlaggedRevsLogs::updateLog( $this->page, $this->dims, $this->oflags, |
562 | 432 | $this->comment, $this->oldid, $oldSvId, false ); |
563 | 433 | |
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 ); |
576 | 438 | |
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 | | - |
583 | 439 | wfProfileOut( __METHOD__ ); |
584 | 440 | return true; |
585 | 441 | } |
— | — | @@ -626,8 +482,97 @@ |
627 | 483 | } |
628 | 484 | wfProfileOut( __METHOD__ ); |
629 | 485 | } |
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 | + |
631 | 575 | ########## Common form & elements ########## |
| 576 | + // @TODO: move to some other class |
632 | 577 | |
633 | 578 | /** |
634 | 579 | * Generates a brief review form for a page. |
— | — | @@ -739,7 +684,7 @@ |
740 | 685 | $pOutput = $parserCache->get( $article, $user ); |
741 | 686 | } |
742 | 687 | # Otherwise (or on cache miss), parse the rev text... |
743 | | - if ( $pOutput == false ) { |
| 688 | + if ( !$pOutput || !isset( $pOutput->fr_fileSHA1Keys ) ) { |
744 | 689 | global $wgParser, $wgEnableParserCache; |
745 | 690 | $text = $rev->getText(); |
746 | 691 | $title = $article->getTitle(); |
— | — | @@ -751,10 +696,10 @@ |
752 | 697 | } |
753 | 698 | } |
754 | 699 | $templateIDs = $pOutput->mTemplateIds; |
755 | | - $imageSHA1Keys = $pOutput->fr_ImageSHA1Keys; |
| 700 | + $imageSHA1Keys = $pOutput->fr_fileSHA1Keys; |
756 | 701 | } |
757 | 702 | list( $templateParams, $imageParams, $fileVersion ) = |
758 | | - FlaggedRevs::getIncludeParams( $article, $templateIDs, $imageSHA1Keys ); |
| 703 | + RevisionReviewForm::getIncludeParams( $article, $templateIDs, $imageSHA1Keys ); |
759 | 704 | |
760 | 705 | $form .= Xml::openElement( 'span', array( 'style' => 'white-space: nowrap;' ) ); |
761 | 706 | # Hide comment input if needed |
Index: trunk/extensions/FlaggedRevs/forms/PageStabilityForm.php |
— | — | @@ -255,27 +255,24 @@ |
256 | 256 | if ( $changed ) { |
257 | 257 | # Update logs and make a null edit |
258 | 258 | $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 | + } |
274 | 273 | } |
275 | 274 | } |
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 ); |
280 | 277 | } |
281 | 278 | # Apply watchlist checkbox value (may be NULL) |
282 | 279 | $this->updateWatchlist(); |
Index: trunk/extensions/FlaggedRevs/FlaggedRevision.php |
— | — | @@ -23,6 +23,8 @@ |
24 | 24 | private $mTitle; |
25 | 25 | private $mPageId; |
26 | 26 | private $mRevId; |
| 27 | + private $mStableTemplates; |
| 28 | + private $mStableFiles; |
27 | 29 | |
28 | 30 | /** |
29 | 31 | * @param mixed $row (DB row or array) |
— | — | @@ -49,21 +51,25 @@ |
50 | 52 | $this->mFlags = isset( $row->fr_flags ) ? |
51 | 53 | explode( ',', $row->fr_flags ) : null; |
52 | 54 | } 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'] ) ); |
59 | 61 | # 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'] ); |
65 | 67 | # 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; |
68 | 74 | } else { |
69 | 75 | throw new MWException( 'FlaggedRevision constructor passed invalid row format.' ); |
70 | 76 | } |
— | — | @@ -115,7 +121,7 @@ |
116 | 122 | } |
117 | 123 | return null; |
118 | 124 | } |
119 | | - |
| 125 | + |
120 | 126 | /** |
121 | 127 | * Get a FlaggedRevision of the stable version of a title. |
122 | 128 | * @param Title $title, page title |
— | — | @@ -129,93 +135,128 @@ |
130 | 136 | } |
131 | 137 | $columns = self::selectFields(); |
132 | 138 | $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 ); |
136 | 140 | 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 ) { |
137 | 162 | return null; |
138 | 163 | } |
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' ), |
143 | 207 | $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 |
147 | 213 | ), |
148 | | - __METHOD__ |
| 214 | + __METHOD__, |
| 215 | + $options |
149 | 216 | ); |
| 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 | + ); |
150 | 254 | 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 | | - } |
214 | 255 | } |
215 | 256 | $frev = new self( $row ); |
216 | 257 | $frev->mTitle = $title; |
217 | 258 | return $frev; |
218 | 259 | } |
219 | | - |
| 260 | + |
220 | 261 | /* |
221 | 262 | * Insert a FlaggedRevision object into the database |
222 | 263 | * |
— | — | @@ -224,11 +265,34 @@ |
225 | 266 | * @param bool $auto autopatrolled |
226 | 267 | * @return bool success |
227 | 268 | */ |
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'; |
230 | 273 | if ( $auto ) $textFlags .= ',auto'; |
231 | 274 | $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 | + } |
233 | 297 | # Our review entry |
234 | 298 | $revRow = array( |
235 | 299 | 'fr_page_id' => $this->getPage(), |
— | — | @@ -254,11 +318,11 @@ |
255 | 319 | $dbw->delete( 'flaggedimages', |
256 | 320 | array( 'fi_rev_id' => $this->getRevId() ), __METHOD__ ); |
257 | 321 | # 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' ); |
260 | 324 | } |
261 | | - if ( !empty( $fileRows ) ) { |
262 | | - $dbw->insert( 'flaggedimages', $fileRows, __METHOD__, 'IGNORE' ); |
| 325 | + if ( $fileInsertRows ) { |
| 326 | + $dbw->insert( 'flaggedimages', $fileInsertRows, __METHOD__, 'IGNORE' ); |
263 | 327 | } |
264 | 328 | return true; |
265 | 329 | } |
— | — | @@ -392,33 +456,18 @@ |
393 | 457 | } |
394 | 458 | |
395 | 459 | /** |
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 | | - /** |
414 | 460 | * Get original template versions at time of review |
| 461 | + * @param int $flags FR_MASTER |
415 | 462 | * @return Array template versions (ns -> dbKey -> rev Id) |
416 | 463 | * Note: 0 used for template rev Id if it didn't exist |
417 | 464 | */ |
418 | | - public function getTemplateVersions() { |
| 465 | + public function getTemplateVersions( $flags = 0 ) { |
419 | 466 | if ( $this->mTemplates == null ) { |
420 | 467 | $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' ), |
423 | 472 | array( 'ft_rev_id' => $this->getRevId() ), |
424 | 473 | __METHOD__ |
425 | 474 | ); |
— | — | @@ -434,14 +483,17 @@ |
435 | 484 | |
436 | 485 | /** |
437 | 486 | * 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) |
440 | 490 | */ |
441 | | - public function getFileVersions() { |
| 491 | + public function getFileVersions( $flags = 0 ) { |
442 | 492 | if ( $this->mFiles == null ) { |
443 | 493 | $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' ), |
446 | 498 | array( 'fi_rev_id' => $this->getRevId() ), |
447 | 499 | __METHOD__ |
448 | 500 | ); |
— | — | @@ -449,12 +501,87 @@ |
450 | 502 | $reviewedTS = trim( $row->fi_img_timestamp ); // may be ''/NULL |
451 | 503 | $reviewedTS = $reviewedTS ? wfTimestamp( TS_MW, $reviewedTS ) : '0'; |
452 | 504 | $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; |
454 | 507 | } |
455 | 508 | } |
456 | 509 | return $this->mFiles; |
457 | 510 | } |
458 | 511 | |
| 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 | + |
459 | 586 | /* |
460 | 587 | * Fetch pending template changes for this reviewed page version. |
461 | 588 | * 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 |
1 | 233 | + native |
Index: trunk/extensions/FlaggedRevs/FlaggedRevs.hooks.php |
— | — | @@ -205,19 +205,21 @@ |
206 | 206 | } |
207 | 207 | |
208 | 208 | /** |
209 | | - * Update flaggedrevs table on article history merge |
| 209 | + * Update flaggedrevs page/tracking tables (revision moving) |
210 | 210 | */ |
211 | 211 | public static function updateFromMerge( Title $sourceTitle, Title $destTitle ) { |
212 | 212 | $oldPageID = $sourceTitle->getArticleID(); |
213 | 213 | $newPageID = $destTitle->getArticleID(); |
214 | 214 | # Get flagged revisions from old page id that point to destination page |
215 | 215 | $dbw = wfGetDB( DB_MASTER ); |
216 | | - $result = $dbw->select( array( 'flaggedrevs', 'revision' ), |
| 216 | + $result = $dbw->select( |
| 217 | + array( 'flaggedrevs', 'revision' ), |
217 | 218 | array( 'fr_rev_id' ), |
218 | 219 | array( 'fr_page_id' => $oldPageID, |
219 | 220 | 'fr_rev_id = rev_id', |
220 | 221 | 'rev_page' => $newPageID ), |
221 | | - __METHOD__ ); |
| 222 | + __METHOD__ |
| 223 | + ); |
222 | 224 | # Update these rows |
223 | 225 | $revIDs = array(); |
224 | 226 | while ( $row = $dbw->fetchObject( $result ) ) { |
— | — | @@ -230,32 +232,31 @@ |
231 | 233 | 'fr_rev_id' => $revIDs ), |
232 | 234 | __METHOD__ ); |
233 | 235 | } |
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 ); |
238 | 239 | return true; |
239 | 240 | } |
240 | 241 | |
241 | 242 | /** |
242 | | - * Update flaggedrevs tracking tables |
| 243 | + * Update flaggedrevs page/tracking tables |
243 | 244 | */ |
244 | | - public static function onArticleDelete( &$article, &$user, $reason, $id ) { |
| 245 | + public static function onArticleDelete( Article $article, $user, $reason, $id ) { |
245 | 246 | FlaggedRevs::clearTrackingRows( $id ); |
246 | 247 | return true; |
247 | 248 | } |
248 | 249 | |
249 | 250 | /** |
250 | | - * Update stable version selection |
| 251 | + * Update flaggedrevs page/tracking tables |
251 | 252 | */ |
252 | | - public static function onRevisionDelete( Title &$title ) { |
253 | | - FlaggedRevs::titleLinksUpdate( $title ); |
| 253 | + public static function onRevisionDelete( Title $title ) { |
| 254 | + FlaggedRevs::stableVersionUpdates( $title ); |
254 | 255 | return true; |
255 | 256 | } |
256 | 257 | |
257 | 258 | /** |
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 |
260 | 261 | */ |
261 | 262 | public static function onTitleMoveComplete( |
262 | 263 | Title $otitle, Title $ntitle, $user, $pageId |
— | — | @@ -264,99 +265,32 @@ |
265 | 266 | // Re-validate NS/config (new title may not be reviewable) |
266 | 267 | if ( $fa->isReviewable( FR_MASTER ) ) { |
267 | 268 | // Moved from non-reviewable to reviewable NS? |
| 269 | + // Auto-review such edits like new pages... |
268 | 270 | if ( !FlaggedRevs::inReviewNamespace( $otitle ) |
269 | 271 | && FlaggedRevs::autoReviewNewPages() |
270 | 272 | && $ntitle->userCan( 'autoreview' ) ) |
271 | 273 | { |
272 | 274 | $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 | + } |
278 | 278 | } |
279 | 279 | } |
280 | | - FlaggedRevs::clearTrackingRows( $pageId ); |
| 280 | + # Update page and tracking tables and clear cache |
| 281 | + FlaggedRevs::stableVersionUpdates( $otitle ); |
| 282 | + FlaggedRevs::stableVersionUpdates( $ntitle ); |
281 | 283 | return true; |
282 | 284 | } |
283 | 285 | |
284 | 286 | // @TODO: replace raw $linksUpdate field accesses |
285 | 287 | public static function onLinksUpdate( LinksUpdate $linksUpdate ) { |
286 | | - global $wgUser; |
287 | 288 | 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 ); |
332 | 291 | wfProfileOut( __METHOD__ ); |
333 | 292 | return true; |
334 | 293 | } |
335 | 294 | |
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 | | - |
361 | 295 | /* |
362 | 296 | * Update pages where only the stable version links to a page |
363 | 297 | * that was just changed in some way. |
— | — | @@ -371,7 +305,7 @@ |
372 | 306 | * Add special fields to parser. |
373 | 307 | */ |
374 | 308 | public static function parserAddFields( Parser $parser ) { |
375 | | - $parser->mOutput->fr_ImageSHA1Keys = array(); |
| 309 | + $parser->mOutput->fr_fileSHA1Keys = array(); |
376 | 310 | $parser->mOutput->fr_includeErrors = array(); |
377 | 311 | return true; |
378 | 312 | } |
— | — | @@ -381,71 +315,54 @@ |
382 | 316 | * Note: $parser can be false |
383 | 317 | */ |
384 | 318 | 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 ) { |
389 | 320 | return true; // nothing to do |
390 | 321 | } |
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 |
401 | 325 | } |
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) |
408 | 331 | } |
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 | + } |
422 | 339 | } |
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 |
434 | 345 | } |
| 346 | + # If $id is zero, don't bother loading it |
| 347 | + } elseif ( !$id ) { |
| 348 | + $skip = true; |
435 | 349 | } |
436 | 350 | return true; |
437 | 351 | } |
438 | 352 | |
439 | 353 | /** |
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 |
441 | 356 | */ |
442 | | - public static function parserMakeStableFileLink( |
| 357 | + public static function parserFetchStableFile( |
443 | 358 | $parser, Title $nt, &$skip, &$time, &$query = false |
444 | 359 | ) { |
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 |
448 | 362 | } |
449 | | - $file = null; |
| 363 | + $incManager = FRInclusionManager::singleton(); |
| 364 | + if ( !$incManager->parserOutputIsStabilized() ) { |
| 365 | + return true; // trigger for stable version parsing only |
| 366 | + } |
450 | 367 | # Normalize NS_MEDIA to NS_FILE |
451 | 368 | if ( $nt->getNamespace() == NS_MEDIA ) { |
452 | 369 | $title = Title::makeTitle( NS_FILE, $nt->getDBkey() ); |
— | — | @@ -453,143 +370,78 @@ |
454 | 371 | } else { |
455 | 372 | $title =& $nt; |
456 | 373 | } |
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 ) ); |
469 | 380 | } |
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 |
480 | 392 | } |
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 |
497 | 396 | } |
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 ) ); |
515 | 403 | } |
516 | | - # Add image metadata to parser output |
517 | | - $parser->mOutput->fr_ImageSHA1Keys[$title->getDBkey()] = array(); |
518 | | - $parser->mOutput->fr_ImageSHA1Keys[$title->getDBkey()][$time] = $sha1; |
519 | 404 | return true; |
520 | 405 | } |
521 | 406 | |
522 | 407 | /** |
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 |
524 | 410 | */ |
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; |
532 | 420 | } |
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; |
545 | 428 | } |
546 | 429 | } |
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 |
555 | 436 | } |
| 437 | + } elseif ( !$time ) { |
| 438 | + $time = "0"; // make sure this the string '0' |
556 | 439 | } |
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; |
572 | 445 | } |
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; |
594 | 446 | return true; |
595 | 447 | } |
596 | 448 | |
— | — | @@ -601,23 +453,25 @@ |
602 | 454 | if ( !isset( $pOutput->mImages ) ) { |
603 | 455 | return true; // sanity check |
604 | 456 | } |
605 | | - $stableOut = !empty( $parser->fr_isStable ); |
606 | 457 | # Fetch the current timestamps of the images. |
607 | 458 | foreach ( $pOutput->mImages as $filename => $x ) { |
608 | 459 | # FIXME: it would be nice not to double fetch these! |
609 | 460 | $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']; |
614 | 466 | } |
615 | 467 | $title = Title::makeTitleSafe( NS_FILE, $filename ); |
616 | 468 | $file = wfFindFile( $title, array( 'time' => $time ) ); |
617 | | - $pOutput->fr_ImageSHA1Keys[$filename] = array(); |
| 469 | + $pOutput->fr_fileSHA1Keys[$filename] = array(); |
618 | 470 | 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(); |
620 | 473 | } else { |
621 | | - $pOutput->fr_ImageSHA1Keys[$filename]["0"] = ''; |
| 474 | + $pOutput->fr_fileSHA1Keys[$filename]['ts'] = '0'; |
| 475 | + $pOutput->fr_fileSHA1Keys[$filename]['sha1'] = ''; |
622 | 476 | } |
623 | 477 | } |
624 | 478 | return true; |
— | — | @@ -657,21 +511,19 @@ |
658 | 512 | |
659 | 513 | public static function parserPagesUsingPendingChanges( &$parser, $ns = '' ) { |
660 | 514 | $nsList = FlaggedRevs::getReviewNamespaces(); |
661 | | - |
662 | | - |
663 | | - if( !$nsList ) { |
| 515 | + if ( !$nsList ) { |
664 | 516 | return 0; |
665 | 517 | } |
666 | 518 | |
667 | | - if( $ns !== '' ) { |
| 519 | + if ( $ns !== '' ) { |
668 | 520 | $ns = intval( $ns ); |
669 | | - if( !in_array( $ns, $nsList ) ) { |
| 521 | + if ( !in_array( $ns, $nsList ) ) { |
670 | 522 | return 0; |
671 | 523 | } |
672 | 524 | } |
673 | 525 | |
674 | 526 | static $pcCounts = null; |
675 | | - if( !$pcCounts ) { |
| 527 | + if ( !$pcCounts ) { |
676 | 528 | $dbr = wfGetDB( DB_SLAVE ); |
677 | 529 | $res = $dbr->select( 'flaggedrevs_stats', '*', array(), __METHOD__ ); |
678 | 530 | $totalCount = 0; |
— | — | @@ -682,7 +534,7 @@ |
683 | 535 | $nsList[ 'all' ] = $totalCount; |
684 | 536 | } |
685 | 537 | |
686 | | - if( $ns === '' ) { |
| 538 | + if ( $ns === '' ) { |
687 | 539 | return $nsList['all']; |
688 | 540 | } else { |
689 | 541 | return $nsList[ "ns-$ns" ]; |
— | — | @@ -694,21 +546,18 @@ |
695 | 547 | */ |
696 | 548 | public static function outputInjectTimestamps( OutputPage $out, ParserOutput $parserOut ) { |
697 | 549 | # 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 | + } |
700 | 553 | # Leave as defaults if missing. Relevant things will be updated only when needed. |
701 | 554 | # 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(); |
704 | 557 | # Add on any new items |
705 | | - $out->fr_ImageSHA1Keys = wfArrayMerge( $out->fr_ImageSHA1Keys, $imageSHA1Keys ); |
| 558 | + $out->fr_fileSHA1Keys = wfArrayMerge( $out->fr_fileSHA1Keys, $fileSHA1Keys ); |
706 | 559 | return true; |
707 | 560 | } |
708 | 561 | |
709 | | - protected static function getLocalFile( Title $title, $time ) { |
710 | | - return RepoGroup::singleton()->getLocalRepo()->findFile( $title, $time ); |
711 | | - } |
712 | | - |
713 | 562 | /** |
714 | 563 | * Check page move and patrol permissions for FlaggedRevs |
715 | 564 | */ |
— | — | @@ -862,7 +711,7 @@ |
863 | 712 | if ( $srev && self::isSelfRevertToStable( $rev, $srev, $baseRevId, $user ) ) { |
864 | 713 | $flags = $srev->getTags(); // use old tags |
865 | 714 | # Review this revision of the page... |
866 | | - FlaggedRevs::autoReviewEdit( $article, $user, $rev->getText(), $rev, $flags ); |
| 715 | + FlaggedRevs::autoReviewEdit( $article, $user, $rev, $flags ); |
867 | 716 | return true; // done! |
868 | 717 | } |
869 | 718 | # Can this user auto-review this page? |
— | — | @@ -887,7 +736,7 @@ |
888 | 737 | $flags = $frev->getTags(); // Dummy edits always keep previous tags |
889 | 738 | } |
890 | 739 | # Review this revision of the page... |
891 | | - FlaggedRevs::autoReviewEdit( $article, $user, $rev->getText(), $rev, $flags ); |
| 740 | + FlaggedRevs::autoReviewEdit( $article, $user, $rev, $flags ); |
892 | 741 | } |
893 | 742 | return true; |
894 | 743 | } |
— | — | @@ -919,7 +768,7 @@ |
920 | 769 | } |
921 | 770 | # Review this revision of the page... |
922 | 771 | return FlaggedRevs::autoReviewEdit( |
923 | | - $article, $user, $rev->getText(), $rev, $flags, false /* manual */ ); |
| 772 | + $article, $user, $rev, $flags, false /* manual */ ); |
924 | 773 | } |
925 | 774 | |
926 | 775 | /** |
— | — | @@ -963,6 +812,7 @@ |
964 | 813 | * When an user makes a null-edit we sometimes want to review it... |
965 | 814 | * (a) Null undo or rollback |
966 | 815 | * (b) Null edit with review box checked |
| 816 | + * Note: called after edit ops are finished |
967 | 817 | */ |
968 | 818 | public static function maybeNullEditReview( |
969 | 819 | Article $article, $user, $text, $s, $m, $a, $b, $flags, $rev, &$status, $baseId |
— | — | @@ -989,9 +839,10 @@ |
990 | 840 | # Was the edit that we tried to revert to reviewed? |
991 | 841 | if ( $frev ) { |
992 | 842 | # Review this revision of the page... |
993 | | - $ok = FlaggedRevs::autoReviewEdit( $article, $user, $rev->getText(), $rev, $flags ); |
| 843 | + $ok = FlaggedRevs::autoReviewEdit( $article, $user, $rev, $flags ); |
994 | 844 | if ( $ok ) { |
995 | 845 | FlaggedRevs::markRevisionPatrolled( $rev ); // reviewed -> patrolled |
| 846 | + FlaggedRevs::stableVersionUpdates( $title ); // update page tables |
996 | 847 | return true; |
997 | 848 | } |
998 | 849 | } |
— | — | @@ -1014,10 +865,10 @@ |
1015 | 866 | } |
1016 | 867 | } |
1017 | 868 | # 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 ); |
1020 | 870 | if ( $ok ) { |
1021 | 871 | FlaggedRevs::markRevisionPatrolled( $rev ); // reviewed -> patrolled |
| 872 | + FlaggedRevs::stableVersionUpdates( $title ); // update page tables |
1022 | 873 | } |
1023 | 874 | } |
1024 | 875 | return true; |
— | — | @@ -1610,7 +1461,6 @@ |
1611 | 1462 | |
1612 | 1463 | public static function onArticleViewHeader( &$article, &$outputDone, &$pcache ) { |
1613 | 1464 | $view = FlaggedArticleView::singleton(); |
1614 | | - $view->maybeUpdateMainCache( $outputDone, $pcache ); |
1615 | 1465 | $view->addStableLink( $outputDone, $pcache ); |
1616 | 1466 | $view->setPageContent( $outputDone, $pcache ); |
1617 | 1467 | return true; |
Index: trunk/extensions/FlaggedRevs/api/ApiReview.php |
— | — | @@ -66,6 +66,7 @@ |
67 | 67 | $form->setDim( $tag, intval( $params['flag_' . $tag] ) ); |
68 | 68 | } |
69 | 69 | if ( $form->isApproval() ) { |
| 70 | + $parserOutput = null; |
70 | 71 | // Now get the template and image parameters needed |
71 | 72 | // If it is the current revision, try the parser cache first |
72 | 73 | $article = new FlaggedArticle( $title, $revid ); |
— | — | @@ -73,7 +74,7 @@ |
74 | 75 | $parserCache = ParserCache::singleton(); |
75 | 76 | $parserOutput = $parserCache->get( $article, $wgUser ); |
76 | 77 | } |
77 | | - if ( empty( $parserOutput ) ) { |
| 78 | + if ( !$parserOutput || !isset( $parserOutput->fr_fileSHA1Keys ) ) { |
78 | 79 | // Miss, we have to reparse the page |
79 | 80 | global $wgParser; |
80 | 81 | $text = $article->getContent(); |
— | — | @@ -82,8 +83,8 @@ |
83 | 84 | } |
84 | 85 | // Set version parameters for review submission |
85 | 86 | list( $templateParams, $imageParams, $fileVersion ) = |
86 | | - FlaggedRevs::getIncludeParams( $article, |
87 | | - $parserOutput->mTemplateIds, $parserOutput->fr_ImageSHA1Keys ); |
| 87 | + RevisionReviewForm::getIncludeParams( $article, |
| 88 | + $parserOutput->mTemplateIds, $parserOutput->fr_fileSHA1Keys ); |
88 | 89 | $form->setTemplateParams( $templateParams ); |
89 | 90 | $form->setFileParams( $imageParams ); |
90 | 91 | $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 |
1 | 185 | + native |
Index: trunk/extensions/FlaggedRevs/FlaggedArticleView.php |
— | — | @@ -496,7 +496,8 @@ |
497 | 497 | # Check if this is a redirect... |
498 | 498 | $redirHtml = $this->getRedirectHtml( $text ); |
499 | 499 | if ( $redirHtml == '' ) { |
500 | | - $parserOut = FlaggedRevs::parseStableText( $this->article, $text, $frev->getRevId() ); |
| 500 | + $parserOut = FlaggedRevs::parseStableText( |
| 501 | + $this->article->getTitle(), $text, $frev->getRevId() ); |
501 | 502 | } |
502 | 503 | # Construct some tagging for non-printable outputs. Note that the pending |
503 | 504 | # notice has all this info already, so don't do this if we added that already. |
— | — | @@ -566,15 +567,18 @@ |
567 | 568 | # Get parsed stable version |
568 | 569 | $redirHtml = ''; |
569 | 570 | $parserOut = FlaggedRevs::getPageCache( $this->article, $wgUser ); |
570 | | - if ( $parserOut == false ) { |
| 571 | + if ( !$parserOut ) { |
571 | 572 | $text = $srev->getRevText(); |
572 | 573 | # Check if this is a redirect... |
573 | 574 | $redirHtml = $this->getRedirectHtml( $text ); |
| 575 | + # Don't parse redirects, use separate handling... |
574 | 576 | if ( $redirHtml == '' ) { |
| 577 | + # Get the new stable output |
575 | 578 | $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 |
578 | 581 | FlaggedRevs::updatePageCache( $this->article, $wgUser, $parserOut ); |
| 582 | + FlaggedRevs::updateCacheTracking( $this->article, $parserOut ); |
579 | 583 | } else { |
580 | 584 | $parserOut = null; |
581 | 585 | } |
— | — | @@ -1035,12 +1039,13 @@ |
1036 | 1040 | if ( $rev ) { |
1037 | 1041 | $templateIDs = $fileSHA1Keys = null; |
1038 | 1042 | # $wgOut may not already have the inclusion IDs, such as for diffonly=1. |
| 1043 | + # RevisionReviewForm will fetch them as needed however. |
1039 | 1044 | if ( $wgOut->getRevisionId() == $rev->getId() |
1040 | 1045 | && isset( $wgOut->mTemplateIds ) |
1041 | | - && isset( $wgOut->fr_ImageSHA1Keys ) ) |
| 1046 | + && isset( $wgOut->fr_fileSHA1Keys ) ) |
1042 | 1047 | { |
1043 | 1048 | $templateIDs = $wgOut->mTemplateIds; |
1044 | | - $fileSHA1Keys = $wgOut->fr_ImageSHA1Keys; |
| 1049 | + $fileSHA1Keys = $wgOut->fr_fileSHA1Keys; |
1045 | 1050 | } |
1046 | 1051 | $form = RevisionReviewForm::buildQuickReview( $wgUser, $this->article, |
1047 | 1052 | $rev, $templateIDs, $fileSHA1Keys, $this->isDiffFromStable ); |
— | — | @@ -1789,35 +1794,4 @@ |
1790 | 1795 | } |
1791 | 1796 | return true; |
1792 | 1797 | } |
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 | | - } |
1824 | 1798 | } |