r35222 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r35221‎ | r35222 | r35223 >
Date:09:03, 23 May 2008
Author:tstarling
Status:old
Tags:
Comment:
In FlaggedRevs:

* Moved the FlaggedRevs class to its own file
* Removed $wgFlaggedArticle, store an instance inside the Article object instead. Also store a reference in the Title object, since some hooks only have a Title available.
* Modified all hooks which were previously calling $wgFlaggedArticle directly to go via an instance loader function.
* Merged hook functions for ArticleViewHeader and DiffViewHeader
* Changed the way FlaggedRevs sets the right image version in ImagePage, to avoid a function call on startup
* Some coding style changes, such as consistent variable case, consistent indenting style, meaningful variable names, etc.
* Fixed typo in updateAutoPromote.php
* Removed the $wgFlaggedRevsVisible feature, this doesn't seem to be in keeping with our mission so I couldn't see the point in spending a lot of time fixing its bugs. Revert if necessary.
* Use Xml::encodeJsVar() to transfer data from PHP to JS, don't DIY
* Use OutputPage::addHeadItem() instead of an infinite-lifetime variable to ensure that only one copy of the header item is added. OutputPage objects may come and go.
Modified paths:
  • /trunk/extensions/FlaggedRevs/FlaggedArticle.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/FlaggedRevs.class.php (added) (history)
  • /trunk/extensions/FlaggedRevs/FlaggedRevs.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/FlaggedRevsPage.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/FlaggedRevsXML.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/flaggedrevs.js (modified) (history)
  • /trunk/extensions/FlaggedRevs/maintenance/updateAutoPromote.php (modified) (history)
  • /trunk/phase3/includes/Article.php (modified) (history)
  • /trunk/phase3/includes/DifferenceEngine.php (modified) (history)
  • /trunk/phase3/includes/ImagePage.php (modified) (history)
  • /trunk/phase3/includes/PageHistory.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/Article.php
@@ -1324,10 +1324,10 @@
13251325 }
13261326 }
13271327
1328 - $extraq = ''; // Give extensions a chance to modify URL query on update
1329 - wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this, &$sectionanchor, &$extraq ) );
 1328+ $extraQuery = ''; // Give extensions a chance to modify URL query on update
 1329+ wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this, &$sectionanchor, &$extraQuery ) );
13301330
1331 - $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraq );
 1331+ $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraQuery );
13321332 }
13331333 return $good;
13341334 }
@@ -1573,16 +1573,16 @@
15741574 *
15751575 * @param boolean $noRedir Add redirect=no
15761576 * @param string $sectionAnchor section to redirect to, including "#"
1577 - * @param string $extraq, extra query params
 1577+ * @param string $extraQuery, extra query params
