Index: trunk/phase3/includes/Article.php |
— | — | @@ -1324,10 +1324,10 @@ |
1325 | 1325 | } |
1326 | 1326 | } |
1327 | 1327 | |
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 ) ); |
1330 | 1330 | |
1331 | | - $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraq ); |
| 1331 | + $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraQuery ); |
1332 | 1332 | } |
1333 | 1333 | return $good; |
1334 | 1334 | } |
— | — | @@ -1573,16 +1573,16 @@ |
1574 | 1574 | * |
1575 | 1575 | * @param boolean $noRedir Add redirect=no |
1576 | 1576 | * @param string $sectionAnchor section to redirect to, including "#" |
1577 | | - * @param string $extraq, extra query params |
| 1577 | + * @param string $extraQuery, extra query params |
1578 | 1578 | */ |
1579 | | - function doRedirect( $noRedir = false, $sectionAnchor = '', $extraq = '' ) { |
| 1579 | + function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) { |
1580 | 1580 | global $wgOut; |
1581 | 1581 | if ( $noRedir ) { |
1582 | 1582 | $query = 'redirect=no'; |
1583 | | - if( $extraq ) |
| 1583 | + if( $extraQuery ) |
1584 | 1584 | $query .= "&$query"; |
1585 | 1585 | } else { |
1586 | | - $query = $extraq; |
| 1586 | + $query = $extraQuery; |
1587 | 1587 | } |
1588 | 1588 | $wgOut->redirect( $this->mTitle->getFullURL( $query ) . $sectionAnchor ); |
1589 | 1589 | } |
Index: trunk/phase3/includes/ImagePage.php |
— | — | @@ -13,45 +13,36 @@ |
14 | 14 | /* private */ var $img; // Image object |
15 | 15 | /* private */ var $displayImg; |
16 | 16 | /* private */ var $repo; |
17 | | - /* private */ var $time; |
18 | 17 | /* private */ var $fileLoaded; |
19 | 18 | var $mExtraDescription = false; |
20 | 19 | var $dupes; |
21 | 20 | |
22 | | - function __construct( $title, $time = null ) { |
| 21 | + function __construct( $title ) { |
23 | 22 | parent::__construct( $title ); |
24 | 23 | |
25 | 24 | global $wgRequest; |
26 | | - $time = is_null($time) ? $wgRequest->getVal( 'filetimestamp' ) : $time; |
27 | | - $time = $time ? $time : false; // be clear about type |
28 | | - $this->time = $time; |
29 | 25 | $this->dupes = null; |
30 | 26 | $this->repo = null; |
31 | 27 | } |
32 | | - |
| 28 | + |
33 | 29 | protected function loadFile() { |
34 | 30 | if ( $this->fileLoaded ) { |
35 | 31 | return true; |
36 | 32 | } |
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 ); |
48 | 41 | } |
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; |
53 | 42 | } |
| 43 | + if ( !$this->displayImg ) { |
| 44 | + $this->displayImg = $this->img; |
| 45 | + } |
54 | 46 | $this->repo = $this->img->getRepo(); |
55 | | - $this->fileLoaded = true; |
56 | 47 | } |
57 | 48 | |
58 | 49 | /** |
— | — | @@ -609,13 +600,11 @@ |
610 | 601 | */ |
611 | 602 | function imageHistory() |
612 | 603 | { |
613 | | - global $wgUser, $wgOut, $wgUseExternalEditor; |
| 604 | + global $wgOut, $wgUseExternalEditor; |
614 | 605 | |
615 | | - $sk = $wgUser->getSkin(); |
616 | | - |
617 | 606 | $this->loadFile(); |
618 | 607 | if ( $this->img->exists() ) { |
619 | | - $list = new ImageHistoryList( $sk, $this->img, $this->displayImg ); |
| 608 | + $list = new ImageHistoryList( $this ); |
620 | 609 | $file = $this->img; |
621 | 610 | $dims = $file->getDimensionsString(); |
622 | 611 | $s = $list->beginImageHistoryList(); |
— | — | @@ -786,15 +775,29 @@ |
787 | 776 | */ |
788 | 777 | class ImageHistoryList { |
789 | 778 | |
790 | | - protected $img, $skin, $title, $repo; |
| 779 | + protected $imagePage, $img, $skin, $title, $repo; |
791 | 780 | |
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; |
797 | 788 | } |
798 | 789 | |
| 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 | + |
799 | 802 | public function beginImageHistoryList() { |
800 | 803 | global $wgOut, $wgUser; |
801 | 804 | return Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'filehist' ) ) |
— | — | @@ -936,9 +939,9 @@ |
937 | 940 | } |
938 | 941 | $row .= '</td>'; |
939 | 942 | |
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'" : ""; |
942 | 945 | |
943 | | - return "<tr{$trCSS}>{$row}</tr>\n"; |
| 946 | + return "<tr{$classAttr}>{$row}</tr>\n"; |
944 | 947 | } |
945 | 948 | } |
Index: trunk/phase3/includes/DifferenceEngine.php |
— | — | @@ -73,6 +73,10 @@ |
74 | 74 | $this->mRefreshCache = $refreshCache; |
75 | 75 | } |
76 | 76 | |
| 77 | + function getTitle() { |
| 78 | + return $this->mTitle; |
| 79 | + } |
| 80 | + |
77 | 81 | function showDiffPage( $diffOnly = false ) { |
78 | 82 | global $wgUser, $wgOut, $wgUseExternalEditor, $wgUseRCPatrol; |
79 | 83 | wfProfileIn( __METHOD__ ); |
Index: trunk/phase3/includes/PageHistory.php |
— | — | @@ -41,6 +41,10 @@ |
42 | 42 | $this->preCacheMessages(); |
43 | 43 | } |
44 | 44 | |
| 45 | + function getArticle() { |
| 46 | + return $this->mArticle; |
| 47 | + } |
| 48 | + |
45 | 49 | /** |
46 | 50 | * As we use the same small set of messages in various methods and that |
47 | 51 | * they are called often, we call them once and save them in $this->message |
— | — | @@ -281,7 +285,7 @@ |
282 | 286 | $s .= ' (' . implode( ' | ', $tools ) . ')'; |
283 | 287 | } |
284 | 288 | |
285 | | - wfRunHooks( 'PageHistoryLineEnding', array( &$row , &$s ) ); |
| 289 | + wfRunHooks( 'PageHistoryLineEnding', array( $this, &$row , &$s ) ); |
286 | 290 | |
287 | 291 | return "<li>$s</li>\n"; |
288 | 292 | } |
Index: trunk/extensions/FlaggedRevs/FlaggedRevs.php |
— | — | @@ -51,12 +51,6 @@ |
52 | 52 | # Below are groups that see the current revision by default. |
53 | 53 | $wgFlaggedRevsExceptions = array( 'user' ); |
54 | 54 | |
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 | | - |
61 | 55 | # Can users make comments that will show up below flagged revisions? |
62 | 56 | $wgFlaggedRevsComments = false; |
63 | 57 | # Redirect users out to review changes since stable version on save? |
— | — | @@ -100,7 +94,7 @@ |
101 | 95 | $wgFlaggedRevTags = array( 'accuracy'=>2, 'depth'=>1, 'style'=>1 ); |
102 | 96 | # How high can we rate these revisions? |
103 | 97 | $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" |
105 | 99 | $wgFlaggedRevPristine = 4; |
106 | 100 | # Who can set what flags to what level? (use -1 or 0 for not at all) |
107 | 101 | # Users cannot lower tags from a level they can't set |
— | — | @@ -121,6 +115,10 @@ |
122 | 116 | # There must be four codes, and only the first four are checked. |
123 | 117 | $wgReviewCodes = array(); |
124 | 118 | |
| 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 | + |
125 | 123 | # Lets some users access the review UI and set some flags |
126 | 124 | $wgAvailableRights[] = 'review'; |
127 | 125 | # Let some users set higher settings |
— | — | @@ -194,19 +192,20 @@ |
195 | 193 | # How far the logs for overseeing quality revisions and depreciations go |
196 | 194 | $wgFlaggedRevsOversightAge = 7 * 24 * 3600; |
197 | 195 | |
198 | | -# Hour many hours pending review is considering long? |
| 196 | +# How many hours pending review is considering long? |
199 | 197 | $wgFlaggedRevsLongPending = 3; |
200 | 198 | |
201 | 199 | # End of configuration variables. |
202 | 200 | ######### |
203 | 201 | |
204 | 202 | # Bump this number every time you change flaggedrevs.css/flaggedrevs.js |
205 | | -$wgFlaggedRevStyleVersion = 21; |
| 203 | +$wgFlaggedRevStyleVersion = 22; |
206 | 204 | |
207 | 205 | $wgExtensionFunctions[] = 'efLoadFlaggedRevs'; |
208 | 206 | |
209 | 207 | $dir = dirname(__FILE__) . '/'; |
210 | 208 | $wgExtensionMessagesFiles['FlaggedRevsPage'] = $dir . 'FlaggedRevsPage.i18n.php'; |
| 209 | +$wgAutoloadClasses['FlaggedRevs'] = $dir.'FlaggedRevs.class.php'; |
211 | 210 | |
212 | 211 | # Load general UI |
213 | 212 | $wgAutoloadClasses['FlaggedRevsXML'] = $dir . 'FlaggedRevsXML.php'; |
— | — | @@ -247,14 +246,81 @@ |
248 | 247 | $wgAutoloadClasses['DepreciationOversight'] = $dir . 'FlaggedRevsPage.php'; |
249 | 248 | $wgSpecialPageGroups['DepreciationOversight'] = 'quality'; |
250 | 249 | |
| 250 | +######### Hook attachments ######### |
251 | 251 | # Remove stand-alone patrolling |
252 | 252 | $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'; |
253 | 292 | |
| 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 | + |
254 | 321 | function efLoadFlaggedRevs() { |
255 | | - global $wgOut, $wgHooks, $wgLang, $wgFlaggedArticle, $wgUseRCPatrol; |
| 322 | + global $wgOut, $wgHooks, $wgLang, $wgUseRCPatrol; |
256 | 323 | # Initialize |
257 | 324 | FlaggedRevs::load(); |
258 | | - $wgFlaggedArticle = null; |
259 | 325 | |
260 | 326 | wfLoadExtensionMessages( 'FlaggedRevsPage' ); |
261 | 327 | |
— | — | @@ -262,103 +328,8 @@ |
263 | 329 | # When revisions are flagged, they count as patrolled |
264 | 330 | $wgUseRCPatrol = true; |
265 | 331 | |
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 | | - ######### |
314 | 332 | } |
315 | 333 | |
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 | | - |
363 | 334 | # Add review log and such |
364 | 335 | $wgLogTypes[] = 'review'; |
365 | 336 | $wgLogNames['review'] = 'review-logpage'; |
— | — | @@ -378,1958 +349,6 @@ |
379 | 350 | |
380 | 351 | $wgHooks['LoadExtensionSchemaUpdates'][] = 'efFlaggedRevsSchemaUpdates'; |
381 | 352 | |
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 | | - |
2334 | 353 | function efFlaggedRevsSchemaUpdates() { |
2335 | 354 | global $wgDBtype, $wgExtNewFields, $wgExtPGNewFields, $wgExtNewIndexes, $wgExtNewTables; |
2336 | 355 | $base = dirname(__FILE__); |
Index: trunk/extensions/FlaggedRevs/maintenance/updateAutoPromote.php |
— | — | @@ -3,7 +3,7 @@ |
4 | 4 | if ( getenv( 'MW_INSTALL_PATH' ) ) { |
5 | 5 | $IP = getenv( 'MW_INSTALL_PATH' ); |
6 | 6 | } else { |
7 | | - $IP = dirname(__FILE__).'/../..'; |
| 7 | + $IP = dirname(__FILE__).'/../../..'; |
8 | 8 | } |
9 | 9 | require "$IP/maintenance/commandLine.inc"; |
10 | 10 | require dirname(__FILE__) . '/updateAutoPromote.inc'; |
Index: trunk/extensions/FlaggedRevs/FlaggedArticle.php |
— | — | @@ -3,45 +3,79 @@ |
4 | 4 | class FlaggedArticle extends Article { |
5 | 5 | public $isDiffFromStable = false; |
6 | 6 | protected $stableRev = null; |
7 | | - protected $pageconfig = null; |
| 7 | + protected $pageConfig = null; |
8 | 8 | protected $flags = null; |
9 | 9 | protected $reviewNotice = ''; |
10 | 10 | protected $file = NULL; |
| 11 | + protected $parent; |
| 12 | + |
| 13 | + |
11 | 14 | /** |
| 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 | + /** |
12 | 46 | * Does the config and current URL params allow |
13 | 47 | * for overriding by stable revisions? |
14 | 48 | */ |
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 |
18 | 52 | $action = $wgRequest->getVal( 'action', 'view' ); |
19 | | - if( ($action !='view' && $action !='purge') || !$this->isReviewable() ) |
| 53 | + if( ($action !='view' && $action !='purge') || !$this->isReviewable() ) |
20 | 54 | return false; |
21 | | - # Does not apply to diffs/old revision. Explicit requests |
| 55 | + # Does not apply to diffs/old revision. Explicit requests |
22 | 56 | # 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') ) |
24 | 58 | return false; |
25 | 59 | # Check user preferences |
26 | 60 | if( $wgUser->getOption('flaggedrevsstable') ) |
27 | 61 | return !( $wgRequest->getIntOrNull('stable') === 0 ); |
28 | 62 | # Get page configuration |
29 | 63 | $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 ); |
38 | 72 | } |
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 | + } |
43 | 77 | # 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? |
46 | 80 | } else if( $wgRequest->getIntOrNull('stable') === 1 ) { |
47 | 81 | return true; |
48 | 82 | } |
— | — | @@ -52,20 +86,20 @@ |
53 | 87 | * Is this user shown the stable version by default for this page? |
54 | 88 | */ |
55 | 89 | public function showStableByDefault() { |
56 | | - global $wgFlaggedRevsOverride, $wgFlaggedRevsExceptions, $wgUser; |
| 90 | + global $wgFlaggedRevsExceptions, $wgUser; |
57 | 91 | # Get page configuration |
58 | 92 | $config = $this->getVisibilitySettings(); |
59 | 93 | if( !$config['override'] ) |
60 | 94 | 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 | + } |
70 | 104 | return true; |
71 | 105 | } |
72 | 106 | |
— | — | @@ -73,7 +107,7 @@ |
74 | 108 | * Is this article reviewable? |
75 | 109 | */ |
76 | 110 | public function isReviewable() { |
77 | | - return FlaggedRevs::isPageReviewable( $this->getTitle() ); |
| 111 | + return FlaggedRevs::isPageReviewable( $this->parent->getTitle() ); |
78 | 112 | } |
79 | 113 | |
80 | 114 | /** |
— | — | @@ -90,23 +124,23 @@ |
91 | 125 | * Adds stable version status/info tags and notes |
92 | 126 | * Adds a quick review form on the bottom if needed |
93 | 127 | */ |
94 | | - public function setPageContent( $article, &$outputDone, &$pcache ) { |
| 128 | + public function setPageContent( &$outputDone, &$pcache ) { |
95 | 129 | global $wgRequest, $wgOut, $wgUser, $wgLang; |
96 | 130 | # Only trigger for reviewable pages |
97 | | - if( !FlaggedRevs::isPageReviewable( $article->getTitle() ) ) { |
| 131 | + if( !FlaggedRevs::isPageReviewable( $this->parent->getTitle() ) ) { |
98 | 132 | return true; |
99 | 133 | } |
100 | 134 | # Only trigger on article view for content pages, not for protect/delete/hist... |
101 | 135 | $action = $wgRequest->getVal( 'action', 'view' ); |
102 | | - if( ($action !='view' && $action !='purge') || !$article || !$article->exists() ) |
| 136 | + if( ($action !='view' && $action !='purge') || !$this->parent->exists() ) |
103 | 137 | return true; |
104 | 138 | # Do not clutter up diffs any further... |
105 | 139 | if( $wgRequest->getVal('diff') ) { |
106 | | - return true; |
| 140 | + return true; |
107 | 141 | } else if( $wgRequest->getVal('oldid') ) { |
108 | 142 | # 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 ); |
111 | 145 | # Give a notice if this rev ID corresponds to a reviewed version... |
112 | 146 | if( !is_null($frev) ) { |
113 | 147 | $time = $wgLang->date( $frev->getTimestamp(), true ); |
— | — | @@ -135,15 +169,15 @@ |
136 | 170 | # Also, check for any explicitly requested old stable version... |
137 | 171 | $reqId = $wgRequest->getVal('stableid'); |
138 | 172 | if( $reqId === "best" ) { |
139 | | - $reqId = FlaggedRevs::getPrimeFlaggedRevId( $article ); |
| 173 | + $reqId = FlaggedRevs::getPrimeFlaggedRevId( $this->parent ); |
140 | 174 | } |
141 | 175 | if( $stableId && $reqId ) { |
142 | 176 | if( $reqId != $stableId ) { |
143 | | - $frev = FlaggedRevs::getFlaggedRev( $article->getTitle(), $reqId, true ); |
| 177 | + $frev = FlaggedRevs::getFlaggedRev( $this->parent->getTitle(), $reqId, true ); |
144 | 178 | $old = true; // old reviewed version requested by ID |
145 | 179 | if( !$frev ) { |
146 | 180 | $wgOut->addWikiText( wfMsg('revreview-invalid') ); |
147 | | - $wgOut->returnToMain( false, $article->getTitle() ); |
| 181 | + $wgOut->returnToMain( false, $this->parent->getTitle() ); |
148 | 182 | # Tell MW that parser output is done |
149 | 183 | $outputDone = true; |
150 | 184 | $pcache = false; |
— | — | @@ -166,25 +200,25 @@ |
167 | 201 | // requesting the stable revision ("&stableid=x"), defer to override |
168 | 202 | // behavior below, since it is the same as ("&stable=1"). |
169 | 203 | if( $old ) { |
170 | | - $revs_since = FlaggedRevs::getRevCountSince( $article, $frev->getRevId() ); |
| 204 | + $revsSince = FlaggedRevs::getRevCountSince( $this->parent, $frev->getRevId() ); |
171 | 205 | $text = $frev->getTextForParse(); |
172 | | - $parserOut = FlaggedRevs::parseStableText( $article, $text, $frev->getRevId() ); |
| 206 | + $parserOut = FlaggedRevs::parseStableText( $this->parent, $text, $frev->getRevId() ); |
173 | 207 | # Construct some tagging for non-printable outputs. Note that the pending |
174 | 208 | # notice has all this info already, so don't do this if we added that already. |
175 | 209 | if( !$wgOut->isPrintable() ) { |
176 | | - $css = $quality ? 'fr-icon-quality' : 'fr-icon-stable'; |
| 210 | + $class = $quality ? 'fr-icon-quality' : 'fr-icon-stable'; |
177 | 211 | $tooltip = $quality ? 'revreview-quality-title' : 'revreview-stable-title'; |
178 | 212 | $tooltip = wfMsgHtml($tooltip); |
179 | 213 | // Simple icon-based UI |
180 | 214 | if( FlaggedRevs::useSimpleUI() ) { |
181 | 215 | $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>" . |
183 | 217 | 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 ); |
185 | 219 | // Standard UI |
186 | 220 | } else { |
187 | 221 | $msg = $quality ? 'revreview-quality-old' : 'revreview-basic-old'; |
188 | | - $tag .= "<span class='{$css}' title=\"{$tooltip}\"></span>" . |
| 222 | + $tag .= "<span class='{$class}' title=\"{$tooltip}\"></span>" . |
189 | 223 | wfMsgExt( $msg, array('parseinline'), $frev->getRevId(), $time ); |
190 | 224 | # Hide clutter |
191 | 225 | if( !empty($flags) ) { |
— | — | @@ -197,9 +231,9 @@ |
198 | 232 | } |
199 | 233 | } |
200 | 234 | # Output HTML |
201 | | - $wgOut->addParserOutput( $parserOut ); |
| 235 | + $wgOut->addParserOutput( $parserOut ); |
202 | 236 | $wgOut->setRevisionId( $frev->getRevId() ); |
203 | | - $notes = $this->ReviewNotes( $frev ); |
| 237 | + $notes = $this->getReviewNotes( $frev ); |
204 | 238 | # Tell MW that parser output is done |
205 | 239 | $outputDone = true; |
206 | 240 | $pcache = false; |
— | — | @@ -208,13 +242,13 @@ |
209 | 243 | // requesting the stable revision ("&stableid=x"), defer to override |
210 | 244 | // behavior below, since it is the same as ("&stable=1"). |
211 | 245 | } 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 ); |
214 | 248 | # Give notice to newewer users if an unreviewed edit was completed... |
215 | 249 | if( $wgRequest->getVal('shownotice') && !$synced && !$wgUser->isAllowed('review') ) { |
216 | 250 | $tooltip = wfMsgHtml('revreview-draft-title'); |
217 | 251 | $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); |
219 | 253 | $pending = "<div id='mw-reviewnotice' class='flaggedrevs_preview plainlinks'>$pending</div>"; |
220 | 254 | # Notice should always use subtitle |
221 | 255 | $this->reviewNotice = $pending; |
— | — | @@ -224,36 +258,36 @@ |
225 | 259 | # Construct some tagging for non-printable outputs. Note that the pending |
226 | 260 | # notice has all this info already, so don't do this if we added that already. |
227 | 261 | if( !$wgOut->isPrintable() && !$pending ) { |
228 | | - $css = 'fr-icon-current'; // default |
| 262 | + $class = 'fr-icon-current'; // default |
229 | 263 | $tooltip = 'revreview-draft-title'; |
230 | 264 | // Simple icon-based UI |
231 | 265 | if( FlaggedRevs::useSimpleUI() ) { |
232 | 266 | if( $synced ) { |
233 | 267 | $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'; |
235 | 269 | $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 ); |
237 | 271 | } else { |
238 | 272 | $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 ); |
240 | 274 | } |
241 | 275 | $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 ); |
244 | 278 | // Standard UI |
245 | 279 | } else { |
246 | 280 | if( $synced ) { |
247 | 281 | $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'; |
249 | 283 | $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 ); |
251 | 285 | } else { |
252 | 286 | $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 ); |
255 | 289 | } |
256 | 290 | $tooltip = wfMsgHtml($tooltip); |
257 | | - $tag .= "<span class='{$css}' title=\"{$tooltip}\"></span>" . $msgHTML; |
| 291 | + $tag .= "<span class='{$class}' title=\"{$tooltip}\"></span>" . $msgHTML; |
258 | 292 | # Hide clutter |
259 | 293 | if( !empty($flags) ) { |
260 | 294 | $tag .= " <a id='mw-revisiontoggle' class='flaggedrevs_toggle' style='display:none;'" . |
— | — | @@ -266,40 +300,40 @@ |
267 | 301 | } |
268 | 302 | // The relevant conditions are met to override the page with the stable version. |
269 | 303 | } 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() ); |
272 | 306 | # Get parsed stable version |
273 | | - $parserOut = FlaggedRevs::getPageCache( $article ); |
| 307 | + $parserOut = FlaggedRevs::getPageCache( $this->parent ); |
274 | 308 | if( $parserOut==false ) { |
275 | 309 | $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 ); |
281 | 315 | # Construct some tagging |
282 | 316 | if( !$wgOut->isPrintable() ) { |
283 | | - $css = $quality ? 'fr-icon-quality' : 'fr-icon-stable'; |
| 317 | + $class = $quality ? 'fr-icon-quality' : 'fr-icon-stable'; |
284 | 318 | $tooltip = $quality ? 'revreview-quality-title' : 'revreview-stable-title'; |
285 | 319 | $tooltip = wfMsgHtml($tooltip); |
286 | 320 | // Simple icon-based UI |
287 | 321 | if( FlaggedRevs::useSimpleUI() ) { |
288 | 322 | $msg = $quality ? 'revreview-quick-quality' : 'revreview-quick-basic'; |
289 | 323 | $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 ); |
292 | 326 | |
293 | | - $tag = FlaggedRevsXML::prettyRatingBox( $frev, $html, $revs_since, true, $synced ); |
| 327 | + $tag = FlaggedRevsXML::prettyRatingBox( $frev, $html, $revsSince, true, $synced ); |
294 | 328 | // Standard UI |
295 | 329 | } else { |
296 | 330 | $msg = $quality ? 'revreview-quality' : 'revreview-basic'; |
297 | 331 | if( $synced ) { |
298 | 332 | $msg .= '-same'; |
299 | | - } else if( $revs_since == 0 ) { |
| 333 | + } else if( $revsSince == 0 ) { |
300 | 334 | $msg .= '-i'; |
301 | 335 | } |
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 ); |
304 | 338 | if( !empty($flags) ) { |
305 | 339 | $tag .= " <a id='mw-revisiontoggle' class='flaggedrevs_toggle' style='display:none;'" . |
306 | 340 | " onclick='toggleRevRatings()' title='" . wfMsgHtml('revreview-toggle-title') . "' >" . |
— | — | @@ -310,9 +344,9 @@ |
311 | 345 | } |
312 | 346 | } |
313 | 347 | # Output HTML |
314 | | - $wgOut->addParserOutput( $parserOut ); |
| 348 | + $wgOut->addParserOutput( $parserOut ); |
315 | 349 | $wgOut->setRevisionId( $frev->getRevId() ); |
316 | | - $notes = $this->ReviewNotes( $frev ); |
| 350 | + $notes = $this->getReviewNotes( $frev ); |
317 | 351 | # Tell MW that parser output is done |
318 | 352 | $outputDone = true; |
319 | 353 | $pcache = false; |
— | — | @@ -336,7 +370,7 @@ |
337 | 371 | # Add revision notes |
338 | 372 | $wgOut->mBodytext = $wgOut->mBodytext . $notes; |
339 | 373 | // 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() ) ) { |
341 | 375 | // Simple icon-based UI |
342 | 376 | if( FlaggedRevs::useSimpleUI() ) { |
343 | 377 | $msg = $old ? 'revreview-quick-invalid' : 'revreview-quick-none'; |
— | — | @@ -355,60 +389,91 @@ |
356 | 390 | $this->displayTag(); |
357 | 391 | |
358 | 392 | return true; |
359 | | - } |
| 393 | + } |
360 | 394 | |
361 | 395 | /** |
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; |
368 | 409 | } |
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__ ); |
377 | 430 | } |
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 |
391 | 453 | } |
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 ); |
393 | 459 | } |
394 | | - return true; |
395 | 460 | } |
396 | 461 | |
397 | | - /** |
| 462 | + /** |
398 | 463 | * Adds latest stable version tag to page when editing |
399 | 464 | */ |
400 | | - public function addToEditView( $editform ) { |
| 465 | + public function addToEditView( $editPage ) { |
401 | 466 | global $wgRequest, $wgOut; |
402 | 467 | # Talk pages cannot be validated |
403 | | - if( !$editform->mArticle || !$this->isReviewable() ) |
| 468 | + if( !$this->isReviewable() ) |
404 | 469 | return false; |
405 | 470 | # 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; |
408 | 473 | } else { |
409 | | - $revid = $editform->mArticle->getLatest(); |
410 | | - } |
| 474 | + $revId = $this->parent->getLatest(); |
| 475 | + } |
411 | 476 | # Grab the ratings for this revision if any |
412 | | - if( !$revid ) |
| 477 | + if( !$revId ) |
413 | 478 | return true; |
414 | 479 | # Set new body html text as that of now |
415 | 480 | $tag = $warning = ''; |
— | — | @@ -419,34 +484,34 @@ |
420 | 485 | |
421 | 486 | $time = $wgLang->date( $frev->getTimestamp(), true ); |
422 | 487 | $flags = $frev->getTags(); |
423 | | - $revs_since = FlaggedRevs::getRevCountSince( $editform->mArticle, $frev->getRevId() ); |
| 488 | + $revsSince = FlaggedRevs::getRevCountSince( $this->parent, $frev->getRevId() ); |
424 | 489 | # Construct some tagging |
425 | 490 | $quality = FlaggedRevs::isQuality( $flags ); |
426 | 491 | # If this will be autoreviewed, notify the user... |
427 | 492 | if( $wgFlaggedRevsAutoReview && $wgUser->isAllowed('review') ) { |
428 | 493 | # If we are editing some reviewed revision, any changes this user |
429 | 494 | # makes will be autoreviewed... |
430 | | - $ofrev = FlaggedRevs::getFlaggedRev( $editform->mArticle->getTitle(), $revid ); |
| 495 | + $ofrev = FlaggedRevs::getFlaggedRev( $this->parent->getTitle(), $revId ); |
431 | 496 | 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'; |
433 | 498 | $warning = "<div id='mw-autoreviewtag' class='flaggedrevs_warning plainlinks'>" . |
434 | 499 | wfMsgExt($msg,array('parseinline')) . "</div>"; |
435 | 500 | } |
436 | 501 | } |
437 | | - if( $frev->getRevId() != $revid ) { |
| 502 | + if( $frev->getRevId() != $revId ) { |
438 | 503 | # Streamlined UI |
439 | 504 | if( FlaggedRevs::useSimpleUI() ) { |
440 | 505 | $msg = $quality ? 'revreview-newest-quality' : 'revreview-newest-basic'; |
441 | | - $msg .= ($revs_since == 0) ? '-i' : ''; |
| 506 | + $msg .= ($revsSince == 0) ? '-i' : ''; |
442 | 507 | $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 ); |
444 | 509 | $tag = "<div id='mw-revisiontag-edit' class='flaggedrevs_editnotice plainlinks'>$tag</div>"; |
445 | 510 | # Standard UI |
446 | 511 | } else { |
447 | 512 | $msg = $quality ? 'revreview-newest-quality' : 'revreview-newest-basic'; |
448 | | - $msg .= ($revs_since == 0) ? '-i' : ''; |
| 513 | + $msg .= ($revsSince == 0) ? '-i' : ''; |
449 | 514 | $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 ); |
451 | 516 | # Hide clutter |
452 | 517 | if( !empty($flags) ) { |
453 | 518 | $tag .= " <a id='mw-revisiontoggle' class='flaggedrevs_toggle' style='display:none;'" . |
— | — | @@ -463,7 +528,7 @@ |
464 | 529 | $leftNote = $quality ? 'revreview-quality-title' : 'revreview-stable-title'; |
465 | 530 | $rightNote = 'revreview-draft-title'; |
466 | 531 | $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 ) { |
468 | 533 | $diffEngine = new DifferenceEngine(); |
469 | 534 | $diffEngine->showDiffStyle(); |
470 | 535 | $wgOut->addHtml( |
— | — | @@ -477,50 +542,51 @@ |
478 | 543 | "<td colspan='2' width='50%' align='center' class='diff-otitle'><b>[" . wfMsgHtml($leftNote) . "]</b></td>" . |
479 | 544 | "<td colspan='2' width='50%' align='center' class='diff-ntitle'><b>[" . wfMsgHtml($rightNote) . "]</b></td>" . |
480 | 545 | "</tr>" . |
481 | | - $diffEngine->generateDiffBody( $text, $editform->textbox1 ) . |
| 546 | + $diffEngine->generateDiffBody( $text, $editPage->textbox1 ) . |
482 | 547 | "</table>" . |
483 | 548 | "</div>\n" ); |
484 | 549 | } |
485 | 550 | } |
486 | 551 | return true; |
487 | | - } |
| 552 | + } |
488 | 553 | |
489 | 554 | /** |
490 | 555 | * Add review form to pages when necessary |
491 | 556 | */ |
492 | | - public function addReviewForm( $out ) { |
493 | | - global $wgRequest, $wgArticle; |
| 557 | + public function addReviewForm( $out ) { |
| 558 | + global $wgRequest; |
494 | 559 | |
495 | | - if( !$wgArticle || !$wgArticle->exists() || !$this->isReviewable() ) |
| 560 | + if( !$this->parent->exists() || !$this->isReviewable() ) { |
496 | 561 | return true; |
| 562 | + } |
497 | 563 | # Check if page is protected |
498 | 564 | $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' ) ) { |
500 | 566 | return true; |
501 | 567 | } |
502 | 568 | # Add review form |
503 | 569 | $this->addQuickReview( $out, (bool)$wgRequest->getVal('diff') ); |
504 | 570 | |
505 | 571 | return true; |
506 | | - } |
| 572 | + } |
507 | 573 | |
508 | 574 | /** |
509 | 575 | * Adds a patrol link to non-reviewable pages |
510 | 576 | */ |
511 | | - public function addPatrolLink( $article, &$outputDone, &$pcache ) { |
| 577 | + public function addPatrolLink( &$outputDone, &$pcache ) { |
512 | 578 | global $wgRequest, $wgOut, $wgUser, $wgLang; |
513 | 579 | # For unreviewable pages, allow for basic patrolling |
514 | | - if( FlaggedRevs::isPagePatrollable( $article->getTitle() ) ) { |
| 580 | + if( FlaggedRevs::isPagePatrollable( $this->parent->getTitle() ) ) { |
515 | 581 | # If we have been passed an &rcid= parameter, we want to give the user a |
516 | 582 | # chance to mark this new article as patrolled. |
517 | 583 | $rcid = $wgRequest->getIntOrNull( 'rcid' ); |
518 | 584 | if( !is_null( $rcid ) && $rcid != 0 && $wgUser->isAllowed( 'review' ) ) { |
519 | | - $reviewtitle = SpecialPage::getTitleFor( 'RevisionReview' ); |
| 585 | + $reviewTitle = SpecialPage::getTitleFor( 'RevisionReview' ); |
520 | 586 | $wgOut->addHTML( "<div class='patrollink'>" . |
521 | 587 | 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 ) ) ) |
525 | 591 | ) . |
526 | 592 | '</div>' |
527 | 593 | ); |
— | — | @@ -531,14 +597,14 @@ |
532 | 598 | /** |
533 | 599 | * Add link to stable version setting to protection form |
534 | 600 | */ |
535 | | - public function addVisibilityLink( $out ) { |
536 | | - global $wgUser, $wgRequest; |
| 601 | + public function addVisibilityLink( $out ) { |
| 602 | + global $wgUser, $wgRequest; |
537 | 603 | |
538 | | - if( !$this->isReviewable() ) |
539 | | - return true; |
| 604 | + if( !$this->isReviewable() ) |
| 605 | + return true; |
540 | 606 | |
541 | | - $action = $wgRequest->getVal( 'action', 'view' ); |
542 | | - if( $action=='protect' || $action=='unprotect' ) { |
| 607 | + $action = $wgRequest->getVal( 'action', 'view' ); |
| 608 | + if( $action=='protect' || $action=='unprotect' ) { |
543 | 609 | # Check for an overridabe revision |
544 | 610 | $frev = $this->getStableRev( true ); |
545 | 611 | if( !$frev ) |
— | — | @@ -550,17 +616,17 @@ |
551 | 617 | "</span>" . $out->mBodytext; |
552 | 618 | } |
553 | 619 | return true; |
554 | | - } |
| 620 | + } |
555 | 621 | |
556 | 622 | /** |
557 | 623 | * Add stable version tabs. Rename some of the others if necessary. |
558 | 624 | */ |
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; |
561 | 627 | # Get the subject page, not all skins have it :( |
562 | | - if( !isset($sktmp->mTitle) ) |
| 628 | + if( !isset($skin->mTitle) ) |
563 | 629 | return true; |
564 | | - $title = $sktmp->mTitle->getSubjectPage(); |
| 630 | + $title = $skin->mTitle->getSubjectPage(); |
565 | 631 | # Non-content pages cannot be validated |
566 | 632 | if( !FlaggedRevs::isPageReviewable( $title ) || !$title->exists() ) |
567 | 633 | return true; |
— | — | @@ -568,155 +634,154 @@ |
569 | 635 | $action = $wgRequest->getVal( 'action', 'view' ); |
570 | 636 | # If we are viewing a page normally, and it was overridden, |
571 | 637 | # 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) ) { |
575 | 641 | return true; |
576 | 642 | } |
577 | | - # Be clear about what is being edited... |
| 643 | + # Be clear about what is being edited... |
578 | 644 | $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'] ) ) { |
581 | 647 | if( $this->showStableByDefault() ) |
582 | | - $content_actions['edit']['text'] = wfMsg('revreview-edit'); |
| 648 | + $contentActions['edit']['text'] = wfMsg('revreview-edit'); |
583 | 649 | # If the user is requesting the draft or some revision, they don't need a diff. |
584 | 650 | 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'] ) ) { |
587 | 653 | if( $this->showStableByDefault() ) |
588 | | - $content_actions['viewsource']['text'] = wfMsg('revreview-source'); |
| 654 | + $contentActions['viewsource']['text'] = wfMsg('revreview-source'); |
589 | 655 | # If the user is requesting the draft or some revision, they don't need a diff. |
590 | 656 | if( $this->pageOverride() ) |
591 | | - $content_actions['viewsource']['href'] = $title->getLocalUrl( 'action=edit&showdiff=1' ); |
| 657 | + $contentActions['viewsource']['href'] = $title->getLocalUrl( 'action=edit&showdiff=1' ); |
592 | 658 | } |
593 | | - } |
| 659 | + } |
594 | 660 | # We can change the behavoir of stable version for this page to be different |
595 | 661 | # 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( |
600 | 666 | '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()) |
603 | 669 | ); |
604 | 670 | } |
605 | 671 | } |
606 | 672 | // Add auxillary tabs... |
607 | | - if( !$wgFlaggedRevTabs || $synced ) |
608 | | - return true; |
| 673 | + if( !$wgFlaggedRevTabs || $synced ) |
| 674 | + return true; |
609 | 675 | // We are looking at the stable version |
610 | 676 | if( $this->pageOverride() || $wgRequest->getVal('stableid') ) { |
611 | | - $DraftTabcss = ''; |
612 | | - $StableTabcss = 'selected'; |
| 677 | + $draftTabCss = ''; |
| 678 | + $stableTabCss = 'selected'; |
613 | 679 | // 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 = ''; |
617 | 683 | // We are looking at the current revision or in edit mode |
618 | 684 | } else { |
619 | | - $DraftTabcss = 'selected'; |
620 | | - $StableTabcss = ''; |
| 685 | + $draftTabCss = 'selected'; |
| 686 | + $stableTabCss = ''; |
621 | 687 | } |
622 | | - $new_actions = array(); $first = true; |
| 688 | + $newActions = array(); |
| 689 | + $first = true; |
623 | 690 | # 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 ) { |
625 | 692 | # Main page tab... |
626 | 693 | if( $first ) { |
627 | 694 | $first = false; |
628 | 695 | if( $synced ) { |
629 | | - $new_actions[$tab_action] = $data; // keep it |
| 696 | + $newActions[$tabAction] = $data; // keep it |
630 | 697 | } else { |
631 | 698 | # Add new tabs after first one |
632 | | - $new_actions['stable'] = array( |
633 | | - 'class' => $StableTabcss, |
| 699 | + $newActions['stable'] = array( |
| 700 | + 'class' => $stableTabCss, |
634 | 701 | 'text' => wfMsg('revreview-stable'), |
635 | 702 | 'href' => $title->getLocalUrl( 'stable=1' ) |
636 | 703 | ); |
637 | | - $new_actions['current'] = array( |
638 | | - 'class' => $DraftTabcss, |
| 704 | + $newActions['current'] = array( |
| 705 | + 'class' => $draftTabCss, |
639 | 706 | 'text' => wfMsg('revreview-current'), |
640 | 707 | 'href' => $title->getLocalUrl( 'stable=0' ) |
641 | 708 | ); |
642 | 709 | } |
643 | 710 | # The others... |
644 | 711 | } else { |
645 | | - $new_actions[$tab_action] = $data; |
| 712 | + $newActions[$tabAction] = $data; |
646 | 713 | } |
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 | + } |
652 | 719 | |
653 | 720 | /** |
654 | 721 | * Add link to stable version of reviewed revisions |
655 | 722 | */ |
656 | | - public function addToHistLine( $row, &$s ) { |
657 | | - global $wgUser; |
| 723 | + public function addToHistLine( $history, $row, &$s ) { |
| 724 | + global $wgUser; |
658 | 725 | # Non-content pages cannot be validated. Stable version must exist. |
659 | 726 | if( !$this->isReviewable() || !$this->getStableRev() ) |
660 | 727 | return true; |
661 | | - # Use same DB object |
662 | | - $this->dbr = isset($this->dbr) ? $this->dbr : wfGetDB( DB_SLAVE ); |
663 | 728 | $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>"; |
667 | 733 | } |
668 | 734 | return true; |
669 | | - } |
| 735 | + } |
670 | 736 | |
671 | 737 | /** |
672 | 738 | * Add link to stable version of reviewed revisions |
673 | 739 | */ |
674 | | - public function addToFileHistLine( $file, &$s, &$css ) { |
675 | | - global $wgUser; |
| 740 | + public function addToFileHistLine( $historyList, $file, &$row, &$class ) { |
| 741 | + global $wgUser; |
676 | 742 | # Non-content pages cannot be validated. Stable version must exist. |
677 | 743 | if( !$this->isReviewable() || !$this->getStableRev() ) |
678 | 744 | 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', |
684 | 748 | array( 'fr_img_sha1' => $file->getSha1(), 'fr_img_timestamp' => $file->getTimestamp() ), |
685 | 749 | __METHOD__ ); |
686 | 750 | if( $quality === false ) { |
687 | 751 | return true; |
688 | 752 | } |
689 | | - $css = FlaggedRevsXML::getQualityColor( $quality ); |
| 753 | + $class = FlaggedRevsXML::getQualityColor( $quality ); |
| 754 | + |
690 | 755 | return true; |
691 | | - } |
| 756 | + } |
692 | 757 | |
693 | 758 | /** |
694 | 759 | * @param FlaggedRevision $frev |
695 | 760 | * @return string, revision review notes |
696 | 761 | */ |
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() ) { |
700 | 765 | return ''; |
701 | 766 | } |
702 | 767 | $notes = "<div class='flaggedrevs_notes plainlinks'>"; |
703 | 768 | $notes .= wfMsgExt('revreview-note', array('parseinline'), User::whoIs( $frev->getUser() ) ); |
704 | 769 | $notes .= '<br/><i>' . $wgUser->getSkin()->formatComment( $frev->getComment() ) . '</i></div>'; |
705 | | - return $notes; |
706 | | - } |
| 770 | + return $notes; |
| 771 | + } |
707 | 772 | |
708 | 773 | /** |
709 | 774 | * When comparing the stable revision to the current after editing a page, show |
710 | 775 | * a tag with some explaination for the diff. |
711 | 776 | */ |
712 | | - public function addDiffNoticeAndIncludes( $diff, $OldRev, $NewRev ) { |
| 777 | + public function addDiffNoticeAndIncludes( $diff, $oldRev, $newRev ) { |
713 | 778 | global $wgRequest, $wgUser, $wgOut; |
714 | 779 | |
715 | | - if( $wgOut->isPrintable() || !FlaggedRevs::isPageReviewable( $NewRev->getTitle() ) ) |
| 780 | + if( $wgOut->isPrintable() || !FlaggedRevs::isPageReviewable( $newRev->getTitle() ) ) |
716 | 781 | return true; |
717 | 782 | # Check if this might be the diff to stable. If so, enhance it. |
718 | | - if( $NewRev->isCurrent() && $OldRev ) { |
| 783 | + if( $newRev->isCurrent() && $oldRev ) { |
719 | 784 | $frev = $this->getStableRev(); |
720 | | - if( $frev && $frev->getRevId() == $OldRev->getID() ) { |
| 785 | + if( $frev && $frev->getRevId() == $oldRev->getID() ) { |
721 | 786 | $changeList = array(); |
722 | 787 | $skin = $wgUser->getSkin(); |
723 | 788 | |
— | — | @@ -799,34 +864,34 @@ |
800 | 865 | } |
801 | 866 | # Set flag for review form to tell it to autoselect tag settings from the |
802 | 867 | # 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() ) ) { |
804 | 869 | $this->isDiffFromStable = true; |
805 | 870 | } |
806 | 871 | } |
807 | 872 | } |
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; |
810 | 875 | # Diff between two revisions |
811 | | - if( $OldRev ) { |
| 876 | + if( $oldRev ) { |
812 | 877 | $wgOut->addHTML( "<table class='fr-diff-ratings' width='100%'><tr>" ); |
813 | 878 | |
814 | | - $css = FlaggedRevsXML::getQualityColor( $oldRevQ ); |
| 879 | + $class = FlaggedRevsXML::getQualityColor( $oldRevQ ); |
815 | 880 | if( $oldRevQ !== false ) { |
816 | 881 | $msg = $oldRevQ ? 'hist-quality' : 'hist-stable'; |
817 | 882 | } else { |
818 | 883 | $msg = 'hist-draft'; |
819 | 884 | } |
820 | 885 | $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>" ); |
822 | 887 | |
823 | | - $css = FlaggedRevsXML::getQualityColor( $newRevQ ); |
| 888 | + $class = FlaggedRevsXML::getQualityColor( $newRevQ ); |
824 | 889 | if( $newRevQ !== false ) { |
825 | 890 | $msg = $newRevQ ? 'hist-quality' : 'hist-stable'; |
826 | 891 | } else { |
827 | 892 | $msg = 'hist-draft'; |
828 | 893 | } |
829 | 894 | $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>" ); |
831 | 896 | |
832 | 897 | $wgOut->addHTML( '</td></tr></table>' ); |
833 | 898 | # New page "diffs" - just one rev |
— | — | @@ -848,29 +913,29 @@ |
849 | 914 | * Add a link to patrol non-reviewable pages. |
850 | 915 | * Also add a diff to stable for other pages if possible. |
851 | 916 | */ |
852 | | - public function addPatrolAndDiffLink( $diff, $OldRev, $NewRev ) { |
| 917 | + public function addPatrolAndDiffLink( $diff, $oldRev, $newRev ) { |
853 | 918 | global $wgUser, $wgOut; |
854 | 919 | // Is there a stable version? |
855 | | - if( FlaggedRevs::isPageReviewable( $NewRev->getTitle() ) ) { |
856 | | - if( !$OldRev ) { |
| 920 | + if( FlaggedRevs::isPageReviewable( $newRev->getTitle() ) ) { |
| 921 | + if( !$oldRev ) { |
857 | 922 | return true; |
858 | 923 | } |
859 | 924 | $frev = $this->getStableRev(); |
860 | | - if( $frev && $frev->getRevId() == $OldRev->getID() && $NewRev->isCurrent() ) { |
| 925 | + if( $frev && $frev->getRevId() == $oldRev->getID() && $newRev->isCurrent() ) { |
861 | 926 | $this->isDiffFromStable = true; |
862 | 927 | } |
863 | 928 | # Give a link to the diff-to-stable if needed |
864 | 929 | if( $frev && !$this->isDiffFromStable ) { |
865 | | - $article = new Article( $NewRev->getTitle() ); |
| 930 | + $article = new Article( $newRev->getTitle() ); |
866 | 931 | # Is the stable revision using the same revision as the current? |
867 | 932 | if( $article->getLatest() != $frev->getRevId() ) { |
868 | | - $patrol = '(' . $wgUser->getSkin()->makeKnownLinkObj( $NewRev->getTitle(), |
| 933 | + $patrol = '(' . $wgUser->getSkin()->makeKnownLinkObj( $newRev->getTitle(), |
869 | 934 | wfMsgHtml( 'review-diff2stable' ), "oldid={$frev->getRevId()}&diff=cur" ) . ')'; |
870 | 935 | $wgOut->addHTML( "<div class='fr-diff-to-stable' align='center'>$patrol</div>" ); |
871 | 936 | } |
872 | 937 | } |
873 | 938 | // 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' ) ) { |
875 | 940 | // If we've been given an explicit change identifier, use it; saves time |
876 | 941 | if( $diff->mRcidMarkPatrolled ) { |
877 | 942 | $rcid = $diff->mRcidMarkPatrolled; |
— | — | @@ -896,10 +961,10 @@ |
897 | 962 | } |
898 | 963 | // Build the link |
899 | 964 | 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 ) ) ) . ']'; |
904 | 969 | } else { |
905 | 970 | $patrol = ''; |
906 | 971 | } |
— | — | @@ -912,26 +977,26 @@ |
913 | 978 | * Redirect users out to review the changes to the stable version. |
914 | 979 | * Only for people who can review and for pages that have a stable version. |
915 | 980 | */ |
916 | | - public function injectReviewDiffURLParams( $article, &$sectionanchor, &$extraq ) { |
917 | | - global $wgUser, $wgReviewChangesAfterEdit; |
| 981 | + public function injectReviewDiffURLParams( &$sectionAnchor, &$extraQuery ) { |
| 982 | + global $wgUser, $wgReviewChangesAfterEdit; |
918 | 983 | # Don't show this for the talk page |
919 | | - if( !$this->isReviewable() || $article->getTitle()->isTalkPage() ) |
| 984 | + if( !$this->isReviewable() || $this->parent->getTitle()->isTalkPage() ) |
920 | 985 | return true; |
921 | 986 | # Get the stable version, from master |
922 | | - $frev = $this->getStableRev( false, true ); |
| 987 | + $frev = $this->getStableRev( false, true ); |
923 | 988 | if( !$frev ) |
924 | 989 | return true; |
925 | | - $latest = $article->getTitle()->getLatestRevID(GAID_FOR_UPDATE); |
| 990 | + $latest = $this->parent->getTitle()->getLatestRevID(GAID_FOR_UPDATE); |
926 | 991 | // If we are supposed to review after edit, and it was not autoreviewed, |
927 | 992 | // and the user can actually make new stable version, take us to the diff... |
928 | 993 | if( $wgReviewChangesAfterEdit && $frev && $latest > $frev->getRevId() && $frev->userCanSetFlags() ) { |
929 | | - $extraq .= "oldid={$frev->getRevId()}&diff=cur"; |
| 994 | + $extraQuery .= "oldid={$frev->getRevId()}&diff=cur"; |
930 | 995 | // ...otherwise, go to the current revision after completing an edit. |
931 | 996 | } else { |
932 | 997 | if( $frev ){ |
933 | | - $extraq .= "stable=0"; |
| 998 | + $extraQuery .= "stable=0"; |
934 | 999 | if( !$wgUser->isAllowed('review') && $this->showStableByDefault() ) { |
935 | | - $extraq .= "&shownotice=1"; |
| 1000 | + $extraQuery .= "&shownotice=1"; |
936 | 1001 | } |
937 | 1002 | } |
938 | 1003 | } |
— | — | @@ -942,24 +1007,24 @@ |
943 | 1008 | * Add a hidden revision ID field to edit form. |
944 | 1009 | * Needed for autoreview so it can select the flags from said revision. |
945 | 1010 | */ |
946 | | - public function addRevisionIDField( $editform, $out ) { |
| 1011 | + public function addRevisionIDField( $editPage, $out ) { |
947 | 1012 | global $wgRequest; |
948 | 1013 | # 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; |
951 | 1016 | } 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 | + } |
955 | 1020 | # If undoing a few consecutive top edits, we know the base ID |
956 | 1021 | 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; |
961 | 1026 | } |
962 | 1027 | } |
963 | | - $out->addHTML( "\n" . Xml::hidden( 'baseRevId', $revid ) ); |
| 1028 | + $out->addHTML( "\n" . Xml::hidden( 'baseRevId', $revId ) ); |
964 | 1029 | return true; |
965 | 1030 | } |
966 | 1031 | |
— | — | @@ -973,54 +1038,54 @@ |
974 | 1039 | if( $this->stableRev === false ) { |
975 | 1040 | return null; // We already looked and found nothing... |
976 | 1041 | } |
977 | | - # Cached results available? |
978 | | - if( !is_null($this->stableRev) ) { |
| 1042 | + # Cached results available? |
| 1043 | + if( !is_null($this->stableRev) ) { |
979 | 1044 | return $this->stableRev; |
980 | 1045 | } |
981 | 1046 | # Get the content page, skip talk |
982 | | - $title = $this->getTitle()->getSubjectPage(); |
| 1047 | + $title = $this->parent->getTitle()->getSubjectPage(); |
983 | 1048 | # Do we have one? |
984 | 1049 | $srev = FlaggedRevs::getStablePageRev( $title, $getText, $forUpdate ); |
985 | | - if( $srev ) { |
| 1050 | + if( $srev ) { |
986 | 1051 | $this->stableRev = $srev; |
987 | 1052 | return $srev; |
988 | | - } else { |
989 | | - $this->stableRev = false; |
990 | | - return null; |
991 | | - } |
| 1053 | + } else { |
| 1054 | + $this->stableRev = false; |
| 1055 | + return null; |
| 1056 | + } |
992 | 1057 | } |
993 | 1058 | |
994 | | - /** |
| 1059 | + /** |
995 | 1060 | * Get visiblity restrictions on page |
996 | 1061 | * @param Bool $forUpdate, use DB master? |
997 | 1062 | * @returns Array (select,override) |
998 | 1063 | */ |
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; |
1003 | 1068 | } |
1004 | 1069 | # Get the content page, skip talk |
1005 | | - $title = $this->getTitle()->getSubjectPage(); |
| 1070 | + $title = $this->parent->getTitle()->getSubjectPage(); |
1006 | 1071 | |
1007 | 1072 | $config = FlaggedRevs::getPageVisibilitySettings( $title, $forUpdate ); |
1008 | | - $this->pageconfig = $config; |
| 1073 | + $this->pageConfig = $config; |
1009 | 1074 | |
1010 | 1075 | return $config; |
1011 | 1076 | } |
1012 | 1077 | |
1013 | 1078 | /** |
1014 | | - * @param int $rev_id |
| 1079 | + * @param int $revId |
1015 | 1080 | * @eturns Array, output of the flags for a given revision |
1016 | 1081 | */ |
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 ); |
1023 | 1088 | # Try to cache results |
1024 | | - $this->flags[$rev_id] = $flags; |
| 1089 | + $this->flags[$revId] = $flags; |
1025 | 1090 | |
1026 | 1091 | return $flags; |
1027 | 1092 | } |
— | — | @@ -1051,7 +1116,7 @@ |
1052 | 1117 | # Variable for sites with no flags, otherwise discarded |
1053 | 1118 | $approve = $wgRequest->getBool('wpApprove'); |
1054 | 1119 | # See if the version being displayed is flagged... |
1055 | | - $oldflags = $this->getFlagsForRevision( $id ); |
| 1120 | + $oldFlags = $this->getFlagsForRevision( $id ); |
1056 | 1121 | # If we are reviewing updates to a page, start off with the stable revision's |
1057 | 1122 | # flags. Otherwise, we just fill them in with the selected revision's flags. |
1058 | 1123 | if( $this->isDiffFromStable ) { |
— | — | @@ -1059,15 +1124,15 @@ |
1060 | 1125 | $flags = $srev->getTags(); |
1061 | 1126 | # Check if user is allowed to renew the stable version. |
1062 | 1127 | # 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; |
1065 | 1130 | } |
1066 | 1131 | } else { |
1067 | 1132 | $flags = $this->getFlagsForRevision( $id ); |
1068 | 1133 | } |
1069 | 1134 | |
1070 | | - $reviewtitle = SpecialPage::getTitleFor( 'RevisionReview' ); |
1071 | | - $action = $reviewtitle->getLocalUrl( 'action=submit' ); |
| 1135 | + $reviewTitle = SpecialPage::getTitleFor( 'RevisionReview' ); |
| 1136 | + $action = $reviewTitle->getLocalUrl( 'action=submit' ); |
1072 | 1137 | $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action ) ); |
1073 | 1138 | $form .= Xml::openElement( 'fieldset', array('class' => 'flaggedrevs_reviewform') ); |
1074 | 1139 | $form .= "<legend>" . wfMsgHtml( 'revreview-flag', $id ) . "</legend>\n"; |
— | — | @@ -1156,9 +1221,9 @@ |
1157 | 1222 | $imageParams = $templateParams = ''; |
1158 | 1223 | # Hack, add NS:title -> rev ID mapping |
1159 | 1224 | foreach( $out->mTemplateIds as $namespace => $title ) { |
1160 | | - foreach( $title as $dbkey => $revid ) { |
| 1225 | + foreach( $title as $dbkey => $revId ) { |
1161 | 1226 | $title = Title::makeTitle( $namespace, $dbkey ); |
1162 | | - $templateParams .= $title->getPrefixedText() . "|" . $revid . "#"; |
| 1227 | + $templateParams .= $title->getPrefixedText() . "|" . $revId . "#"; |
1163 | 1228 | } |
1164 | 1229 | } |
1165 | 1230 | # Hack, image -> timestamp mapping |
— | — | @@ -1168,8 +1233,9 @@ |
1169 | 1234 | } |
1170 | 1235 | } |
1171 | 1236 | # 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() . "#"; |
1174 | 1240 | } |
1175 | 1241 | |
1176 | 1242 | $form .= Xml::openElement( 'span', array('style' => 'white-space: nowrap;') ); |
— | — | @@ -1185,8 +1251,8 @@ |
1186 | 1252 | $form .= Xml::closeElement( 'div' ); |
1187 | 1253 | |
1188 | 1254 | # 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"; |
1191 | 1257 | $form .= Xml::hidden( 'oldid', $id ) . "\n"; |
1192 | 1258 | $form .= Xml::hidden( 'action', 'submit') . "\n"; |
1193 | 1259 | $form .= Xml::hidden( 'wpEditToken', $wgUser->editToken() ) . "\n"; |
— | — | @@ -1207,13 +1273,13 @@ |
1208 | 1274 | } else { |
1209 | 1275 | $wgOut->addHTML( $form ); |
1210 | 1276 | } |
1211 | | - } |
| 1277 | + } |
1212 | 1278 | |
1213 | 1279 | /** |
1214 | 1280 | * Set permalink to stable version if we are viewing a stable version. |
1215 | 1281 | * Also sets the citation link if that extension is on. |
1216 | 1282 | */ |
1217 | | - public function setPermaLink( $sktmp, &$nav_urls, &$revid, &$id ) { |
| 1283 | + public function setPermaLink( $skin, &$navUrls, &$revId, &$id ) { |
1218 | 1284 | # Non-content pages cannot be validated |
1219 | 1285 | if( !$this->pageOverride() ) |
1220 | 1286 | return true; |
— | — | @@ -1222,28 +1288,28 @@ |
1223 | 1289 | if( !$frev ) |
1224 | 1290 | return true; |
1225 | 1291 | # Replace "permalink" with an actual permanent link |
1226 | | - $nav_urls['permalink'] = array( |
| 1292 | + $navUrls['permalink'] = array( |
1227 | 1293 | 'text' => wfMsg( 'permalink' ), |
1228 | | - 'href' => $this->getTitle()->getFullURL( "stableid={$frev->getRevId()}" ) |
| 1294 | + 'href' => $this->parent->getTitle()->getFullURL( "stableid={$frev->getRevId()}" ) |
1229 | 1295 | ); |
1230 | 1296 | # Are we using the popular cite extension? |
1231 | 1297 | global $wgHooks; |
1232 | 1298 | 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( |
1235 | 1301 | '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()}" ) |
1237 | 1303 | ); |
1238 | 1304 | } |
1239 | 1305 | } |
1240 | 1306 | return true; |
1241 | | - } |
| 1307 | + } |
1242 | 1308 | |
1243 | 1309 | /** |
1244 | 1310 | * If viewing a stable version, adjust the last modified header |
1245 | 1311 | */ |
1246 | | - public function setLastModified( $sktmp, &$tpl ) { |
1247 | | - global $wgArticle, $wgLang, $wgRequest; |
| 1312 | + public function setLastModified( $skin, &$tpl ) { |
| 1313 | + global $wgLang, $wgRequest; |
1248 | 1314 | # Non-content pages cannot be validated |
1249 | 1315 | if( !$this->isReviewable() ) |
1250 | 1316 | return true; |
— | — | @@ -1256,7 +1322,7 @@ |
1257 | 1323 | if( !$this->pageOverride() ) |
1258 | 1324 | return true; |
1259 | 1325 | $frev = $this->getStableRev( true ); |
1260 | | - if( !$frev || $frev->getRevId() == $wgArticle->getLatest() ) |
| 1326 | + if( !$frev || $frev->getRevId() == $this->parent->getLatest() ) |
1261 | 1327 | return true; |
1262 | 1328 | # Get the timestamp of this revision |
1263 | 1329 | $timestamp = $frev->getRevTimestamp(); |
— | — | @@ -1278,23 +1344,23 @@ |
1279 | 1345 | /** |
1280 | 1346 | * Updates parser cache output to included needed versioning params. |
1281 | 1347 | */ |
1282 | | - public function maybeUpdateMainCache( $article, &$outputDone, &$pcache ) { |
| 1348 | + public function maybeUpdateMainCache( &$outputDone, &$pcache ) { |
1283 | 1349 | global $wgUser, $wgRequest; |
1284 | 1350 | |
1285 | 1351 | $action = $wgRequest->getVal( 'action', 'view' ); |
1286 | 1352 | # Only trigger on article view for content pages, not for protect/delete/hist |
1287 | 1353 | if( ($action !='view' && $action !='purge') || !$wgUser->isAllowed( 'review' ) ) |
1288 | 1354 | return true; |
1289 | | - if( !$article || !$article->exists() || !FlaggedRevs::isPageReviewable( $article->getTitle() ) ) |
| 1355 | + if( !$this->parent->exists() || !FlaggedRevs::isPageReviewable( $this->parent->getTitle() ) ) |
1290 | 1356 | return true; |
1291 | 1357 | |
1292 | 1358 | $parserCache = ParserCache::singleton(); |
1293 | | - $parserOut = $parserCache->get( $article, $wgUser ); |
| 1359 | + $parserOut = $parserCache->get( $this->parent, $wgUser ); |
1294 | 1360 | if( $parserOut ) { |
1295 | 1361 | # Clear older, incomplete, cached versions |
1296 | 1362 | # We need the IDs of templates and timestamps of images used |
1297 | 1363 | if( !isset($parserOut->fr_newestTemplateID) || !isset($parserOut->fr_newestImageTime) ) |
1298 | | - $article->getTitle()->invalidateCache(); |
| 1364 | + $this->parent->getTitle()->invalidateCache(); |
1299 | 1365 | } |
1300 | 1366 | return true; |
1301 | 1367 | } |
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 |
1 | 2002 | + native |
Index: trunk/extensions/FlaggedRevs/FlaggedRevsXML.php |
— | — | @@ -121,14 +121,14 @@ |
122 | 122 | /** |
123 | 123 | * @param Row $trev, flagged revision row |
124 | 124 | * @param string $html, the short message HTML |
125 | | - * @param int $revs_since, revisions since review |
| 125 | + * @param int $revsSince, revisions since review |
126 | 126 | * @param bool $stable, are we referring to the stable revision? |
127 | 127 | * @param bool $synced, does stable=current and this is one of them? |
128 | 128 | * @param bool $old, is this an old stable version? |
129 | 129 | * @return string |
130 | 130 | * Generates a review box using a table using FlaggedRevsXML::addTagRatings() |
131 | 131 | */ |
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 ) { |
133 | 133 | global $wgLang; |
134 | 134 | # Get quality level |
135 | 135 | $flags = $frev->getTags(); |
— | — | @@ -149,7 +149,7 @@ |
150 | 150 | # Construct some tagging |
151 | 151 | if( $synced ) { |
152 | 152 | $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 ); |
154 | 154 | } else if( $old ) { |
155 | 155 | $msg = $quality ? 'revreview-quality-old' : 'revreview-basic-old'; |
156 | 156 | $html = wfMsgExt($msg, array('parseinline'), $frev->getRevId(), $time ); |
— | — | @@ -159,8 +159,8 @@ |
160 | 160 | } else { |
161 | 161 | $msg = $quality ? 'revreview-newest-quality' : 'revreview-newest-basic'; |
162 | 162 | } |
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 ); |
165 | 165 | } |
166 | 166 | # Make fancy box... |
167 | 167 | $box = "<table border='0' cellspacing='0' style='background: none;'>\n"; |
Index: trunk/extensions/FlaggedRevs/flaggedrevs.js |
— | — | @@ -36,8 +36,9 @@ |
37 | 37 | var allzero = true; |
38 | 38 | var somezero = false; |
39 | 39 | |
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); |
42 | 43 | var selectedlevel = 0; // default |
43 | 44 | |
44 | 45 | if( levels[0].nodeName == 'SELECT' ) { |
— | — | @@ -61,9 +62,9 @@ |
62 | 63 | } |
63 | 64 | |
64 | 65 | // Get quality level for this tag |
65 | | - QL = wgFlaggedRevsJSparams[flag]; |
| 66 | + qualityLevel = wgFlaggedRevsParams.tags[tag]; |
66 | 67 | |
67 | | - if( selectedlevel < QL ) { |
| 68 | + if( selectedlevel < qualityLevel ) { |
68 | 69 | quality = false; // not a quality review |
69 | 70 | } |
70 | 71 | if( selectedlevel > 0 ) { |
Index: trunk/extensions/FlaggedRevs/FlaggedRevsPage.php |
— | — | @@ -69,7 +69,6 @@ |
70 | 70 | $this->imageParams = $wgRequest->getVal( 'imageParams' ); |
71 | 71 | $this->validatedParams = $wgRequest->getVal( 'validatedParams' ); |
72 | 72 | |
73 | | - global $wgReviewCodes; |
74 | 73 | # Special token to discourage fiddling... |
75 | 74 | $checkCode = self::getValidationKey( $this->templateParams, $this->imageParams, $wgUser->getID(), $this->oldid ); |
76 | 75 | # Must match up |