15781578 */
1579 - function doRedirect( $noRedir = false, $sectionAnchor = '', $extraq = '' ) {
 1579+ function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) {
15801580 global $wgOut;
15811581 if ( $noRedir ) {
15821582 $query = 'redirect=no';
1583 - if( $extraq )
 1583+ if( $extraQuery )
15841584 $query .= "&$query";
15851585 } else {
1586 - $query = $extraq;
 1586+ $query = $extraQuery;
15871587 }
15881588 $wgOut->redirect( $this->mTitle->getFullURL( $query ) . $sectionAnchor );
15891589 }
Index: trunk/phase3/includes/ImagePage.php
@@ -13,45 +13,36 @@
1414 /* private */ var $img; // Image object
1515 /* private */ var $displayImg;
1616 /* private */ var $repo;
17 - /* private */ var $time;
1817 /* private */ var $fileLoaded;
1918 var $mExtraDescription = false;
2019 var $dupes;
2120
22 - function __construct( $title, $time = null ) {
 21+ function __construct( $title ) {
2322 parent::__construct( $title );
2423
2524 global $wgRequest;
26 - $time = is_null($time) ? $wgRequest->getVal( 'filetimestamp' ) : $time;
27 - $time = $time ? $time : false; // be clear about type
28 - $this->time = $time;
2925 $this->dupes = null;
3026 $this->repo = null;
3127 }
32 -
 28+
3329 protected function loadFile() {
3430 if ( $this->fileLoaded ) {
3531 return true;
3632 }
37 - $this->displayImg = wfFindFile( $this->mTitle, $this->time );
38 - # If none found, and no time given, use a valid local placeholder
39 - if ( !$this->displayImg && !$this->time ) {
40 - $this->displayImg = wfLocalFile( $this->mTitle );
41 - $this->img = $this->displayImg;
42 - # If none found, and time given, try current
43 - } else if ( !$this->displayImg && $this->time ) {
44 - $this->displayImg = wfFindFile( $this->mTitle );
45 - # If none found, use a valid local placeholder
46 - if( !$this->displayImg ) {
47 - $this->displayImg = wfLocalFile( $this->mTitle ); // fallback to current
 33+ $this->fileLoaded = true;
 34+
 35+ $this->displayImg = $this->img = false;
 36+ wfRunHooks( 'ImagePageFindFile', array( $this, &$this->img, &$this->displayImg ) );
 37+ if ( !$this->img ) {
 38+ $this->img = wfFindFile( $this->mTitle );
 39+ if ( !$this->img ) {
 40+ $this->img = wfLocalFile( $this->mTitle );
4841 }
49 - $this->img = $this->displayImg;
50 - # If found, set $this->img. This will be the same if no time given
51 - } else {
52 - $this->img = $this->time ? wfFindFile( $this->mTitle ) : $this->displayImg;
5342 }
 43+ if ( !$this->displayImg ) {
 44+ $this->displayImg = $this->img;
 45+ }
5446 $this->repo = $this->img->getRepo();
55 - $this->fileLoaded = true;
5647 }
5748
5849 /**
@@ -609,13 +600,11 @@
610601 */
611602 function imageHistory()
612603 {
613 - global $wgUser, $wgOut, $wgUseExternalEditor;
 604+ global $wgOut, $wgUseExternalEditor;
614605
615 - $sk = $wgUser->getSkin();
616 -
617606 $this->loadFile();
618607 if ( $this->img->exists() ) {
619 - $list = new ImageHistoryList( $sk, $this->img, $this->displayImg );
 608+ $list = new ImageHistoryList( $this );
620609 $file = $this->img;
621610 $dims = $file->getDimensionsString();
622611 $s = $list->beginImageHistoryList();
@@ -786,15 +775,29 @@
787776 */
788777 class ImageHistoryList {
789778
790 - protected $img, $skin, $title, $repo;
 779+ protected $imagePage, $img, $skin, $title, $repo;
791780
792 - public function __construct( $skin, $curimg, $img ) {
793 - $this->skin = $skin;
794 - $this->current = $curimg;
795 - $this->img = $img;
796 - $this->title = $img->getTitle();
 781+ public function __construct( $imagePage ) {
 782+ global $wgUser;
 783+ $this->skin = $wgUser->getSkin();
 784+ $this->current = $imagePage->getFile();
 785+ $this->img = $imagePage->getDisplayedFile();
 786+ $this->title = $imagePage->getTitle();
 787+ $this->imagePage = $imagePage;
797788 }
798789
 790+ function getImagePage() {
 791+ return $this->imagePage;
 792+ }
 793+
 794+ function getSkin() {
 795+ return $this->skin;
 796+ }
 797+
 798+ function getFile() {
 799+ return $this->img;
 800+ }
 801+
799802 public function beginImageHistoryList() {
800803 global $wgOut, $wgUser;
801804 return Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'filehist' ) )
@@ -936,9 +939,9 @@
937940 }
938941 $row .= '</td>';
939942
940 - wfRunHooks( 'ImagePageFileHistoryLine', array( &$file, &$row, &$css ) );
941 - $trCSS = $css ? " class='$css'" : "";
 943+ wfRunHooks( 'ImagePageFileHistoryLine', array( $this, $file, &$row, &$rowClass ) );
 944+ $classAttr = $rowClass ? " class='$rowClass'" : "";
942945
943 - return "<tr{$trCSS}>{$row}</tr>\n";
 946+ return "<tr{$classAttr}>{$row}</tr>\n";
944947 }
945948 }
Index: trunk/phase3/includes/DifferenceEngine.php
@@ -73,6 +73,10 @@
7474 $this->mRefreshCache = $refreshCache;
7575 }
7676
 77+ function getTitle() {
 78+ return $this->mTitle;
 79+ }
 80+
7781 function showDiffPage( $diffOnly = false ) {
7882 global $wgUser, $wgOut, $wgUseExternalEditor, $wgUseRCPatrol;
7983 wfProfileIn( __METHOD__ );
Index: trunk/phase3/includes/PageHistory.php
@@ -41,6 +41,10 @@
4242 $this->preCacheMessages();
4343 }
4444
 45+ function getArticle() {
 46+ return $this->mArticle;
 47+ }
 48+
4549 /**
4650 * As we use the same small set of messages in various methods and that
4751 * they are called often, we call them once and save them in $this->message
@@ -281,7 +285,7 @@
282286 $s .= ' (' . implode( ' | ', $tools ) . ')';
283287 }
284288
285 - wfRunHooks( 'PageHistoryLineEnding', array( &$row , &$s ) );
 289+ wfRunHooks( 'PageHistoryLineEnding', array( $this, &$row , &$s ) );
286290
287291 return "<li>$s</li>\n";
288292 }
Index: trunk/extensions/FlaggedRevs/FlaggedRevs.php
@@ -51,12 +51,6 @@
5252 # Below are groups that see the current revision by default.
5353 $wgFlaggedRevsExceptions = array( 'user' );
5454
55 -# Flagged revisions are always visible to users with rights below.
56 -# Use '*' for non-user accounts.
57 -$wgFlaggedRevsVisible = array();
58 -# Always allow viewing of talk pages for the users above.
59 -$wgFlaggedRevsTalkVisible = false;
60 -
6155 # Can users make comments that will show up below flagged revisions?
6256 $wgFlaggedRevsComments = false;
6357 # Redirect users out to review changes since stable version on save?
@@ -100,7 +94,7 @@
10195 $wgFlaggedRevTags = array( 'accuracy'=>2, 'depth'=>1, 'style'=>1 );
10296 # How high can we rate these revisions?
10397 $wgFlaggedRevValues = 4;
104 -# A revision with all tages rated at least to this level is considered "pristine"/"featured"
 98+# A revision with all tags rated at least to this level is considered "pristine"/"featured"
10599 $wgFlaggedRevPristine = 4;
106100 # Who can set what flags to what level? (use -1 or 0 for not at all)
107101 # Users cannot lower tags from a level they can't set
@@ -121,6 +115,10 @@
122116 # There must be four codes, and only the first four are checked.
123117 $wgReviewCodes = array();
124118
 119+# URL location for flaggedrevs.css and flaggedrevs.js
 120+# Use a literal $wgScriptPath as a placeholder for the runtime value of $wgScriptPath
 121+$wgFlaggedRevsStylePath = '$wgScriptPath/extensions/FlaggedRevs';
 122+
125123 # Lets some users access the review UI and set some flags
126124 $wgAvailableRights[] = 'review';
127125 # Let some users set higher settings
@@ -194,19 +192,20 @@
195193 # How far the logs for overseeing quality revisions and depreciations go
196194 $wgFlaggedRevsOversightAge = 7 * 24 * 3600;
197195
198 -# Hour many hours pending review is considering long?
 196+# How many hours pending review is considering long?
199197 $wgFlaggedRevsLongPending = 3;
200198
201199 # End of configuration variables.
202200 #########
203201
204202 # Bump this number every time you change flaggedrevs.css/flaggedrevs.js
205 -$wgFlaggedRevStyleVersion = 21;
 203+$wgFlaggedRevStyleVersion = 22;
206204
207205 $wgExtensionFunctions[] = 'efLoadFlaggedRevs';
208206
209207 $dir = dirname(__FILE__) . '/';
210208 $wgExtensionMessagesFiles['FlaggedRevsPage'] = $dir . 'FlaggedRevsPage.i18n.php';
 209+$wgAutoloadClasses['FlaggedRevs'] = $dir.'FlaggedRevs.class.php';
211210
212211 # Load general UI
213212 $wgAutoloadClasses['FlaggedRevsXML'] = $dir . 'FlaggedRevsXML.php';
@@ -247,14 +246,81 @@
248247 $wgAutoloadClasses['DepreciationOversight'] = $dir . 'FlaggedRevsPage.php';
249248 $wgSpecialPageGroups['DepreciationOversight'] = 'quality';
250249
 250+######### Hook attachments #########
251251 # Remove stand-alone patrolling
252252 $wgHooks['UserGetRights'][] = 'FlaggedRevs::stripPatrolRights';
 253+# Autopromote Editors
 254+$wgHooks['ArticleSaveComplete'][] = 'FlaggedRevs::autoPromoteUser';
 255+# Adds table link references to include ones from the stable version
 256+$wgHooks['LinksUpdateConstructed'][] = 'FlaggedRevs::extraLinksUpdate';
 257+# Empty flagged page settings row on delete
 258+$wgHooks['ArticleDeleteComplete'][] = 'FlaggedRevs::deleteVisiblitySettings';
 259+# Check on undelete/merge/revisiondelete for changes to stable version
 260+$wgHooks['ArticleUndelete'][] = 'FlaggedRevs::titleLinksUpdate';
 261+$wgHooks['ArticleRevisionVisiblitySet'][] = 'FlaggedRevs::titleLinksUpdate';
 262+$wgHooks['ArticleMergeComplete'][] = 'FlaggedRevs::updateFromMerge';
 263+# Clean up after undeletion
 264+$wgHooks['ArticleRevisionUndeleted'][] = 'FlaggedRevs::updateFromRestore';
 265+# Parser hooks, selects the desired images/templates
 266+$wgHooks['ParserClearState'][] = 'FlaggedRevs::parserAddFields';
 267+$wgHooks['BeforeParserrenderImageGallery'][] = 'FlaggedRevs::parserMakeGalleryStable';
 268+$wgHooks['BeforeGalleryFindFile'][] = 'FlaggedRevs::galleryFindStableFileTime';
 269+$wgHooks['BeforeParserFetchTemplateAndtitle'][] = 'FlaggedRevs::parserFetchStableTemplate';
 270+$wgHooks['BeforeParserMakeImageLinkObj'][] = 'FlaggedRevs::parserMakeStableImageLink';
 271+# Additional parser versioning
 272+$wgHooks['ParserAfterTidy'][] = 'FlaggedRevs::parserInjectTimestamps';
 273+$wgHooks['OutputPageParserOutput'][] = 'FlaggedRevs::outputInjectTimestamps';
 274+# Auto-reviewing
 275+$wgHooks['ArticleSaveComplete'][] = 'FlaggedRevs::autoMarkPatrolled';
 276+$wgHooks['NewRevisionFromEditComplete'][] = 'FlaggedRevs::maybeMakeEditReviewed';
 277+# Disallow moves of stable pages
 278+$wgHooks['userCan'][] = 'FlaggedRevs::userCanMove';
 279+# Log parameter
 280+$wgHooks['LogLine'][] = 'FlaggedRevs::reviewLogLine';
 281+# Disable auto-promotion
 282+$wgHooks['UserRights'][] = 'FlaggedRevs::recordDemote';
 283+# Local user account preference
 284+$wgHooks['RenderPreferencesForm'][] = 'FlaggedRevs::injectPreferences';
 285+$wgHooks['InitPreferencesForm'][] = 'FlaggedRevs::injectFormPreferences';
 286+$wgHooks['ResetPreferences'][] = 'FlaggedRevs::resetPreferences';
 287+$wgHooks['SavePreferences'][] = 'FlaggedRevs::savePreferences';
 288+# Special page CSS
 289+$wgHooks['BeforePageDisplay'][] = 'FlaggedRevs::InjectStyleForSpecial';
 290+# Image version display
 291+$wgHooks['ImagePageFindFile'][] = 'FlaggedRevs::imagePageFindFile';
253292
 293+# Main hooks, overrides pages content, adds tags, sets tabs and permalink
 294+$wgHooks['SkinTemplateTabs'][] = 'FlaggedRevs::setActionTabs';
 295+# Change last-modified footer
 296+$wgHooks['SkinTemplateOutputPageBeforeExec'][] = 'FlaggedRevs::setLastModified';
 297+# Update older, incomplete, page caches (ones that lack template Ids/image timestamps)
 298+$wgHooks['ArticleViewHeader'][] = 'FlaggedRevs::onArticleViewHeader';
 299+# Add page notice
 300+$wgHooks['SkinTemplateBuildNavUrlsNav_urlsAfterPermalink'][] = 'FlaggedRevs::setPermaLink';
 301+# Add tags do edit view
 302+$wgHooks['EditPage::showEditForm:initial'][] = 'FlaggedRevs::addToEditView';
 303+# Add review form
 304+$wgHooks['BeforePageDisplay'][] = 'FlaggedRevs::addReviewForm';
 305+$wgHooks['BeforePageDisplay'][] = 'FlaggedRevs::addVisibilityLink';
 306+# Mark of items in page history
 307+$wgHooks['PageHistoryLineEnding'][] = 'FlaggedRevs::addToHistLine';
 308+$wgHooks['ImagePageFileHistoryLine'][] = 'FlaggedRevs::addToFileHistLine';
 309+# Page review on edit
 310+$wgHooks['ArticleUpdateBeforeRedirect'][] = 'FlaggedRevs::injectReviewDiffURLParams';
 311+$wgHooks['DiffViewHeader'][] = 'FlaggedRevs::onDiffViewHeader';
 312+# Autoreview stuff
 313+$wgHooks['EditPage::showEditForm:fields'][] = 'FlaggedRevs::addRevisionIDField';
 314+# Add CSS/JS
 315+$wgHooks['OutputPageParserOutput'][] = 'FlaggedRevs::injectStyleAndJS';
 316+$wgHooks['EditPage::showEditForm:initial'][] = 'FlaggedRevs::injectStyleAndJS';
 317+$wgHooks['PageHistoryBeforeList'][] = 'FlaggedRevs::injectStyleAndJS';
 318+
 319+#########
 320+
254321 function efLoadFlaggedRevs() {
255 - global $wgOut, $wgHooks, $wgLang, $wgFlaggedArticle, $wgUseRCPatrol;
 322+ global $wgOut, $wgHooks, $wgLang, $wgUseRCPatrol;
256323 # Initialize
257324 FlaggedRevs::load();
258 - $wgFlaggedArticle = null;
259325
260326 wfLoadExtensionMessages( 'FlaggedRevsPage' );
261327
@@ -262,103 +328,8 @@
263329 # When revisions are flagged, they count as patrolled
264330 $wgUseRCPatrol = true;
265331
266 - global $wgScriptPath, $wgFlaggedRevStyleVersion;
267 - if( !defined( 'FLAGGED_CSS' ) )
268 - define( 'FLAGGED_CSS', $wgScriptPath . '/extensions/FlaggedRevs/flaggedrevs.css?' . $wgFlaggedRevStyleVersion );
269 - if( !defined( 'FLAGGED_JS' ) )
270 - define( 'FLAGGED_JS', $wgScriptPath . '/extensions/FlaggedRevs/flaggedrevs.js?' . $wgFlaggedRevStyleVersion );
271 -
272 - ######### Hook attachments #########
273 - # Set $wgFlaggedArticle
274 - $wgHooks['MediaWikiPerformAction'][] = 'wfInitFlaggedArticle';
275 - # Autopromote Editors
276 - $wgHooks['ArticleSaveComplete'][] = 'FlaggedRevs::autoPromoteUser';
277 - # Adds table link references to include ones from the stable version
278 - $wgHooks['LinksUpdateConstructed'][] = 'FlaggedRevs::extraLinksUpdate';
279 - # Empty flagged page settings row on delete
280 - $wgHooks['ArticleDeleteComplete'][] = 'FlaggedRevs::deleteVisiblitySettings';
281 - # Check on undelete/merge/revisiondelete for changes to stable version
282 - $wgHooks['ArticleUndelete'][] = 'FlaggedRevs::titleLinksUpdate';
283 - $wgHooks['ArticleRevisionVisiblitySet'][] = 'FlaggedRevs::titleLinksUpdate';
284 - $wgHooks['ArticleMergeComplete'][] = 'FlaggedRevs::updateFromMerge';
285 - # Clean up after undeletion
286 - $wgHooks['ArticleRevisionUndeleted'][] = 'FlaggedRevs::updateFromRestore';
287 - # Parser hooks, selects the desired images/templates
288 - $wgHooks['ParserClearState'][] = 'FlaggedRevs::parserAddFields';
289 - $wgHooks['BeforeParserrenderImageGallery'][] = 'FlaggedRevs::parserMakeGalleryStable';
290 - $wgHooks['BeforeGalleryFindFile'][] = 'FlaggedRevs::galleryFindStableFileTime';
291 - $wgHooks['BeforeParserFetchTemplateAndtitle'][] = 'FlaggedRevs::parserFetchStableTemplate';
292 - $wgHooks['BeforeParserMakeImageLinkObj'][] = 'FlaggedRevs::parserMakeStableImageLink';
293 - # Additional parser versioning
294 - $wgHooks['ParserAfterTidy'][] = 'FlaggedRevs::parserInjectTimestamps';
295 - $wgHooks['OutputPageParserOutput'][] = 'FlaggedRevs::outputInjectTimestamps';
296 - # Auto-reviewing
297 - $wgHooks['ArticleSaveComplete'][] = 'FlaggedRevs::autoMarkPatrolled';
298 - $wgHooks['NewRevisionFromEditComplete'][] = 'FlaggedRevs::maybeMakeEditReviewed';
299 - # Disallow moves of stable pages
300 - $wgHooks['userCan'][] = 'FlaggedRevs::userCanMove';
301 - $wgHooks['userCan'][] = 'FlaggedRevs::userCanView';
302 - # Log parameter
303 - $wgHooks['LogLine'][] = 'FlaggedRevs::reviewLogLine';
304 - # Disable auto-promotion
305 - $wgHooks['UserRights'][] = 'FlaggedRevs::recordDemote';
306 - # Local user account preference
307 - $wgHooks['RenderPreferencesForm'][] = 'FlaggedRevs::injectPreferences';
308 - $wgHooks['InitPreferencesForm'][] = 'FlaggedRevs::injectFormPreferences';
309 - $wgHooks['ResetPreferences'][] = 'FlaggedRevs::resetPreferences';
310 - $wgHooks['SavePreferences'][] = 'FlaggedRevs::savePreferences';
311 - # Special page CSS
312 - $wgHooks['BeforePageDisplay'][] = 'FlaggedRevs::InjectStyleForSpecial';
313 - #########
314332 }
315333
316 -function wfInitFlaggedArticle( $output, $article, $title, $user, $request ) {
317 - global $wgFlaggedArticle, $wgHooks;
318 - # Load when needed
319 - if( !FlaggedRevs::isPageReviewable($title) && !FlaggedRevs::isPagePatrollable($title) )
320 - return true;
321 - # Initialize object and set article hooks
322 - $wgFlaggedArticle = new FlaggedArticle( $title );
323 - # Set image version
324 - $wgFlaggedArticle->setImageVersion();
325 - # Always prevent hooks from doubling up
326 - if( FlaggedRevs::$articleLoaded ) {
327 - wfDebug( 'Warning - $wgFlaggedArticle already loaded!' );
328 - return true;
329 - }
330 - FlaggedRevs::$articleLoaded = true;
331 - # Main hooks, overrides pages content, adds tags, sets tabs and permalink
332 - $wgHooks['SkinTemplateTabs'][] = array( $wgFlaggedArticle, 'setActionTabs' );
333 - # Change last-modified footer
334 - $wgHooks['SkinTemplateOutputPageBeforeExec'][] = array( $wgFlaggedArticle, 'setLastModified' );
335 - # Update older, incomplete, page caches (ones that lack template Ids/image timestamps)
336 - $wgHooks['ArticleViewHeader'][] = array( $wgFlaggedArticle, 'maybeUpdateMainCache' );
337 - $wgHooks['ArticleViewHeader'][] = array( $wgFlaggedArticle, 'setPageContent' );
338 - $wgHooks['ArticleViewHeader'][] = array( $wgFlaggedArticle, 'addPatrolLink' );
339 - # Add page notice
340 - $wgHooks['SkinTemplateBuildNavUrlsNav_urlsAfterPermalink'][] = array( $wgFlaggedArticle, 'setPermaLink' );
341 - # Add tags do edit view
342 - $wgHooks['EditPage::showEditForm:initial'][] = array( $wgFlaggedArticle, 'addToEditView' );
343 - # Add review form
344 - $wgHooks['BeforePageDisplay'][] = array( $wgFlaggedArticle, 'addReviewForm' );
345 - $wgHooks['BeforePageDisplay'][] = array( $wgFlaggedArticle, 'addVisibilityLink' );
346 - # Mark of items in page history
347 - $wgHooks['PageHistoryLineEnding'][] = array( $wgFlaggedArticle, 'addToHistLine' );
348 - $wgHooks['ImagePageFileHistoryLine'][] = array( $wgFlaggedArticle, 'addToFileHistLine' );
349 - # Page review on edit
350 - $wgHooks['ArticleUpdateBeforeRedirect'][] = array( $wgFlaggedArticle, 'injectReviewDiffURLParams' );
351 - $wgHooks['DiffViewHeader'][] = array( $wgFlaggedArticle, 'addPatrolAndDiffLink' );
352 - $wgHooks['DiffViewHeader'][] = array( $wgFlaggedArticle, 'addDiffNoticeAndIncludes' );
353 - # Autoreview stuff
354 - $wgHooks['EditPage::showEditForm:fields'][] = array( $wgFlaggedArticle, 'addRevisionIDField' );
355 - # Add CSS/JS
356 - $wgHooks['OutputPageParserOutput'][] = 'FlaggedRevs::InjectStyleAndJS';
357 - $wgHooks['EditPage::showEditForm:initial'][] = 'FlaggedRevs::InjectStyleAndJS';
358 - $wgHooks['PageHistoryBeforeList'][] = 'FlaggedRevs::InjectStyleAndJS';
359 - # Done!
360 - return true;
361 -}
362 -
363334 # Add review log and such
364335 $wgLogTypes[] = 'review';
365336 $wgLogNames['review'] = 'review-logpage';
@@ -378,1958 +349,6 @@
379350
380351 $wgHooks['LoadExtensionSchemaUpdates'][] = 'efFlaggedRevsSchemaUpdates';
381352
382 -class FlaggedRevs {
383 - public static $dimensions = array();
384 - public static $articleLoaded = false;
385 - public static $styleLoaded = false;
386 - protected static $loaded = false;
387 - protected static $qualityVersions = false;
388 - protected static $pristineVersions = false;
389 - protected static $extStorage = false;
390 - protected static $allowComments = false;
391 -
392 - public static function load() {
393 - global $wgFlaggedRevTags, $wgFlaggedRevValues, $wgFlaggedRevsComments;
394 - if( self::$loaded ) {
395 - return true;
396 - }
397 - # Assume true, then set to false if needed
398 - if( !empty($wgFlaggedRevTags) ) {
399 - self::$qualityVersions = true;
400 - }
401 - foreach( $wgFlaggedRevTags as $tag => $minQL ) {
402 - $safeTag = htmlspecialchars($tag);
403 - if( strpos($tag,':') || strpos($tag,'\n') || $safeTag !== $tag ) {
404 - throw new MWException( 'FlaggedRevs given invalid tag name!' );
405 - } else if( intval($minQL) != $minQL ) {
406 - throw new MWException( 'FlaggedRevs given invalid tag value!' );
407 - }
408 - self::$dimensions[$tag] = array();
409 - for( $i=0; $i <= $wgFlaggedRevValues; $i++ ) {
410 - self::$dimensions[$tag][$i] = "{$tag}-{$i}";
411 - }
412 - if( $minQL > $wgFlaggedRevValues ) {
413 - self::$qualityVersions = false;
414 - }
415 - }
416 - global $wgFlaggedRevPristine;
417 - if( $wgFlaggedRevValues >= $wgFlaggedRevPristine ) {
418 - self::$pristineVersions = true;
419 - }
420 - global $wgFlaggedRevsExternalStore, $wgDefaultExternalStore;
421 - self::$extStorage = $wgFlaggedRevsExternalStore ?
422 - $wgFlaggedRevsExternalStore : $wgDefaultExternalStore;
423 -
424 - self::$allowComments = (bool)$wgFlaggedRevsComments;
425 -
426 - self::$loaded = true;
427 - }
428 -
429 - ################# Basic accessors #################
430 -
431 - /**
432 - * Are quality versions enabled?
433 - */
434 - public static function qualityVersions() {
435 - self::load();
436 - return self::$qualityVersions;
437 - }
438 -
439 - /**
440 - * Are pristine versions enabled?
441 - */
442 - public static function pristineVersions() {
443 - self::load();
444 - return self::$pristineVersions;
445 - }
446 -
447 - /**
448 - * Get external storage array. Default to main storage.
449 - */
450 - public static function getExternalStorage() {
451 - self::load();
452 - return self::$extStorage;
453 - }
454 -
455 - /**
456 - * Should this be using a simple icon-based UI?
457 - * Check the user's preferences first, using the site settings as the default.
458 - */
459 - public static function useSimpleUI() {
460 - global $wgUser, $wgSimpleFlaggedRevsUI;
461 -
462 - return $wgUser->getOption( 'flaggedrevssimpleui', intval($wgSimpleFlaggedRevsUI) );
463 - }
464 -
465 - /**
466 - * Should comments be allowed on pages and forms?
467 - */
468 - public static function allowComments() {
469 - self::load();
470 - return self::$allowComments;
471 - }
472 -
473 - ################# Parsing functions #################
474 -
475 - /**
476 - * @param string $text
477 - * @param Title $title
478 - * @param integer $id, revision id
479 - * @return array( string, array, array, bool, int )
480 - * All included pages/arguments are expanded out
481 - */
482 - public static function expandText( $text='', $title, $id ) {
483 - global $wgParser;
484 - # Make our hooks to trigger
485 - $wgParser->fr_isStable = true;
486 - $wgParser->fr_includesMatched = true;
487 - # Parse with default options
488 - $options = new ParserOptions();
489 - $options->setRemoveComments( true ); // Save some bandwidth ;)
490 - $outputText = $wgParser->preprocess( $text, $title, $options, $id );
491 - $expandedText = array( $outputText, $wgParser->mOutput->mTemplates, $wgParser->mOutput->mTemplateIds,
492 - $wgParser->fr_includesMatched, $wgParser->mOutput->fr_newestTemplateID );
493 - # Done with parser!
494 - $wgParser->fr_isStable = false;
495 - $wgParser->fr_includesMatched = false;
496 - # Return data array
497 - return $expandedText;
498 - }
499 -
500 - /**
501 - * Get the HTML output of a revision based on $text.
502 - * If the text is being reparsed from fr_text (expanded text),
503 - * it should be specified...In such cases, the parser will not have
504 - * template ID data. We need to know this so we can just get the data from the DB.
505 - * @param Article $article
506 - * @param string $text
507 - * @param int $id
508 - * @param bool $reparsed (is this being reparsed from fr_text?)
509 - * @return ParserOutput
510 - */
511 - public static function parseStableText( $article, $text='', $id, $reparsed = true ) {
512 - global $wgParser, $wgUseStableTemplates;
513 - $title = $article->getTitle(); // avoid pass-by-reference error
514 - # Make our hooks to trigger
515 - $wgParser->fr_isStable = true;
516 - $wgParser->fr_includesMatched = true;
517 - # Don't show section-edit links, they can be old and misleading
518 - $options = self::makeParserOptions();
519 - #$options->setEditSection( $id == $title->getLatestRevID(GAID_FOR_UPDATE) );
520 - # Parse the new body, wikitext -> html
521 - $parserOut = $wgParser->parse( $text, $title, $options, true, true, $id );
522 - $parserOut->fr_includesMatched = $wgParser->fr_includesMatched;
523 - # Done with parser!
524 - $wgParser->fr_isStable = false;
525 - $wgParser->fr_includesMatched = false;
526 - # Do we need to set the template uses via DB?
527 - if( $reparsed && !$wgUseStableTemplates ) {
528 - $dbr = wfGetDB( DB_SLAVE );
529 - $res = $dbr->select( array('flaggedtemplates','revision'),
530 - array( 'ft_namespace', 'ft_title', 'ft_tmp_rev_id AS rev_id', 'rev_page AS page_id' ),
531 - array( 'ft_rev_id' => $id, 'rev_id = ft_rev_id' ),
532 - __METHOD__ );
533 - # Add template metadata to output
534 - $maxTempID = 0;
535 - while( $row = $res->fetchObject() ) {
536 - if( !isset($parserOut->mTemplates[$row->ft_namespace]) ) {
537 - $parserOut->mTemplates[$row->ft_namespace] = array();
538 - }
539 - $parserOut->mTemplates[$row->ft_namespace][$row->ft_title] = $row->page_id;
540 -
541 - if( !isset($parserOut->mTemplateIds[$row->ft_namespace]) ) {
542 - $parserOut->mTemplateIds[$row->ft_namespace] = array();
543 - }
544 - $parserOut->mTemplateIds[$row->ft_namespace][$row->ft_title] = $row->rev_id;
545 - if( $row->rev_id > $maxTempID ) {
546 - $maxTempID = $row->rev_id;
547 - }
548 - }
549 - $parserOut->fr_newestTemplateID = $maxTempID;
550 - }
551 - return $parserOut;
552 - }
553 -
554 - /**
555 - * Get standard parser options
556 - */
557 - public static function makeParserOptions( $user = NULL ) {
558 - $options = $user ? ParserOptions::newFromUser( $user ) : new ParserOptions();
559 - # Show inclusion/loop reports
560 - $options->enableLimitReport();
561 - # Fix bad HTML
562 - $options->setTidy( true );
563 - return $options;
564 - }
565 -
566 - /**
567 - * @param Article $article
568 - * @return ParserOutput
569 - * Get the page cache for the top stable revision of an article
570 - */
571 - public static function getPageCache( $article ) {
572 - global $wgUser, $parserMemc, $wgCacheEpoch;
573 -
574 - wfProfileIn( __METHOD__ );
575 - # Make sure it is valid
576 - if( !$article->getId() )
577 - return null;
578 -
579 - $parserCache = ParserCache::singleton();
580 - $key = self::getCacheKey( $parserCache, $article, $wgUser );
581 - # Get the cached HTML
582 - wfDebug( "Trying parser cache $key\n" );
583 - $value = $parserMemc->get( $key );
584 - if( is_object( $value ) ) {
585 - wfDebug( "Found.\n" );
586 - # Delete if article has changed since the cache was made
587 - $canCache = $article->checkTouched();
588 - $cacheTime = $value->getCacheTime();
589 - $touched = $article->mTouched;
590 - if( !$canCache || $value->expired( $touched ) ) {
591 - if( !$canCache ) {
592 - wfIncrStats( "pcache_miss_invalid" );
593 - wfDebug( "Invalid cached redirect, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" );
594 - } else {
595 - wfIncrStats( "pcache_miss_expired" );
596 - wfDebug( "Key expired, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" );
597 - }
598 - $parserMemc->delete( $key );
599 - $value = false;
600 - } else {
601 - if( isset( $value->mTimestamp ) ) {
602 - $article->mTimestamp = $value->mTimestamp;
603 - }
604 - wfIncrStats( "pcache_hit" );
605 - }
606 - } else {
607 - wfDebug( "Parser cache miss.\n" );
608 - wfIncrStats( "pcache_miss_absent" );
609 - $value = false;
610 - }
611 -
612 - wfProfileOut( __METHOD__ );
613 -
614 - return $value;
615 - }
616 -
617 - /**
618 - * Like ParserCache::getKey() with stable-pcache instead of pcache
619 - */
620 - public static function getCacheKey( $parserCache, $article, &$user ) {
621 - $key = $parserCache->getKey( $article, $user );
622 - $key = str_replace( ':pcache:', ':stable-pcache:', $key );
623 - return $key;
624 - }
625 -
626 - /**
627 - * @param Article $article
628 - * @param parerOutput $parserOut
629 - * Updates the stable cache of a page with the given $parserOut
630 - */
631 - public static function updatePageCache( $article, $parserOut=null ) {
632 - global $wgUser, $parserMemc, $wgParserCacheExpireTime;
633 - # Make sure it is valid
634 - if( is_null($parserOut) )
635 - return false;
636 -
637 - $parserCache = ParserCache::singleton();
638 - $key = self::getCacheKey( $parserCache, $article, $wgUser );
639 - # Add cache mark to HTML
640 - $now = wfTimestampNow();
641 - $parserOut->setCacheTime( $now );
642 - # Save the timestamp so that we don't have to load the revision row on view
643 - $parserOut->mTimestamp = $article->getTimestamp();
644 - $parserOut->mText .= "\n<!-- Saved in stable version parser cache with key $key and timestamp $now -->";
645 - # Set expire time
646 - if( $parserOut->containsOldMagic() ){
647 - $expire = 3600; // 1 hour
648 - } else {
649 - $expire = $wgParserCacheExpireTime;
650 - }
651 - # Save to objectcache
652 - $parserMemc->set( $key, $parserOut, $expire );
653 -
654 - return true;
655 - }
656 -
657 - ################# Synchronization and link update functions #################
658 -
659 - /**
660 - * @param FlaggedRevision $frev
661 - * @param Article $article
662 - * @param ParserOutput $stableOutput, will fetch if not given
663 - * @param ParserOutput $currentOutput, will fetch if not given
664 - * @return bool
665 - * See if a flagged revision is synced with the current
666 - */
667 - public static function flaggedRevIsSynced( $frev, $article, $stableOutput=null, $currentOutput=null ) {
668 - # Must be the same revision
669 - if( $frev->getRevId() != $article->getTitle()->getLatestRevID(GAID_FOR_UPDATE) ) {
670 - return false;
671 - }
672 - # Must have same file
673 - if( $article instanceof ImagePage && $article->getFile() ) {
674 - if( $frev->getFileTimestamp() != $article->getFile()->getTimestamp() ) {
675 - return false;
676 - }
677 - }
678 - global $wgMemc;
679 - # Try the cache. Uses format <page ID>-<UNIX timestamp>.
680 - $key = wfMemcKey( 'flaggedrevs', 'syncStatus', $article->getId(), $article->getTouched() );
681 - $syncvalue = $wgMemc->get($key);
682 - # Convert string value to boolean and return it
683 - if( $syncvalue ) {
684 - if( $syncvalue == "true" ) {
685 - return true;
686 - } else if( $syncvalue == "false" ) {
687 - return false;
688 - }
689 - }
690 - # If parseroutputs not given, fetch them...
691 - if( is_null($stableOutput) || !isset($stableOutput->fr_newestTemplateID) ) {
692 - # Get parsed stable version
693 - $stableOutput = self::getPageCache( $article );
694 - if( $stableOutput==false ) {
695 - $text = $frev->getTextForParse();
696 - $stableOutput = self::parseStableText( $article, $text, $frev->getRevId() );
697 - # Update the stable version cache
698 - self::updatePageCache( $article, $stableOutput );
699 - }
700 - }
701 - if( is_null($currentOutput) || !isset($currentOutput->fr_newestTemplateID) ) {
702 - global $wgUser, $wgParser;
703 - # Get parsed current version
704 - $parserCache = ParserCache::singleton();
705 - $currentOutput = $parserCache->get( $article, $wgUser );
706 - if( $currentOutput==false ) {
707 - $text = $article->getContent();
708 - $title = $article->getTitle();
709 - $options = self::makeParserOptions( $wgUser );
710 - $currentOutput = $wgParser->parse( $text, $title, $options );
711 - # Might as well save the cache while we're at it
712 - global $wgEnableParserCache;
713 - if( $wgEnableParserCache )
714 - $parserCache->save( $currentOutput, $article, $wgUser );
715 - }
716 - }
717 - # Only current of revisions of inclusions can be reviewed. Since the stable and current revisions
718 - # have the same text, the only thing that can make them different is updating a template or image.
719 - # If this is the case, the current revision will have a newer template or image version used somewhere.
720 - if( $currentOutput->fr_newestImageTime > $stableOutput->fr_newestImageTime ) {
721 - $synced = false;
722 - } else if( $currentOutput->fr_newestTemplateID > $stableOutput->fr_newestTemplateID ) {
723 - $synced = false;
724 - } else {
725 - $synced = true;
726 - }
727 - # Save to cache. This will be updated whenever the page is re-parsed as well. This means
728 - # that MW can check a light-weight key first. Uses format <page ID>-<UNIX timestamp>.
729 - global $wgParserCacheExpireTime;
730 - $syncData = $synced ? "true" : "false";
731 - $wgMemc->set( $key, $syncData, $wgParserCacheExpireTime );
732 -
733 - return $synced;
734 - }
735 -
736 - /**
737 - * @param Article $article
738 - * @param int $from_rev
739 - * @return int
740 - * Get number of revs since a certain revision
741 - */
742 - public static function getRevCountSince( $article, $from_rev ) {
743 - # Check if the count is zero by using $article->getLatest().
744 - # I don't trust using memcache and PHP for values like '0'
745 - # as it may confuse "expired" with "0". -aaron
746 - if( $article->getTitle()->getLatestRevID(GAID_FOR_UPDATE) == $from_rev ) {
747 - return 0;
748 - }
749 - global $wgMemc;
750 - # Try the cache
751 - $key = wfMemcKey( 'flaggedrevs', 'unreviewedrevs', $article->getId() );
752 - if( !$count = intval($wgMemc->get($key)) ) {
753 - $dbr = wfGetDB( DB_SLAVE );
754 - $count = $dbr->selectField( 'revision', 'COUNT(*)',
755 - array('rev_page' => $article->getId(), "rev_id > " . intval($from_rev) ),
756 - __METHOD__ );
757 - # Save to cache
758 - $wgMemc->set( $key, $count, 3600*24*7 );
759 - }
760 - return $count;
761 - }
762 -
763 - /**
764 - * @param Article $article
765 - * @param Integer $rev_id, the stable version rev_id
766 - * @param mixed $latest, the latest rev ID (optional)
767 - * Updates the fp_stable and fp_reviewed fields
768 - */
769 - public static function updateArticleOn( $article, $rev_id, $latest=NULL ) {
770 - global $wgMemc;
771 - wfProfileIn( __METHOD__ );
772 -
773 - $lastID = $latest ? $latest : $article->getTitle()->getLatestRevID(GAID_FOR_UPDATE);
774 -
775 - $dbw = wfGetDB( DB_MASTER );
776 - # Get the highest quality revision (not necessarily this one).
777 - $maxQuality = $dbw->selectField( array('flaggedrevs','revision'),
778 - 'fr_quality',
779 - array( 'fr_page_id' => $article->getTitle()->getArticleID(),
780 - 'rev_id = fr_rev_id',
781 - 'rev_page = fr_page_id',
782 - 'rev_deleted & '.Revision::DELETED_TEXT => 0 ),
783 - __METHOD__,
784 - array( 'ORDER BY' => 'fr_quality DESC', 'LIMIT' => 1 ) );
785 - $maxQuality = $maxQuality===false ? null : $maxQuality;
786 - # Alter table metadata
787 - $dbw->replace( 'flaggedpages',
788 - array( 'fp_page_id' ),
789 - array( 'fp_stable' => $rev_id,
790 - 'fp_reviewed' => ($lastID == $rev_id) ? 1 : 0,
791 - 'fp_quality' => $maxQuality,
792 - 'fp_page_id' => $article->getId() ),
793 - __METHOD__ );
794 - # Update the cache
795 - $key = wfMemcKey( 'flaggedrevs', 'unreviewedrevs', $article->getId() );
796 -
797 - $count = $dbw->selectField( 'revision', 'COUNT(*)',
798 - array('rev_page' => $article->getId(), "rev_id > " . intval($rev_id) ),
799 - __METHOD__ );
800 -
801 - $wgMemc->set( $key, $count, 3600*24*7 );
802 -
803 - wfProfileOut( __METHOD__ );
804 - return true;
805 - }
806 -
807 - /**
808 - * Clears cache for a page when merges are done.
809 - * We may have lost the stable revision to another page.
810 - */
811 - public static function articleLinksUpdate( $article ) {
812 - global $wgUser, $wgParser;
813 - # Update the links tables as the stable version may now be the default page...
814 - $parserCache = ParserCache::singleton();
815 - $poutput = $parserCache->get( $article, $wgUser );
816 - if( $poutput==false ) {
817 - $text = $article->getContent();
818 - $options = self::makeParserOptions( $wgUser );
819 - $poutput = $wgParser->parse($text, $article->getTitle(), $options);
820 - # Might as well save the cache while we're at it
821 - global $wgEnableParserCache;
822 - if( $wgEnableParserCache )
823 - $parserCache->save( $poutput, $article, $wgUser );
824 - }
825 - $u = new LinksUpdate( $article->getTitle(), $poutput );
826 - $u->doUpdate(); // this will trigger our hook to add stable links too...
827 -
828 - return true;
829 - }
830 -
831 - /**
832 - * Clears cache for a page when revisiondelete/undelete is used
833 - */
834 - public static function titleLinksUpdate( $title ) {
835 - return self::articleLinksUpdate( new Article($title) );
836 - }
837 -
838 - ################# Revision functions #################
839 -
840 - /**
841 - * @param Title $title
842 - * @param int $rev_id
843 - * @param bool $getText, fetch fr_text and fr_flags too?
844 - * @param bool $forUpdate, use master?
845 - * @param int $page_id, optional page ID to use, will defer to $title if not given
846 - * @returns mixed FlaggedRevision (null on failure)
847 - * Will not return a revision if deleted
848 - */
849 - public static function getFlaggedRev( $title, $rev_id, $getText=false, $forUpdate=false, $page_id=false ) {
850 - $columns = FlaggedRevision::selectFields();
851 - if( $getText ) {
852 - $columns[] = 'fr_text';
853 - $columns[] = 'fr_flags';
854 - }
855 - $db = $forUpdate ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
856 - $flags = $forUpdate ? GAID_FOR_UPDATE : 0;
857 - $page_id = $page_id ? $page_id : $title->getArticleID( $flags );
858 - # Skip deleted revisions
859 - $row = $db->selectRow( array('flaggedrevs','revision'),
860 - $columns,
861 - array( 'fr_page_id' => $page_id,
862 - 'fr_rev_id' => $rev_id,
863 - 'rev_id = fr_rev_id',
864 - 'rev_page = fr_page_id',
865 - 'rev_deleted & '.Revision::DELETED_TEXT => 0 ),
866 - __METHOD__ );
867 - # Sorted from highest to lowest, so just take the first one if any
868 - if( $row ) {
869 - return new FlaggedRevision( $title, $row );
870 - }
871 - return null;
872 - }
873 -
874 - /**
875 - * Get latest quality rev, if not, the latest reviewed one.
876 - * @param Title $title, page title
877 - * @param bool $getText, fetch fr_text and fr_flags too?
878 - * @param bool $forUpdate, use master DB and avoid using fp_stable?
879 - * @returns mixed FlaggedRevision (null on failure)
880 - */
881 - public static function getStablePageRev( $title, $getText=false, $forUpdate=false ) {
882 - $columns = FlaggedRevision::selectFields();
883 - if( $getText ) {
884 - $columns[] = 'fr_text';
885 - $columns[] = 'fr_flags';
886 - }
887 - $row = null;
888 - # If we want the text, then get the text flags too
889 - if( !$forUpdate ) {
890 - $dbr = wfGetDB( DB_SLAVE );
891 - $row = $dbr->selectRow( array('flaggedpages','flaggedrevs'),
892 - $columns,
893 - array( 'fp_page_id' => $title->getArticleId(),
894 - 'fr_page_id' => $title->getArticleId(),
895 - 'fp_stable = fr_rev_id' ),
896 - __METHOD__ );
897 - if( !$row )
898 - return null;
899 - } else {
900 - # Get visiblity settings...
901 - $config = self::getPageVisibilitySettings( $title, $forUpdate );
902 - $dbw = wfGetDB( DB_MASTER );
903 - # Look for the latest pristine revision...
904 - if( self::pristineVersions() && $config['select'] != FLAGGED_VIS_LATEST ) {
905 - $prow = $dbw->selectRow( array('flaggedrevs','revision'),
906 - $columns,
907 - array( 'fr_page_id' => $title->getArticleID(),
908 - 'fr_quality = 2',
909 - 'rev_id = fr_rev_id',
910 - 'rev_page = fr_page_id',
911 - 'rev_deleted & '.Revision::DELETED_TEXT => 0),
912 - __METHOD__,
913 - array( 'ORDER BY' => 'fr_rev_id DESC') );
914 - # Looks like a plausible revision
915 - $row = $prow ? $prow : null;
916 - }
917 - # Look for the latest quality revision...
918 - if( self::qualityVersions() && $config['select'] != FLAGGED_VIS_LATEST ) {
919 - // If we found a pristine rev above, this one must be newer, unless
920 - // we specifically want pristine revs to have precedence...
921 - $newerClause = ($row && $config['select'] != FLAGGED_VIS_PRISTINE) ?
922 - "fr_rev_id > {$row->fr_rev_id}" : "1 = 1";
923 - $qrow = $dbw->selectRow( array('flaggedrevs','revision'),
924 - $columns,
925 - array( 'fr_page_id' => $title->getArticleID(),
926 - 'fr_quality = 1',
927 - $newerClause,
928 - 'rev_id = fr_rev_id',
929 - 'rev_page = fr_page_id',
930 - 'rev_deleted & '.Revision::DELETED_TEXT => 0),
931 - __METHOD__,
932 - array( 'ORDER BY' => 'fr_rev_id DESC') );
933 - $row = $qrow ? $qrow : $row;
934 - }
935 - # Do we have one? If not, try the latest reviewed revision...
936 - if( !$row ) {
937 - $row = $dbw->selectRow( array('flaggedrevs','revision'),
938 - $columns,
939 - array( 'fr_page_id' => $title->getArticleID(),
940 - 'rev_id = fr_rev_id',
941 - 'rev_page = fr_page_id',
942 - 'rev_deleted & '.Revision::DELETED_TEXT => 0),
943 - __METHOD__,
944 - array( 'ORDER BY' => 'fr_rev_id DESC' ) );
945 - if( !$row )
946 - return null;
947 - }
948 - }
949 - return new FlaggedRevision( $title, $row );
950 - }
951 -
952 - /**
953 - * Get flags for a revision
954 - * @param Title $title
955 - * @param int $rev_id
956 - * @return Array
957 - */
958 - public static function getRevisionTags( $title, $rev_id ) {
959 - $dbr = wfGetDB( DB_SLAVE );
960 - $tags = $dbr->selectField( 'flaggedrevs', 'fr_tags',
961 - array( 'fr_rev_id' => $rev_id,
962 - 'fr_page_id' => $title->getArticleId() ),
963 - __METHOD__ );
964 - if( !$tags )
965 - return false;
966 -
967 - return FlaggedRevision::expandRevisionTags( strval($tags) );
968 - }
969 -
970 - /**
971 - * @param Title $title
972 - * @param int $rev_id
973 - * @param $flags, GAID_FOR_UPDATE
974 - * @returns mixed (int or false)
975 - * Get quality of a revision
976 - */
977 - public static function getRevQuality( $title, $rev_id, $flags=0 ) {
978 - $db = ($flags & GAID_FOR_UPDATE) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
979 - $quality = $db->selectField( 'flaggedrevs',
980 - 'fr_quality',
981 - array( 'fr_page_id' => $title->getArticleID( $flags ),
982 - 'fr_rev_id' => $rev_id ),
983 - __METHOD__,
984 - array( 'FORCE INDEX' => 'PRIMARY' )
985 - );
986 - return $quality;
987 - }
988 -
989 - /**
990 - * @param Title $title
991 - * @param int $rev_id
992 - * @param $flags, GAID_FOR_UPDATE
993 - * @returns bool
994 - * Useful for quickly pinging to see if a revision is flagged
995 - */
996 - public static function revIsFlagged( $title, $rev_id, $flags=0 ) {
997 - $quality = self::getRevQuality( $title, $rev_id, $flags );
998 - return ($quality !== false);
999 - }
1000 -
1001 - /**
1002 - * Get the "prime" flagged revision of a page
1003 - * @param Article $article
1004 - * @returns mixed (integer/false)
1005 - * Will not return a revision if deleted
1006 - */
1007 - public static function getPrimeFlaggedRevId( $article ) {
1008 - $dbr = wfGetDB( DB_SLAVE );
1009 - # Get the highest quality revision (not necessarily this one).
1010 - $oldid = $dbr->selectField( array('flaggedrevs','revision'),
1011 - 'fr_rev_id',
1012 - array( 'fr_page_id' => $article->getId(),
1013 - 'rev_page = fr_page_id',
1014 - 'rev_id = fr_rev_id'),
1015 - __METHOD__,
1016 - array( 'ORDER BY' => 'fr_quality DESC, fr_rev_id DESC',
1017 - 'USE INDEX' => array('flaggedrevs' => 'page_qal_rev','revision' => 'PRIMARY') )
1018 - );
1019 - return $oldid;
1020 - }
1021 -
1022 - ################# Page configuration functions #################
1023 -
1024 - /**
1025 - * Get visiblity restrictions on page
1026 - * @param Title $title, page title
1027 - * @param bool $forUpdate, use master DB?
1028 - * @returns Array (select,override)
1029 - */
1030 - public static function getPageVisibilitySettings( $title, $forUpdate=false ) {
1031 - $db = $forUpdate ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
1032 - $row = $db->selectRow( 'flaggedpage_config',
1033 - array( 'fpc_select', 'fpc_override', 'fpc_expiry' ),
1034 - array( 'fpc_page_id' => $title->getArticleID() ),
1035 - __METHOD__ );
1036 -
1037 - if( $row ) {
1038 - $now = wfTimestampNow();
1039 - # This code should be refactored, now that it's being used more generally.
1040 - $expiry = Block::decodeExpiry( $row->fpc_expiry );
1041 - # Only apply the settigns if they haven't expired
1042 - if( !$expiry || $expiry < $now ) {
1043 - $row = null;
1044 - self::purgeExpiredConfigurations();
1045 - }
1046 - }
1047 -
1048 - if( !$row ) {
1049 - global $wgFlaggedRevsOverride, $wgFlaggedRevsPrecedence;
1050 - # Keep this consistent across settings. 1 -> override, 0 -> don't
1051 - $override = $wgFlaggedRevsOverride ? 1 : 0;
1052 - # Keep this consistent across settings. 0 -> precedence, 0 -> none
1053 - $select = $wgFlaggedRevsPrecedence ? FLAGGED_VIS_NORMAL : FLAGGED_VIS_LATEST;
1054 - return array('select' => $select, 'override' => $override, 'expiry' => 'infinity');
1055 - }
1056 -
1057 - return array('select' => $row->fpc_select, 'override' => $row->fpc_override, 'expiry' => $row->fpc_expiry);
1058 - }
1059 -
1060 - /**
1061 - * Purge expired restrictions from the flaggedpage_config table
1062 - */
1063 - public static function purgeExpiredConfigurations() {
1064 - $dbw = wfGetDB( DB_MASTER );
1065 - $dbw->delete( 'flaggedpage_config',
1066 - array( 'fpc_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
1067 - __METHOD__ );
1068 - }
1069 -
1070 - ################# Other utility functions #################
1071 -
1072 - /**
1073 - * @param Title $title
1074 - * @return bool, is $title the main page?
1075 - */
1076 - public static function isMainPage( $title ) {
1077 - return $title->equals( Title::newMainPage() );
1078 - }
1079 -
1080 - /**
1081 - * @param Array $flags
1082 - * @return bool, is this revision at quality condition?
1083 - */
1084 - public static function isQuality( $flags ) {
1085 - global $wgFlaggedRevTags;
1086 -
1087 - if( empty($flags) )
1088 - return false;
1089 -
1090 - foreach( $wgFlaggedRevTags as $f => $v ) {
1091 - if( !isset($flags[$f]) || $v > $flags[$f] )
1092 - return false;
1093 - }
1094 -
1095 - return true;
1096 - }
1097 -
1098 - /**
1099 - * @param Array $flags
1100 - * @return bool, is this revision at optimal condition?
1101 - */
1102 - public static function isPristine( $flags ) {
1103 - global $wgFlaggedRevTags, $wgFlaggedRevPristine;
1104 -
1105 - if( empty($flags) )
1106 - return false;
1107 -
1108 - foreach( $wgFlaggedRevTags as $f => $v ) {
1109 - if( !isset($flags[$f]) || $flags[$f] < $wgFlaggedRevPristine )
1110 - return false;
1111 - }
1112 -
1113 - return true;
1114 - }
1115 -
1116 - /**
1117 - * Is this page in reviewable namespace?
1118 - * @param Title, $title
1119 - * @return bool
1120 - */
1121 - public static function isPageReviewable( $title ) {
1122 - global $wgFlaggedRevsNamespaces;
1123 - # FIXME: Treat NS_MEDIA as NS_IMAGE
1124 - $ns = ( $title->getNamespace() == NS_MEDIA ) ? NS_IMAGE : $title->getNamespace();
1125 - return ( in_array($ns,$wgFlaggedRevsNamespaces) && !$title->isTalkPage() );
1126 - }
1127 -
1128 - /**
1129 - * Is this page in patrolable namespace?
1130 - * @param Title, $title
1131 - * @return bool
1132 - */
1133 - public static function isPagePatrollable( $title ) {
1134 - global $wgFlaggedRevsPatrolNamespaces;
1135 - # No collisions!
1136 - if( self::isPageReviewable($title) ) {
1137 - return false;
1138 - }
1139 - # FIXME: Treat NS_MEDIA as NS_IMAGE
1140 - $ns = ( $title->getNamespace() == NS_MEDIA ) ? NS_IMAGE : $title->getNamespace();
1141 - return ( in_array($ns,$wgFlaggedRevsPatrolNamespaces) && !$title->isTalkPage() );
1142 - }
1143 -
1144 - /**
1145 - * Make stable version link and return the css
1146 - * @param Title $title
1147 - * @param int $rev_id
1148 - * @param Database $db, optional
1149 - * @returns array (string,string)
1150 - */
1151 - public static function makeStableVersionLink( $title, $rev_id, $skin, $db = NULL ) {
1152 - $db = $db ? $db : wfGetDB( DB_SLAVE );
1153 - $row = $db->selectRow( 'flaggedrevs',
1154 - array( 'fr_quality', 'fr_user' ),
1155 - array( 'fr_page_id' => $title->getArticleID(),
1156 - 'fr_rev_id' => $rev_id ),
1157 - __METHOD__,
1158 - array( 'FORCE INDEX' => 'PRIMARY' )
1159 - );
1160 - if( $row ) {
1161 - $css = FlaggedRevsXML::getQualityColor( $row->fr_quality );
1162 - $user = User::whois( $row->fr_user );
1163 - $msg = ($row->fr_quality >= 1) ? 'hist-quality-user' : 'hist-stable-user';
1164 - $st = $title->getPrefixedDBkey();
1165 - $link = "<span class='plainlinks'>".wfMsgExt($msg,array('parseinline'),$st,$rev_id,$user)."</span>";
1166 - } else {
1167 - return array("","");
1168 - }
1169 - return array($link,$css);
1170 - }
1171 -
1172 - /**
1173 - * Get JS script params for onloading
1174 - */
1175 - public static function getJSParams() {
1176 - # Param to pass to JS function to know if tags are at quality level
1177 - global $wgFlaggedRevTags;
1178 - $JSparams = '';
1179 - foreach( $wgFlaggedRevTags as $tag => $QL ) {
1180 - $valuepair = ($JSparams) ? ", \"wp$tag\": $QL" : "\"wp$tag\": $QL";
1181 - $JSparams .= $valuepair;
1182 - }
1183 - return trim($JSparams);
1184 - }
1185 -
1186 - /**
1187 - * Get params for a user
1188 - * @param User $user
1189 - */
1190 - public static function getUserParams( $user ) {
1191 - $dbw = wfGetDB( DB_MASTER );
1192 - $row = $dbw->selectRow( 'flaggedrevs_promote', 'frp_user_params',
1193 - array( 'frp_user_id' => $user->getId() ),
1194 - __METHOD__ );
1195 - # Parse params
1196 - $params = array();
1197 - if( $row ) {
1198 - $flatPars = explode( "\n", trim($row->frp_user_params) );
1199 - foreach( $flatPars as $pair ) {
1200 - $m = explode( '=', trim($pair), 2 );
1201 - $key = $m[0];
1202 - $value = isset($m[1]) ? $m[1] : null;
1203 - $params[$key] = $value;
1204 - }
1205 - }
1206 - return $params;
1207 - }
1208 -
1209 - /**
1210 - * Save params for a user
1211 - * @param User $user
1212 - * @param Array $params
1213 - */
1214 - public static function saveUserParams( $user, $params ) {
1215 - $flatParams = '';
1216 - foreach( $params as $key => $value ) {
1217 - $flatParams .= "{$key}={$value}\n";
1218 - }
1219 - $dbw = wfGetDB( DB_MASTER );
1220 - $row = $dbw->replace( 'flaggedrevs_promote',
1221 - array( 'frp_user_id' ),
1222 - array( 'frp_user_id' => $user->getId(),
1223 - 'frp_user_params' => trim($flatParams) ),
1224 - __METHOD__ );
1225 -
1226 - return ( $dbw->affectedRows() > 0 );
1227 - }
1228 -
1229 - ################# Auto-review function #################
1230 -
1231 - /**
1232 - * Automatically review an edit and add a log entry in the review log.
1233 - * LinksUpdate was already called via edit operations, so the page
1234 - * fields will be up to date. This updates the stable version.
1235 - */
1236 - public static function autoReviewEdit( $article, $user, $text, $rev, $flags, $patrol = true ) {
1237 - global $wgParser, $wgFlaggedRevsAutoReview;
1238 -
1239 - wfProfileIn( __METHOD__ );
1240 -
1241 - $quality = 0;
1242 - if( self::isQuality($flags) ) {
1243 - $quality = self::isPristine($flags) ? 2 : 1;
1244 - }
1245 - $tmpset = $imgset = array();
1246 - $poutput = false;
1247 -
1248 - # Use master to avoid lag issues.
1249 - $latestID = $article->getTitle()->getLatestRevID(GAID_FOR_UPDATE);
1250 - $latestID = $latestID ? $latestID : $rev->getId(); // new pages, page row not added yet
1251 -
1252 - $title = $article->getTitle();
1253 - # Rev ID is not put into parser on edit, so do the same here.
1254 - # Also, a second parse would be triggered otherwise.
1255 - $parseId = ($rev->getId() == $latestID) ? null : $rev->getId();
1256 - # Parse the revision HTML output
1257 - $editInfo = $article->prepareTextForEdit( $text, $parseId );
1258 - $poutput = $editInfo->output;
1259 -
1260 - # NS:title -> rev ID mapping
1261 - foreach( $poutput->mTemplateIds as $namespace => $titleAndID ) {
1262 - foreach( $titleAndID as $dbkey => $id ) {
1263 - $tmpset[] = array(
1264 - 'ft_rev_id' => $rev->getId(),
1265 - 'ft_namespace' => $namespace,
1266 - 'ft_title' => $dbkey,
1267 - 'ft_tmp_rev_id' => $id
1268 - );
1269 - }
1270 - }
1271 - # Image -> timestamp mapping
1272 - foreach( $poutput->fr_ImageSHA1Keys as $dbkey => $timeAndSHA1 ) {
1273 - foreach( $timeAndSHA1 as $time => $sha1 ) {
1274 - $imgset[] = array(
1275 - 'fi_rev_id' => $rev->getId(),
1276 - 'fi_name' => $dbkey,
1277 - 'fi_img_timestamp' => $time,
1278 - 'fi_img_sha1' => $sha1
1279 - );
1280 - }
1281 - }
1282 -
1283 - $dbw = wfGetDB( DB_MASTER );
1284 - $dbw->begin();
1285 - # Update our versioning pointers
1286 - if( !empty( $tmpset ) ) {
1287 - $dbw->replace( 'flaggedtemplates',
1288 - array( array('ft_rev_id','ft_namespace','ft_title') ), $tmpset,
1289 - __METHOD__ );
1290 - }
1291 - if( !empty( $imgset ) ) {
1292 - $dbw->replace( 'flaggedimages',
1293 - array( array('fi_rev_id','fi_name') ), $imgset,
1294 - __METHOD__ );
1295 - }
1296 - # Get the page text and resolve all templates
1297 - list($fulltext,$templateIDs,$complete,$maxID) = self::expandText( $text, $article->getTitle(), $rev->getId() );
1298 -
1299 - # Compress $fulltext, passed by reference
1300 - $textFlags = FlaggedRevision::compressText( $fulltext );
1301 -
1302 - # Write to external storage if required
1303 - $storage = self::getExternalStorage();
1304 - if( $storage ) {
1305 - if( is_array($storage) ) {
1306 - # Distribute storage across multiple clusters
1307 - $store = $storage[mt_rand(0, count( $storage ) - 1)];
1308 - } else {
1309 - $store = $storage;
1310 - }
1311 - # Store and get the URL
1312 - $fulltext = ExternalStore::insert( $store, $fulltext );
1313 - if( !$fulltext ) {
1314 - # This should only happen in the case of a configuration error, where the external store is not valid
1315 - wfProfileOut( __METHOD__ );
1316 - throw new MWException( "Unable to store text to external storage $store" );
1317 - }
1318 - if( $textFlags ) {
1319 - $textFlags .= ',';
1320 - }
1321 - $textFlags .= 'external';
1322 - }
1323 -
1324 - # If this is an image page, store corresponding file info
1325 - $fileData = array();
1326 - if( $title->getNamespace() == NS_IMAGE && $file = wfFindFile($title) ) {
1327 - $fileData['name'] = $title->getDBkey();
1328 - $fileData['timestamp'] = $file->getTimestamp();
1329 - $fileData['sha1'] = $file->getSha1();
1330 - }
1331 -
1332 - # Our review entry
1333 - $revisionset = array(
1334 - 'fr_page_id' => $rev->getPage(),
1335 - 'fr_rev_id' => $rev->getId(),
1336 - 'fr_user' => $user->getId(),
1337 - 'fr_timestamp' => $dbw->timestamp( wfTimestampNow() ),
1338 - 'fr_comment' => "",
1339 - 'fr_quality' => $quality,
1340 - 'fr_tags' => FlaggedRevision::flattenRevisionTags( $flags ),
1341 - 'fr_text' => $fulltext, # Store expanded text for speed
1342 - 'fr_flags' => $textFlags,
1343 - 'fr_img_name' => $fileData ? $fileData['name'] : null,
1344 - 'fr_img_timestamp' => $fileData ? $fileData['timestamp'] : null,
1345 - 'fr_img_sha1' => $fileData ? $fileData['sha1'] : null
1346 - );
1347 - # Update flagged revisions table
1348 - $dbw->replace( 'flaggedrevs',
1349 - array( array('fr_page_id','fr_rev_id') ), $revisionset,
1350 - __METHOD__ );
1351 - # Mark as patrolled
1352 - if( $patrol ) {
1353 - $dbw->update( 'recentchanges',
1354 - array( 'rc_patrolled' => 1 ),
1355 - array( 'rc_this_oldid' => $rev->getId(),
1356 - 'rc_user_text' => $rev->getRawUserText(),
1357 - 'rc_timestamp' => $dbw->timestamp( $rev->getTimestamp() ) ),
1358 - __METHOD__,
1359 - array( 'LIMIT' => 1 ) );
1360 - }
1361 - # Done!
1362 - $dbw->commit();
1363 -
1364 - # Update the article review log
1365 - RevisionReview::updateLog( $title, $flags, array(), wfMsgForContent('revreview-auto'), $rev->getID(), true, true );
1366 -
1367 - # If we know that this is now the new stable version
1368 - # (which it probably is), save it to the cache...
1369 - $sv = self::getStablePageRev( $article->getTitle(), false, true );
1370 - if( $sv && $sv->getRevId() == $rev->getId() ) {
1371 - # Update stable cache
1372 - self::updatePageCache( $article, $poutput );
1373 - # Update page fields
1374 - self::updateArticleOn( $article, $rev->getId(), $rev->getId() );
1375 - # Purge squid for this page only
1376 - $article->getTitle()->purgeSquid();
1377 - }
1378 -
1379 - wfProfileOut( __METHOD__ );
1380 -
1381 - return true;
1382 - }
1383 -
1384 - ################# Hooked functions #################
1385 -
1386 - /**
1387 - * Remove 'patrol' and 'autopatrol' rights. Reviewing revisions will patrol them as well.
1388 - */
1389 - public static function stripPatrolRights( $user, &$rights ) {
1390 - # Use only our extension mechanisms
1391 - foreach( $rights as $n => $right ) {
1392 - if( $right == 'patrol' || $right == 'autopatrol' ) {
1393 - unset($rights[$n]);
1394 - }
1395 - }
1396 - return true;
1397 - }
1398 -
1399 - /**
1400 - * Add FlaggedRevs css/js.
1401 - */
1402 - public static function InjectStyleAndJS() {
1403 - global $wgOut, $wgJsMimeType, $wgFlaggedArticle;
1404 - # Don't double-load
1405 - if( self::$styleLoaded || !$wgFlaggedArticle )
1406 - return true;
1407 - # UI CSS
1408 - $wgOut->addLink( array(
1409 - 'rel' => 'stylesheet',
1410 - 'type' => 'text/css',
1411 - 'media' => 'screen, projection',
1412 - 'href' => FLAGGED_CSS,
1413 - ) );
1414 - # Handle onload parameters
1415 - $JSparams = self::getJSParams();
1416 - $frev = $wgFlaggedArticle->getStableRev( true );
1417 - $stableId = $frev ? $frev->getRevId() : 0;
1418 -
1419 - $script = "<script type=\"{$wgJsMimeType}\">\n";
1420 - $script .= "var wgFlaggedRevsJSparams = {". $JSparams . "};\n";
1421 - $script .= "var wgStableRevisionId = ". $stableId . ";";
1422 - $script .= "</script>\n";
1423 - $wgOut->addScript( $script );
1424 - # UI JS
1425 - $wgOut->addScript( "<script type=\"{$wgJsMimeType}\" src=\"" . FLAGGED_JS . "\"></script>\n" );
1426 -
1427 - self::$styleLoaded = true;
1428 -
1429 - return true;
1430 - }
1431 -
1432 - /**
1433 - * Add FlaggedRevs css for relevant special pages.
1434 - */
1435 - public static function InjectStyleForSpecial() {
1436 - global $wgTitle, $wgOut;
1437 - $spPages = array();
1438 - $spPages[] = SpecialPage::getTitleFor( 'UnreviewedPages' );
1439 - $spPages[] = SpecialPage::getTitleFor( 'OldReviewedPages' );
1440 - foreach( $spPages as $n => $title ) {
1441 - if( $wgTitle->equals( $title ) ) {
1442 - # UI CSS
1443 - $wgOut->addLink( array(
1444 - 'rel' => 'stylesheet',
1445 - 'type' => 'text/css',
1446 - 'media' => 'screen, projection',
1447 - 'href' => FLAGGED_CSS,
1448 - ) );
1449 - break;
1450 - }
1451 - }
1452 - return true;
1453 - }
1454 -
1455 - /**
1456 - * Update flaggedrevs table on revision restore
1457 - */
1458 - public static function updateFromRestore( $title, $revision, $oldPageID ) {
1459 - $dbw = wfGetDB( DB_MASTER );
1460 - # Some revisions may have had null rev_id values stored when deleted.
1461 - # This hook is called after insertOn() however, in which case it is set
1462 - # as a new one.
1463 - $dbw->update( 'flaggedrevs',
1464 - array( 'fr_page_id' => $revision->getPage() ),
1465 - array( 'fr_page_id' => $oldPageID,
1466 - 'fr_rev_id' => $revision->getID() ),
1467 - __METHOD__ );
1468 -
1469 - return true;
1470 - }
1471 -
1472 - /**
1473 - * Update flaggedrevs table on article history merge
1474 - */
1475 - public static function updateFromMerge( $sourceTitle, $destTitle ) {
1476 - wfProfileIn( __METHOD__ );
1477 -
1478 - $oldPageID = $sourceTitle->getArticleID();
1479 - $newPageID = $destTitle->getArticleID();
1480 - # Get flagged revisions from old page id that point to destination page
1481 - $dbw = wfGetDB( DB_MASTER );
1482 - $result = $dbw->select( array('flaggedrevs','revision'),
1483 - array( 'fr_rev_id' ),
1484 - array( 'fr_page_id' => $oldPageID,
1485 - 'fr_rev_id = rev_id',
1486 - 'rev_page' => $newPageID ),
1487 - __METHOD__ );
1488 - # Update these rows
1489 - $revIDs = array();
1490 - while( $row = $dbw->fetchObject($result) ) {
1491 - $revIDs[] = $row->fr_rev_id;
1492 - }
1493 - if( !empty($revIDs) ) {
1494 - $dbw->update( 'flaggedrevs',
1495 - array( 'fr_page_id' => $newPageID ),
1496 - array( 'fr_page_id' => $oldPageID,
1497 - 'fr_rev_id' => $revIDs ),
1498 - __METHOD__ );
1499 - }
1500 - # Update pages
1501 - self::titleLinksUpdate( $sourceTitle );
1502 - self::titleLinksUpdate( $destTitle );
1503 -
1504 - wfProfileOut( __METHOD__ );
1505 - return true;
1506 - }
1507 -
1508 - /**
1509 - * Clears visiblity settings on page delete
1510 - */
1511 - public static function deleteVisiblitySettings( $article, $user, $reason ) {
1512 - $dbw = wfGetDB( DB_MASTER );
1513 - $dbw->delete( 'flaggedpage_config',
1514 - array( 'fpc_page_id' => $article->getID() ),
1515 - __METHOD__ );
1516 -
1517 - return true;
1518 - }
1519 -
1520 - /**
1521 - * Inject stable links on LinksUpdate
1522 - */
1523 - public static function extraLinksUpdate( $linksUpdate ) {
1524 - wfProfileIn( __METHOD__ );
1525 - if( !self::isPageReviewable( $linksUpdate->mTitle ) ) {
1526 - wfProfileOut( __METHOD__ );
1527 - return true;
1528 - }
1529 - # Check if this page has a stable version by fetching it. Do not
1530 - # get the fr_text field if we are to use the latest stable template revisions.
1531 - global $wgUseStableTemplates;
1532 - $sv = self::getStablePageRev( $linksUpdate->mTitle, !$wgUseStableTemplates, true );
1533 - if( !$sv ) {
1534 - wfProfileOut( __METHOD__ );
1535 - return true;
1536 - }
1537 - # Get the either the full flagged revision text or the revision text
1538 - $article = new Article( $linksUpdate->mTitle );
1539 - # Try stable version cache. This should be updated before this is called.
1540 - $parserOut = self::getPageCache( $article );
1541 - if( $parserOut==false ) {
1542 - $text = $sv->getTextForParse();
1543 - # Parse the text
1544 - $parserOut = self::parseStableText( $article, $text, $sv->getRevId() );
1545 - }
1546 - # Update page fields
1547 - self::updateArticleOn( $article, $sv->getRevId() );
1548 - # Update the links tables to include these
1549 - # We want the UNION of links between the current
1550 - # and stable version. Therefore, we only care about
1551 - # links that are in the stable version and not the regular one.
1552 - foreach( $parserOut->getLinks() as $ns => $titles ) {
1553 - foreach( $titles as $title => $id ) {
1554 - if( !isset($linksUpdate->mLinks[$ns]) ) {
1555 - $linksUpdate->mLinks[$ns] = array();
1556 - $linksUpdate->mLinks[$ns][$title] = $id;
1557 - } else if( !isset($linksUpdate->mLinks[$ns][$title]) ) {
1558 - $linksUpdate->mLinks[$ns][$title] = $id;
1559 - }
1560 - }
1561 - }
1562 - foreach( $parserOut->getImages() as $image => $n ) {
1563 - if( !isset($linksUpdate->mImages[$image]) )
1564 - $linksUpdate->mImages[$image] = $n;
1565 - }
1566 - foreach( $parserOut->getTemplates() as $ns => $titles ) {
1567 - foreach( $titles as $title => $id ) {
1568 - if( !isset($linksUpdate->mTemplates[$ns]) ) {
1569 - $linksUpdate->mTemplates[$ns] = array();
1570 - $linksUpdate->mTemplates[$ns][$title] = $id;
1571 - } else if( !isset($linksUpdate->mTemplates[$ns][$title]) ) {
1572 - $linksUpdate->mTemplates[$ns][$title] = $id;
1573 - }
1574 - }
1575 - }
1576 - foreach( $parserOut->getExternalLinks() as $url => $n ) {
1577 - if( !isset($linksUpdate->mExternals[$url]) )
1578 - $linksUpdate->mExternals[$url] = $n;
1579 - }
1580 - foreach( $parserOut->getCategories() as $category => $sort ) {
1581 - if( !isset($linksUpdate->mCategories[$category]) )
1582 - $linksUpdate->mCategories[$category] = $sort;
1583 - }
1584 - foreach( $parserOut->getLanguageLinks() as $n => $link ) {
1585 - list( $key, $title ) = explode( ':', $link, 2 );
1586 - if( !isset($linksUpdate->mInterlangs[$key]) )
1587 - $linksUpdate->mInterlangs[$key] = $title;
1588 - }
1589 - foreach( $parserOut->getProperties() as $prop => $val ) {
1590 - if( !isset($linksUpdate->mProperties[$prop]) )
1591 - $linksUpdate->mProperties[$prop] = $val;
1592 - }
1593 - wfProfileOut( __METHOD__ );
1594 - return true;
1595 - }
1596 -
1597 - /**
1598 - * Add special fields to parser.
1599 - */
1600 - public static function parserAddFields( $parser ) {
1601 - $parser->mOutput->fr_ImageSHA1Keys = array();
1602 - $parser->mOutput->fr_newestImageTime = "0";
1603 - $parser->mOutput->fr_newestTemplateID = 0;
1604 - return true;
1605 - }
1606 -
1607 - /**
1608 - * Select the desired templates based on the selected stable revision IDs
1609 - * NOTE: $p comes in false from this hook ... weird
1610 - */
1611 - public static function parserFetchStableTemplate( $p=false, $title, &$skip, &$id ) {
1612 - global $wgParser;
1613 - # Trigger for stable version parsing only
1614 - $parser =& $wgParser;
1615 - if( !isset($parser->fr_isStable) || !$parser->fr_isStable )
1616 - return true;
1617 - # Special namespace ... ?
1618 - if( $title->getNamespace() < 0 )
1619 - return true;
1620 - # Only called to make fr_text, right after template/image specifiers
1621 - # are added to the DB. Slaves may not have it yet...
1622 - $dbw = wfGetDB( DB_MASTER );
1623 - # Check for stable version of template if this feature is enabled.
1624 - # Should be in reviewable namespace, this saves unneeded DB checks as
1625 - # well as enforce site settings if they are later changed.
1626 - global $wgUseStableTemplates;
1627 - if( $wgUseStableTemplates && self::isPageReviewable( $title ) ) {
1628 - $id = $dbw->selectField( 'flaggedpages', 'fp_stable',
1629 - array( 'fp_page_id' => $title->getArticleId() ),
1630 - __METHOD__ );
1631 - }
1632 - # If there is no stable version (or that feature is not enabled), use
1633 - # the template revision during review time.
1634 - if( !$id ) {
1635 - $id = $dbw->selectField( 'flaggedtemplates', 'ft_tmp_rev_id',
1636 - array( 'ft_rev_id' => $parser->mRevisionId,
1637 - 'ft_namespace' => $title->getNamespace(),
1638 - 'ft_title' => $title->getDBkey() ),
1639 - __METHOD__ );
1640 - }
1641 - # If none specified, see if we are allowed to use the current revision
1642 - if( !$id ) {
1643 - global $wgUseCurrentTemplates;
1644 - if( $id === false ) {
1645 - $parser->fr_includesMatched = false; // May want to give an error
1646 - if( !$wgUseCurrentTemplates ) {
1647 - $skip = true;
1648 - }
1649 - } else {
1650 - $skip = true;
1651 - }
1652 - }
1653 - if( $id > $parser->mOutput->fr_newestTemplateID ) {
1654 - $parser->mOutput->fr_newestTemplateID = $id;
1655 - }
1656 -
1657 - return true;
1658 - }
1659 -
1660 - /**
1661 - * Select the desired images based on the selected stable revision times/SHA-1s
1662 - */
1663 - public static function parserMakeStableImageLink( $parser, $nt, &$skip, &$time, &$query=false ) {
1664 - # Trigger for stable version parsing only
1665 - if( !isset($parser->fr_isStable) || !$parser->fr_isStable )
1666 - return true;
1667 - # Only called to make fr_text, right after template/image specifiers
1668 - # are added to the DB. Slaves may not have it yet...
1669 - $dbw = wfGetDB( DB_MASTER );
1670 - # Check for stable version of image if this feature is enabled.
1671 - # Should be in reviewable namespace, this saves unneeded DB checks as
1672 - # well as enforce site settings if they are later changed.
1673 - $sha1 = "";
1674 - global $wgUseStableImages;
1675 - if( $wgUseStableImages && self::isPageReviewable( $nt ) ) {
1676 - $srev = self::getStablePageRev( $nt, false, true );
1677 - if( $srev ) {
1678 - $time = $srev->getFileTimestamp();
1679 - $sha1 = $srev->getFileSha1();
1680 - // B/C, may be stored in associated image version metadata table
1681 - if( !$time || !$sha1 ) {
1682 - $row = $dbw->selectRow( 'flaggedimages',
1683 - array( 'fi_img_timestamp', 'fi_img_sha1' ),
1684 - array( 'fi_rev_id' => $srev->getRevId(),
1685 - 'fi_name' => $nt->getDBkey() ),
1686 - __METHOD__ );
1687 - $time = $row ? $row->fi_img_timestamp : $time;
1688 - $sha1 = $row ? $row->fi_img_sha1 : $sha1;
1689 - }
1690 - }
1691 - }
1692 - # If there is no stable version (or that feature is not enabled), use
1693 - # the image revision during review time.
1694 - if( !$time ) {
1695 - $row = $dbw->selectRow( 'flaggedimages',
1696 - array( 'fi_img_timestamp', 'fi_img_sha1' ),
1697 - array( 'fi_rev_id' => $parser->mRevisionId,
1698 - 'fi_name' => $nt->getDBkey() ),
1699 - __METHOD__ );
1700 - $time = $row ? $row->fi_img_timestamp : $time;
1701 - $sha1 = $row ? $row->fi_img_sha1 : $sha1;
1702 - $query = $row ? "filetimestamp=" . urlencode( wfTimestamp(TS_MW,$row->fi_img_timestamp) ) : "";
1703 - }
1704 - # If none specified, see if we are allowed to use the current revision
1705 - if( !$time ) {
1706 - global $wgUseCurrentImages;
1707 - # If the DB found nothing...
1708 - if( $time === false ) {
1709 - $parser->fr_includesMatched = false; // May want to give an error
1710 - if( !$wgUseCurrentImages ) {
1711 - $time = "0";
1712 - } else {
1713 - $file = wfFindFile( $nt );
1714 - $time = $file ? $file->getTimestamp() : "0"; // Use current
1715 - }
1716 - } else {
1717 - $time = "0";
1718 - }
1719 - }
1720 - # Add image metadata to parser output
1721 - $parser->mOutput->fr_ImageSHA1Keys[$nt->getDBkey()] = array();
1722 - $parser->mOutput->fr_ImageSHA1Keys[$nt->getDBkey()][$time] = $sha1;
1723 -
1724 - if( $time > $parser->mOutput->fr_newestImageTime ) {
1725 - $parser->mOutput->fr_newestImageTime = $time;
1726 - }
1727 -
1728 - return true;
1729 - }
1730 -
1731 - /**
1732 - * Select the desired images based on the selected stable revision times/SHA-1s
1733 - */
1734 - public static function galleryFindStableFileTime( $ig, $nt, &$time, &$query=false ) {
1735 - # Trigger for stable version parsing only
1736 - if( !isset($ig->fr_isStable) || !$ig->fr_isStable )
1737 - return true;
1738 - # Slaves may not have it yet...
1739 - $dbw = wfGetDB( DB_MASTER );
1740 - # Check for stable version of image if this feature is enabled.
1741 - # Should be in reviewable namespace, this saves unneeded DB checks as
1742 - # well as enforce site settings if they are later changed.
1743 - $sha1 = "";
1744 - global $wgUseStableImages;
1745 - if( $wgUseStableImages && self::isPageReviewable( $nt ) ) {
1746 - $srev = self::getStablePageRev( $nt, false, true );
1747 - if( $srev ) {
1748 - $time = $srev->getFileTimestamp();
1749 - $sha1 = $srev->getFileSha1();
1750 - // B/C, may be stored in associated image version metadata table
1751 - if( !$time || !$sha1 ) {
1752 - $row = $dbw->selectRow( 'flaggedimages',
1753 - array( 'fi_img_timestamp', 'fi_img_sha1' ),
1754 - array( 'fi_rev_id' => $srev->getRevId(),
1755 - 'fi_name' => $nt->getDBkey() ),
1756 - __METHOD__ );
1757 - $time = $row ? $row->fi_img_timestamp : $time;
1758 - $sha1 = $row ? $row->fi_img_sha1 : $sha1;
1759 - }
1760 - }
1761 - }
1762 - # If there is no stable version (or that feature is not enabled), use
1763 - # the image revision during review time.
1764 - if( !$time ) {
1765 - $row = $dbw->selectRow( 'flaggedimages',
1766 - array( 'fi_img_timestamp', 'fi_img_sha1' ),
1767 - array('fi_rev_id' => $ig->mRevisionId,
1768 - 'fi_name' => $nt->getDBkey() ),
1769 - __METHOD__ );
1770 - $time = $row ? $row->fi_img_timestamp : $time;
1771 - $sha1 = $row ? $row->fi_img_sha1 : $sha1;
1772 - $query = $row ? "filetimestamp=" . urlencode( wfTimestamp(TS_MW,$row->fi_img_timestamp) ) : "";
1773 - }
1774 - # If none specified, see if we are allowed to use the current revision
1775 - if( !$time ) {
1776 - global $wgUseCurrentImages;
1777 - # If the DB found nothing...
1778 - if( $time === false ) {
1779 - $ig->fr_parentParser->fr_includesMatched = false; // May want to give an error
1780 - if( !$wgUseCurrentImages ) {
1781 - $time = "0";
1782 - } else {
1783 - $file = wfFindFile( $nt );
1784 - $time = $file ? $file->getTimestamp() : "0";
1785 - }
1786 - } else {
1787 - $time = "0";
1788 - }
1789 - }
1790 - # Add image metadata to parser output
1791 - $ig->fr_parentParser->mOutput->fr_ImageSHA1Keys[$nt->getDBkey()] = array();
1792 - $ig->fr_parentParser->mOutput->fr_ImageSHA1Keys[$nt->getDBkey()][$time] = $sha1;
1793 -
1794 - if( $time > $ig->fr_parentParser->mOutput->fr_newestImageTime ) {
1795 - $ig->fr_parentParser->mOutput->fr_newestImageTime = $time;
1796 - }
1797 -
1798 - return true;
1799 - }
1800 -
1801 - /**
1802 - * Flag of an image galley as stable
1803 - */
1804 - public static function parserMakeGalleryStable( $parser, $ig ) {
1805 - # Trigger for stable version parsing only
1806 - if( !isset($parser->fr_isStable) || !$parser->fr_isStable )
1807 - return true;
1808 -
1809 - $ig->fr_isStable = true;
1810 - $ig->fr_parentParser =& $parser; // hack
1811 -
1812 - return true;
1813 - }
1814 -
1815 - /**
1816 - * Insert image timestamps/SHA-1 keys into parser output
1817 - */
1818 - public static function parserInjectTimestamps( $parser, &$text ) {
1819 - # Don't trigger this for stable version parsing...it will do it separately.
1820 - if( isset($parser->fr_isStable) && $parser->fr_isStable )
1821 - return true;
1822 -
1823 - wfProfileIn( __METHOD__ );
1824 -
1825 - $maxRevision = 0;
1826 - # Record the max template revision ID
1827 - if( !empty($parser->mOutput->mTemplateIds) ) {
1828 - foreach( $parser->mOutput->mTemplateIds as $namespace => $DBkey_rev ) {
1829 - foreach( $DBkey_rev as $DBkey => $revID ) {
1830 - if( $revID > $maxRevision ) {
1831 - $maxRevision = $revID;
1832 - }
1833 - }
1834 - }
1835 - }
1836 - $parser->mOutput->fr_newestTemplateID = $maxRevision;
1837 -
1838 - $maxTimestamp = "0";
1839 - # Fetch the current timestamps of the images.
1840 - if( !empty($parser->mOutput->mImages) ) {
1841 - $filenames = array_keys($parser->mOutput->mImages);
1842 - foreach( $filenames as $filename ) {
1843 - $file = wfFindFile( Title::makeTitle( NS_IMAGE, $filename ) );
1844 - $parser->mOutput->fr_ImageSHA1Keys[$filename] = array();
1845 - if( $file ) {
1846 - if( $file->getTimestamp() > $maxTimestamp ) {
1847 - $maxTimestamp = $file->getTimestamp();
1848 - }
1849 - $parser->mOutput->fr_ImageSHA1Keys[$filename][$file->getTimestamp()] = $file->getSha1();
1850 - } else {
1851 - $parser->mOutput->fr_ImageSHA1Keys[$filename]["0"] = '';
1852 - }
1853 - }
1854 - }
1855 - # Record the max timestamp
1856 - $parser->mOutput->fr_newestImageTime = $maxTimestamp;
1857 -
1858 - wfProfileOut( __METHOD__ );
1859 - return true;
1860 - }
1861 -
1862 - /**
1863 - * Insert image timestamps/SHA-1s into page output
1864 - */
1865 - public static function outputInjectTimestamps( $out, $parserOut ) {
1866 - # Leave as defaults if missing. Relevant things will be updated only when needed.
1867 - # We don't want to go around resetting caches all over the place if avoidable...
1868 - $out->fr_ImageSHA1Keys = isset($parserOut->fr_ImageSHA1Keys) ? $parserOut->fr_ImageSHA1Keys : array();
1869 -
1870 - return true;
1871 - }
1872 -
1873 - /**
1874 - * Don't let users vandalize pages by moving them.
1875 - */
1876 - public static function userCanMove( $title, $user, $action, $result ) {
1877 - global $wgTitle, $wgFlaggedArticle;
1878 -
1879 - if( $action != 'move' || !self::isPageReviewable( $title ) )
1880 - return true;
1881 - # See if there is a stable version
1882 - if( $wgFlaggedArticle && $wgTitle && $wgTitle->equals( $title ) ) {
1883 - // Cache stable version while we are at it.
1884 - $frev = $wgFlaggedArticle->getStableRev( true );
1885 - } else {
1886 - $frev = self::getStablePageRev( $title );
1887 - }
1888 - if( !$frev )
1889 - return true;
1890 - # Allow for only editors/reviewers to move this
1891 - $right = $frev->getQuality() ? 'validate' : 'review';
1892 - if( !$user->isAllowed($right) && !$user->isAllowed('movestable') ) {
1893 - $result = false;
1894 - return false;
1895 - }
1896 - return true;
1897 - }
1898 -
1899 - /**
1900 - * Allow users to view reviewed pages.
1901 - */
1902 - public static function userCanView( $title, $user, $action, $result ) {
1903 - global $wgFlaggedRevsVisible, $wgFlaggedRevsTalkVisible, $wgTitle, $wgFlaggedArticle;
1904 - # Assume $action may still not be set, in which case, treat it as 'view'...
1905 - if( $action != 'read' )
1906 - return true;
1907 - # Admin may set this to false, rather than array()...
1908 - $groups = $user->getGroups();
1909 - $groups[] = '*';
1910 - if( empty($wgFlaggedRevsVisible) || !array_intersect($groups,$wgFlaggedRevsVisible) )
1911 - return true;
1912 - # Is this a talk page?
1913 - if( $wgFlaggedRevsTalkVisible && $title->isTalkPage() ) {
1914 - $result = true;
1915 - return true;
1916 - }
1917 - # See if there is a stable version. Also, see if, given the page
1918 - # config and URL params, the page can be overriden.
1919 - if( $wgFlaggedArticle && $wgTitle && $wgTitle->equals( $title ) ) {
1920 - global $wgFlaggedArticle;
1921 - // Cache stable version while we are at it.
1922 - if( $wgFlaggedArticle->pageOverride() && $wgFlaggedArticle->getStableRev( true ) ) {
1923 - $result = true;
1924 - }
1925 - } else {
1926 - if( self::getStablePageRev( $title ) ) {
1927 - $result = true;
1928 - }
1929 - }
1930 - return true;
1931 - }
1932 -
1933 - /**
1934 - * When an edit is made by a reviewer, if the current revision is the stable
1935 - * version, try to automatically review it.
1936 - */
1937 - public static function maybeMakeEditReviewed( $article, $rev, $baseRevID = false ) {
1938 - global $wgFlaggedRevsAutoReview, $wgFlaggedArticle, $wgRequest;
1939 - # Get the user
1940 - $user = User::newFromId( $rev->getUser() );
1941 - if( !$wgFlaggedRevsAutoReview || !$user->isAllowed('autoreview') )
1942 - return true;
1943 - # Must be in reviewable namespace
1944 - $title = $article->getTitle();
1945 - if( !$title || !self::isPageReviewable( $title ) ) {
1946 - return true;
1947 - }
1948 - $frev = null;
1949 - $reviewableNewPage = false;
1950 - # Get the revision ID the incoming one was based off
1951 - $baseRevID = $baseRevID ? $baseRevID : $wgRequest->getIntOrNull('baseRevId');
1952 - # Get what was just the current revision ID
1953 - $prevRevID = $title->getPreviousRevisionId( $rev->getId(), GAID_FOR_UPDATE );
1954 - # If baseRevId not given, assume the previous revision ID
1955 - $baseRevID = $baseRevID ? $baseRevID : $prevRevID;
1956 - if( $baseRevID ) {
1957 - $frev = self::getFlaggedRev( $title, $baseRevID, false, true, $rev->getPage() );
1958 - # If the base revision was not reviewed, check if the previous one was
1959 - $frev = $frev ? $frev : self::getFlaggedRev( $title, $prevRevID, false, true, $rev->getPage() );
1960 - } else {
1961 - $prevRevID = $title->getPreviousRevisionId( $rev->getId(), GAID_FOR_UPDATE );
1962 - $prevRev = $prevRevID ? Revision::newFromID( $prevRevID ) : null;
1963 - # Check for null edits
1964 - if( $prevRev && $prevRev->getTextId() == $rev->getTextId() ) {
1965 - $frev = self::getFlaggedRev( $title, $prevRev->getId() );
1966 - # Check for new pages
1967 - } else if( !$prevRevID ) {
1968 - global $wgFlaggedRevsAutoReviewNew;
1969 - $reviewableNewPage = ($wgFlaggedRevsAutoReviewNew && $user->isAllowed('review'));
1970 - }
1971 - }
1972 - # Is this an edit directly to the stable version?
1973 - if( $reviewableNewPage || !is_null($frev) ) {
1974 - # Assume basic flagging level
1975 - $flags = array();
1976 - foreach( self::$dimensions as $tag => $minQL ) {
1977 - $flags[$tag] = 1;
1978 - }
1979 - # Review this revision of the page. Let articlesavecomplete hook do rc_patrolled bit...
1980 - self::autoReviewEdit( $article, $user, $rev->getText(), $rev, $flags, false );
1981 - }
1982 - return true;
1983 - }
1984 -
1985 - /**
1986 - * When an edit is made to a page that can't be reviewed, autopatrol if allowed.
1987 - * This is not loggged for perfomance reasons and no one cares if talk pages and such
1988 - * are autopatrolled.
1989 - */
1990 - public static function autoMarkPatrolled( $article, $user, $text, $c, $m, $a, $b, $flags, $rev ) {
1991 - if( !$rev ) {
1992 - return true; // NULL edit
1993 - }
1994 - $title = $article->getTitle();
1995 - $patrol = false;
1996 - // Is the page reviewable?
1997 - if( self::isPageReviewable($title) ) {
1998 - $patrol = self::revIsFlagged( $title, $rev->getId(), GAID_FOR_UPDATE );
1999 - // Can this be patrolled?
2000 - } else if( self::isPagePatrollable($title) ) {
2001 - $patrol = $user->isAllowed('autopatrolother');
2002 - } else {
2003 - $patrol = true; // mark by default
2004 - }
2005 - if( $patrol ) {
2006 - $dbw = wfGetDB( DB_MASTER );
2007 - $dbw->update( 'recentchanges',
2008 - array( 'rc_patrolled' => 1 ),
2009 - array( 'rc_this_oldid' => $rev->getId(),
2010 - 'rc_user_text' => $rev->getRawUserText(),
2011 - 'rc_timestamp' => $dbw->timestamp( $rev->getTimestamp() ) ),
2012 - __METHOD__,
2013 - array( 'USE INDEX' => 'rc_user_text', 'LIMIT' => 1 ) );
2014 - }
2015 - return true;
2016 - }
2017 -
2018 - /**
2019 - * Callback that autopromotes user according to the setting in
2020 - * $wgFlaggedRevsAutopromote. This is not as efficient as it should be
2021 - */
2022 - public static function autoPromoteUser( $article, $user, &$text, &$summary, &$m, &$a, &$b, &$f, $rev ) {
2023 - global $wgFlaggedRevsAutopromote, $wgMemc;
2024 -
2025 - if( empty($wgFlaggedRevsAutopromote) || !$rev )
2026 - return true;
2027 -
2028 - wfProfileIn( __METHOD__ );
2029 - # Grab current groups
2030 - $groups = $user->getGroups();
2031 - # Do not give this to current holders or bots
2032 - if( in_array( 'bot', $groups ) || in_array( 'editor', $groups ) ) {
2033 - wfProfileOut( __METHOD__ );
2034 - return true;
2035 - }
2036 - # Do not re-add status if it was previously removed!
2037 - $p = self::getUserParams( $user );
2038 - if( isset($p['demoted']) && $p['demoted'] ) {
2039 - wfProfileOut( __METHOD__ );
2040 - return true;
2041 - }
2042 - # Update any special counters for non-null revisions
2043 - $changed = false;
2044 - $pages = array();
2045 - $p['uniqueContentPages'] = isset($p['uniqueContentPages']) ? $p['uniqueContentPages'] : '';
2046 - $p['totalContentEdits'] = isset($p['totalContentEdits']) ? $p['totalContentEdits'] : 0;
2047 - $p['editComments'] = isset($p['editComments']) ? $p['editComments'] : 0;
2048 - if( $article->getTitle()->isContentPage() ) {
2049 - $pages = explode( ',', trim($p['uniqueContentPages']) ); // page IDs
2050 - # Don't let this get bloated for no reason
2051 - if( count($pages) < $wgFlaggedRevsAutopromote['uniqueContentPages'] && !in_array($article->getId(),$pages) ) {
2052 - $pages[] = $article->getId();
2053 - $p['uniqueContentPages'] = preg_replace('/^,/','',implode(',',$pages)); // clear any garbage
2054 - }
2055 - $p['totalContentEdits'] += 1;
2056 - $changed = true;
2057 - }
2058 - if( $summary ) {
2059 - $p['editComments'] += 1;
2060 - $changed = true;
2061 - }
2062 - # Save any updates to user params
2063 - if( $changed ) {
2064 - self::saveUserParams( $user, $p );
2065 - }
2066 - # Check if user edited enough content pages
2067 - if( $wgFlaggedRevsAutopromote['totalContentEdits'] > $p['totalContentEdits'] ) {
2068 - wfProfileOut( __METHOD__ );
2069 - return true;
2070 - }
2071 - # Check if user edited enough unique pages
2072 - if( $wgFlaggedRevsAutopromote['uniqueContentPages'] > count($pages) ) {
2073 - wfProfileOut( __METHOD__ );
2074 - return true;
2075 - }
2076 - # Check edit comment use
2077 - if( $wgFlaggedRevsAutopromote['editComments'] > $p['editComments'] ) {
2078 - wfProfileOut( __METHOD__ );
2079 - return true;
2080 - }
2081 - # Check if results are cached to avoid DB queries
2082 - $key = wfMemcKey( 'flaggedrevs', 'autopromote-skip', $user->getID() );
2083 - $value = $wgMemc->get( $key );
2084 - if( $value == 'true' ) {
2085 - wfProfileOut( __METHOD__ );
2086 - return true;
2087 - }
2088 - # Check basic, already available, promotion heuristics first...
2089 - $now = time();
2090 - $usercreation = wfTimestamp( TS_UNIX, $user->getRegistration() );
2091 - $userage = floor(($now - $usercreation) / 86400);
2092 - if( $userage < $wgFlaggedRevsAutopromote['days'] ) {
2093 - wfProfileOut( __METHOD__ );
2094 - return true;
2095 - }
2096 - if( $user->getEditCount() < $wgFlaggedRevsAutopromote['edits'] ) {
2097 - wfProfileOut( __METHOD__ );
2098 - return true;
2099 - }
2100 - if( $wgFlaggedRevsAutopromote['email'] && !$user->isEmailConfirmed() ) {
2101 - wfProfileOut( __METHOD__ );
2102 - return true;
2103 - }
2104 - # Don't grant to currently blocked users...
2105 - if( $user->isBlocked() ) {
2106 - wfProfileOut( __METHOD__ );
2107 - return true;
2108 - }
2109 - # Check if user was ever blocked before
2110 - if( $wgFlaggedRevsAutopromote['neverBlocked'] ) {
2111 - $dbr = wfGetDB( DB_SLAVE );
2112 - $blocked = $dbr->selectField( 'logging', '1',
2113 - array( 'log_namespace' => NS_USER,
2114 - 'log_title' => $user->getUserPage()->getDBKey(),
2115 - 'log_type' => 'block',
2116 - 'log_action' => 'block' ),
2117 - __METHOD__,
2118 - array( 'USE INDEX' => 'user_time' ) );
2119 - if( $blocked ) {
2120 - # Make a key to store the results
2121 - $wgMemc->set( $key, 'true', 3600*24*7 );
2122 - wfProfileOut( __METHOD__ );
2123 - return true;
2124 - }
2125 - }
2126 - # See if the page actually has sufficient content...
2127 - if( $wgFlaggedRevsAutopromote['userpage'] ) {
2128 - if( !$user->getUserPage()->exists() ) {
2129 - wfProfileOut( __METHOD__ );
2130 - return true;
2131 - }
2132 - $dbr = isset($dbr) ? $dbr : wfGetDB( DB_SLAVE );
2133 - $size = $dbr->selectField( 'page', 'page_len',
2134 - array( 'page_namespace' => $user->getUserPage()->getNamespace(),
2135 - 'page_title' => $user->getUserPage()->getDBKey() ),
2136 - __METHOD__ );
2137 - if( $size < $wgFlaggedRevsAutopromote['userpageBytes'] ) {
2138 - wfProfileOut( __METHOD__ );
2139 - return true;
2140 - }
2141 - }
2142 - # Check for edit spacing. This lets us know that the account has
2143 - # been used over N different days, rather than all in one lump.
2144 - if( $wgFlaggedRevsAutopromote['spacing'] > 0 && $wgFlaggedRevsAutopromote['benchmarks'] > 1 ) {
2145 - # Convert days to seconds...
2146 - $spacing = $wgFlaggedRevsAutopromote['spacing'] * 24 * 3600;
2147 - # Check the oldest edit
2148 - $dbr = isset($dbr) ? $dbr : wfGetDB( DB_SLAVE );
2149 - $lower = $dbr->selectField( 'revision', 'rev_timestamp',
2150 - array( 'rev_user' => $user->getID() ),
2151 - __METHOD__,
2152 - array( 'ORDER BY' => 'rev_timestamp ASC',
2153 - 'USE INDEX' => 'user_timestamp' ) );
2154 - # Recursively check for an edit $spacing seconds later, until we are done.
2155 - # The first edit counts, so we have one less scans to do...
2156 - $benchmarks = 0;
2157 - $needed = $wgFlaggedRevsAutopromote['benchmarks'] - 1;
2158 - while( $lower && $benchmarks < $needed ) {
2159 - $next = wfTimestamp( TS_UNIX, $lower ) + $spacing;
2160 - $lower = $dbr->selectField( 'revision', 'rev_timestamp',
2161 - array( 'rev_user' => $user->getID(),
2162 - 'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp($next) ) ),
2163 - __METHOD__,
2164 - array( 'ORDER BY' => 'rev_timestamp ASC',
2165 - 'USE INDEX' => 'user_timestamp' ) );
2166 - if( $lower !== false )
2167 - $benchmarks++;
2168 - }
2169 - if( $benchmarks < $needed ) {
2170 - # Make a key to store the results
2171 - $wgMemc->set( $key, 'true', 3600*24*$spacing*($benchmarks - $needed - 1) );
2172 - wfProfileOut( __METHOD__ );
2173 - return true;
2174 - }
2175 - }
2176 - # Check if this user is sharing IPs with another users
2177 - if( $wgFlaggedRevsAutopromote['uniqueIPAddress'] ) {
2178 - $uid = $user->getId();
2179 -
2180 - $dbr = isset($dbr) ? $dbr : wfGetDB( DB_SLAVE );
2181 - $shared = $dbr->selectField( 'recentchanges', '1',
2182 - array( 'rc_ip' => wfGetIP(),
2183 - "rc_user != '$uid'" ),
2184 - __METHOD__,
2185 - array( 'USE INDEX' => 'rc_ip' ) );
2186 - if( $shared ) {
2187 - # Make a key to store the results
2188 - $wgMemc->set( $key, 'true', 3600*24*7 );
2189 - wfProfileOut( __METHOD__ );
2190 - return true;
2191 - }
2192 - }
2193 - # Check for bot attacks/sleepers
2194 - global $wgSorbsUrl, $wgProxyWhitelist;
2195 - if( $wgSorbsUrl && $wgFlaggedRevsAutopromote['noSorbsMatches'] ) {
2196 - $ip = wfGetIP();
2197 - if( !in_array($ip,$wgProxyWhitelist) && $user->inDnsBlacklist( $ip, $wgSorbsUrl ) ) {
2198 - # Make a key to store the results
2199 - $wgMemc->set( $key, 'true', 3600*24*7 );
2200 - wfProfileOut( __METHOD__ );
2201 - return true;
2202 - }
2203 - }
2204 - # Check if the user has any recent content edits
2205 - if( $wgFlaggedRevsAutopromote['recentContentEdits'] > 0 ) {
2206 - global $wgContentNamespaces;
2207 -
2208 - $dbr = isset($dbr) ? $dbr : wfGetDB( DB_SLAVE );
2209 - $res = $dbr->select( 'recentchanges', '1',
2210 - array( 'rc_user_text' => $user->getName(),
2211 - 'rc_namespace' => $wgContentNamespaces ),
2212 - __METHOD__,
2213 - array( 'USE INDEX' => 'rc_ns_usertext',
2214 - 'LIMIT' => $wgFlaggedRevsAutopromote['recentContent'] ) );
2215 - if( $dbr->numRows($res) < $wgFlaggedRevsAutopromote['recentContent'] ) {
2216 - wfProfileOut( __METHOD__ );
2217 - return true;
2218 - }
2219 - }
2220 - # Check to see if the user has so many deleted edits that
2221 - # they don't actually enough live edits. This is because
2222 - # $user->getEditCount() is the count of edits made, not live.
2223 - if( $wgFlaggedRevsAutopromote['excludeDeleted'] ) {
2224 - $dbr = isset($dbr) ? $dbr : wfGetDB( DB_SLAVE );
2225 - $minDiff = $user->getEditCount() - $wgFlaggedRevsAutopromote['days'] + 1;
2226 - # Use an estimate if the number starts to get large
2227 - if( $minDiff <= 100 ) {
2228 - $res = $dbr->select( 'archive', '1',
2229 - array( 'ar_user_text' => $user->getName() ),
2230 - __METHOD__,
2231 - array( 'USE INDEX' => 'usertext_timestamp', 'LIMIT' => $minDiff ) );
2232 - $deletedEdits = $dbr->numRows($res);
2233 - } else {
2234 - $deletedEdits = $dbr->estimateRowCount( 'archive', '1',
2235 - array( 'ar_user_text' => $user->getName() ),
2236 - __METHOD__,
2237 - array( 'USE INDEX' => 'usertext_timestamp' ) );
2238 - }
2239 - if( $deletedEdits >= $minDiff ) {
2240 - wfProfileOut( __METHOD__ );
2241 - return true;
2242 - }
2243 - }
2244 - # Add editor rights
2245 - $newGroups = $groups ;
2246 - array_push( $newGroups, 'editor' );
2247 - # Lets NOT spam RC, set $RC to false
2248 - $log = new LogPage( 'rights', false );
2249 - $log->addEntry( 'rights', $user->getUserPage(), wfMsg('rights-editor-autosum'),
2250 - array( implode(', ',$groups), implode(', ',$newGroups) ) );
2251 - $user->addGroup('editor');
2252 -
2253 - wfProfileOut( __METHOD__ );
2254 - return true;
2255 - }
2256 -
2257 - /**
2258 - * Record demotion so that auto-promote will be disabled
2259 - */
2260 - public static function recordDemote( $u, $addgroup, $removegroup ) {
2261 - if( $removegroup && in_array('editor',$removegroup) ) {
2262 - $params = self::getUserParams( $u );
2263 - $params['demoted'] = 1;
2264 - self::saveUserParams( $u, $params );
2265 - }
2266 - return true;
2267 - }
2268 -
2269 - /**
2270 - * Add user preference to form HTML
2271 - */
2272 - public static function injectPreferences( $form, $out ) {
2273 - $prefsHtml = FlaggedRevsXML::stabilityPreferences( $form );
2274 - $out->addHTML( $prefsHtml );
2275 - return true;
2276 - }
2277 -
2278 - /**
2279 - * Add user preference to form object based on submission
2280 - */
2281 - public static function injectFormPreferences( $form, $request ) {
2282 - global $wgUser;
2283 - $form->mFlaggedRevsStable = $request->getInt( 'wpFlaggedRevsStable' );
2284 - $form->mFlaggedRevsSUI = $request->getInt( 'wpFlaggedRevsSUI' );
2285 - $form->mFlaggedRevsWatch = $wgUser->isAllowed( 'review' ) ? $request->getInt( 'wpFlaggedRevsWatch' ) : 0;
2286 - return true;
2287 - }
2288 -
2289 - /**
2290 - * Set preferences on form based on user settings
2291 - */
2292 - public static function resetPreferences( $form, $user ) {
2293 - global $wgSimpleFlaggedRevsUI;
2294 - $form->mFlaggedRevsStable = $user->getOption( 'flaggedrevsstable' );
2295 - $form->mFlaggedRevsSUI = $user->getOption( 'flaggedrevssimpleui', intval($wgSimpleFlaggedRevsUI) );
2296 - $form->mFlaggedRevsWatch = $user->getOption( 'flaggedrevswatch' );
2297 - return true;
2298 - }
2299 -
2300 - /**
2301 - * Set user preferences into user object before it is applied to DB
2302 - */
2303 - public static function savePreferences( $form, $user, &$msg ) {
2304 - $user->setOption( 'flaggedrevsstable', $form->validateInt( $form->mFlaggedRevsStable, 0, 1 ) );
2305 - $user->setOption( 'flaggedrevssimpleui', $form->validateInt( $form->mFlaggedRevsSUI, 0, 1 ) );
2306 - $user->setOption( 'flaggedrevswatch', $form->validateInt( $form->mFlaggedRevsWatch, 0, 1 ) );
2307 - return true;
2308 - }
2309 -
2310 - /**
2311 - * Create revision link for log line entry
2312 - * @param string $type
2313 - * @param string $action
2314 - * @param object $title
2315 - * @param array $paramArray
2316 - * @param string $c
2317 - * @param string $r user tool links
2318 - * @param string $t timestamp of the log entry
2319 - * @return bool true
2320 - */
2321 - public static function reviewLogLine( $type = '', $action = '', $title = null, $paramArray = array(), &$c = '', &$r = '', $t = '' ) {
2322 - # Show link to page with oldid=x
2323 - if( $type == 'review' && in_array($action,array('approve','approve2','unapprove','unapprove2')) ) {
2324 - global $wgUser;
2325 - if( is_object($title) && isset($paramArray[0]) ) {
2326 - $r = '(' . $wgUser->getSkin()->makeKnownLinkObj( $title,
2327 - wfMsgHtml('review-logentry-id',$paramArray[0]), "oldid={$paramArray[0]}") . ')';
2328 - }
2329 - }
2330 - return true;
2331 - }
2332 -}
2333 -
2334353 function efFlaggedRevsSchemaUpdates() {
2335354 global $wgDBtype, $wgExtNewFields, $wgExtPGNewFields, $wgExtNewIndexes, $wgExtNewTables;
2336355 $base = dirname(__FILE__);
Index: trunk/extensions/FlaggedRevs/maintenance/updateAutoPromote.php
@@ -3,7 +3,7 @@
44 if ( getenv( 'MW_INSTALL_PATH' ) ) {
55 $IP = getenv( 'MW_INSTALL_PATH' );
66 } else {
7 - $IP = dirname(__FILE__).'/../..';
 7+ $IP = dirname(__FILE__).'/../../..';
88 }
99 require "$IP/maintenance/commandLine.inc";
1010 require dirname(__FILE__) . '/updateAutoPromote.inc';
Index: trunk/extensions/FlaggedRevs/FlaggedArticle.php
@@ -3,45 +3,79 @@
44 class FlaggedArticle extends Article {
55 public $isDiffFromStable = false;
66 protected $stableRev = null;
7 - protected $pageconfig = null;
 7+ protected $pageConfig = null;
88 protected $flags = null;
99 protected $reviewNotice = '';
1010 protected $file = NULL;
 11+ protected $parent;
 12+
 13+
1114 /**
 15+ * Get an instance of FlaggedArticle for a given Article or Title object
 16+ */
 17+ static function getInstance( $object ) {
 18+ if ( !isset( $object->flaggedRevsArticle ) ) {
 19+ if ( $object instanceof Title ) {
 20+ $article = new Article( $object );
 21+ $article->flaggedRevsArticle = new FlaggedArticle( $article );
 22+ $object->flaggedRevsArticle =& $article->flaggedRevsArticle;
 23+ } elseif ( $object instanceof Article ) {
 24+ if ( isset( $object->getTitle()->flaggedRevsArticle ) ) {
 25+ $object->flaggedRevsArticle =& $object->getTitle()->flaggedRevsArticle;
 26+ } else {
 27+ $object->flaggedRevsArticle = new FlaggedArticle( $object );
 28+ $object->getTitle()->flaggedRevsArticle =& $object->flaggedRevsArticle;
 29+ }
 30+ } else {
 31+ throw new MWException( __METHOD__.': invalid argument' );
 32+ }
 33+ }
 34+ return $object->flaggedRevsArticle;
 35+ }
 36+
 37+ /**
 38+ * Construct a new FlaggedArticle from its Article parent
 39+ * Should not be called directly, use FlaggedArticle::getInstance()
 40+ */
 41+ function __construct( $parent ) {
 42+ $this->parent = $parent;
 43+ }
 44+
 45+ /**
1246 * Does the config and current URL params allow
1347 * for overriding by stable revisions?
1448 */
15 - public function pageOverride() {
16 - global $wgUser, $wgRequest;
17 - # This only applies to viewing content pages
 49+ public function pageOverride() {
 50+ global $wgUser, $wgRequest;
 51+ # This only applies to viewing content pages
1852 $action = $wgRequest->getVal( 'action', 'view' );
19 - if( ($action !='view' && $action !='purge') || !$this->isReviewable() )
 53+ if( ($action !='view' && $action !='purge') || !$this->isReviewable() )
2054 return false;
21 - # Does not apply to diffs/old revision. Explicit requests
 55+ # Does not apply to diffs/old revision. Explicit requests
2256 # for a certain stable version will be handled elsewhere.
23 - if( $wgRequest->getVal('oldid') || $wgRequest->getVal('diff') || $wgRequest->getVal('stableid') )
 57+ if( $wgRequest->getVal('oldid') || $wgRequest->getVal('diff') || $wgRequest->getVal('stableid') )
2458 return false;
2559 # Check user preferences
2660 if( $wgUser->getOption('flaggedrevsstable') )
2761 return !( $wgRequest->getIntOrNull('stable') === 0 );
2862 # Get page configuration
2963 $config = $this->getVisibilitySettings();
30 - # Does the stable version override the current one?
31 - if( $config['override'] ) {
32 - global $wgFlaggedRevsExceptions;
33 - # Viewer sees current by default (editors, insiders, ect...) ?
34 - foreach( $wgFlaggedRevsExceptions as $group ) {
35 - if( $group == 'user' ) {
36 - if( $wgUser->getID() ) {
37 - return ( $wgRequest->getIntOrNull('stable') === 1 );
 64+ # Does the stable version override the current one?
 65+ if( $config['override'] ) {
 66+ global $wgFlaggedRevsExceptions;
 67+ # Viewer sees current by default (editors, insiders, ect...) ?
 68+ foreach( $wgFlaggedRevsExceptions as $group ) {
 69+ if( $group == 'user' ) {
 70+ if( $wgUser->getID() ) {
 71+ return ( $wgRequest->getIntOrNull('stable') === 1 );
3872 }
39 - } else if( in_array( $group, $wgUser->getGroups() ) ) {
40 - return ( $wgRequest->getIntOrNull('stable') === 1 );
41 - }
42 - }
 73+ } else if( in_array( $group, $wgUser->getGroups() ) ) {
 74+ return ( $wgRequest->getIntOrNull('stable') === 1 );
 75+ }
 76+ }
4377 # Viewer sees stable by default
44 - return !( $wgRequest->getIntOrNull('stable') === 0 );
45 - # We are explicity requesting the stable version?
 78+ return !( $wgRequest->getIntOrNull('stable') === 0 );
 79+ # We are explicity requesting the stable version?
4680 } else if( $wgRequest->getIntOrNull('stable') === 1 ) {
4781 return true;
4882 }
@@ -52,20 +86,20 @@
5387 * Is this user shown the stable version by default for this page?
5488 */
5589 public function showStableByDefault() {
56 - global $wgFlaggedRevsOverride, $wgFlaggedRevsExceptions, $wgUser;
 90+ global $wgFlaggedRevsExceptions, $wgUser;
5791 # Get page configuration
5892 $config = $this->getVisibilitySettings();
5993 if( !$config['override'] )
6094 return false;
61 - # Viewer sees current by default (editors, insiders, ect...) ?
62 - foreach( $wgFlaggedRevsExceptions as $group ) {
63 - if( $group == 'user' ) {
64 - if( !$wgUser->isAnon() )
65 - return false;
66 - } else if( in_array( $group, $wgUser->getGroups() ) ) {
67 - return false;
68 - }
69 - }
 95+ # Viewer sees current by default (editors, insiders, ect...) ?
 96+ foreach( $wgFlaggedRevsExceptions as $group ) {
 97+ if( $group == 'user' ) {
 98+ if( !$wgUser->isAnon() )
 99+ return false;
 100+ } else if( in_array( $group, $wgUser->getGroups() ) ) {
 101+ return false;
 102+ }
 103+ }
70104 return true;
71105 }
72106
@@ -73,7 +107,7 @@
74108 * Is this article reviewable?
75109 */
76110 public function isReviewable() {
77 - return FlaggedRevs::isPageReviewable( $this->getTitle() );
 111+ return FlaggedRevs::isPageReviewable( $this->parent->getTitle() );
78112 }
79113
80114 /**
@@ -90,23 +124,23 @@
91125 * Adds stable version status/info tags and notes
92126 * Adds a quick review form on the bottom if needed
93127 */
94 - public function setPageContent( $article, &$outputDone, &$pcache ) {
 128+ public function setPageContent( &$outputDone, &$pcache ) {
95129 global $wgRequest, $wgOut, $wgUser, $wgLang;
96130 # Only trigger for reviewable pages
97 - if( !FlaggedRevs::isPageReviewable( $article->getTitle() ) ) {
 131+ if( !FlaggedRevs::isPageReviewable( $this->parent->getTitle() ) ) {
98132 return true;
99133 }
100134 # Only trigger on article view for content pages, not for protect/delete/hist...
101135 $action = $wgRequest->getVal( 'action', 'view' );
102 - if( ($action !='view' && $action !='purge') || !$article || !$article->exists() )
 136+ if( ($action !='view' && $action !='purge') || !$this->parent->exists() )
103137 return true;
104138 # Do not clutter up diffs any further...
105139 if( $wgRequest->getVal('diff') ) {
106 - return true;
 140+ return true;
107141 } else if( $wgRequest->getVal('oldid') ) {
108142 # We may have nav links like "direction=prev&oldid=x"
109 - $revID = $article->getOldIDFromRequest();
110 - $frev = FlaggedRevs::getFlaggedRev( $article->getTitle(), $revID );
 143+ $revID = $this->parent->getOldIDFromRequest();
 144+ $frev = FlaggedRevs::getFlaggedRev( $this->parent->getTitle(), $revID );
111145 # Give a notice if this rev ID corresponds to a reviewed version...
112146 if( !is_null($frev) ) {
113147 $time = $wgLang->date( $frev->getTimestamp(), true );
@@ -135,15 +169,15 @@
136170 # Also, check for any explicitly requested old stable version...
137171 $reqId = $wgRequest->getVal('stableid');
138172 if( $reqId === "best" ) {
139 - $reqId = FlaggedRevs::getPrimeFlaggedRevId( $article );
 173+ $reqId = FlaggedRevs::getPrimeFlaggedRevId( $this->parent );
140174 }
141175 if( $stableId && $reqId ) {
142176 if( $reqId != $stableId ) {
143 - $frev = FlaggedRevs::getFlaggedRev( $article->getTitle(), $reqId, true );
 177+ $frev = FlaggedRevs::getFlaggedRev( $this->parent->getTitle(), $reqId, true );
144178 $old = true; // old reviewed version requested by ID
145179 if( !$frev ) {
146180 $wgOut->addWikiText( wfMsg('revreview-invalid') );
147 - $wgOut->returnToMain( false, $article->getTitle() );
 181+ $wgOut->returnToMain( false, $this->parent->getTitle() );
148182 # Tell MW that parser output is done
149183 $outputDone = true;
150184 $pcache = false;
@@ -166,25 +200,25 @@
167201 // requesting the stable revision ("&stableid=x"), defer to override
168202 // behavior below, since it is the same as ("&stable=1").
169203 if( $old ) {
170 - $revs_since = FlaggedRevs::getRevCountSince( $article, $frev->getRevId() );
 204+ $revsSince = FlaggedRevs::getRevCountSince( $this->parent, $frev->getRevId() );
171205 $text = $frev->getTextForParse();
172 - $parserOut = FlaggedRevs::parseStableText( $article, $text, $frev->getRevId() );
 206+ $parserOut = FlaggedRevs::parseStableText( $this->parent, $text, $frev->getRevId() );
173207 # Construct some tagging for non-printable outputs. Note that the pending
174208 # notice has all this info already, so don't do this if we added that already.
175209 if( !$wgOut->isPrintable() ) {
176 - $css = $quality ? 'fr-icon-quality' : 'fr-icon-stable';
 210+ $class = $quality ? 'fr-icon-quality' : 'fr-icon-stable';
177211 $tooltip = $quality ? 'revreview-quality-title' : 'revreview-stable-title';
178212 $tooltip = wfMsgHtml($tooltip);
179213 // Simple icon-based UI
180214 if( FlaggedRevs::useSimpleUI() ) {
181215 $msg = $quality ? 'revreview-quick-quality-old' : 'revreview-quick-basic-old';
182 - $html = "<span class='{$css}' title=\"{$tooltip}\"></span>" .
 216+ $html = "<span class='{$class}' title=\"{$tooltip}\"></span>" .
183217 wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $time );
184 - $tag .= FlaggedRevsXML::prettyRatingBox( $frev, $html, $revs_since, true, false, $old );
 218+ $tag .= FlaggedRevsXML::prettyRatingBox( $frev, $html, $revsSince, true, false, $old );
185219 // Standard UI
186220 } else {
187221 $msg = $quality ? 'revreview-quality-old' : 'revreview-basic-old';
188 - $tag .= "<span class='{$css}' title=\"{$tooltip}\"></span>" .
 222+ $tag .= "<span class='{$class}' title=\"{$tooltip}\"></span>" .
189223 wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $time );
190224 # Hide clutter
191225 if( !empty($flags) ) {
@@ -197,9 +231,9 @@
198232 }
199233 }
200234 # Output HTML
201 - $wgOut->addParserOutput( $parserOut );
 235+ $wgOut->addParserOutput( $parserOut );
202236 $wgOut->setRevisionId( $frev->getRevId() );
203 - $notes = $this->ReviewNotes( $frev );
 237+ $notes = $this->getReviewNotes( $frev );
204238 # Tell MW that parser output is done
205239 $outputDone = true;
206240 $pcache = false;
@@ -208,13 +242,13 @@
209243 // requesting the stable revision ("&stableid=x"), defer to override
210244 // behavior below, since it is the same as ("&stable=1").
211245 } else if( !$stable && !$this->pageOverride() ) {
212 - $revs_since = FlaggedRevs::getRevCountSince( $article, $frev->getRevId() );
213 - $synced = FlaggedRevs::flaggedRevIsSynced( $frev, $article );
 246+ $revsSince = FlaggedRevs::getRevCountSince( $this->parent, $frev->getRevId() );
 247+ $synced = FlaggedRevs::flaggedRevIsSynced( $frev, $this->parent );
214248 # Give notice to newewer users if an unreviewed edit was completed...
215249 if( $wgRequest->getVal('shownotice') && !$synced && !$wgUser->isAllowed('review') ) {
216250 $tooltip = wfMsgHtml('revreview-draft-title');
217251 $pending = "<span class='fr-icon-current' title=\"{$tooltip}\"></span>" .
218 - wfMsgExt('revreview-edited',array('parseinline'),$frev->getRevId(),$revs_since);
 252+ wfMsgExt('revreview-edited',array('parseinline'),$frev->getRevId(),$revsSince);
219253 $pending = "<div id='mw-reviewnotice' class='flaggedrevs_preview plainlinks'>$pending</div>";
220254 # Notice should always use subtitle
221255 $this->reviewNotice = $pending;
@@ -224,36 +258,36 @@
225259 # Construct some tagging for non-printable outputs. Note that the pending
226260 # notice has all this info already, so don't do this if we added that already.
227261 if( !$wgOut->isPrintable() && !$pending ) {
228 - $css = 'fr-icon-current'; // default
 262+ $class = 'fr-icon-current'; // default
229263 $tooltip = 'revreview-draft-title';
230264 // Simple icon-based UI
231265 if( FlaggedRevs::useSimpleUI() ) {
232266 if( $synced ) {
233267 $msg = $quality ? 'revreview-quick-quality-same' : 'revreview-quick-basic-same';
234 - $css = $quality ? 'fr-icon-quality' : 'fr-icon-stable';
 268+ $class = $quality ? 'fr-icon-quality' : 'fr-icon-stable';
235269 $tooltip = $quality ? 'revreview-quality-title' : 'revreview-stable-title';
236 - $msgHTML = wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $revs_since );
 270+ $msgHTML = wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $revsSince );
237271 } else {
238272 $msg = $quality ? 'revreview-quick-see-quality' : 'revreview-quick-see-basic';
239 - $msgHTML = wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $revs_since );
 273+ $msgHTML = wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $revsSince );
240274 }
241275 $tooltip = wfMsgHtml($tooltip);
242 - $msgHTML = "<span class='{$css}' title=\"{$tooltip}\"></span>$msgHTML";
243 - $tag .= FlaggedRevsXML::prettyRatingBox( $frev, $msgHTML, $revs_since, $synced, $synced, $old );
 276+ $msgHTML = "<span class='{$class}' title=\"{$tooltip}\"></span>$msgHTML";
 277+ $tag .= FlaggedRevsXML::prettyRatingBox( $frev, $msgHTML, $revsSince, $synced, $synced, $old );
244278 // Standard UI
245279 } else {
246280 if( $synced ) {
247281 $msg = $quality ? 'revreview-quality-same' : 'revreview-basic-same';
248 - $css = $quality ? 'fr-icon-quality' : 'fr-icon-stable';
 282+ $class = $quality ? 'fr-icon-quality' : 'fr-icon-stable';
249283 $tooltip = $quality ? 'revreview-quality-title' : 'revreview-stable-title';
250 - $msgHTML = wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $time, $revs_since );
 284+ $msgHTML = wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $time, $revsSince );
251285 } else {
252286 $msg = $quality ? 'revreview-newest-quality' : 'revreview-newest-basic';
253 - $msg .= ($revs_since == 0) ? '-i' : '';
254 - $msgHTML = wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $time, $revs_since );
 287+ $msg .= ($revsSince == 0) ? '-i' : '';
 288+ $msgHTML = wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $time, $revsSince );
255289 }
256290 $tooltip = wfMsgHtml($tooltip);
257 - $tag .= "<span class='{$css}' title=\"{$tooltip}\"></span>" . $msgHTML;
 291+ $tag .= "<span class='{$class}' title=\"{$tooltip}\"></span>" . $msgHTML;
258292 # Hide clutter
259293 if( !empty($flags) ) {
260294 $tag .= " <a id='mw-revisiontoggle' class='flaggedrevs_toggle' style='display:none;'" .
@@ -266,40 +300,40 @@
267301 }
268302 // The relevant conditions are met to override the page with the stable version.
269303 } else {
270 - # We will be looking at the reviewed revision...
271 - $revs_since = FlaggedRevs::getRevCountSince( $article, $frev->getRevId() );
 304+ # We will be looking at the reviewed revision...
 305+ $revsSince = FlaggedRevs::getRevCountSince( $this->parent, $frev->getRevId() );
272306 # Get parsed stable version
273 - $parserOut = FlaggedRevs::getPageCache( $article );
 307+ $parserOut = FlaggedRevs::getPageCache( $this->parent );
274308 if( $parserOut==false ) {
275309 $text = $frev->getTextForParse();
276 - $parserOut = FlaggedRevs::parseStableText( $article, $text, $frev->getRevId() );
277 - # Update the stable version cache
278 - FlaggedRevs::updatePageCache( $article, $parserOut );
279 - }
280 - $synced = FlaggedRevs::flaggedRevIsSynced( $frev, $article, $parserOut, null );
 310+ $parserOut = FlaggedRevs::parseStableText( $this->parent, $text, $frev->getRevId() );
 311+ # Update the stable version cache
 312+ FlaggedRevs::updatePageCache( $this->parent, $parserOut );
 313+ }
 314+ $synced = FlaggedRevs::flaggedRevIsSynced( $frev, $this->parent, $parserOut, null );
281315 # Construct some tagging
282316 if( !$wgOut->isPrintable() ) {
283 - $css = $quality ? 'fr-icon-quality' : 'fr-icon-stable';
 317+ $class = $quality ? 'fr-icon-quality' : 'fr-icon-stable';
284318 $tooltip = $quality ? 'revreview-quality-title' : 'revreview-stable-title';
285319 $tooltip = wfMsgHtml($tooltip);
286320 // Simple icon-based UI
287321 if( FlaggedRevs::useSimpleUI() ) {
288322 $msg = $quality ? 'revreview-quick-quality' : 'revreview-quick-basic';
289323 $msg = $synced ? "{$msg}-same" : $msg;
290 - $html = "<span class='{$css}' title=\"{$tooltip}\"></span>" .
291 - wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $revs_since );
 324+ $html = "<span class='{$class}' title=\"{$tooltip}\"></span>" .
 325+ wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $revsSince );
292326
293 - $tag = FlaggedRevsXML::prettyRatingBox( $frev, $html, $revs_since, true, $synced );
 327+ $tag = FlaggedRevsXML::prettyRatingBox( $frev, $html, $revsSince, true, $synced );
294328 // Standard UI
295329 } else {
296330 $msg = $quality ? 'revreview-quality' : 'revreview-basic';
297331 if( $synced ) {
298332 $msg .= '-same';
299 - } else if( $revs_since == 0 ) {
 333+ } else if( $revsSince == 0 ) {
300334 $msg .= '-i';
301335 }
302 - $tag = "<span class='{$css} plainlinks' title=\"{$tooltip}\"></span>" .
303 - wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $time, $revs_since );
 336+ $tag = "<span class='{$class} plainlinks' title=\"{$tooltip}\"></span>" .
 337+ wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $time, $revsSince );
304338 if( !empty($flags) ) {
305339 $tag .= " <a id='mw-revisiontoggle' class='flaggedrevs_toggle' style='display:none;'" .
306340 " onclick='toggleRevRatings()' title='" . wfMsgHtml('revreview-toggle-title') . "' >" .
@@ -310,9 +344,9 @@
311345 }
312346 }
313347 # Output HTML
314 - $wgOut->addParserOutput( $parserOut );
 348+ $wgOut->addParserOutput( $parserOut );
315349 $wgOut->setRevisionId( $frev->getRevId() );
316 - $notes = $this->ReviewNotes( $frev );
 350+ $notes = $this->getReviewNotes( $frev );
317351 # Tell MW that parser output is done
318352 $outputDone = true;
319353 $pcache = false;
@@ -336,7 +370,7 @@
337371 # Add revision notes
338372 $wgOut->mBodytext = $wgOut->mBodytext . $notes;
339373 // Add "no reviewed version" tag, but not for main page or printable output.
340 - } else if( !$wgOut->isPrintable() && !FlaggedRevs::isMainPage( $article->getTitle() ) ) {
 374+ } else if( !$wgOut->isPrintable() && !FlaggedRevs::isMainPage( $this->parent->getTitle() ) ) {
341375 // Simple icon-based UI
342376 if( FlaggedRevs::useSimpleUI() ) {
343377 $msg = $old ? 'revreview-quick-invalid' : 'revreview-quick-none';
@@ -355,60 +389,91 @@
356390 $this->displayTag();
357391
358392 return true;
359 - }
 393+ }
360394
361395 /**
362 - * Set the image revision to display
363 - */
364 - public function setImageVersion() {
365 - global $wgArticle;
366 - if( !$wgArticle instanceof ImagePage ) {
367 - return false;
 396+ * Get the normal and display files for the underlying ImagePage.
 397+ * If the a stable version needs to be displayed, this will set $normalFile
 398+ * to the current version, and $displayFile to the desired version.
 399+ *
 400+ * If no stable version is required, the reference parameters will not be set
 401+ *
 402+ * Depends on $wgRequest
 403+ */
 404+ public function imagePageFindFile( &$normalFile, &$displayFile ) {
 405+ global $wgRequest;
 406+ if( !$this->parent instanceof ImagePage ) {
 407+ wfDebug( __METHOD__.": not an ImagePage!\n" );
 408+ return;
368409 }
369 - if( $this->getTitle()->getNamespace() == NS_IMAGE && $this->isReviewable() ) {
370 - global $wgRequest;
371 - # A reviewed version may have explicitly been requested...
372 - $frev = null;
373 - if( $reqId = $wgRequest->getVal('stableid') ) {
374 - $frev = FlaggedRevs::getFlaggedRev( $this->getTitle(), $reqId );
375 - } else if( $this->pageOverride() ) {
376 - $frev = $this->getStableRev( true );
 410+
 411+ # Determine timestamp
 412+ # A reviewed version may have explicitly been requested...
 413+ $frev = null;
 414+ $time = false;
 415+ if( $reqId = $wgRequest->getVal('stableid') ) {
 416+ $frev = FlaggedRevs::getFlaggedRev( $this->parent->getTitle(), $reqId );
 417+ } else if( $this->pageOverride() ) {
 418+ $frev = $this->getStableRev( true );
 419+ }
 420+ if( !is_null($frev) ) {
 421+ $time = $frev->getFileTimestamp();
 422+ // B/C, may be stored in associated image version metadata table
 423+ if( !$time ) {
 424+ $dbr = wfGetDB( DB_SLAVE );
 425+ $time = $dbr->selectField( 'flaggedimages',
 426+ 'fi_img_timestamp',
 427+ array( 'fi_rev_id' => $frev->getRevId(),
 428+ 'fi_name' => $this->parent->getTitle()->getDBkey() ),
 429+ __METHOD__ );
377430 }
378 - if( !is_null($frev) ) {
379 - $timestamp = $frev->getFileTimestamp();
380 - // B/C, may be stored in associated image version metadata table
381 - if( !$timestamp ) {
382 - $dbr = wfGetDB( DB_SLAVE );
383 - $timestamp = $dbr->selectField( 'flaggedimages',
384 - 'fi_img_timestamp',
385 - array( 'fi_rev_id' => $frev->getRevId(),
386 - 'fi_name' => $this->getTitle()->getDBkey() ),
387 - __METHOD__ );
388 - }
389 - # NOTE: if not found, this will use the current
390 - $wgArticle = new ImagePage( $this->getTitle(), $timestamp );
 431+ # NOTE: if not found, this will use the current
 432+ $this->parent = new ImagePage( $this->parent->getTitle(), $time );
 433+ }
 434+ if ( !$time ) {
 435+ # Try request parameter
 436+ $time = $wgRequest->getVal( 'filetimestamp', false );
 437+ }
 438+
 439+ if ( !$time ) {
 440+ // Use the default behaviour
 441+ return;
 442+ }
 443+
 444+ $title = $this->parent->getTitle();
 445+ $displayFile = wfFindFile( $title, $time );
 446+ # If none found, try current
 447+ if ( !$displayFile ) {
 448+ wfDebug( __METHOD__.": {$title->getPrefixedDBkey()}: $time not found, using current\n" );
 449+ $displayFile = wfFindFile( $title );
 450+ # If none found, use a valid local placeholder
 451+ if( !$displayFile ) {
 452+ $displayFile = wfLocalFile( $title ); // fallback to current
391453 }
392 - $this->file = $wgArticle->getFile();
 454+ $normalFile = $displayFile;
 455+ # If found, set $normalFile
 456+ } else {
 457+ wfDebug( __METHOD__.": {$title->getPrefixedDBkey()}: using timestamp $time\n" );
 458+ $normalFile = wfFindFile( $title );
393459 }
394 - return true;
395460 }
396461
397 - /**
 462+ /**
398463 * Adds latest stable version tag to page when editing
399464 */
400 - public function addToEditView( $editform ) {
 465+ public function addToEditView( $editPage ) {
401466 global $wgRequest, $wgOut;
402467 # Talk pages cannot be validated
403 - if( !$editform->mArticle || !$this->isReviewable() )
 468+ if( !$this->isReviewable() )
404469 return false;
405470 # Find out revision id
406 - if( $editform->mArticle->mRevision ) {
407 - $revid = $editform->mArticle->mRevision->mId;
 471+ if( $this->parent->mRevision ) {
 472+ $revId = $this->parent->mRevision->mId;
408473 } else {
409 - $revid = $editform->mArticle->getLatest();
410 - }
 474+ $revId = $this->parent->getLatest();
 475+ }
411476 # Grab the ratings for this revision if any
412 - if( !$revid )
 477+ if( !$revId )
413478 return true;
414479 # Set new body html text as that of now
415480 $tag = $warning = '';
@@ -419,34 +484,34 @@
420485
421486 $time = $wgLang->date( $frev->getTimestamp(), true );
422487 $flags = $frev->getTags();
423 - $revs_since = FlaggedRevs::getRevCountSince( $editform->mArticle, $frev->getRevId() );
 488+ $revsSince = FlaggedRevs::getRevCountSince( $this->parent, $frev->getRevId() );
424489 # Construct some tagging
425490 $quality = FlaggedRevs::isQuality( $flags );
426491 # If this will be autoreviewed, notify the user...
427492 if( $wgFlaggedRevsAutoReview && $wgUser->isAllowed('review') ) {
428493 # If we are editing some reviewed revision, any changes this user
429494 # makes will be autoreviewed...
430 - $ofrev = FlaggedRevs::getFlaggedRev( $editform->mArticle->getTitle(), $revid );
 495+ $ofrev = FlaggedRevs::getFlaggedRev( $this->parent->getTitle(), $revId );
431496 if( !is_null($ofrev) ) {
432 - $msg = ( $revid==$frev->getRevId() ) ? 'revreview-auto-w' : 'revreview-auto-w-old';
 497+ $msg = ( $revId==$frev->getRevId() ) ? 'revreview-auto-w' : 'revreview-auto-w-old';
433498 $warning = "<div id='mw-autoreviewtag' class='flaggedrevs_warning plainlinks'>" .
434499 wfMsgExt($msg,array('parseinline')) . "</div>";
435500 }
436501 }
437 - if( $frev->getRevId() != $revid ) {
 502+ if( $frev->getRevId() != $revId ) {
438503 # Streamlined UI
439504 if( FlaggedRevs::useSimpleUI() ) {
440505 $msg = $quality ? 'revreview-newest-quality' : 'revreview-newest-basic';
441 - $msg .= ($revs_since == 0) ? '-i' : '';
 506+ $msg .= ($revsSince == 0) ? '-i' : '';
442507 $tag = "<span class='fr-checkbox'></span>" .
443 - wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $time, $revs_since );
 508+ wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $time, $revsSince );
444509 $tag = "<div id='mw-revisiontag-edit' class='flaggedrevs_editnotice plainlinks'>$tag</div>";
445510 # Standard UI
446511 } else {
447512 $msg = $quality ? 'revreview-newest-quality' : 'revreview-newest-basic';
448 - $msg .= ($revs_since == 0) ? '-i' : '';
 513+ $msg .= ($revsSince == 0) ? '-i' : '';
449514 $tag = "<span class='fr-checkbox'></span>" .
450 - wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $time, $revs_since );
 515+ wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $time, $revsSince );
451516 # Hide clutter
452517 if( !empty($flags) ) {
453518 $tag .= " <a id='mw-revisiontoggle' class='flaggedrevs_toggle' style='display:none;'" .
@@ -463,7 +528,7 @@
464529 $leftNote = $quality ? 'revreview-quality-title' : 'revreview-stable-title';
465530 $rightNote = 'revreview-draft-title';
466531 $text = $frev->getRevText();
467 - if( $text !==false && $wgRequest->getIntOrNull('showdiff') && strcmp($text,$editform->textbox1) !== 0 ) {
 532+ if( $text !==false && $wgRequest->getIntOrNull('showdiff') && strcmp($text,$editPage->textbox1) !== 0 ) {
468533 $diffEngine = new DifferenceEngine();
469534 $diffEngine->showDiffStyle();
470535 $wgOut->addHtml(
@@ -477,50 +542,51 @@
478543 "<td colspan='2' width='50%' align='center' class='diff-otitle'><b>[" . wfMsgHtml($leftNote) . "]</b></td>" .
479544 "<td colspan='2' width='50%' align='center' class='diff-ntitle'><b>[" . wfMsgHtml($rightNote) . "]</b></td>" .
480545 "</tr>" .
481 - $diffEngine->generateDiffBody( $text, $editform->textbox1 ) .
 546+ $diffEngine->generateDiffBody( $text, $editPage->textbox1 ) .
482547 "</table>" .
483548 "</div>\n" );
484549 }
485550 }
486551 return true;
487 - }
 552+ }
488553
489554 /**
490555 * Add review form to pages when necessary
491556 */
492 - public function addReviewForm( $out ) {
493 - global $wgRequest, $wgArticle;
 557+ public function addReviewForm( $out ) {
 558+ global $wgRequest;
494559
495 - if( !$wgArticle || !$wgArticle->exists() || !$this->isReviewable() )
 560+ if( !$this->parent->exists() || !$this->isReviewable() ) {
496561 return true;
 562+ }
497563 # Check if page is protected
498564 $action = $wgRequest->getVal( 'action', 'view' );
499 - if( ($action !='view' && $action !='purge') || !$wgArticle->getTitle()->quickUserCan( 'edit' ) ) {
 565+ if( ($action !='view' && $action !='purge') || !$this->parent->getTitle()->quickUserCan( 'edit' ) ) {
500566 return true;
501567 }
502568 # Add review form
503569 $this->addQuickReview( $out, (bool)$wgRequest->getVal('diff') );
504570
505571 return true;
506 - }
 572+ }
507573
508574 /**
509575 * Adds a patrol link to non-reviewable pages
510576 */
511 - public function addPatrolLink( $article, &$outputDone, &$pcache ) {
 577+ public function addPatrolLink( &$outputDone, &$pcache ) {
512578 global $wgRequest, $wgOut, $wgUser, $wgLang;
513579 # For unreviewable pages, allow for basic patrolling
514 - if( FlaggedRevs::isPagePatrollable( $article->getTitle() ) ) {
 580+ if( FlaggedRevs::isPagePatrollable( $this->parent->getTitle() ) ) {
515581 # If we have been passed an &rcid= parameter, we want to give the user a
516582 # chance to mark this new article as patrolled.
517583 $rcid = $wgRequest->getIntOrNull( 'rcid' );
518584 if( !is_null( $rcid ) && $rcid != 0 && $wgUser->isAllowed( 'review' ) ) {
519 - $reviewtitle = SpecialPage::getTitleFor( 'RevisionReview' );
 585+ $reviewTitle = SpecialPage::getTitleFor( 'RevisionReview' );
520586 $wgOut->addHTML( "<div class='patrollink'>" .
521587 wfMsgHtml( 'markaspatrolledlink',
522 - $wgUser->getSkin()->makeKnownLinkObj( $reviewtitle, wfMsgHtml('markaspatrolledtext'),
523 - "patrolonly=1&target={$article->getTitle()->getPrefixedUrl()}&rcid={$rcid}" .
524 - "&token=" . urlencode( $wgUser->editToken( $article->getTitle()->getPrefixedText(), $rcid ) ) )
 588+ $wgUser->getSkin()->makeKnownLinkObj( $reviewTitle, wfMsgHtml('markaspatrolledtext'),
 589+ "patrolonly=1&target={$this->parent->getTitle()->getPrefixedUrl()}&rcid={$rcid}" .
 590+ "&token=" . urlencode( $wgUser->editToken( $this->parent->getTitle()->getPrefixedText(), $rcid ) ) )
525591 ) .
526592 '</div>'
527593 );
@@ -531,14 +597,14 @@
532598 /**
533599 * Add link to stable version setting to protection form
534600 */
535 - public function addVisibilityLink( $out ) {
536 - global $wgUser, $wgRequest;
 601+ public function addVisibilityLink( $out ) {
 602+ global $wgUser, $wgRequest;
537603
538 - if( !$this->isReviewable() )
539 - return true;
 604+ if( !$this->isReviewable() )
 605+ return true;
540606
541 - $action = $wgRequest->getVal( 'action', 'view' );
542 - if( $action=='protect' || $action=='unprotect' ) {
 607+ $action = $wgRequest->getVal( 'action', 'view' );
 608+ if( $action=='protect' || $action=='unprotect' ) {
543609 # Check for an overridabe revision
544610 $frev = $this->getStableRev( true );
545611 if( !$frev )
@@ -550,17 +616,17 @@
551617 "</span>" . $out->mBodytext;
552618 }
553619 return true;
554 - }
 620+ }
555621
556622 /**
557623 * Add stable version tabs. Rename some of the others if necessary.
558624 */
559 - public function setActionTabs( $sktmp, &$content_actions ) {
560 - global $wgRequest, $wgUser, $wgFlaggedRevsOverride, $wgFlaggedRevTabs;
 625+ public function setActionTabs( $skin, &$contentActions ) {
 626+ global $wgRequest, $wgUser, $wgFlaggedRevsOverride, $wgFlaggedRevTabs;
561627 # Get the subject page, not all skins have it :(
562 - if( !isset($sktmp->mTitle) )
 628+ if( !isset($skin->mTitle) )
563629 return true;
564 - $title = $sktmp->mTitle->getSubjectPage();
 630+ $title = $skin->mTitle->getSubjectPage();
565631 # Non-content pages cannot be validated
566632 if( !FlaggedRevs::isPageReviewable( $title ) || !$title->exists() )
567633 return true;
@@ -568,155 +634,154 @@
569635 $action = $wgRequest->getVal( 'action', 'view' );
570636 # If we are viewing a page normally, and it was overridden,
571637 # change the edit tab to a "current revision" tab
572 - $frev = $this->getStableRev( true );
573 - # No quality revs? Find the last reviewed one
574 - if( is_null($frev) ) {
 638+ $frev = $this->getStableRev( true );
 639+ # No quality revs? Find the last reviewed one
 640+ if( is_null($frev) ) {
575641 return true;
576642 }
577 - # Be clear about what is being edited...
 643+ # Be clear about what is being edited...
578644 $synced = FlaggedRevs::flaggedRevIsSynced( $frev, $article );
579 - if( !$sktmp->mTitle->isTalkPage() && !$synced ) {
580 - if( isset( $content_actions['edit'] ) ) {
 645+ if( !$skin->mTitle->isTalkPage() && !$synced ) {
 646+ if( isset( $contentActions['edit'] ) ) {
581647 if( $this->showStableByDefault() )
582 - $content_actions['edit']['text'] = wfMsg('revreview-edit');
 648+ $contentActions['edit']['text'] = wfMsg('revreview-edit');
583649 # If the user is requesting the draft or some revision, they don't need a diff.
584650 if( $this->pageOverride() )
585 - $content_actions['edit']['href'] = $title->getLocalUrl( 'action=edit&showdiff=1' );
586 - } if( isset( $content_actions['viewsource'] ) ) {
 651+ $contentActions['edit']['href'] = $title->getLocalUrl( 'action=edit&showdiff=1' );
 652+ } if( isset( $contentActions['viewsource'] ) ) {
587653 if( $this->showStableByDefault() )
588 - $content_actions['viewsource']['text'] = wfMsg('revreview-source');
 654+ $contentActions['viewsource']['text'] = wfMsg('revreview-source');
589655 # If the user is requesting the draft or some revision, they don't need a diff.
590656 if( $this->pageOverride() )
591 - $content_actions['viewsource']['href'] = $title->getLocalUrl( 'action=edit&showdiff=1' );
 657+ $contentActions['viewsource']['href'] = $title->getLocalUrl( 'action=edit&showdiff=1' );
592658 }
593 - }
 659+ }
594660 # We can change the behavoir of stable version for this page to be different
595661 # than the site default.
596 - if( !$sktmp->mTitle->isTalkPage() && $wgUser->isAllowed('stablesettings') ) {
597 - $stabTitle = SpecialPage::getTitleFor( 'Stabilization' );
598 - if( !isset($content_actions['protect']) && !isset($content_actions['unprotect']) ) {
599 - $content_actions['default'] = array(
 662+ if( !$skin->mTitle->isTalkPage() && $wgUser->isAllowed('stablesettings') ) {
 663+ $stableTitle = SpecialPage::getTitleFor( 'Stabilization' );
 664+ if( !isset($contentActions['protect']) && !isset($contentActions['unprotect']) ) {
 665+ $contentActions['default'] = array(
600666 'class' => false,
601 - 'text' => wfmsg('stabilization-tab'),
602 - 'href' => $stabTitle->getLocalUrl('page='.$title->getPrefixedUrl())
 667+ 'text' => wfMsg('stabilization-tab'),
 668+ 'href' => $stableTitle->getLocalUrl('page='.$title->getPrefixedUrl())
603669 );
604670 }
605671 }
606672 // Add auxillary tabs...
607 - if( !$wgFlaggedRevTabs || $synced )
608 - return true;
 673+ if( !$wgFlaggedRevTabs || $synced )
 674+ return true;
609675 // We are looking at the stable version
610676 if( $this->pageOverride() || $wgRequest->getVal('stableid') ) {
611 - $DraftTabcss = '';
612 - $StableTabcss = 'selected';
 677+ $draftTabCss = '';
 678+ $stableTabCss = 'selected';
613679 // We are looking at the talk page or diffs/hist/oldids
614 - } else if( !in_array($action,array('view','purge','edit')) || $sktmp->mTitle->isTalkPage() ) {
615 - $DraftTabcss = '';
616 - $StableTabcss = '';
 680+ } else if( !in_array($action,array('view','purge','edit')) || $skin->mTitle->isTalkPage() ) {
 681+ $draftTabCss = '';
 682+ $stableTabCss = '';
617683 // We are looking at the current revision or in edit mode
618684 } else {
619 - $DraftTabcss = 'selected';
620 - $StableTabcss = '';
 685+ $draftTabCss = 'selected';
 686+ $stableTabCss = '';
621687 }
622 - $new_actions = array(); $first = true;
 688+ $newActions = array();
 689+ $first = true;
623690 # Straighten out order, set the tab AFTER the main tab is set
624 - foreach( $content_actions as $tab_action => $data ) {
 691+ foreach( $contentActions as $tabAction => $data ) {
625692 # Main page tab...
626693 if( $first ) {
627694 $first = false;
628695 if( $synced ) {
629 - $new_actions[$tab_action] = $data; // keep it
 696+ $newActions[$tabAction] = $data; // keep it
630697 } else {
631698 # Add new tabs after first one
632 - $new_actions['stable'] = array(
633 - 'class' => $StableTabcss,
 699+ $newActions['stable'] = array(
 700+ 'class' => $stableTabCss,
634701 'text' => wfMsg('revreview-stable'),
635702 'href' => $title->getLocalUrl( 'stable=1' )
636703 );
637 - $new_actions['current'] = array(
638 - 'class' => $DraftTabcss,
 704+ $newActions['current'] = array(
 705+ 'class' => $draftTabCss,
639706 'text' => wfMsg('revreview-current'),
640707 'href' => $title->getLocalUrl( 'stable=0' )
641708 );
642709 }
643710 # The others...
644711 } else {
645 - $new_actions[$tab_action] = $data;
 712+ $newActions[$tabAction] = $data;
646713 }
647 - }
648 - # Reset static array
649 - $content_actions = $new_actions;
650 - return true;
651 - }
 714+ }
 715+ # Reset static array
 716+ $contentActions = $newActions;
 717+ return true;
 718+ }
652719
653720 /**
654721 * Add link to stable version of reviewed revisions
655722 */
656 - public function addToHistLine( $row, &$s ) {
657 - global $wgUser;
 723+ public function addToHistLine( $history, $row, &$s ) {
 724+ global $wgUser;
658725 # Non-content pages cannot be validated. Stable version must exist.
659726 if( !$this->isReviewable() || !$this->getStableRev() )
660727 return true;
661 - # Use same DB object
662 - $this->dbr = isset($this->dbr) ? $this->dbr : wfGetDB( DB_SLAVE );
663728 $skin = $wgUser->getSkin();
664 - list($link,$css) = FlaggedRevs::makeStableVersionLink( $this->getTitle(), $row->rev_id, $skin, $this->dbr );
665 - if( $link ) {
666 - $s = "<span class='$css'>$s</span> <small><strong>[$link]</strong></small>";
 729+ list($link,$class) = FlaggedRevs::makeStableVersionLink(
 730+ $this->parent->getTitle(), $row->rev_id, $skin, wfGetDB( DB_SLAVE ) );
 731+ if( $link ) {
 732+ $s = "<span class='$class'>$s</span> <small><strong>[$link]</strong></small>";
667733 }
668734 return true;
669 - }
 735+ }
670736
671737 /**
672738 * Add link to stable version of reviewed revisions
673739 */
674 - public function addToFileHistLine( $file, &$s, &$css ) {
675 - global $wgUser;
 740+ public function addToFileHistLine( $historyList, $file, &$row, &$class ) {
 741+ global $wgUser;
676742 # Non-content pages cannot be validated. Stable version must exist.
677743 if( !$this->isReviewable() || !$this->getStableRev() )
678744 return true;
679 - # Use same DB object
680 - $this->dbr = isset($this->dbr) ? $this->dbr : wfGetDB( DB_SLAVE );
681 - $skin = $wgUser->getSkin();
682 - # See if this is reivewed
683 - $quality = $this->dbr->selectField( 'flaggedrevs', 'fr_quality',
 745+ # See if this is reviewed
 746+ # fixme: O(N) DB queries
 747+ $quality = wfGetDB(DB_SLAVE)->selectField( 'flaggedrevs', 'fr_quality',
684748 array( 'fr_img_sha1' => $file->getSha1(), 'fr_img_timestamp' => $file->getTimestamp() ),
685749 __METHOD__ );
686750 if( $quality === false ) {
687751 return true;
688752 }
689 - $css = FlaggedRevsXML::getQualityColor( $quality );
 753+ $class = FlaggedRevsXML::getQualityColor( $quality );
 754+
690755 return true;
691 - }
 756+ }
692757
693758 /**
694759 * @param FlaggedRevision $frev
695760 * @return string, revision review notes
696761 */
697 - public function ReviewNotes( $frev ) {
698 - global $wgUser;
699 - if( !FlaggedRevs::allowComments() || !$frev || !$frev->getComment() ) {
 762+ public function getReviewNotes( $frev ) {
 763+ global $wgUser;
 764+ if( !FlaggedRevs::allowComments() || !$frev || !$frev->getComment() ) {
700765 return '';
701766 }
702767 $notes = "<div class='flaggedrevs_notes plainlinks'>";
703768 $notes .= wfMsgExt('revreview-note', array('parseinline'), User::whoIs( $frev->getUser() ) );
704769 $notes .= '<br/><i>' . $wgUser->getSkin()->formatComment( $frev->getComment() ) . '</i></div>';
705 - return $notes;
706 - }
 770+ return $notes;
 771+ }
707772
708773 /**
709774 * When comparing the stable revision to the current after editing a page, show
710775 * a tag with some explaination for the diff.
711776 */
712 - public function addDiffNoticeAndIncludes( $diff, $OldRev, $NewRev ) {
 777+ public function addDiffNoticeAndIncludes( $diff, $oldRev, $newRev ) {
713778 global $wgRequest, $wgUser, $wgOut;
714779
715 - if( $wgOut->isPrintable() || !FlaggedRevs::isPageReviewable( $NewRev->getTitle() ) )
 780+ if( $wgOut->isPrintable() || !FlaggedRevs::isPageReviewable( $newRev->getTitle() ) )
716781 return true;
717782 # Check if this might be the diff to stable. If so, enhance it.
718 - if( $NewRev->isCurrent() && $OldRev ) {
 783+ if( $newRev->isCurrent() && $oldRev ) {
719784 $frev = $this->getStableRev();
720 - if( $frev && $frev->getRevId() == $OldRev->getID() ) {
 785+ if( $frev && $frev->getRevId() == $oldRev->getID() ) {
721786 $changeList = array();
722787 $skin = $wgUser->getSkin();
723788
@@ -799,34 +864,34 @@
800865 }
801866 # Set flag for review form to tell it to autoselect tag settings from the
802867 # old revision unless the current one is tagged to.
803 - if( !FlaggedRevs::getFlaggedRev( $diff->mTitle, $NewRev->getID() ) ) {
 868+ if( !FlaggedRevs::getFlaggedRev( $diff->mTitle, $newRev->getID() ) ) {
804869 $this->isDiffFromStable = true;
805870 }
806871 }
807872 }
808 - $newRevQ = FlaggedRevs::getRevQuality( $NewRev->getTitle(), $NewRev->getId() );
809 - $oldRevQ = $OldRev ? FlaggedRevs::getRevQuality( $NewRev->getTitle(), $OldRev->getId() ) : false;
 873+ $newRevQ = FlaggedRevs::getRevQuality( $newRev->getTitle(), $newRev->getId() );
 874+ $oldRevQ = $oldRev ? FlaggedRevs::getRevQuality( $newRev->getTitle(), $oldRev->getId() ) : false;
810875 # Diff between two revisions
811 - if( $OldRev ) {
 876+ if( $oldRev ) {
812877 $wgOut->addHTML( "<table class='fr-diff-ratings' width='100%'><tr>" );
813878
814 - $css = FlaggedRevsXML::getQualityColor( $oldRevQ );
 879+ $class = FlaggedRevsXML::getQualityColor( $oldRevQ );
815880 if( $oldRevQ !== false ) {
816881 $msg = $oldRevQ ? 'hist-quality' : 'hist-stable';
817882 } else {
818883 $msg = 'hist-draft';
819884 }
820885 $wgOut->addHTML( "<td width='50%' align='center'>" );
821 - $wgOut->addHTML( "<span class='$css'><b>[" . wfMsgHtml( $msg ) . "]</b></span>" );
 886+ $wgOut->addHTML( "<span class='$class'><b>[" . wfMsgHtml( $msg ) . "]</b></span>" );
822887
823 - $css = FlaggedRevsXML::getQualityColor( $newRevQ );
 888+ $class = FlaggedRevsXML::getQualityColor( $newRevQ );
824889 if( $newRevQ !== false ) {
825890 $msg = $newRevQ ? 'hist-quality' : 'hist-stable';
826891 } else {
827892 $msg = 'hist-draft';
828893 }
829894 $wgOut->addHTML( "</td><td width='50%' align='center'>" );
830 - $wgOut->addHTML( "<span class='$css'><b>[" . wfMsgHtml( $msg ) . "]</b></span>" );
 895+ $wgOut->addHTML( "<span class='$class'><b>[" . wfMsgHtml( $msg ) . "]</b></span>" );
831896
832897 $wgOut->addHTML( '</td></tr></table>' );
833898 # New page "diffs" - just one rev
@@ -848,29 +913,29 @@
849914 * Add a link to patrol non-reviewable pages.
850915 * Also add a diff to stable for other pages if possible.
851916 */
852 - public function addPatrolAndDiffLink( $diff, $OldRev, $NewRev ) {
 917+ public function addPatrolAndDiffLink( $diff, $oldRev, $newRev ) {
853918 global $wgUser, $wgOut;
854919 // Is there a stable version?
855 - if( FlaggedRevs::isPageReviewable( $NewRev->getTitle() ) ) {
856 - if( !$OldRev ) {
 920+ if( FlaggedRevs::isPageReviewable( $newRev->getTitle() ) ) {
 921+ if( !$oldRev ) {
857922 return true;
858923 }
859924 $frev = $this->getStableRev();
860 - if( $frev && $frev->getRevId() == $OldRev->getID() && $NewRev->isCurrent() ) {
 925+ if( $frev && $frev->getRevId() == $oldRev->getID() && $newRev->isCurrent() ) {
861926 $this->isDiffFromStable = true;
862927 }
863928 # Give a link to the diff-to-stable if needed
864929 if( $frev && !$this->isDiffFromStable ) {
865 - $article = new Article( $NewRev->getTitle() );
 930+ $article = new Article( $newRev->getTitle() );
866931 # Is the stable revision using the same revision as the current?
867932 if( $article->getLatest() != $frev->getRevId() ) {
868 - $patrol = '(' . $wgUser->getSkin()->makeKnownLinkObj( $NewRev->getTitle(),
 933+ $patrol = '(' . $wgUser->getSkin()->makeKnownLinkObj( $newRev->getTitle(),
869934 wfMsgHtml( 'review-diff2stable' ), "oldid={$frev->getRevId()}&diff=cur" ) . ')';
870935 $wgOut->addHTML( "<div class='fr-diff-to-stable' align='center'>$patrol</div>" );
871936 }
872937 }
873938 // Prepare a change patrol link, if applicable
874 - } else if( FlaggedRevs::isPagePatrollable( $NewRev->getTitle() ) && $wgUser->isAllowed( 'review' ) ) {
 939+ } else if( FlaggedRevs::isPagePatrollable( $newRev->getTitle() ) && $wgUser->isAllowed( 'review' ) ) {
875940 // If we've been given an explicit change identifier, use it; saves time
876941 if( $diff->mRcidMarkPatrolled ) {
877942 $rcid = $diff->mRcidMarkPatrolled;
@@ -896,10 +961,10 @@
897962 }
898963 // Build the link
899964 if( $rcid ) {
900 - $reviewtitle = SpecialPage::getTitleFor( 'RevisionReview' );
901 - $patrol = '[' . $wgUser->getSkin()->makeKnownLinkObj( $reviewtitle, wfMsgHtml( 'revreview-patrol' ),
902 - "patrolonly=1&target=" . $NewRev->getTitle()->getPrefixedUrl() . "&rcid={$rcid}" .
903 - "&token=" . urlencode( $wgUser->editToken( $NewRev->getTitle()->getPrefixedText(), $rcid ) ) ) . ']';
 965+ $reviewTitle = SpecialPage::getTitleFor( 'RevisionReview' );
 966+ $patrol = '[' . $wgUser->getSkin()->makeKnownLinkObj( $reviewTitle, wfMsgHtml( 'revreview-patrol' ),
 967+ "patrolonly=1&target=" . $newRev->getTitle()->getPrefixedUrl() . "&rcid={$rcid}" .
 968+ "&token=" . urlencode( $wgUser->editToken( $newRev->getTitle()->getPrefixedText(), $rcid ) ) ) . ']';
904969 } else {
905970 $patrol = '';
906971 }
@@ -912,26 +977,26 @@
913978 * Redirect users out to review the changes to the stable version.
914979 * Only for people who can review and for pages that have a stable version.
915980 */
916 - public function injectReviewDiffURLParams( $article, &$sectionanchor, &$extraq ) {
917 - global $wgUser, $wgReviewChangesAfterEdit;
 981+ public function injectReviewDiffURLParams( &$sectionAnchor, &$extraQuery ) {
 982+ global $wgUser, $wgReviewChangesAfterEdit;
918983 # Don't show this for the talk page
919 - if( !$this->isReviewable() || $article->getTitle()->isTalkPage() )
 984+ if( !$this->isReviewable() || $this->parent->getTitle()->isTalkPage() )
920985 return true;
921986 # Get the stable version, from master
922 - $frev = $this->getStableRev( false, true );
 987+ $frev = $this->getStableRev( false, true );
923988 if( !$frev )
924989 return true;
925 - $latest = $article->getTitle()->getLatestRevID(GAID_FOR_UPDATE);
 990+ $latest = $this->parent->getTitle()->getLatestRevID(GAID_FOR_UPDATE);
926991 // If we are supposed to review after edit, and it was not autoreviewed,
927992 // and the user can actually make new stable version, take us to the diff...
928993 if( $wgReviewChangesAfterEdit && $frev && $latest > $frev->getRevId() && $frev->userCanSetFlags() ) {
929 - $extraq .= "oldid={$frev->getRevId()}&diff=cur";
 994+ $extraQuery .= "oldid={$frev->getRevId()}&diff=cur";
930995 // ...otherwise, go to the current revision after completing an edit.
931996 } else {
932997 if( $frev ){
933 - $extraq .= "stable=0";
 998+ $extraQuery .= "stable=0";
934999 if( !$wgUser->isAllowed('review') && $this->showStableByDefault() ) {
935 - $extraq .= "&shownotice=1";
 1000+ $extraQuery .= "&shownotice=1";
9361001 }
9371002 }
9381003 }
@@ -942,24 +1007,24 @@
9431008 * Add a hidden revision ID field to edit form.
9441009 * Needed for autoreview so it can select the flags from said revision.
9451010 */
946 - public function addRevisionIDField( $editform, $out ) {
 1011+ public function addRevisionIDField( $editPage, $out ) {
9471012 global $wgRequest;
9481013 # Find out revision id
949 - if( $editform->mArticle->mRevision ) {
950 - $revid = $editform->mArticle->mRevision->mId;
 1014+ if( $this->parent->mRevision ) {
 1015+ $revId = $this->parent->mRevision->mId;
9511016 } else {
952 - $latest = $editform->mTitle->getLatestRevID(GAID_FOR_UPDATE);
953 - $revid = $latest;
954 - }
 1017+ $latest = $this->parent->getTitle()->getLatestRevID(GAID_FOR_UPDATE);
 1018+ $revId = $latest;
 1019+ }
9551020 # If undoing a few consecutive top edits, we know the base ID
9561021 if( $undo = $wgRequest->getIntOrNull('undo') ) {
957 - $undoafter = $wgRequest->getIntOrNull('undoafter');
958 - $latest = isset($latest) ? $latest : $editform->mTitle->getLatestRevID(GAID_FOR_UPDATE);
959 - if( $undoafter && $undo == $editform->mArticle->getLatest() ) {
960 - $revid = $undoafter;
 1022+ $undoAfter = $wgRequest->getIntOrNull('undoAfter');
 1023+ $latest = isset($latest) ? $latest : $this->parent->getTitle()->getLatestRevID(GAID_FOR_UPDATE);
 1024+ if( $undoAfter && $undo == $this->parent->getLatest() ) {
 1025+ $revId = $undoAfter;
9611026 }
9621027 }
963 - $out->addHTML( "\n" . Xml::hidden( 'baseRevId', $revid ) );
 1028+ $out->addHTML( "\n" . Xml::hidden( 'baseRevId', $revId ) );
9641029 return true;
9651030 }
9661031
@@ -973,54 +1038,54 @@
9741039 if( $this->stableRev === false ) {
9751040 return null; // We already looked and found nothing...
9761041 }
977 - # Cached results available?
978 - if( !is_null($this->stableRev) ) {
 1042+ # Cached results available?
 1043+ if( !is_null($this->stableRev) ) {
9791044 return $this->stableRev;
9801045 }
9811046 # Get the content page, skip talk
982 - $title = $this->getTitle()->getSubjectPage();
 1047+ $title = $this->parent->getTitle()->getSubjectPage();
9831048 # Do we have one?
9841049 $srev = FlaggedRevs::getStablePageRev( $title, $getText, $forUpdate );
985 - if( $srev ) {
 1050+ if( $srev ) {
9861051 $this->stableRev = $srev;
9871052 return $srev;
988 - } else {
989 - $this->stableRev = false;
990 - return null;
991 - }
 1053+ } else {
 1054+ $this->stableRev = false;
 1055+ return null;
 1056+ }
9921057 }
9931058
994 - /**
 1059+ /**
9951060 * Get visiblity restrictions on page
9961061 * @param Bool $forUpdate, use DB master?
9971062 * @returns Array (select,override)
9981063 */
999 - public function getVisibilitySettings( $forUpdate=false ) {
1000 - # Cached results available?
1001 - if( !is_null($this->pageconfig) ) {
1002 - return $this->pageconfig;
 1064+ public function getVisibilitySettings( $forUpdate=false ) {
 1065+ # Cached results available?
 1066+ if( !is_null($this->pageConfig) ) {
 1067+ return $this->pageConfig;
10031068 }
10041069 # Get the content page, skip talk
1005 - $title = $this->getTitle()->getSubjectPage();
 1070+ $title = $this->parent->getTitle()->getSubjectPage();
10061071
10071072 $config = FlaggedRevs::getPageVisibilitySettings( $title, $forUpdate );
1008 - $this->pageconfig = $config;
 1073+ $this->pageConfig = $config;
10091074
10101075 return $config;
10111076 }
10121077
10131078 /**
1014 - * @param int $rev_id
 1079+ * @param int $revId
10151080 * @eturns Array, output of the flags for a given revision
10161081 */
1017 - public function getFlagsForRevision( $rev_id ) {
1018 - # Cached results?
1019 - if( isset($this->flags[$rev_id]) && $this->flags[$rev_id] )
1020 - return $this->flags[$rev_id];
1021 - # Get the flags
1022 - $flags = FlaggedRevs::getRevisionTags( $this->getTitle(), $rev_id );
 1082+ public function getFlagsForRevision( $revId ) {
 1083+ # Cached results?
 1084+ if( isset($this->flags[$revId]) && $this->flags[$revId] )
 1085+ return $this->flags[$revId];
 1086+ # Get the flags
 1087+ $flags = FlaggedRevs::getRevisionTags( $this->parent->getTitle(), $revId );
10231088 # Try to cache results
1024 - $this->flags[$rev_id] = $flags;
 1089+ $this->flags[$revId] = $flags;
10251090
10261091 return $flags;
10271092 }
@@ -1051,7 +1116,7 @@
10521117 # Variable for sites with no flags, otherwise discarded
10531118 $approve = $wgRequest->getBool('wpApprove');
10541119 # See if the version being displayed is flagged...
1055 - $oldflags = $this->getFlagsForRevision( $id );
 1120+ $oldFlags = $this->getFlagsForRevision( $id );
10561121 # If we are reviewing updates to a page, start off with the stable revision's
10571122 # flags. Otherwise, we just fill them in with the selected revision's flags.
10581123 if( $this->isDiffFromStable ) {
@@ -1059,15 +1124,15 @@
10601125 $flags = $srev->getTags();
10611126 # Check if user is allowed to renew the stable version.
10621127 # If not, then get the flags for the new revision itself.
1063 - if( !RevisionReview::userCanSetFlags( $oldflags ) ) {
1064 - $flags = $oldflags;
 1128+ if( !RevisionReview::userCanSetFlags( $oldFlags ) ) {
 1129+ $flags = $oldFlags;
10651130 }
10661131 } else {
10671132 $flags = $this->getFlagsForRevision( $id );
10681133 }
10691134
1070 - $reviewtitle = SpecialPage::getTitleFor( 'RevisionReview' );
1071 - $action = $reviewtitle->getLocalUrl( 'action=submit' );
 1135+ $reviewTitle = SpecialPage::getTitleFor( 'RevisionReview' );
 1136+ $action = $reviewTitle->getLocalUrl( 'action=submit' );
10721137 $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action ) );
10731138 $form .= Xml::openElement( 'fieldset', array('class' => 'flaggedrevs_reviewform') );
10741139 $form .= "<legend>" . wfMsgHtml( 'revreview-flag', $id ) . "</legend>\n";
@@ -1156,9 +1221,9 @@
11571222 $imageParams = $templateParams = '';
11581223 # Hack, add NS:title -> rev ID mapping
11591224 foreach( $out->mTemplateIds as $namespace => $title ) {
1160 - foreach( $title as $dbkey => $revid ) {
 1225+ foreach( $title as $dbkey => $revId ) {
11611226 $title = Title::makeTitle( $namespace, $dbkey );
1162 - $templateParams .= $title->getPrefixedText() . "|" . $revid . "#";
 1227+ $templateParams .= $title->getPrefixedText() . "|" . $revId . "#";
11631228 }
11641229 }
11651230 # Hack, image -> timestamp mapping
@@ -1168,8 +1233,9 @@
11691234 }
11701235 }
11711236 # For image pages, note the current image version
1172 - if( $this->getTitle()->getNamespace() == NS_IMAGE && $this->file ) {
1173 - $imageParams .= $this->getTitle()->getDBkey() . "|" . $this->file->getTimestamp() . "|" . $this->file->getSha1() . "#";
 1237+ if( $this->parent instanceof ImagePage ) {
 1238+ $file = $this->parent->getDisplayedFile();
 1239+ $imageParams .= $file->getName() . "|" . $file->getTimestamp() . "|" . $file->getSha1() . "#";
11741240 }
11751241
11761242 $form .= Xml::openElement( 'span', array('style' => 'white-space: nowrap;') );
@@ -1185,8 +1251,8 @@
11861252 $form .= Xml::closeElement( 'div' );
11871253
11881254 # Hidden params
1189 - $form .= Xml::hidden( 'title', $reviewtitle->getPrefixedText() ) . "\n";
1190 - $form .= Xml::hidden( 'target', $this->getTitle()->getPrefixedText() ) . "\n";
 1255+ $form .= Xml::hidden( 'title', $reviewTitle->getPrefixedText() ) . "\n";
 1256+ $form .= Xml::hidden( 'target', $this->parent->getTitle()->getPrefixedText() ) . "\n";
11911257 $form .= Xml::hidden( 'oldid', $id ) . "\n";
11921258 $form .= Xml::hidden( 'action', 'submit') . "\n";
11931259 $form .= Xml::hidden( 'wpEditToken', $wgUser->editToken() ) . "\n";
@@ -1207,13 +1273,13 @@
12081274 } else {
12091275 $wgOut->addHTML( $form );
12101276 }
1211 - }
 1277+ }
12121278
12131279 /**
12141280 * Set permalink to stable version if we are viewing a stable version.
12151281 * Also sets the citation link if that extension is on.
12161282 */
1217 - public function setPermaLink( $sktmp, &$nav_urls, &$revid, &$id ) {
 1283+ public function setPermaLink( $skin, &$navUrls, &$revId, &$id ) {
12181284 # Non-content pages cannot be validated
12191285 if( !$this->pageOverride() )
12201286 return true;
@@ -1222,28 +1288,28 @@
12231289 if( !$frev )
12241290 return true;
12251291 # Replace "permalink" with an actual permanent link
1226 - $nav_urls['permalink'] = array(
 1292+ $navUrls['permalink'] = array(
12271293 'text' => wfMsg( 'permalink' ),
1228 - 'href' => $this->getTitle()->getFullURL( "stableid={$frev->getRevId()}" )
 1294+ 'href' => $this->parent->getTitle()->getFullURL( "stableid={$frev->getRevId()}" )
12291295 );
12301296 # Are we using the popular cite extension?
12311297 global $wgHooks;
12321298 if( in_array('wfSpecialCiteNav',$wgHooks['SkinTemplateBuildNavUrlsNav_urlsAfterPermalink']) ) {
1233 - if( $this->isReviewable() && $revid !== 0 ) {
1234 - $nav_urls['cite'] = array(
 1299+ if( $this->isReviewable() && $revId !== 0 ) {
 1300+ $navUrls['cite'] = array(
12351301 'text' => wfMsg( 'cite_article_link' ),
1236 - 'href' => $sktmp->makeSpecialUrl( 'Cite', "page=" . wfUrlencode( "{$sktmp->thispage}" ) . "&id={$frev->getRevId()}" )
 1302+ 'href' => $skin->makeSpecialUrl( 'Cite', "page=" . wfUrlencode( "{$skin->thispage}" ) . "&id={$frev->getRevId()}" )
12371303 );
12381304 }
12391305 }
12401306 return true;
1241 - }
 1307+ }
12421308
12431309 /**
12441310 * If viewing a stable version, adjust the last modified header
12451311 */
1246 - public function setLastModified( $sktmp, &$tpl ) {
1247 - global $wgArticle, $wgLang, $wgRequest;
 1312+ public function setLastModified( $skin, &$tpl ) {
 1313+ global $wgLang, $wgRequest;
12481314 # Non-content pages cannot be validated
12491315 if( !$this->isReviewable() )
12501316 return true;
@@ -1256,7 +1322,7 @@
12571323 if( !$this->pageOverride() )
12581324 return true;
12591325 $frev = $this->getStableRev( true );
1260 - if( !$frev || $frev->getRevId() == $wgArticle->getLatest() )
 1326+ if( !$frev || $frev->getRevId() == $this->parent->getLatest() )
12611327 return true;
12621328 # Get the timestamp of this revision
12631329 $timestamp = $frev->getRevTimestamp();
@@ -1278,23 +1344,23 @@
12791345 /**
12801346 * Updates parser cache output to included needed versioning params.
12811347 */
1282 - public function maybeUpdateMainCache( $article, &$outputDone, &$pcache ) {
 1348+ public function maybeUpdateMainCache( &$outputDone, &$pcache ) {
12831349 global $wgUser, $wgRequest;
12841350
12851351 $action = $wgRequest->getVal( 'action', 'view' );
12861352 # Only trigger on article view for content pages, not for protect/delete/hist
12871353 if( ($action !='view' && $action !='purge') || !$wgUser->isAllowed( 'review' ) )
12881354 return true;
1289 - if( !$article || !$article->exists() || !FlaggedRevs::isPageReviewable( $article->getTitle() ) )
 1355+ if( !$this->parent->exists() || !FlaggedRevs::isPageReviewable( $this->parent->getTitle() ) )
12901356 return true;
12911357
12921358 $parserCache = ParserCache::singleton();
1293 - $parserOut = $parserCache->get( $article, $wgUser );
 1359+ $parserOut = $parserCache->get( $this->parent, $wgUser );
12941360 if( $parserOut ) {
12951361 # Clear older, incomplete, cached versions
12961362 # We need the IDs of templates and timestamps of images used
12971363 if( !isset($parserOut->fr_newestTemplateID) || !isset($parserOut->fr_newestImageTime) )
1298 - $article->getTitle()->invalidateCache();
 1364+ $this->parent->getTitle()->invalidateCache();
12991365 }
13001366 return true;
13011367 }
Index: trunk/extensions/FlaggedRevs/FlaggedRevs.class.php
@@ -0,0 +1,2000 @@
 2+<?php
 3+
 4+class FlaggedRevs {
 5+ public static $dimensions = array();
 6+ public static $articleLoaded = false;
 7+ protected static $loaded = false;
 8+ protected static $qualityVersions = false;
 9+ protected static $pristineVersions = false;
 10+ protected static $extStorage = false;
 11+ protected static $allowComments = false;
 12+
 13+ public static function load() {
 14+ global $wgFlaggedRevTags, $wgFlaggedRevValues, $wgFlaggedRevsComments;
 15+ if( self::$loaded ) {
 16+ return true;
 17+ }
 18+ # Assume true, then set to false if needed
 19+ if( !empty($wgFlaggedRevTags) ) {
 20+ self::$qualityVersions = true;
 21+ }
 22+ foreach( $wgFlaggedRevTags as $tag => $minQL ) {
 23+ $safeTag = htmlspecialchars($tag);
 24+ if( strpos($tag,':') || strpos($tag,'\n') || $safeTag !== $tag ) {
 25+ throw new MWException( 'FlaggedRevs given invalid tag name!' );
 26+ } else if( intval($minQL) != $minQL ) {
 27+ throw new MWException( 'FlaggedRevs given invalid tag value!' );
 28+ }
 29+ self::$dimensions[$tag] = array();
 30+ for( $i=0; $i <= $wgFlaggedRevValues; $i++ ) {
 31+ self::$dimensions[$tag][$i] = "{$tag}-{$i}";
 32+ }
 33+ if( $minQL > $wgFlaggedRevValues ) {
 34+ self::$qualityVersions = false;
 35+ }
 36+ }
 37+ global $wgFlaggedRevPristine;
 38+ if( $wgFlaggedRevValues >= $wgFlaggedRevPristine ) {
 39+ self::$pristineVersions = true;
 40+ }
 41+ global $wgFlaggedRevsExternalStore, $wgDefaultExternalStore;
 42+ self::$extStorage = $wgFlaggedRevsExternalStore ?
 43+ $wgFlaggedRevsExternalStore : $wgDefaultExternalStore;
 44+
 45+ self::$allowComments = (bool)$wgFlaggedRevsComments;
 46+
 47+ self::$loaded = true;
 48+ }
 49+
 50+ ################# Basic accessors #################
 51+
 52+ /**
 53+ * Are quality versions enabled?
 54+ */
 55+ public static function qualityVersions() {
 56+ self::load();
 57+ return self::$qualityVersions;
 58+ }
 59+
 60+ /**
 61+ * Are pristine versions enabled?
 62+ */
 63+ public static function pristineVersions() {
 64+ self::load();
 65+ return self::$pristineVersions;
 66+ }
 67+
 68+ /**
 69+ * Get external storage array. Default to main storage.
 70+ */
 71+ public static function getExternalStorage() {
 72+ self::load();
 73+ return self::$extStorage;
 74+ }
 75+
 76+ /**
 77+ * Should this be using a simple icon-based UI?
 78+ * Check the user's preferences first, using the site settings as the default.
 79+ */
 80+ public static function useSimpleUI() {
 81+ global $wgUser, $wgSimpleFlaggedRevsUI;
 82+
 83+ return $wgUser->getOption( 'flaggedrevssimpleui', intval($wgSimpleFlaggedRevsUI) );
 84+ }
 85+
 86+ /**
 87+ * Should comments be allowed on pages and forms?
 88+ */
 89+ public static function allowComments() {
 90+ self::load();
 91+ return self::$allowComments;
 92+ }
 93+
 94+ ################# Parsing functions #################
 95+
 96+ /**
 97+ * @param string $text
 98+ * @param Title $title
 99+ * @param integer $id, revision id
 100+ * @return array( string, array, array, bool, int )
 101+ * All included pages/arguments are expanded out
 102+ */
 103+ public static function expandText( $text='', $title, $id ) {
 104+ global $wgParser;
 105+ # Make our hooks to trigger
 106+ $wgParser->fr_isStable = true;
 107+ $wgParser->fr_includesMatched = true;
 108+ # Parse with default options
 109+ $options = new ParserOptions();
 110+ $options->setRemoveComments( true ); // Save some bandwidth ;)
 111+ $outputText = $wgParser->preprocess( $text, $title, $options, $id );
 112+ $expandedText = array( $outputText, $wgParser->mOutput->mTemplates, $wgParser->mOutput->mTemplateIds,
 113+ $wgParser->fr_includesMatched, $wgParser->mOutput->fr_newestTemplateID );
 114+ # Done with parser!
 115+ $wgParser->fr_isStable = false;
 116+ $wgParser->fr_includesMatched = false;
 117+ # Return data array
 118+ return $expandedText;
 119+ }
 120+
 121+ /**
 122+ * Get the HTML output of a revision based on $text.
 123+ * If the text is being reparsed from fr_text (expanded text),
 124+ * it should be specified...In such cases, the parser will not have
 125+ * template ID data. We need to know this so we can just get the data from the DB.
 126+ * @param Article $article
 127+ * @param string $text
 128+ * @param int $id
 129+ * @param bool $reparsed (is this being reparsed from fr_text?)
 130+ * @return ParserOutput
 131+ */
 132+ public static function parseStableText( $article, $text='', $id, $reparsed = true ) {
 133+ global $wgParser, $wgUseStableTemplates;
 134+ $title = $article->getTitle(); // avoid pass-by-reference error
 135+ # Make our hooks to trigger
 136+ $wgParser->fr_isStable = true;
 137+ $wgParser->fr_includesMatched = true;
 138+ # Don't show section-edit links, they can be old and misleading
 139+ $options = self::makeParserOptions();
 140+ #$options->setEditSection( $id == $title->getLatestRevID(GAID_FOR_UPDATE) );
 141+ # Parse the new body, wikitext -> html
 142+ $parserOut = $wgParser->parse( $text, $title, $options, true, true, $id );
 143+ $parserOut->fr_includesMatched = $wgParser->fr_includesMatched;
 144+ # Done with parser!
 145+ $wgParser->fr_isStable = false;
 146+ $wgParser->fr_includesMatched = false;
 147+ # Do we need to set the template uses via DB?
 148+ if( $reparsed && !$wgUseStableTemplates ) {
 149+ $dbr = wfGetDB( DB_SLAVE );
 150+ $res = $dbr->select( array('flaggedtemplates','revision'),
 151+ array( 'ft_namespace', 'ft_title', 'ft_tmp_rev_id AS rev_id', 'rev_page AS page_id' ),
 152+ array( 'ft_rev_id' => $id, 'rev_id = ft_rev_id' ),
 153+ __METHOD__ );
 154+ # Add template metadata to output
 155+ $maxTempID = 0;
 156+ while( $row = $res->fetchObject() ) {
 157+ if( !isset($parserOut->mTemplates[$row->ft_namespace]) ) {
 158+ $parserOut->mTemplates[$row->ft_namespace] = array();
 159+ }
 160+ $parserOut->mTemplates[$row->ft_namespace][$row->ft_title] = $row->page_id;
 161+
 162+ if( !isset($parserOut->mTemplateIds[$row->ft_namespace]) ) {
 163+ $parserOut->mTemplateIds[$row->ft_namespace] = array();
 164+ }
 165+ $parserOut->mTemplateIds[$row->ft_namespace][$row->ft_title] = $row->rev_id;
 166+ if( $row->rev_id > $maxTempID ) {
 167+ $maxTempID = $row->rev_id;
 168+ }
 169+ }
 170+ $parserOut->fr_newestTemplateID = $maxTempID;
 171+ }
 172+ return $parserOut;
 173+ }
 174+
 175+ /**
 176+ * Get standard parser options
 177+ */
 178+ public static function makeParserOptions( $user = NULL ) {
 179+ $options = $user ? ParserOptions::newFromUser( $user ) : new ParserOptions();
 180+ # Show inclusion/loop reports
 181+ $options->enableLimitReport();
 182+ # Fix bad HTML
 183+ $options->setTidy( true );
 184+ return $options;
 185+ }
 186+
 187+ /**
 188+ * @param Article $article
 189+ * @return ParserOutput
 190+ * Get the page cache for the top stable revision of an article
 191+ */
 192+ public static function getPageCache( $article ) {
 193+ global $wgUser, $parserMemc, $wgCacheEpoch;
 194+
 195+ wfProfileIn( __METHOD__ );
 196+ # Make sure it is valid
 197+ if( !$article->getId() )
 198+ return null;
 199+
 200+ $parserCache = ParserCache::singleton();
 201+ $key = self::getCacheKey( $parserCache, $article, $wgUser );
 202+ # Get the cached HTML
 203+ wfDebug( "Trying parser cache $key\n" );
 204+ $value = $parserMemc->get( $key );
 205+ if( is_object( $value ) ) {
 206+ wfDebug( "Found.\n" );
 207+ # Delete if article has changed since the cache was made
 208+ $canCache = $article->checkTouched();
 209+ $cacheTime = $value->getCacheTime();
 210+ $touched = $article->mTouched;
 211+ if( !$canCache || $value->expired( $touched ) ) {
 212+ if( !$canCache ) {
 213+ wfIncrStats( "pcache_miss_invalid" );
 214+ wfDebug( "Invalid cached redirect, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" );
 215+ } else {
 216+ wfIncrStats( "pcache_miss_expired" );
 217+ wfDebug( "Key expired, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" );
 218+ }
 219+ $parserMemc->delete( $key );
 220+ $value = false;
 221+ } else {
 222+ if( isset( $value->mTimestamp ) ) {
 223+ $article->mTimestamp = $value->mTimestamp;
 224+ }
 225+ wfIncrStats( "pcache_hit" );
 226+ }
 227+ } else {
 228+ wfDebug( "Parser cache miss.\n" );
 229+ wfIncrStats( "pcache_miss_absent" );
 230+ $value = false;
 231+ }
 232+
 233+ wfProfileOut( __METHOD__ );
 234+
 235+ return $value;
 236+ }
 237+
 238+ /**
 239+ * Like ParserCache::getKey() with stable-pcache instead of pcache
 240+ */
 241+ public static function getCacheKey( $parserCache, $article, &$user ) {
 242+ $key = $parserCache->getKey( $article, $user );
 243+ $key = str_replace( ':pcache:', ':stable-pcache:', $key );
 244+ return $key;
 245+ }
 246+
 247+ /**
 248+ * @param Article $article
 249+ * @param parerOutput $parserOut
 250+ * Updates the stable cache of a page with the given $parserOut
 251+ */
 252+ public static function updatePageCache( $article, $parserOut=null ) {
 253+ global $wgUser, $parserMemc, $wgParserCacheExpireTime;
 254+ # Make sure it is valid
 255+ if( is_null($parserOut) )
 256+ return false;
 257+
 258+ $parserCache = ParserCache::singleton();
 259+ $key = self::getCacheKey( $parserCache, $article, $wgUser );
 260+ # Add cache mark to HTML
 261+ $now = wfTimestampNow();
 262+ $parserOut->setCacheTime( $now );
 263+ # Save the timestamp so that we don't have to load the revision row on view
 264+ $parserOut->mTimestamp = $article->getTimestamp();
 265+ $parserOut->mText .= "\n<!-- Saved in stable version parser cache with key $key and timestamp $now -->";
 266+ # Set expire time
 267+ if( $parserOut->containsOldMagic() ){
 268+ $expire = 3600; // 1 hour
 269+ } else {
 270+ $expire = $wgParserCacheExpireTime;
 271+ }
 272+ # Save to objectcache
 273+ $parserMemc->set( $key, $parserOut, $expire );
 274+
 275+ return true;
 276+ }
 277+
 278+ ################# Synchronization and link update functions #################
 279+
 280+ /**
 281+ * @param FlaggedRevision $frev
 282+ * @param Article $article
 283+ * @param ParserOutput $stableOutput, will fetch if not given
 284+ * @param ParserOutput $currentOutput, will fetch if not given
 285+ * @return bool
 286+ * See if a flagged revision is synced with the current
 287+ */
 288+ public static function flaggedRevIsSynced( $frev, $article, $stableOutput=null, $currentOutput=null ) {
 289+ # Must be the same revision
 290+ if( $frev->getRevId() != $article->getTitle()->getLatestRevID(GAID_FOR_UPDATE) ) {
 291+ return false;
 292+ }
 293+ # Must have same file
 294+ if( $article instanceof ImagePage && $article->getFile() ) {
 295+ if( $frev->getFileTimestamp() != $article->getFile()->getTimestamp() ) {
 296+ return false;
 297+ }
 298+ }
 299+ global $wgMemc;
 300+ # Try the cache. Uses format <page ID>-<UNIX timestamp>.
 301+ $key = wfMemcKey( 'flaggedrevs', 'syncStatus', $article->getId(), $article->getTouched() );
 302+ $syncvalue = $wgMemc->get($key);
 303+ # Convert string value to boolean and return it
 304+ if( $syncvalue ) {
 305+ if( $syncvalue == "true" ) {
 306+ return true;
 307+ } else if( $syncvalue == "false" ) {
 308+ return false;
 309+ }
 310+ }
 311+ # If parseroutputs not given, fetch them...
 312+ if( is_null($stableOutput) || !isset($stableOutput->fr_newestTemplateID) ) {
 313+ # Get parsed stable version
 314+ $stableOutput = self::getPageCache( $article );
 315+ if( $stableOutput==false ) {
 316+ $text = $frev->getTextForParse();
 317+ $stableOutput = self::parseStableText( $article, $text, $frev->getRevId() );
 318+ # Update the stable version cache
 319+ self::updatePageCache( $article, $stableOutput );
 320+ }
 321+ }
 322+ if( is_null($currentOutput) || !isset($currentOutput->fr_newestTemplateID) ) {
 323+ global $wgUser, $wgParser;
 324+ # Get parsed current version
 325+ $parserCache = ParserCache::singleton();
 326+ $currentOutput = $parserCache->get( $article, $wgUser );
 327+ if( $currentOutput==false ) {
 328+ $text = $article->getContent();
 329+ $title = $article->getTitle();
 330+ $options = self::makeParserOptions( $wgUser );
 331+ $currentOutput = $wgParser->parse( $text, $title, $options );
 332+ # Might as well save the cache while we're at it
 333+ global $wgEnableParserCache;
 334+ if( $wgEnableParserCache )
 335+ $parserCache->save( $currentOutput, $article, $wgUser );
 336+ }
 337+ }
 338+ # Only current of revisions of inclusions can be reviewed. Since the stable and current revisions
 339+ # have the same text, the only thing that can make them different is updating a template or image.
 340+ # If this is the case, the current revision will have a newer template or image version used somewhere.
 341+ if( $currentOutput->fr_newestImageTime > $stableOutput->fr_newestImageTime ) {
 342+ $synced = false;
 343+ } else if( $currentOutput->fr_newestTemplateID > $stableOutput->fr_newestTemplateID ) {
 344+ $synced = false;
 345+ } else {
 346+ $synced = true;
 347+ }
 348+ # Save to cache. This will be updated whenever the page is re-parsed as well. This means
 349+ # that MW can check a light-weight key first. Uses format <page ID>-<UNIX timestamp>.
 350+ global $wgParserCacheExpireTime;
 351+ $syncData = $synced ? "true" : "false";
 352+ $wgMemc->set( $key, $syncData, $wgParserCacheExpireTime );
 353+
 354+ return $synced;
 355+ }
 356+
 357+ /**
 358+ * @param Article $article
 359+ * @param int $from_rev
 360+ * @return int
 361+ * Get number of revs since a certain revision
 362+ */
 363+ public static function getRevCountSince( $article, $from_rev ) {
 364+ # Check if the count is zero by using $article->getLatest().
 365+ # I don't trust using memcache and PHP for values like '0'
 366+ # as it may confuse "expired" with "0". -aaron
 367+ if( $article->getTitle()->getLatestRevID(GAID_FOR_UPDATE) == $from_rev ) {
 368+ return 0;
 369+ }
 370+ global $wgMemc;
 371+ # Try the cache
 372+ $key = wfMemcKey( 'flaggedrevs', 'unreviewedrevs', $article->getId() );
 373+ if( !$count = intval($wgMemc->get($key)) ) {
 374+ $dbr = wfGetDB( DB_SLAVE );
 375+ $count = $dbr->selectField( 'revision', 'COUNT(*)',
 376+ array('rev_page' => $article->getId(), "rev_id > " . intval($from_rev) ),
 377+ __METHOD__ );
 378+ # Save to cache
 379+ $wgMemc->set( $key, $count, 3600*24*7 );
 380+ }
 381+ return $count;
 382+ }
 383+
 384+ /**
 385+ * @param Article $article
 386+ * @param Integer $rev_id, the stable version rev_id
 387+ * @param mixed $latest, the latest rev ID (optional)
 388+ * Updates the fp_stable and fp_reviewed fields
 389+ */
 390+ public static function updateArticleOn( $article, $rev_id, $latest=NULL ) {
 391+ global $wgMemc;
 392+ wfProfileIn( __METHOD__ );
 393+
 394+ $lastID = $latest ? $latest : $article->getTitle()->getLatestRevID(GAID_FOR_UPDATE);
 395+
 396+ $dbw = wfGetDB( DB_MASTER );
 397+ # Get the highest quality revision (not necessarily this one).
 398+ $maxQuality = $dbw->selectField( array('flaggedrevs','revision'),
 399+ 'fr_quality',
 400+ array( 'fr_page_id' => $article->getTitle()->getArticleID(),
 401+ 'rev_id = fr_rev_id',
 402+ 'rev_page = fr_page_id',
 403+ 'rev_deleted & '.Revision::DELETED_TEXT => 0 ),
 404+ __METHOD__,
 405+ array( 'ORDER BY' => 'fr_quality DESC', 'LIMIT' => 1 ) );
 406+ $maxQuality = $maxQuality===false ? null : $maxQuality;
 407+ # Alter table metadata
 408+ $dbw->replace( 'flaggedpages',
 409+ array( 'fp_page_id' ),
 410+ array( 'fp_stable' => $rev_id,
 411+ 'fp_reviewed' => ($lastID == $rev_id) ? 1 : 0,
 412+ 'fp_quality' => $maxQuality,
 413+ 'fp_page_id' => $article->getId() ),
 414+ __METHOD__ );
 415+ # Update the cache
 416+ $key = wfMemcKey( 'flaggedrevs', 'unreviewedrevs', $article->getId() );
 417+
 418+ $count = $dbw->selectField( 'revision', 'COUNT(*)',
 419+ array('rev_page' => $article->getId(), "rev_id > " . intval($rev_id) ),
 420+ __METHOD__ );
 421+
 422+ $wgMemc->set( $key, $count, 3600*24*7 );
 423+
 424+ wfProfileOut( __METHOD__ );
 425+ return true;
 426+ }
 427+
 428+ /**
 429+ * Clears cache for a page when merges are done.
 430+ * We may have lost the stable revision to another page.
 431+ */
 432+ public static function articleLinksUpdate( $article ) {
 433+ global $wgUser, $wgParser;
 434+ # Update the links tables as the stable version may now be the default page...
 435+ $parserCache = ParserCache::singleton();
 436+ $poutput = $parserCache->get( $article, $wgUser );
 437+ if( $poutput==false ) {
 438+ $text = $article->getContent();
 439+ $options = self::makeParserOptions( $wgUser );
 440+ $poutput = $wgParser->parse($text, $article->getTitle(), $options);
 441+ # Might as well save the cache while we're at it
 442+ global $wgEnableParserCache;
 443+ if( $wgEnableParserCache )
 444+ $parserCache->save( $poutput, $article, $wgUser );
 445+ }
 446+ $u = new LinksUpdate( $article->getTitle(), $poutput );
 447+ $u->doUpdate(); // this will trigger our hook to add stable links too...
 448+
 449+ return true;
 450+ }
 451+
 452+ /**
 453+ * Clears cache for a page when revisiondelete/undelete is used
 454+ */
 455+ public static function titleLinksUpdate( $title ) {
 456+ return self::articleLinksUpdate( new Article($title) );
 457+ }
 458+
 459+ ################# Revision functions #################
 460+
 461+ /**
 462+ * @param Title $title
 463+ * @param int $rev_id
 464+ * @param bool $getText, fetch fr_text and fr_flags too?
 465+ * @param bool $forUpdate, use master?
 466+ * @param int $page_id, optional page ID to use, will defer to $title if not given
 467+ * @returns mixed FlaggedRevision (null on failure)
 468+ * Will not return a revision if deleted
 469+ */
 470+ public static function getFlaggedRev( $title, $rev_id, $getText=false, $forUpdate=false, $page_id=false ) {
 471+ $columns = FlaggedRevision::selectFields();
 472+ if( $getText ) {
 473+ $columns[] = 'fr_text';
 474+ $columns[] = 'fr_flags';
 475+ }
 476+ $db = $forUpdate ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
 477+ $flags = $forUpdate ? GAID_FOR_UPDATE : 0;
 478+ $page_id = $page_id ? $page_id : $title->getArticleID( $flags );
 479+ # Skip deleted revisions
 480+ $row = $db->selectRow( array('flaggedrevs','revision'),
 481+ $columns,
 482+ array( 'fr_page_id' => $page_id,
 483+ 'fr_rev_id' => $rev_id,
 484+ 'rev_id = fr_rev_id',
 485+ 'rev_page = fr_page_id',
 486+ 'rev_deleted & '.Revision::DELETED_TEXT => 0 ),
 487+ __METHOD__ );
 488+ # Sorted from highest to lowest, so just take the first one if any
 489+ if( $row ) {
 490+ return new FlaggedRevision( $title, $row );
 491+ }
 492+ return null;
 493+ }
 494+
 495+ /**
 496+ * Get latest quality rev, if not, the latest reviewed one.
 497+ * @param Title $title, page title
 498+ * @param bool $getText, fetch fr_text and fr_flags too?
 499+ * @param bool $forUpdate, use master DB and avoid using fp_stable?
 500+ * @returns mixed FlaggedRevision (null on failure)
 501+ */
 502+ public static function getStablePageRev( $title, $getText=false, $forUpdate=false ) {
 503+ $columns = FlaggedRevision::selectFields();
 504+ if( $getText ) {
 505+ $columns[] = 'fr_text';
 506+ $columns[] = 'fr_flags';
 507+ }
 508+ $row = null;
 509+ # If we want the text, then get the text flags too
 510+ if( !$forUpdate ) {
 511+ $dbr = wfGetDB( DB_SLAVE );
 512+ $row = $dbr->selectRow( array('flaggedpages','flaggedrevs'),
 513+ $columns,
 514+ array( 'fp_page_id' => $title->getArticleId(),
 515+ 'fr_page_id' => $title->getArticleId(),
 516+ 'fp_stable = fr_rev_id' ),
 517+ __METHOD__ );
 518+ if( !$row )
 519+ return null;
 520+ } else {
 521+ # Get visiblity settings...
 522+ $config = self::getPageVisibilitySettings( $title, $forUpdate );
 523+ $dbw = wfGetDB( DB_MASTER );
 524+ # Look for the latest pristine revision...
 525+ if( self::pristineVersions() && $config['select'] != FLAGGED_VIS_LATEST ) {
 526+ $prow = $dbw->selectRow( array('flaggedrevs','revision'),
 527+ $columns,
 528+ array( 'fr_page_id' => $title->getArticleID(),
 529+ 'fr_quality = 2',
 530+ 'rev_id = fr_rev_id',
 531+ 'rev_page = fr_page_id',
 532+ 'rev_deleted & '.Revision::DELETED_TEXT => 0),
 533+ __METHOD__,
 534+ array( 'ORDER BY' => 'fr_rev_id DESC') );
 535+ # Looks like a plausible revision
 536+ $row = $prow ? $prow : null;
 537+ }
 538+ # Look for the latest quality revision...
 539+ if( self::qualityVersions() && $config['select'] != FLAGGED_VIS_LATEST ) {
 540+ // If we found a pristine rev above, this one must be newer, unless
 541+ // we specifically want pristine revs to have precedence...
 542+ $newerClause = ($row && $config['select'] != FLAGGED_VIS_PRISTINE) ?
 543+ "fr_rev_id > {$row->fr_rev_id}" : "1 = 1";
 544+ $qrow = $dbw->selectRow( array('flaggedrevs','revision'),
 545+ $columns,
 546+ array( 'fr_page_id' => $title->getArticleID(),
 547+ 'fr_quality = 1',
 548+ $newerClause,
 549+ 'rev_id = fr_rev_id',
 550+ 'rev_page = fr_page_id',
 551+ 'rev_deleted & '.Revision::DELETED_TEXT => 0),
 552+ __METHOD__,
 553+ array( 'ORDER BY' => 'fr_rev_id DESC') );
 554+ $row = $qrow ? $qrow : $row;
 555+ }
 556+ # Do we have one? If not, try the latest reviewed revision...
 557+ if( !$row ) {
 558+ $row = $dbw->selectRow( array('flaggedrevs','revision'),
 559+ $columns,
 560+ array( 'fr_page_id' => $title->getArticleID(),
 561+ 'rev_id = fr_rev_id',
 562+ 'rev_page = fr_page_id',
 563+ 'rev_deleted & '.Revision::DELETED_TEXT => 0),
 564+ __METHOD__,
 565+ array( 'ORDER BY' => 'fr_rev_id DESC' ) );
 566+ if( !$row )
 567+ return null;
 568+ }
 569+ }
 570+ return new FlaggedRevision( $title, $row );
 571+ }
 572+
 573+ /**
 574+ * Get flags for a revision
 575+ * @param Title $title
 576+ * @param int $rev_id
 577+ * @return Array
 578+ */
 579+ public static function getRevisionTags( $title, $rev_id ) {
 580+ $dbr = wfGetDB( DB_SLAVE );
 581+ $tags = $dbr->selectField( 'flaggedrevs', 'fr_tags',
 582+ array( 'fr_rev_id' => $rev_id,
 583+ 'fr_page_id' => $title->getArticleId() ),
 584+ __METHOD__ );
 585+ if( !$tags )
 586+ return false;
 587+
 588+ return FlaggedRevision::expandRevisionTags( strval($tags) );
 589+ }
 590+
 591+ /**
 592+ * @param Title $title
 593+ * @param int $rev_id
 594+ * @param $flags, GAID_FOR_UPDATE
 595+ * @returns mixed (int or false)
 596+ * Get quality of a revision
 597+ */
 598+ public static function getRevQuality( $title, $rev_id, $flags=0 ) {
 599+ $db = ($flags & GAID_FOR_UPDATE) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
 600+ $quality = $db->selectField( 'flaggedrevs',
 601+ 'fr_quality',
 602+ array( 'fr_page_id' => $title->getArticleID( $flags ),
 603+ 'fr_rev_id' => $rev_id ),
 604+ __METHOD__,
 605+ array( 'FORCE INDEX' => 'PRIMARY' )
 606+ );
 607+ return $quality;
 608+ }
 609+
 610+ /**
 611+ * @param Title $title
 612+ * @param int $rev_id
 613+ * @param $flags, GAID_FOR_UPDATE
 614+ * @returns bool
 615+ * Useful for quickly pinging to see if a revision is flagged
 616+ */
 617+ public static function revIsFlagged( $title, $rev_id, $flags=0 ) {
 618+ $quality = self::getRevQuality( $title, $rev_id, $flags );
 619+ return ($quality !== false);
 620+ }
 621+
 622+ /**
 623+ * Get the "prime" flagged revision of a page
 624+ * @param Article $article
 625+ * @returns mixed (integer/false)
 626+ * Will not return a revision if deleted
 627+ */
 628+ public static function getPrimeFlaggedRevId( $article ) {
 629+ $dbr = wfGetDB( DB_SLAVE );
 630+ # Get the highest quality revision (not necessarily this one).
 631+ $oldid = $dbr->selectField( array('flaggedrevs','revision'),
 632+ 'fr_rev_id',
 633+ array( 'fr_page_id' => $article->getId(),
 634+ 'rev_page = fr_page_id',
 635+ 'rev_id = fr_rev_id'),
 636+ __METHOD__,
 637+ array( 'ORDER BY' => 'fr_quality DESC, fr_rev_id DESC',
 638+ 'USE INDEX' => array('flaggedrevs' => 'page_qal_rev','revision' => 'PRIMARY') )
 639+ );
 640+ return $oldid;
 641+ }
 642+
 643+ ################# Page configuration functions #################
 644+
 645+ /**
 646+ * Get visiblity restrictions on page
 647+ * @param Title $title, page title
 648+ * @param bool $forUpdate, use master DB?
 649+ * @returns Array (select,override)
 650+ */
 651+ public static function getPageVisibilitySettings( $title, $forUpdate=false ) {
 652+ $db = $forUpdate ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
 653+ $row = $db->selectRow( 'flaggedpage_config',
 654+ array( 'fpc_select', 'fpc_override', 'fpc_expiry' ),
 655+ array( 'fpc_page_id' => $title->getArticleID() ),
 656+ __METHOD__ );
 657+
 658+ if( $row ) {
 659+ $now = wfTimestampNow();
 660+ # This code should be refactored, now that it's being used more generally.
 661+ $expiry = Block::decodeExpiry( $row->fpc_expiry );
 662+ # Only apply the settigns if they haven't expired
 663+ if( !$expiry || $expiry < $now ) {
 664+ $row = null;
 665+ self::purgeExpiredConfigurations();
 666+ }
 667+ }
 668+
 669+ if( !$row ) {
 670+ global $wgFlaggedRevsOverride, $wgFlaggedRevsPrecedence;
 671+ # Keep this consistent across settings. 1 -> override, 0 -> don't
 672+ $override = $wgFlaggedRevsOverride ? 1 : 0;
 673+ # Keep this consistent across settings. 0 -> precedence, 0 -> none
 674+ $select = $wgFlaggedRevsPrecedence ? FLAGGED_VIS_NORMAL : FLAGGED_VIS_LATEST;
 675+ return array('select' => $select, 'override' => $override, 'expiry' => 'infinity');
 676+ }
 677+
 678+ return array('select' => $row->fpc_select, 'override' => $row->fpc_override, 'expiry' => $row->fpc_expiry);
 679+ }
 680+
 681+ /**
 682+ * Purge expired restrictions from the flaggedpage_config table
 683+ */
 684+ public static function purgeExpiredConfigurations() {
 685+ $dbw = wfGetDB( DB_MASTER );
 686+ $dbw->delete( 'flaggedpage_config',
 687+ array( 'fpc_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
 688+ __METHOD__ );
 689+ }
 690+
 691+ ################# Other utility functions #################
 692+
 693+ /**
 694+ * @param Title $title
 695+ * @return bool, is $title the main page?
 696+ */
 697+ public static function isMainPage( $title ) {
 698+ return $title->equals( Title::newMainPage() );
 699+ }
 700+
 701+ /**
 702+ * @param Array $flags
 703+ * @return bool, is this revision at quality condition?
 704+ */
 705+ public static function isQuality( $flags ) {
 706+ global $wgFlaggedRevTags;
 707+
 708+ if( empty($flags) )
 709+ return false;
 710+
 711+ foreach( $wgFlaggedRevTags as $f => $v ) {
 712+ if( !isset($flags[$f]) || $v > $flags[$f] )
 713+ return false;
 714+ }
 715+
 716+ return true;
 717+ }
 718+
 719+ /**
 720+ * @param Array $flags
 721+ * @return bool, is this revision at optimal condition?
 722+ */
 723+ public static function isPristine( $flags ) {
 724+ global $wgFlaggedRevTags, $wgFlaggedRevPristine;
 725+
 726+ if( empty($flags) )
 727+ return false;
 728+
 729+ foreach( $wgFlaggedRevTags as $f => $v ) {
 730+ if( !isset($flags[$f]) || $flags[$f] < $wgFlaggedRevPristine )
 731+ return false;
 732+ }
 733+
 734+ return true;
 735+ }
 736+
 737+ /**
 738+ * Is this page in reviewable namespace?
 739+ * @param Title, $title
 740+ * @return bool
 741+ */
 742+ public static function isPageReviewable( $title ) {
 743+ global $wgFlaggedRevsNamespaces;
 744+ # FIXME: Treat NS_MEDIA as NS_IMAGE
 745+ $ns = ( $title->getNamespace() == NS_MEDIA ) ? NS_IMAGE : $title->getNamespace();
 746+ return ( in_array($ns,$wgFlaggedRevsNamespaces) && !$title->isTalkPage() );
 747+ }
 748+
 749+ /**
 750+ * Is this page in patrolable namespace?
 751+ * @param Title, $title
 752+ * @return bool
 753+ */
 754+ public static function isPagePatrollable( $title ) {
 755+ global $wgFlaggedRevsPatrolNamespaces;
 756+ # No collisions!
 757+ if( self::isPageReviewable($title) ) {
 758+ return false;
 759+ }
 760+ # FIXME: Treat NS_MEDIA as NS_IMAGE
 761+ $ns = ( $title->getNamespace() == NS_MEDIA ) ? NS_IMAGE : $title->getNamespace();
 762+ return ( in_array($ns,$wgFlaggedRevsPatrolNamespaces) && !$title->isTalkPage() );
 763+ }
 764+
 765+ /**
 766+ * Make stable version link and return the css
 767+ * @param Title $title
 768+ * @param int $rev_id
 769+ * @param Database $db, optional
 770+ * @returns array (string,string)
 771+ */
 772+ public static function makeStableVersionLink( $title, $rev_id, $skin, $db = NULL ) {
 773+ $db = $db ? $db : wfGetDB( DB_SLAVE );
 774+ $row = $db->selectRow( 'flaggedrevs',
 775+ array( 'fr_quality', 'fr_user' ),
 776+ array( 'fr_page_id' => $title->getArticleID(),
 777+ 'fr_rev_id' => $rev_id ),
 778+ __METHOD__,
 779+ array( 'FORCE INDEX' => 'PRIMARY' )
 780+ );
 781+ if( $row ) {
 782+ $css = FlaggedRevsXML::getQualityColor( $row->fr_quality );
 783+ $user = User::whois( $row->fr_user );
 784+ $msg = ($row->fr_quality >= 1) ? 'hist-quality-user' : 'hist-stable-user';
 785+ $st = $title->getPrefixedDBkey();
 786+ $link = "<span class='plainlinks'>".wfMsgExt($msg,array('parseinline'),$st,$rev_id,$user)."</span>";
 787+ } else {
 788+ return array("","");
 789+ }
 790+ return array($link,$css);
 791+ }
 792+
 793+ /**
 794+ * Get JS script params for onloading
 795+ */
 796+ public static function getJSParams() {
 797+ # Param to pass to JS function to know if tags are at quality level
 798+ global $wgFlaggedRevTags;
 799+ $params = array( 'tags' => (object)$wgFlaggedRevTags );
 800+ return Xml::encodeJsVar( (object)$params );
 801+ }
 802+
 803+ /**
 804+ * Get params for a user
 805+ * @param User $user
 806+ */
 807+ public static function getUserParams( $user ) {
 808+ $dbw = wfGetDB( DB_MASTER );
 809+ $row = $dbw->selectRow( 'flaggedrevs_promote', 'frp_user_params',
 810+ array( 'frp_user_id' => $user->getId() ),
 811+ __METHOD__ );
 812+ # Parse params
 813+ $params = array();
 814+ if( $row ) {
 815+ $flatPars = explode( "\n", trim($row->frp_user_params) );
 816+ foreach( $flatPars as $pair ) {
 817+ $m = explode( '=', trim($pair), 2 );
 818+ $key = $m[0];
 819+ $value = isset($m[1]) ? $m[1] : null;
 820+ $params[$key] = $value;
 821+ }
 822+ }
 823+ return $params;
 824+ }
 825+
 826+ /**
 827+ * Save params for a user
 828+ * @param User $user
 829+ * @param Array $params
 830+ */
 831+ public static function saveUserParams( $user, $params ) {
 832+ $flatParams = '';
 833+ foreach( $params as $key => $value ) {
 834+ $flatParams .= "{$key}={$value}\n";
 835+ }
 836+ $dbw = wfGetDB( DB_MASTER );
 837+ $row = $dbw->replace( 'flaggedrevs_promote',
 838+ array( 'frp_user_id' ),
 839+ array( 'frp_user_id' => $user->getId(),
 840+ 'frp_user_params' => trim($flatParams) ),
 841+ __METHOD__ );
 842+
 843+ return ( $dbw->affectedRows() > 0 );
 844+ }
 845+
 846+ ################# Auto-review function #################
 847+
 848+ /**
 849+ * Automatically review an edit and add a log entry in the review log.
 850+ * LinksUpdate was already called via edit operations, so the page
 851+ * fields will be up to date. This updates the stable version.
 852+ */
 853+ public static function autoReviewEdit( $article, $user, $text, $rev, $flags, $patrol = true ) {
 854+ global $wgParser, $wgFlaggedRevsAutoReview;
 855+
 856+ wfProfileIn( __METHOD__ );
 857+
 858+ $quality = 0;
 859+ if( self::isQuality($flags) ) {
 860+ $quality = self::isPristine($flags) ? 2 : 1;
 861+ }
 862+ $tmpset = $imgset = array();
 863+ $poutput = false;
 864+
 865+ # Use master to avoid lag issues.
 866+ $latestID = $article->getTitle()->getLatestRevID(GAID_FOR_UPDATE);
 867+ $latestID = $latestID ? $latestID : $rev->getId(); // new pages, page row not added yet
 868+
 869+ $title = $article->getTitle();
 870+ # Rev ID is not put into parser on edit, so do the same here.
 871+ # Also, a second parse would be triggered otherwise.
 872+ $parseId = ($rev->getId() == $latestID) ? null : $rev->getId();
 873+ # Parse the revision HTML output
 874+ $editInfo = $article->prepareTextForEdit( $text, $parseId );
 875+ $poutput = $editInfo->output;
 876+
 877+ # NS:title -> rev ID mapping
 878+ foreach( $poutput->mTemplateIds as $namespace => $titleAndID ) {
 879+ foreach( $titleAndID as $dbkey => $id ) {
 880+ $tmpset[] = array(
 881+ 'ft_rev_id' => $rev->getId(),
 882+ 'ft_namespace' => $namespace,
 883+ 'ft_title' => $dbkey,
 884+ 'ft_tmp_rev_id' => $id
 885+ );
 886+ }
 887+ }
 888+ # Image -> timestamp mapping
 889+ foreach( $poutput->fr_ImageSHA1Keys as $dbkey => $timeAndSHA1 ) {
 890+ foreach( $timeAndSHA1 as $time => $sha1 ) {
 891+ $imgset[] = array(
 892+ 'fi_rev_id' => $rev->getId(),
 893+ 'fi_name' => $dbkey,
 894+ 'fi_img_timestamp' => $time,
 895+ 'fi_img_sha1' => $sha1
 896+ );
 897+ }
 898+ }
 899+
 900+ $dbw = wfGetDB( DB_MASTER );
 901+ $dbw->begin();
 902+ # Update our versioning pointers
 903+ if( !empty( $tmpset ) ) {
 904+ $dbw->replace( 'flaggedtemplates',
 905+ array( array('ft_rev_id','ft_namespace','ft_title') ), $tmpset,
 906+ __METHOD__ );
 907+ }
 908+ if( !empty( $imgset ) ) {
 909+ $dbw->replace( 'flaggedimages',
 910+ array( array('fi_rev_id','fi_name') ), $imgset,
 911+ __METHOD__ );
 912+ }
 913+ # Get the page text and resolve all templates
 914+ list($fulltext,$templateIDs,$complete,$maxID) = self::expandText( $text, $article->getTitle(), $rev->getId() );
 915+
 916+ # Compress $fulltext, passed by reference
 917+ $textFlags = FlaggedRevision::compressText( $fulltext );
 918+
 919+ # Write to external storage if required
 920+ $storage = self::getExternalStorage();
 921+ if( $storage ) {
 922+ if( is_array($storage) ) {
 923+ # Distribute storage across multiple clusters
 924+ $store = $storage[mt_rand(0, count( $storage ) - 1)];
 925+ } else {
 926+ $store = $storage;
 927+ }
 928+ # Store and get the URL
 929+ $fulltext = ExternalStore::insert( $store, $fulltext );
 930+ if( !$fulltext ) {
 931+ # This should only happen in the case of a configuration error, where the external store is not valid
 932+ wfProfileOut( __METHOD__ );
 933+ throw new MWException( "Unable to store text to external storage $store" );
 934+ }
 935+ if( $textFlags ) {
 936+ $textFlags .= ',';
 937+ }
 938+ $textFlags .= 'external';
 939+ }
 940+
 941+ # If this is an image page, store corresponding file info
 942+ $fileData = array();
 943+ if( $title->getNamespace() == NS_IMAGE && $file = wfFindFile($title) ) {
 944+ $fileData['name'] = $title->getDBkey();
 945+ $fileData['timestamp'] = $file->getTimestamp();
 946+ $fileData['sha1'] = $file->getSha1();
 947+ }
 948+
 949+ # Our review entry
 950+ $revisionset = array(
 951+ 'fr_page_id' => $rev->getPage(),
 952+ 'fr_rev_id' => $rev->getId(),
 953+ 'fr_user' => $user->getId(),
 954+ 'fr_timestamp' => $dbw->timestamp( wfTimestampNow() ),
 955+ 'fr_comment' => "",
 956+ 'fr_quality' => $quality,
 957+ 'fr_tags' => FlaggedRevision::flattenRevisionTags( $flags ),
 958+ 'fr_text' => $fulltext, # Store expanded text for speed
 959+ 'fr_flags' => $textFlags,
 960+ 'fr_img_name' => $fileData ? $fileData['name'] : null,
 961+ 'fr_img_timestamp' => $fileData ? $fileData['timestamp'] : null,
 962+ 'fr_img_sha1' => $fileData ? $fileData['sha1'] : null
 963+ );
 964+ # Update flagged revisions table
 965+ $dbw->replace( 'flaggedrevs',
 966+ array( array('fr_page_id','fr_rev_id') ), $revisionset,
 967+ __METHOD__ );
 968+ # Mark as patrolled
 969+ if( $patrol ) {
 970+ $dbw->update( 'recentchanges',
 971+ array( 'rc_patrolled' => 1 ),
 972+ array( 'rc_this_oldid' => $rev->getId(),
 973+ 'rc_user_text' => $rev->getRawUserText(),
 974+ 'rc_timestamp' => $dbw->timestamp( $rev->getTimestamp() ) ),
 975+ __METHOD__,
 976+ array( 'LIMIT' => 1 ) );
 977+ }
 978+ # Done!
 979+ $dbw->commit();
 980+
 981+ # Update the article review log
 982+ RevisionReview::updateLog( $title, $flags, array(), wfMsgForContent('revreview-auto'), $rev->getID(), true, true );
 983+
 984+ # If we know that this is now the new stable version
 985+ # (which it probably is), save it to the cache...
 986+ $sv = self::getStablePageRev( $article->getTitle(), false, true );
 987+ if( $sv && $sv->getRevId() == $rev->getId() ) {
 988+ # Update stable cache
 989+ self::updatePageCache( $article, $poutput );
 990+ # Update page fields
 991+ self::updateArticleOn( $article, $rev->getId(), $rev->getId() );
 992+ # Purge squid for this page only
 993+ $article->getTitle()->purgeSquid();
 994+ }
 995+
 996+ wfProfileOut( __METHOD__ );
 997+
 998+ return true;
 999+ }
 1000+
 1001+ ################# Hooked functions #################
 1002+
 1003+ /**
 1004+ * Remove 'patrol' and 'autopatrol' rights. Reviewing revisions will patrol them as well.
 1005+ */
 1006+ public static function stripPatrolRights( $user, &$rights ) {
 1007+ # Use only our extension mechanisms
 1008+ foreach( $rights as $n => $right ) {
 1009+ if( $right == 'patrol' || $right == 'autopatrol' ) {
 1010+ unset($rights[$n]);
 1011+ }
 1012+ }
 1013+ return true;
 1014+ }
 1015+
 1016+ /**
 1017+ * Add FlaggedRevs css/js.
 1018+ */
 1019+ public static function injectStyleAndJS() {
 1020+ global $wgOut, $wgJsMimeType;
 1021+ # Don't double-load
 1022+ if ( $wgOut->hasHeadItem( 'FlaggedRevs' ) ) {
 1023+ return true;
 1024+ }
 1025+ if ( !$wgOut->isArticleRelated() ) {
 1026+ return true;
 1027+ }
 1028+
 1029+ global $wgArticle, $wgScriptPath, $wgFlaggedRevStyleVersion, $wgJsMimeType, $wgFlaggedRevsStylePath;
 1030+
 1031+ $flaggedArticle = FlaggedArticle::getInstance( $wgArticle );
 1032+ $stylePath = str_replace( '$wgScriptPath', $wgScriptPath, $wgFlaggedRevsStylePath );
 1033+ $JSparams = self::getJSParams();
 1034+ $frev = $flaggedArticle->getStableRev( true );
 1035+ $stableId = $frev ? $frev->getRevId() : 0;
 1036+ $encCssFile = htmlspecialchars( "$stylePath/flaggedrevs.css?$wgFlaggedRevStyleVersion" );
 1037+ $encJsFile = htmlspecialchars( "$stylePath/flaggedrevs.js?$wgFlaggedRevStyleVersion" );
 1038+ $head = <<<EOT
 1039+<link rel="stylesheet" type="text/css" media="screen, projection" href="$encCssFile"/>
 1040+<script type="$wgJsMimeType">
 1041+var wgFlaggedRevsParams = $JSparams;
 1042+var wgStableRevisionId = $stableId;
 1043+</script>
 1044+<script type="$wgJsMimeType" src="$encJsFile"></script>
 1045+
 1046+EOT;
 1047+ $wgOut->addHeadItem( 'FlaggedRevs', $head );
 1048+ return true;
 1049+ }
 1050+
 1051+ /**
 1052+ * Add FlaggedRevs css for relevant special pages.
 1053+ */
 1054+ public static function InjectStyleForSpecial() {
 1055+ global $wgTitle, $wgOut;
 1056+ $spPages = array();
 1057+ $spPages[] = SpecialPage::getTitleFor( 'UnreviewedPages' );
 1058+ $spPages[] = SpecialPage::getTitleFor( 'OldReviewedPages' );
 1059+ foreach( $spPages as $n => $title ) {
 1060+ if( $wgTitle->equals( $title ) ) {
 1061+ # UI CSS
 1062+ $wgOut->addLink( array(
 1063+ 'rel' => 'stylesheet',
 1064+ 'type' => 'text/css',
 1065+ 'media' => 'screen, projection',
 1066+ 'href' => FLAGGED_CSS,
 1067+ ) );
 1068+ break;
 1069+ }
 1070+ }
 1071+ return true;
 1072+ }
 1073+
 1074+ /**
 1075+ * Update flaggedrevs table on revision restore
 1076+ */
 1077+ public static function updateFromRestore( $title, $revision, $oldPageID ) {
 1078+ $dbw = wfGetDB( DB_MASTER );
 1079+ # Some revisions may have had null rev_id values stored when deleted.
 1080+ # This hook is called after insertOn() however, in which case it is set
 1081+ # as a new one.
 1082+ $dbw->update( 'flaggedrevs',
 1083+ array( 'fr_page_id' => $revision->getPage() ),
 1084+ array( 'fr_page_id' => $oldPageID,
 1085+ 'fr_rev_id' => $revision->getID() ),
 1086+ __METHOD__ );
 1087+
 1088+ return true;
 1089+ }
 1090+
 1091+ /**
 1092+ * Update flaggedrevs table on article history merge
 1093+ */
 1094+ public static function updateFromMerge( $sourceTitle, $destTitle ) {
 1095+ wfProfileIn( __METHOD__ );
 1096+
 1097+ $oldPageID = $sourceTitle->getArticleID();
 1098+ $newPageID = $destTitle->getArticleID();
 1099+ # Get flagged revisions from old page id that point to destination page
 1100+ $dbw = wfGetDB( DB_MASTER );
 1101+ $result = $dbw->select( array('flaggedrevs','revision'),
 1102+ array( 'fr_rev_id' ),
 1103+ array( 'fr_page_id' => $oldPageID,
 1104+ 'fr_rev_id = rev_id',
 1105+ 'rev_page' => $newPageID ),
 1106+ __METHOD__ );
 1107+ # Update these rows
 1108+ $revIDs = array();
 1109+ while( $row = $dbw->fetchObject($result) ) {
 1110+ $revIDs[] = $row->fr_rev_id;
 1111+ }
 1112+ if( !empty($revIDs) ) {
 1113+ $dbw->update( 'flaggedrevs',
 1114+ array( 'fr_page_id' => $newPageID ),
 1115+ array( 'fr_page_id' => $oldPageID,
 1116+ 'fr_rev_id' => $revIDs ),
 1117+ __METHOD__ );
 1118+ }
 1119+ # Update pages
 1120+ self::titleLinksUpdate( $sourceTitle );
 1121+ self::titleLinksUpdate( $destTitle );
 1122+
 1123+ wfProfileOut( __METHOD__ );
 1124+ return true;
 1125+ }
 1126+
 1127+ /**
 1128+ * Clears visiblity settings on page delete
 1129+ */
 1130+ public static function deleteVisiblitySettings( $article, $user, $reason ) {
 1131+ $dbw = wfGetDB( DB_MASTER );
 1132+ $dbw->delete( 'flaggedpage_config',
 1133+ array( 'fpc_page_id' => $article->getID() ),
 1134+ __METHOD__ );
 1135+
 1136+ return true;
 1137+ }
 1138+
 1139+ /**
 1140+ * Inject stable links on LinksUpdate
 1141+ */
 1142+ public static function extraLinksUpdate( $linksUpdate ) {
 1143+ wfProfileIn( __METHOD__ );
 1144+ if( !self::isPageReviewable( $linksUpdate->mTitle ) ) {
 1145+ wfProfileOut( __METHOD__ );
 1146+ return true;
 1147+ }
 1148+ # Check if this page has a stable version by fetching it. Do not
 1149+ # get the fr_text field if we are to use the latest stable template revisions.
 1150+ global $wgUseStableTemplates;
 1151+ $sv = self::getStablePageRev( $linksUpdate->mTitle, !$wgUseStableTemplates, true );
 1152+ if( !$sv ) {
 1153+ wfProfileOut( __METHOD__ );
 1154+ return true;
 1155+ }
 1156+ # Get the either the full flagged revision text or the revision text
 1157+ $article = new Article( $linksUpdate->mTitle );
 1158+ # Try stable version cache. This should be updated before this is called.
 1159+ $parserOut = self::getPageCache( $article );
 1160+ if( $parserOut==false ) {
 1161+ $text = $sv->getTextForParse();
 1162+ # Parse the text
 1163+ $parserOut = self::parseStableText( $article, $text, $sv->getRevId() );
 1164+ }
 1165+ # Update page fields
 1166+ self::updateArticleOn( $article, $sv->getRevId() );
 1167+ # Update the links tables to include these
 1168+ # We want the UNION of links between the current
 1169+ # and stable version. Therefore, we only care about
 1170+ # links that are in the stable version and not the regular one.
 1171+ foreach( $parserOut->getLinks() as $ns => $titles ) {
 1172+ foreach( $titles as $title => $id ) {
 1173+ if( !isset($linksUpdate->mLinks[$ns]) ) {
 1174+ $linksUpdate->mLinks[$ns] = array();
 1175+ $linksUpdate->mLinks[$ns][$title] = $id;
 1176+ } else if( !isset($linksUpdate->mLinks[$ns][$title]) ) {
 1177+ $linksUpdate->mLinks[$ns][$title] = $id;
 1178+ }
 1179+ }
 1180+ }
 1181+ foreach( $parserOut->getImages() as $image => $n ) {
 1182+ if( !isset($linksUpdate->mImages[$image]) )
 1183+ $linksUpdate->mImages[$image] = $n;
 1184+ }
 1185+ foreach( $parserOut->getTemplates() as $ns => $titles ) {
 1186+ foreach( $titles as $title => $id ) {
 1187+ if( !isset($linksUpdate->mTemplates[$ns]) ) {
 1188+ $linksUpdate->mTemplates[$ns] = array();
 1189+ $linksUpdate->mTemplates[$ns][$title] = $id;
 1190+ } else if( !isset($linksUpdate->mTemplates[$ns][$title]) ) {
 1191+ $linksUpdate->mTemplates[$ns][$title] = $id;
 1192+ }
 1193+ }
 1194+ }
 1195+ foreach( $parserOut->getExternalLinks() as $url => $n ) {
 1196+ if( !isset($linksUpdate->mExternals[$url]) )
 1197+ $linksUpdate->mExternals[$url] = $n;
 1198+ }
 1199+ foreach( $parserOut->getCategories() as $category => $sort ) {
 1200+ if( !isset($linksUpdate->mCategories[$category]) )
 1201+ $linksUpdate->mCategories[$category] = $sort;
 1202+ }
 1203+ foreach( $parserOut->getLanguageLinks() as $n => $link ) {
 1204+ list( $key, $title ) = explode( ':', $link, 2 );
 1205+ if( !isset($linksUpdate->mInterlangs[$key]) )
 1206+ $linksUpdate->mInterlangs[$key] = $title;
 1207+ }
 1208+ foreach( $parserOut->getProperties() as $prop => $val ) {
 1209+ if( !isset($linksUpdate->mProperties[$prop]) )
 1210+ $linksUpdate->mProperties[$prop] = $val;
 1211+ }
 1212+ wfProfileOut( __METHOD__ );
 1213+ return true;
 1214+ }
 1215+
 1216+ /**
 1217+ * Add special fields to parser.
 1218+ */
 1219+ public static function parserAddFields( $parser ) {
 1220+ $parser->mOutput->fr_ImageSHA1Keys = array();
 1221+ $parser->mOutput->fr_newestImageTime = "0";
 1222+ $parser->mOutput->fr_newestTemplateID = 0;
 1223+ return true;
 1224+ }
 1225+
 1226+ /**
 1227+ * Select the desired templates based on the selected stable revision IDs
 1228+ * NOTE: $p comes in false from this hook ... weird
 1229+ */
 1230+ public static function parserFetchStableTemplate( $p=false, $title, &$skip, &$id ) {
 1231+ global $wgParser;
 1232+ # Trigger for stable version parsing only
 1233+ $parser =& $wgParser;
 1234+ if( !isset($parser->fr_isStable) || !$parser->fr_isStable )
 1235+ return true;
 1236+ # Special namespace ... ?
 1237+ if( $title->getNamespace() < 0 )
 1238+ return true;
 1239+ # Only called to make fr_text, right after template/image specifiers
 1240+ # are added to the DB. Slaves may not have it yet...
 1241+ $dbw = wfGetDB( DB_MASTER );
 1242+ # Check for stable version of template if this feature is enabled.
 1243+ # Should be in reviewable namespace, this saves unneeded DB checks as
 1244+ # well as enforce site settings if they are later changed.
 1245+ global $wgUseStableTemplates;
 1246+ if( $wgUseStableTemplates && self::isPageReviewable( $title ) ) {
 1247+ $id = $dbw->selectField( 'flaggedpages', 'fp_stable',
 1248+ array( 'fp_page_id' => $title->getArticleId() ),
 1249+ __METHOD__ );
 1250+ }
 1251+ # If there is no stable version (or that feature is not enabled), use
 1252+ # the template revision during review time.
 1253+ if( !$id ) {
 1254+ $id = $dbw->selectField( 'flaggedtemplates', 'ft_tmp_rev_id',
 1255+ array( 'ft_rev_id' => $parser->mRevisionId,
 1256+ 'ft_namespace' => $title->getNamespace(),
 1257+ 'ft_title' => $title->getDBkey() ),
 1258+ __METHOD__ );
 1259+ }
 1260+ # If none specified, see if we are allowed to use the current revision
 1261+ if( !$id ) {
 1262+ global $wgUseCurrentTemplates;
 1263+ if( $id === false ) {
 1264+ $parser->fr_includesMatched = false; // May want to give an error
 1265+ if( !$wgUseCurrentTemplates ) {
 1266+ $skip = true;
 1267+ }
 1268+ } else {
 1269+ $skip = true;
 1270+ }
 1271+ }
 1272+ if( $id > $parser->mOutput->fr_newestTemplateID ) {
 1273+ $parser->mOutput->fr_newestTemplateID = $id;
 1274+ }
 1275+
 1276+ return true;
 1277+ }
 1278+
 1279+ /**
 1280+ * Select the desired images based on the selected stable revision times/SHA-1s
 1281+ */
 1282+ public static function parserMakeStableImageLink( $parser, $nt, &$skip, &$time, &$query=false ) {
 1283+ # Trigger for stable version parsing only
 1284+ if( !isset($parser->fr_isStable) || !$parser->fr_isStable )
 1285+ return true;
 1286+ # Only called to make fr_text, right after template/image specifiers
 1287+ # are added to the DB. Slaves may not have it yet...
 1288+ $dbw = wfGetDB( DB_MASTER );
 1289+ # Check for stable version of image if this feature is enabled.
 1290+ # Should be in reviewable namespace, this saves unneeded DB checks as
 1291+ # well as enforce site settings if they are later changed.
 1292+ $sha1 = "";
 1293+ global $wgUseStableImages;
 1294+ if( $wgUseStableImages && self::isPageReviewable( $nt ) ) {
 1295+ $srev = self::getStablePageRev( $nt, false, true );
 1296+ if( $srev ) {
 1297+ $time = $srev->getFileTimestamp();
 1298+ $sha1 = $srev->getFileSha1();
 1299+ // B/C, may be stored in associated image version metadata table
 1300+ if( !$time || !$sha1 ) {
 1301+ $row = $dbw->selectRow( 'flaggedimages',
 1302+ array( 'fi_img_timestamp', 'fi_img_sha1' ),
 1303+ array( 'fi_rev_id' => $srev->getRevId(),
 1304+ 'fi_name' => $nt->getDBkey() ),
 1305+ __METHOD__ );
 1306+ $time = $row ? $row->fi_img_timestamp : $time;
 1307+ $sha1 = $row ? $row->fi_img_sha1 : $sha1;
 1308+ }
 1309+ }
 1310+ }
 1311+ # If there is no stable version (or that feature is not enabled), use
 1312+ # the image revision during review time.
 1313+ if( !$time ) {
 1314+ $row = $dbw->selectRow( 'flaggedimages',
 1315+ array( 'fi_img_timestamp', 'fi_img_sha1' ),
 1316+ array( 'fi_rev_id' => $parser->mRevisionId,
 1317+ 'fi_name' => $nt->getDBkey() ),
 1318+ __METHOD__ );
 1319+ $time = $row ? $row->fi_img_timestamp : $time;
 1320+ $sha1 = $row ? $row->fi_img_sha1 : $sha1;
 1321+ $query = $row ? "filetimestamp=" . urlencode( wfTimestamp(TS_MW,$row->fi_img_timestamp) ) : "";
 1322+ }
 1323+ # If none specified, see if we are allowed to use the current revision
 1324+ if( !$time ) {
 1325+ global $wgUseCurrentImages;
 1326+ # If the DB found nothing...
 1327+ if( $time === false ) {
 1328+ $parser->fr_includesMatched = false; // May want to give an error
 1329+ if( !$wgUseCurrentImages ) {
 1330+ $time = "0";
 1331+ } else {
 1332+ $file = wfFindFile( $nt );
 1333+ $time = $file ? $file->getTimestamp() : "0"; // Use current
 1334+ }
 1335+ } else {
 1336+ $time = "0";
 1337+ }
 1338+ }
 1339+ # Add image metadata to parser output
 1340+ $parser->mOutput->fr_ImageSHA1Keys[$nt->getDBkey()] = array();
 1341+ $parser->mOutput->fr_ImageSHA1Keys[$nt->getDBkey()][$time] = $sha1;
 1342+
 1343+ if( $time > $parser->mOutput->fr_newestImageTime ) {
 1344+ $parser->mOutput->fr_newestImageTime = $time;
 1345+ }
 1346+
 1347+ return true;
 1348+ }
 1349+
 1350+ /**
 1351+ * Select the desired images based on the selected stable revision times/SHA-1s
 1352+ */
 1353+ public static function galleryFindStableFileTime( $ig, $nt, &$time, &$query=false ) {
 1354+ # Trigger for stable version parsing only
 1355+ if( !isset($ig->fr_isStable) || !$ig->fr_isStable )
 1356+ return true;
 1357+ # Slaves may not have it yet...
 1358+ $dbw = wfGetDB( DB_MASTER );
 1359+ # Check for stable version of image if this feature is enabled.
 1360+ # Should be in reviewable namespace, this saves unneeded DB checks as
 1361+ # well as enforce site settings if they are later changed.
 1362+ $sha1 = "";
 1363+ global $wgUseStableImages;
 1364+ if( $wgUseStableImages && self::isPageReviewable( $nt ) ) {
 1365+ $srev = self::getStablePageRev( $nt, false, true );
 1366+ if( $srev ) {
 1367+ $time = $srev->getFileTimestamp();
 1368+ $sha1 = $srev->getFileSha1();
 1369+ // B/C, may be stored in associated image version metadata table
 1370+ if( !$time || !$sha1 ) {
 1371+ $row = $dbw->selectRow( 'flaggedimages',
 1372+ array( 'fi_img_timestamp', 'fi_img_sha1' ),
 1373+ array( 'fi_rev_id' => $srev->getRevId(),
 1374+ 'fi_name' => $nt->getDBkey() ),
 1375+ __METHOD__ );
 1376+ $time = $row ? $row->fi_img_timestamp : $time;
 1377+ $sha1 = $row ? $row->fi_img_sha1 : $sha1;
 1378+ }
 1379+ }
 1380+ }
 1381+ # If there is no stable version (or that feature is not enabled), use
 1382+ # the image revision during review time.
 1383+ if( !$time ) {
 1384+ $row = $dbw->selectRow( 'flaggedimages',
 1385+ array( 'fi_img_timestamp', 'fi_img_sha1' ),
 1386+ array('fi_rev_id' => $ig->mRevisionId,
 1387+ 'fi_name' => $nt->getDBkey() ),
 1388+ __METHOD__ );
 1389+ $time = $row ? $row->fi_img_timestamp : $time;
 1390+ $sha1 = $row ? $row->fi_img_sha1 : $sha1;
 1391+ $query = $row ? "filetimestamp=" . urlencode( wfTimestamp(TS_MW,$row->fi_img_timestamp) ) : "";
 1392+ }
 1393+ # If none specified, see if we are allowed to use the current revision
 1394+ if( !$time ) {
 1395+ global $wgUseCurrentImages;
 1396+ # If the DB found nothing...
 1397+ if( $time === false ) {
 1398+ $ig->fr_parentParser->fr_includesMatched = false; // May want to give an error
 1399+ if( !$wgUseCurrentImages ) {
 1400+ $time = "0";
 1401+ } else {
 1402+ $file = wfFindFile( $nt );
 1403+ $time = $file ? $file->getTimestamp() : "0";
 1404+ }
 1405+ } else {
 1406+ $time = "0";
 1407+ }
 1408+ }
 1409+ # Add image metadata to parser output
 1410+ $ig->fr_parentParser->mOutput->fr_ImageSHA1Keys[$nt->getDBkey()] = array();
 1411+ $ig->fr_parentParser->mOutput->fr_ImageSHA1Keys[$nt->getDBkey()][$time] = $sha1;
 1412+
 1413+ if( $time > $ig->fr_parentParser->mOutput->fr_newestImageTime ) {
 1414+ $ig->fr_parentParser->mOutput->fr_newestImageTime = $time;
 1415+ }
 1416+
 1417+ return true;
 1418+ }
 1419+
 1420+ /**
 1421+ * Flag of an image galley as stable
 1422+ */
 1423+ public static function parserMakeGalleryStable( $parser, $ig ) {
 1424+ # Trigger for stable version parsing only
 1425+ if( !isset($parser->fr_isStable) || !$parser->fr_isStable )
 1426+ return true;
 1427+
 1428+ $ig->fr_isStable = true;
 1429+ $ig->fr_parentParser =& $parser; // hack
 1430+
 1431+ return true;
 1432+ }
 1433+
 1434+ /**
 1435+ * Insert image timestamps/SHA-1 keys into parser output
 1436+ */
 1437+ public static function parserInjectTimestamps( $parser, &$text ) {
 1438+ # Don't trigger this for stable version parsing...it will do it separately.
 1439+ if( isset($parser->fr_isStable) && $parser->fr_isStable )
 1440+ return true;
 1441+
 1442+ wfProfileIn( __METHOD__ );
 1443+
 1444+ $maxRevision = 0;
 1445+ # Record the max template revision ID
 1446+ if( !empty($parser->mOutput->mTemplateIds) ) {
 1447+ foreach( $parser->mOutput->mTemplateIds as $namespace => $DBkey_rev ) {
 1448+ foreach( $DBkey_rev as $DBkey => $revID ) {
 1449+ if( $revID > $maxRevision ) {
 1450+ $maxRevision = $revID;
 1451+ }
 1452+ }
 1453+ }
 1454+ }
 1455+ $parser->mOutput->fr_newestTemplateID = $maxRevision;
 1456+
 1457+ $maxTimestamp = "0";
 1458+ # Fetch the current timestamps of the images.
 1459+ if( !empty($parser->mOutput->mImages) ) {
 1460+ $filenames = array_keys($parser->mOutput->mImages);
 1461+ foreach( $filenames as $filename ) {
 1462+ $file = wfFindFile( Title::makeTitle( NS_IMAGE, $filename ) );
 1463+ $parser->mOutput->fr_ImageSHA1Keys[$filename] = array();
 1464+ if( $file ) {
 1465+ if( $file->getTimestamp() > $maxTimestamp ) {
 1466+ $maxTimestamp = $file->getTimestamp();
 1467+ }
 1468+ $parser->mOutput->fr_ImageSHA1Keys[$filename][$file->getTimestamp()] = $file->getSha1();
 1469+ } else {
 1470+ $parser->mOutput->fr_ImageSHA1Keys[$filename]["0"] = '';
 1471+ }
 1472+ }
 1473+ }
 1474+ # Record the max timestamp
 1475+ $parser->mOutput->fr_newestImageTime = $maxTimestamp;
 1476+
 1477+ wfProfileOut( __METHOD__ );
 1478+ return true;
 1479+ }
 1480+
 1481+ /**
 1482+ * Insert image timestamps/SHA-1s into page output
 1483+ */
 1484+ public static function outputInjectTimestamps( $out, $parserOut ) {
 1485+ # Leave as defaults if missing. Relevant things will be updated only when needed.
 1486+ # We don't want to go around resetting caches all over the place if avoidable...
 1487+ $out->fr_ImageSHA1Keys = isset($parserOut->fr_ImageSHA1Keys) ? $parserOut->fr_ImageSHA1Keys : array();
 1488+
 1489+ return true;
 1490+ }
 1491+
 1492+ /**
 1493+ * Don't let users vandalize pages by moving them.
 1494+ */
 1495+ public static function userCanMove( $title, $user, $action, $result ) {
 1496+ if( $action != 'move' || !self::isPageReviewable( $title ) )
 1497+ return true;
 1498+
 1499+ $flaggedArticle = FlaggedArticle::getInstance( $title );
 1500+ $frev = $flaggedArticle->getStableRev();
 1501+ if( !$frev )
 1502+ return true;
 1503+
 1504+ # Allow for only editors/reviewers to move this
 1505+ $right = $frev->getQuality() ? 'validate' : 'review';
 1506+ if( !$user->isAllowed($right) && !$user->isAllowed('movestable') ) {
 1507+ $result = false;
 1508+ return false;
 1509+ }
 1510+ return true;
 1511+ }
 1512+
 1513+ /**
 1514+ * When an edit is made by a reviewer, if the current revision is the stable
 1515+ * version, try to automatically review it.
 1516+ */
 1517+ public static function maybeMakeEditReviewed( $article, $rev, $baseRevID = false ) {
 1518+ global $wgFlaggedRevsAutoReview, $wgRequest;
 1519+ # Get the user
 1520+ $user = User::newFromId( $rev->getUser() );
 1521+ if( !$wgFlaggedRevsAutoReview || !$user->isAllowed('autoreview') )
 1522+ return true;
 1523+ # Must be in reviewable namespace
 1524+ $title = $article->getTitle();
 1525+ if( !$title || !self::isPageReviewable( $title ) ) {
 1526+ return true;
 1527+ }
 1528+ $frev = null;
 1529+ $reviewableNewPage = false;
 1530+ # Get the revision ID the incoming one was based off
 1531+ if ( !$baseRevID ) {
 1532+ $baseRevID = $wgRequest->getIntOrNull('baseRevId');
 1533+ }
 1534+ # Get what was just the current revision ID
 1535+ $prevRevID = $title->getPreviousRevisionId( $rev->getId(), GAID_FOR_UPDATE );
 1536+ # If baseRevId not given, assume the previous revision ID
 1537+ if ( !$baseRevID ) {
 1538+ $baseRevID = $prevRevID;
 1539+ }
 1540+ if( $baseRevID ) {
 1541+ $frev = self::getFlaggedRev( $title, $baseRevID, false, true, $rev->getPage() );
 1542+ # If the base revision was not reviewed, check if the previous one was
 1543+ if ( !$frev ) {
 1544+ $frev = self::getFlaggedRev( $title, $prevRevID, false, true, $rev->getPage() );
 1545+ }
 1546+ } else {
 1547+ $prevRevID = $title->getPreviousRevisionId( $rev->getId(), GAID_FOR_UPDATE );
 1548+ $prevRev = $prevRevID ? Revision::newFromID( $prevRevID ) : null;
 1549+ # Check for null edits
 1550+ if( $prevRev && $prevRev->getTextId() == $rev->getTextId() ) {
 1551+ $frev = self::getFlaggedRev( $title, $prevRev->getId() );
 1552+ # Check for new pages
 1553+ } else if( !$prevRevID ) {
 1554+ global $wgFlaggedRevsAutoReviewNew;
 1555+ $reviewableNewPage = ($wgFlaggedRevsAutoReviewNew && $user->isAllowed('review'));
 1556+ }
 1557+ }
 1558+ # Is this an edit directly to the stable version?
 1559+ if( $reviewableNewPage || !is_null($frev) ) {
 1560+ # Assume basic flagging level
 1561+ $flags = array();
 1562+ foreach( self::$dimensions as $tag => $minQL ) {
 1563+ $flags[$tag] = 1;
 1564+ }
 1565+ # Review this revision of the page. Let articlesavecomplete hook do rc_patrolled bit...
 1566+ self::autoReviewEdit( $article, $user, $rev->getText(), $rev, $flags, false );
 1567+ }
 1568+ return true;
 1569+ }
 1570+
 1571+ /**
 1572+ * When an edit is made to a page that can't be reviewed, autopatrol if allowed.
 1573+ * This is not loggged for perfomance reasons and no one cares if talk pages and such
 1574+ * are autopatrolled.
 1575+ */
 1576+ public static function autoMarkPatrolled( $article, $user, $text, $c, $m, $a, $b, $flags, $rev ) {
 1577+ if( !$rev ) {
 1578+ return true; // NULL edit
 1579+ }
 1580+ $title = $article->getTitle();
 1581+ $patrol = false;
 1582+ // Is the page reviewable?
 1583+ if( self::isPageReviewable($title) ) {
 1584+ $patrol = self::revIsFlagged( $title, $rev->getId(), GAID_FOR_UPDATE );
 1585+ // Can this be patrolled?
 1586+ } else if( self::isPagePatrollable($title) ) {
 1587+ $patrol = $user->isAllowed('autopatrolother');
 1588+ } else {
 1589+ $patrol = true; // mark by default
 1590+ }
 1591+ if( $patrol ) {
 1592+ $dbw = wfGetDB( DB_MASTER );
 1593+ $dbw->update( 'recentchanges',
 1594+ array( 'rc_patrolled' => 1 ),
 1595+ array( 'rc_this_oldid' => $rev->getId(),
 1596+ 'rc_user_text' => $rev->getRawUserText(),
 1597+ 'rc_timestamp' => $dbw->timestamp( $rev->getTimestamp() ) ),
 1598+ __METHOD__,
 1599+ array( 'USE INDEX' => 'rc_user_text', 'LIMIT' => 1 ) );
 1600+ }
 1601+ return true;
 1602+ }
 1603+
 1604+ /**
 1605+ * Callback that autopromotes user according to the setting in
 1606+ * $wgFlaggedRevsAutopromote. This is not as efficient as it should be
 1607+ */
 1608+ public static function autoPromoteUser( $article, $user, &$text, &$summary, &$m, &$a, &$b, &$f, $rev ) {
 1609+ global $wgFlaggedRevsAutopromote, $wgMemc;
 1610+
 1611+ if( empty($wgFlaggedRevsAutopromote) || !$rev )
 1612+ return true;
 1613+
 1614+ wfProfileIn( __METHOD__ );
 1615+ # Grab current groups
 1616+ $groups = $user->getGroups();
 1617+ # Do not give this to current holders or bots
 1618+ if( in_array( 'bot', $groups ) || in_array( 'editor', $groups ) ) {
 1619+ wfProfileOut( __METHOD__ );
 1620+ return true;
 1621+ }
 1622+ # Do not re-add status if it was previously removed!
 1623+ $p = self::getUserParams( $user );
 1624+ if( isset($p['demoted']) && $p['demoted'] ) {
 1625+ wfProfileOut( __METHOD__ );
 1626+ return true;
 1627+ }
 1628+ # Update any special counters for non-null revisions
 1629+ $changed = false;
 1630+ $pages = array();
 1631+ $p['uniqueContentPages'] = isset($p['uniqueContentPages']) ? $p['uniqueContentPages'] : '';
 1632+ $p['totalContentEdits'] = isset($p['totalContentEdits']) ? $p['totalContentEdits'] : 0;
 1633+ $p['editComments'] = isset($p['editComments']) ? $p['editComments'] : 0;
 1634+ if( $article->getTitle()->isContentPage() ) {
 1635+ $pages = explode( ',', trim($p['uniqueContentPages']) ); // page IDs
 1636+ # Don't let this get bloated for no reason
 1637+ if( count($pages) < $wgFlaggedRevsAutopromote['uniqueContentPages'] && !in_array($article->getId(),$pages) ) {
 1638+ $pages[] = $article->getId();
 1639+ $p['uniqueContentPages'] = preg_replace('/^,/','',implode(',',$pages)); // clear any garbage
 1640+ }
 1641+ $p['totalContentEdits'] += 1;
 1642+ $changed = true;
 1643+ }
 1644+ if( $summary ) {
 1645+ $p['editComments'] += 1;
 1646+ $changed = true;
 1647+ }
 1648+ # Save any updates to user params
 1649+ if( $changed ) {
 1650+ self::saveUserParams( $user, $p );
 1651+ }
 1652+ # Check if user edited enough content pages
 1653+ if( $wgFlaggedRevsAutopromote['totalContentEdits'] > $p['totalContentEdits'] ) {
 1654+ wfProfileOut( __METHOD__ );
 1655+ return true;
 1656+ }
 1657+ # Check if user edited enough unique pages
 1658+ if( $wgFlaggedRevsAutopromote['uniqueContentPages'] > count($pages) ) {
 1659+ wfProfileOut( __METHOD__ );
 1660+ return true;
 1661+ }
 1662+ # Check edit comment use
 1663+ if( $wgFlaggedRevsAutopromote['editComments'] > $p['editComments'] ) {
 1664+ wfProfileOut( __METHOD__ );
 1665+ return true;
 1666+ }
 1667+ # Check if results are cached to avoid DB queries
 1668+ $key = wfMemcKey( 'flaggedrevs', 'autopromote-skip', $user->getID() );
 1669+ $value = $wgMemc->get( $key );
 1670+ if( $value == 'true' ) {
 1671+ wfProfileOut( __METHOD__ );
 1672+ return true;
 1673+ }
 1674+ # Check basic, already available, promotion heuristics first...
 1675+ $now = time();
 1676+ $usercreation = wfTimestamp( TS_UNIX, $user->getRegistration() );
 1677+ $userage = floor(($now - $usercreation) / 86400);
 1678+ if( $userage < $wgFlaggedRevsAutopromote['days'] ) {
 1679+ wfProfileOut( __METHOD__ );
 1680+ return true;
 1681+ }
 1682+ if( $user->getEditCount() < $wgFlaggedRevsAutopromote['edits'] ) {
 1683+ wfProfileOut( __METHOD__ );
 1684+ return true;
 1685+ }
 1686+ if( $wgFlaggedRevsAutopromote['email'] && !$user->isEmailConfirmed() ) {
 1687+ wfProfileOut( __METHOD__ );
 1688+ return true;
 1689+ }
 1690+ # Don't grant to currently blocked users...
 1691+ if( $user->isBlocked() ) {
 1692+ wfProfileOut( __METHOD__ );
 1693+ return true;
 1694+ }
 1695+ # Check if user was ever blocked before
 1696+ if( $wgFlaggedRevsAutopromote['neverBlocked'] ) {
 1697+ $dbr = wfGetDB( DB_SLAVE );
 1698+ $blocked = $dbr->selectField( 'logging', '1',
 1699+ array( 'log_namespace' => NS_USER,
 1700+ 'log_title' => $user->getUserPage()->getDBKey(),
 1701+ 'log_type' => 'block',
 1702+ 'log_action' => 'block' ),
 1703+ __METHOD__,
 1704+ array( 'USE INDEX' => 'user_time' ) );
 1705+ if( $blocked ) {
 1706+ # Make a key to store the results
 1707+ $wgMemc->set( $key, 'true', 3600*24*7 );
 1708+ wfProfileOut( __METHOD__ );
 1709+ return true;
 1710+ }
 1711+ }
 1712+ # See if the page actually has sufficient content...
 1713+ if( $wgFlaggedRevsAutopromote['userpage'] ) {
 1714+ if( !$user->getUserPage()->exists() ) {
 1715+ wfProfileOut( __METHOD__ );
 1716+ return true;
 1717+ }
 1718+ $dbr = isset($dbr) ? $dbr : wfGetDB( DB_SLAVE );
 1719+ $size = $dbr->selectField( 'page', 'page_len',
 1720+ array( 'page_namespace' => $user->getUserPage()->getNamespace(),
 1721+ 'page_title' => $user->getUserPage()->getDBKey() ),
 1722+ __METHOD__ );
 1723+ if( $size < $wgFlaggedRevsAutopromote['userpageBytes'] ) {
 1724+ wfProfileOut( __METHOD__ );
 1725+ return true;
 1726+ }
 1727+ }
 1728+ # Check for edit spacing. This lets us know that the account has
 1729+ # been used over N different days, rather than all in one lump.
 1730+ if( $wgFlaggedRevsAutopromote['spacing'] > 0 && $wgFlaggedRevsAutopromote['benchmarks'] > 1 ) {
 1731+ # Convert days to seconds...
 1732+ $spacing = $wgFlaggedRevsAutopromote['spacing'] * 24 * 3600;
 1733+ # Check the oldest edit
 1734+ $dbr = isset($dbr) ? $dbr : wfGetDB( DB_SLAVE );
 1735+ $lower = $dbr->selectField( 'revision', 'rev_timestamp',
 1736+ array( 'rev_user' => $user->getID() ),
 1737+ __METHOD__,
 1738+ array( 'ORDER BY' => 'rev_timestamp ASC',
 1739+ 'USE INDEX' => 'user_timestamp' ) );
 1740+ # Recursively check for an edit $spacing seconds later, until we are done.
 1741+ # The first edit counts, so we have one less scans to do...
 1742+ $benchmarks = 0;
 1743+ $needed = $wgFlaggedRevsAutopromote['benchmarks'] - 1;
 1744+ while( $lower && $benchmarks < $needed ) {
 1745+ $next = wfTimestamp( TS_UNIX, $lower ) + $spacing;
 1746+ $lower = $dbr->selectField( 'revision', 'rev_timestamp',
 1747+ array( 'rev_user' => $user->getID(),
 1748+ 'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp($next) ) ),
 1749+ __METHOD__,
 1750+ array( 'ORDER BY' => 'rev_timestamp ASC',
 1751+ 'USE INDEX' => 'user_timestamp' ) );
 1752+ if( $lower !== false )
 1753+ $benchmarks++;
 1754+ }
 1755+ if( $benchmarks < $needed ) {
 1756+ # Make a key to store the results
 1757+ $wgMemc->set( $key, 'true', 3600*24*$spacing*($benchmarks - $needed - 1) );
 1758+ wfProfileOut( __METHOD__ );
 1759+ return true;
 1760+ }
 1761+ }
 1762+ # Check if this user is sharing IPs with another users
 1763+ if( $wgFlaggedRevsAutopromote['uniqueIPAddress'] ) {
 1764+ $uid = $user->getId();
 1765+
 1766+ $dbr = isset($dbr) ? $dbr : wfGetDB( DB_SLAVE );
 1767+ $shared = $dbr->selectField( 'recentchanges', '1',
 1768+ array( 'rc_ip' => wfGetIP(),
 1769+ "rc_user != '$uid'" ),
 1770+ __METHOD__,
 1771+ array( 'USE INDEX' => 'rc_ip' ) );
 1772+ if( $shared ) {
 1773+ # Make a key to store the results
 1774+ $wgMemc->set( $key, 'true', 3600*24*7 );
 1775+ wfProfileOut( __METHOD__ );
 1776+ return true;
 1777+ }
 1778+ }
 1779+ # Check for bot attacks/sleepers
 1780+ global $wgSorbsUrl, $wgProxyWhitelist;
 1781+ if( $wgSorbsUrl && $wgFlaggedRevsAutopromote['noSorbsMatches'] ) {
 1782+ $ip = wfGetIP();
 1783+ if( !in_array($ip,$wgProxyWhitelist) && $user->inDnsBlacklist( $ip, $wgSorbsUrl ) ) {
 1784+ # Make a key to store the results
 1785+ $wgMemc->set( $key, 'true', 3600*24*7 );
 1786+ wfProfileOut( __METHOD__ );
 1787+ return true;
 1788+ }
 1789+ }
 1790+ # Check if the user has any recent content edits
 1791+ if( $wgFlaggedRevsAutopromote['recentContentEdits'] > 0 ) {
 1792+ global $wgContentNamespaces;
 1793+
 1794+ $dbr = isset($dbr) ? $dbr : wfGetDB( DB_SLAVE );
 1795+ $res = $dbr->select( 'recentchanges', '1',
 1796+ array( 'rc_user_text' => $user->getName(),
 1797+ 'rc_namespace' => $wgContentNamespaces ),
 1798+ __METHOD__,
 1799+ array( 'USE INDEX' => 'rc_ns_usertext',
 1800+ 'LIMIT' => $wgFlaggedRevsAutopromote['recentContent'] ) );
 1801+ if( $dbr->numRows($res) < $wgFlaggedRevsAutopromote['recentContent'] ) {
 1802+ wfProfileOut( __METHOD__ );
 1803+ return true;
 1804+ }
 1805+ }
 1806+ # Check to see if the user has so many deleted edits that
 1807+ # they don't actually enough live edits. This is because
 1808+ # $user->getEditCount() is the count of edits made, not live.
 1809+ if( $wgFlaggedRevsAutopromote['excludeDeleted'] ) {
 1810+ $dbr = isset($dbr) ? $dbr : wfGetDB( DB_SLAVE );
 1811+ $minDiff = $user->getEditCount() - $wgFlaggedRevsAutopromote['days'] + 1;
 1812+ # Use an estimate if the number starts to get large
 1813+ if( $minDiff <= 100 ) {
 1814+ $res = $dbr->select( 'archive', '1',
 1815+ array( 'ar_user_text' => $user->getName() ),
 1816+ __METHOD__,
 1817+ array( 'USE INDEX' => 'usertext_timestamp', 'LIMIT' => $minDiff ) );
 1818+ $deletedEdits = $dbr->numRows($res);
 1819+ } else {
 1820+ $deletedEdits = $dbr->estimateRowCount( 'archive', '1',
 1821+ array( 'ar_user_text' => $user->getName() ),
 1822+ __METHOD__,
 1823+ array( 'USE INDEX' => 'usertext_timestamp' ) );
 1824+ }
 1825+ if( $deletedEdits >= $minDiff ) {
 1826+ wfProfileOut( __METHOD__ );
 1827+ return true;
 1828+ }
 1829+ }
 1830+ # Add editor rights
 1831+ $newGroups = $groups ;
 1832+ array_push( $newGroups, 'editor' );
 1833+ # Lets NOT spam RC, set $RC to false
 1834+ $log = new LogPage( 'rights', false );
 1835+ $log->addEntry( 'rights', $user->getUserPage(), wfMsg('rights-editor-autosum'),
 1836+ array( implode(', ',$groups), implode(', ',$newGroups) ) );
 1837+ $user->addGroup('editor');
 1838+
 1839+ wfProfileOut( __METHOD__ );
 1840+ return true;
 1841+ }
 1842+
 1843+ /**
 1844+ * Record demotion so that auto-promote will be disabled
 1845+ */
 1846+ public static function recordDemote( $u, $addgroup, $removegroup ) {
 1847+ if( $removegroup && in_array('editor',$removegroup) ) {
 1848+ $params = self::getUserParams( $u );
 1849+ $params['demoted'] = 1;
 1850+ self::saveUserParams( $u, $params );
 1851+ }
 1852+ return true;
 1853+ }
 1854+
 1855+ /**
 1856+ * Add user preference to form HTML
 1857+ */
 1858+ public static function injectPreferences( $form, $out ) {
 1859+ $prefsHtml = FlaggedRevsXML::stabilityPreferences( $form );
 1860+ $out->addHTML( $prefsHtml );
 1861+ return true;
 1862+ }
 1863+
 1864+ /**
 1865+ * Add user preference to form object based on submission
 1866+ */
 1867+ public static function injectFormPreferences( $form, $request ) {
 1868+ global $wgUser;
 1869+ $form->mFlaggedRevsStable = $request->getInt( 'wpFlaggedRevsStable' );
 1870+ $form->mFlaggedRevsSUI = $request->getInt( 'wpFlaggedRevsSUI' );
 1871+ $form->mFlaggedRevsWatch = $wgUser->isAllowed( 'review' ) ? $request->getInt( 'wpFlaggedRevsWatch' ) : 0;
 1872+ return true;
 1873+ }
 1874+
 1875+ /**
 1876+ * Set preferences on form based on user settings
 1877+ */
 1878+ public static function resetPreferences( $form, $user ) {
 1879+ global $wgSimpleFlaggedRevsUI;
 1880+ $form->mFlaggedRevsStable = $user->getOption( 'flaggedrevsstable' );
 1881+ $form->mFlaggedRevsSUI = $user->getOption( 'flaggedrevssimpleui', intval($wgSimpleFlaggedRevsUI) );
 1882+ $form->mFlaggedRevsWatch = $user->getOption( 'flaggedrevswatch' );
 1883+ return true;
 1884+ }
 1885+
 1886+ /**
 1887+ * Set user preferences into user object before it is applied to DB
 1888+ */
 1889+ public static function savePreferences( $form, $user, &$msg ) {
 1890+ $user->setOption( 'flaggedrevsstable', $form->validateInt( $form->mFlaggedRevsStable, 0, 1 ) );
 1891+ $user->setOption( 'flaggedrevssimpleui', $form->validateInt( $form->mFlaggedRevsSUI, 0, 1 ) );
 1892+ $user->setOption( 'flaggedrevswatch', $form->validateInt( $form->mFlaggedRevsWatch, 0, 1 ) );
 1893+ return true;
 1894+ }
 1895+
 1896+ /**
 1897+ * Create revision link for log line entry
 1898+ * @param string $type
 1899+ * @param string $action
 1900+ * @param object $title
 1901+ * @param array $paramArray
 1902+ * @param string $c
 1903+ * @param string $r user tool links
 1904+ * @param string $t timestamp of the log entry
 1905+ * @return bool true
 1906+ */
 1907+ public static function reviewLogLine( $type = '', $action = '', $title = null, $paramArray = array(), &$c = '', &$r = '', $t = '' ) {
 1908+ # Show link to page with oldid=x
 1909+ if( $type == 'review' && in_array($action,array('approve','approve2','unapprove','unapprove2')) ) {
 1910+ global $wgUser;
 1911+ if( is_object($title) && isset($paramArray[0]) ) {
 1912+ $r = '(' . $wgUser->getSkin()->makeKnownLinkObj( $title,
 1913+ wfMsgHtml('review-logentry-id',$paramArray[0]), "oldid={$paramArray[0]}") . ')';
 1914+ }
 1915+ }
 1916+ return true;
 1917+ }
 1918+
 1919+ public static function imagePageFindFile( $imagePage, &$normalFile, &$displayFile ) {
 1920+ $flaggedArticle = FlaggedArticle::getInstance( $imagePage );
 1921+ $flaggedArticle->imagePageFindFile( $normalFile, $displayFile );
 1922+ return true;
 1923+ }
 1924+
 1925+ static function setActionTabs( $skin, &$contentActions ) {
 1926+ global $wgArticle;
 1927+ if ( $wgArticle ) {
 1928+ FlaggedArticle::getInstance( $wgArticle )->setActionTabs( $skin, $contentActions );
 1929+ }
 1930+ return true;
 1931+ }
 1932+
 1933+ static function setLastModified( $skin, &$tpl ) {
 1934+ global $wgArticle;
 1935+ if ( $wgArticle ) {
 1936+ FlaggedArticle::getInstance( $wgArticle )->setLastModified( $skin, $tpl );
 1937+ }
 1938+ return true;
 1939+ }
 1940+
 1941+ static function onArticleViewHeader( $article, &$outputDone, &$pcache ) {
 1942+ $flaggedArticle = FlaggedArticle::getInstance( $article );
 1943+ $flaggedArticle->maybeUpdateMainCache( &$outputDone, &$pcache );
 1944+ $flaggedArticle->setPageContent( &$outputDone, &$pcache );
 1945+ $flaggedArticle->addPatrolLink( &$outputDone, &$pcache );
 1946+ return true;
 1947+ }
 1948+
 1949+ static function setPermaLink( $skin, &$navUrls, &$revId, &$id ) {
 1950+ global $wgArticle;
 1951+ if ( $wgArticle ) {
 1952+ FlaggedArticle::getInstance( $wgArticle )->setPermaLink( $skin, &$navUrls, &$revId, &$id );
 1953+ }
 1954+ return true;
 1955+ }
 1956+
 1957+ static function addToEditView( $editPage ) {
 1958+ return FlaggedArticle::getInstance( $editPage->mArticle )->addToEditView( $editPage );
 1959+ }
 1960+
 1961+ static function addReviewForm( $out ) {
 1962+ global $wgArticle;
 1963+ if ( $out->isArticleRelated() ) {
 1964+ FlaggedArticle::getInstance( $wgArticle )->addReviewForm( $out );
 1965+ }
 1966+ return true;
 1967+ }
 1968+
 1969+ static function addVisibilityLink( $out ) {
 1970+ global $wgArticle;
 1971+ if ( $out->isArticleRelated() ) {
 1972+ FlaggedArticle::getInstance( $wgArticle )->addVisibilityLink( $out );
 1973+ }
 1974+ return true;
 1975+ }
 1976+
 1977+ static function addToHistLine( $history, $row, &$s ) {
 1978+ return FlaggedArticle::getInstance( $history->getArticle() )->addToHistLine( $history, $row, &$s );
 1979+ }
 1980+
 1981+ static function addToFileHistLine( $historyList, $file, &$row, &$rowClass ) {
 1982+ return FlaggedArticle::getInstance( $historyList->getImagePage() )
 1983+ -> addToFileHistLine( $historyList, $file, &$row, &$rowClass );
 1984+ }
 1985+
 1986+ static function injectReviewDiffURLParams( $article, &$sectionAnchor, &$extraQuery ) {
 1987+ return FlaggedArticle::getInstance( $article )->injectReviewDiffURLParams( &$sectionAnchor, &$extraQuery );
 1988+ }
 1989+
 1990+ static function onDiffViewHeader( $diff, $oldRev, $newRev ) {
 1991+ $flaggedArticle = FlaggedArticle::getInstance( $diff->getTitle() );
 1992+ $flaggedArticle->addPatrolAndDiffLink( $diff, $oldRev, $newRev );
 1993+ $flaggedArticle->addDiffNoticeAndIncludes( $diff, $oldRev, $newRev );
 1994+ return true;
 1995+ }
 1996+
 1997+ static function addRevisionIDField( $editPage, $out ) {
 1998+ return FlaggedArticle::getInstance( $editPage->mArticle )->addRevisionIDField( $editPage, $out );
 1999+ }
 2000+}
 2001+
Property changes on: trunk/extensions/FlaggedRevs/FlaggedRevs.class.php
___________________________________________________________________
Name: svn:eol-style
12002 + native
Index: trunk/extensions/FlaggedRevs/FlaggedRevsXML.php
@@ -121,14 +121,14 @@
122122 /**
123123 * @param Row $trev, flagged revision row
124124 * @param string $html, the short message HTML
125 - * @param int $revs_since, revisions since review
 125+ * @param int $revsSince, revisions since review
126126 * @param bool $stable, are we referring to the stable revision?
127127 * @param bool $synced, does stable=current and this is one of them?
128128 * @param bool $old, is this an old stable version?
129129 * @return string
130130 * Generates a review box using a table using FlaggedRevsXML::addTagRatings()
131131 */
132 - public static function prettyRatingBox( $frev, $shtml, $revs_since, $stable=true, $synced=false, $old=false ) {
 132+ public static function prettyRatingBox( $frev, $shtml, $revsSince, $stable=true, $synced=false, $old=false ) {
133133 global $wgLang;
134134 # Get quality level
135135 $flags = $frev->getTags();
@@ -149,7 +149,7 @@
150150 # Construct some tagging
151151 if( $synced ) {
152152 $msg = $quality ? 'revreview-quality-same' : 'revreview-basic-same';
153 - $html = wfMsgExt($msg, array('parseinline'), $frev->getRevId(), $time, $revs_since );
 153+ $html = wfMsgExt($msg, array('parseinline'), $frev->getRevId(), $time, $revsSince );
154154 } else if( $old ) {
155155 $msg = $quality ? 'revreview-quality-old' : 'revreview-basic-old';
156156 $html = wfMsgExt($msg, array('parseinline'), $frev->getRevId(), $time );
@@ -159,8 +159,8 @@
160160 } else {
161161 $msg = $quality ? 'revreview-newest-quality' : 'revreview-newest-basic';
162162 }
163 - $msg .= ($revs_since == 0) ? '-i' : '';
164 - $html = wfMsgExt($msg, array('parseinline'), $frev->getRevId(), $time, $revs_since );
 163+ $msg .= ($revsSince == 0) ? '-i' : '';
 164+ $html = wfMsgExt($msg, array('parseinline'), $frev->getRevId(), $time, $revsSince );
165165 }
166166 # Make fancy box...
167167 $box = "<table border='0' cellspacing='0' style='background: none;'>\n";
Index: trunk/extensions/FlaggedRevs/flaggedrevs.js
@@ -36,8 +36,9 @@
3737 var allzero = true;
3838 var somezero = false;
3939
40 - for( flag in wgFlaggedRevsJSparams ) {
41 - var levels = document.getElementsByName(flag);
 40+ for( tag in wgFlaggedRevsParams.tags ) {
 41+ var controlName = "wp" + tag;
 42+ var levels = document.getElementsByName(controlName);
4243 var selectedlevel = 0; // default
4344
4445 if( levels[0].nodeName == 'SELECT' ) {
@@ -61,9 +62,9 @@
6263 }
6364
6465 // Get quality level for this tag
65 - QL = wgFlaggedRevsJSparams[flag];
 66+ qualityLevel = wgFlaggedRevsParams.tags[tag];
6667
67 - if( selectedlevel < QL ) {
 68+ if( selectedlevel < qualityLevel ) {
6869 quality = false; // not a quality review
6970 }
7071 if( selectedlevel > 0 ) {
Index: trunk/extensions/FlaggedRevs/FlaggedRevsPage.php
@@ -69,7 +69,6 @@
7070 $this->imageParams = $wgRequest->getVal( 'imageParams' );
7171 $this->validatedParams = $wgRequest->getVal( 'validatedParams' );
7272
73 - global $wgReviewCodes;
7473 # Special token to discourage fiddling...
7574 $checkCode = self::getValidationKey( $this->templateParams, $this->imageParams, $wgUser->getID(), $this->oldid );
7675 # Must match up

Follow-up revisions

RevisionCommit summaryAuthorDate
r79646Removed change of $wgArticle, no effect since ImagePage's constructor has no ...ialex15:02, 5 January 2011

Status & tagging log