r99295 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r99294‎ | r99295 | r99296 >
Date:02:39, 8 October 2011
Author:aaron
Status:ok (Comments)
Tags:
Comment:
Renamed classes to avoid implication that the page *is* flagged:
* FlaggedPage -> FlaggableWikiPage
* FlaggedPageView -> FlaggablePageView
* FlaggedPageConfig -> FRPageConfig
Modified paths:
  • /trunk/extensions/FlaggedRevs/FlaggedRevs.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/api/actions/ApiReview.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/api/actions/ApiReviewActivity.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/business/PageStabilityForm.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/business/RevisionReviewForm.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/dataclasses/FRPageConfig.php (added) (history)
  • /trunk/extensions/FlaggedRevs/dataclasses/FlaggableWikiPage.php (added) (history)
  • /trunk/extensions/FlaggedRevs/dataclasses/FlaggedPage.php (deleted) (history)
  • /trunk/extensions/FlaggedRevs/dataclasses/FlaggedPageConfig.php (deleted) (history)
  • /trunk/extensions/FlaggedRevs/dataclasses/FlaggedRevision.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/dataclasses/FlaggedRevs.class.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/dataclasses/FlaggedRevs.hooks.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/dataclasses/FlaggedRevsLog.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/maintenance/purgeReviewablePages.inc (modified) (history)
  • /trunk/extensions/FlaggedRevs/maintenance/updateTracking.inc (modified) (history)
  • /trunk/extensions/FlaggedRevs/presentation/FlaggablePageView.php (added) (history)
  • /trunk/extensions/FlaggedRevs/presentation/FlaggedPageView.php (deleted) (history)
  • /trunk/extensions/FlaggedRevs/presentation/FlaggedRevsUI.hooks.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/presentation/FlaggedRevsXML.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/presentation/RevisionReviewFormUI.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/presentation/modules/review.js (modified) (history)
  • /trunk/extensions/FlaggedRevs/presentation/specialpages/reports/ConfiguredPages_body.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/presentation/specialpages/reports/StablePages_body.php (modified) (history)
  • /trunk/extensions/FlaggedRevs/tests/FlaggedPageTest.php (modified) (history)

Diff [purge]

Index: trunk/extensions/FlaggedRevs/FlaggedRevs.php
@@ -238,7 +238,7 @@
239239 $wgAutoloadClasses['FlaggedRevs'] = $accessDir . 'FlaggedRevs.class.php';
240240 $wgAutoloadClasses['FRUserCounters'] = $accessDir . 'FRUserCounters.php';
241241 $wgAutoloadClasses['FRUserActivity'] = $accessDir . 'FRUserActivity.php';
242 -$wgAutoloadClasses['FlaggedPageConfig'] = $accessDir . 'FlaggedPageConfig.php';
 242+$wgAutoloadClasses['FRPageConfig'] = $accessDir . 'FRPageConfig.php';
243243 $wgAutoloadClasses['FlaggedRevsLog'] = $accessDir . 'FlaggedRevsLog.php';
244244 $wgAutoloadClasses['FRInclusionCache'] = $accessDir . 'FRInclusionCache.php';
245245 $wgAutoloadClasses['FlaggedRevsStats'] = $accessDir . 'FlaggedRevsStats.php';
@@ -248,7 +248,7 @@
249249 $wgAutoloadClasses['FRSquidUpdate'] = $accessDir . 'FRExtraCacheUpdate.php';
250250 $wgAutoloadClasses['FRDependencyUpdate'] = $accessDir . 'FRDependencyUpdate.php';
251251 $wgAutoloadClasses['FRInclusionManager'] = $accessDir . 'FRInclusionManager.php';
252 -$wgAutoloadClasses['FlaggedPage'] = $accessDir . 'FlaggedPage.php';
 252+$wgAutoloadClasses['FlaggableWikiPage'] = $accessDir . 'FlaggableWikiPage.php';
253253 $wgAutoloadClasses['FlaggedRevision'] = $accessDir . 'FlaggedRevision.php';
254254 $wgAutoloadClasses['FRParserCacheStable'] = $accessDir . 'FRParserCacheStable.php';
255255
@@ -267,7 +267,7 @@
268268 $wgAutoloadClasses['PageStabilityProtectForm'] = $dir . 'business/PageStabilityForm.php';
269269
270270 # Presentation classes...
271 -$wgAutoloadClasses['FlaggedPageView'] = $dir . 'presentation/FlaggedPageView.php';
 271+$wgAutoloadClasses['FlaggablePageView'] = $dir . 'presentation/FlaggablePageView.php';
272272 $wgAutoloadClasses['FlaggedRevsLogView'] = $dir . 'presentation/FlaggedRevsLogView.php';
273273 $wgAutoloadClasses['FlaggedRevsXML'] = $dir . 'presentation/FlaggedRevsXML.php';
274274 $wgAutoloadClasses['RevisionReviewFormUI'] = $dir . 'presentation/RevisionReviewFormUI.php';
@@ -618,7 +618,7 @@
619619
620620 # AJAX functions
621621 $wgAjaxExportList[] = 'RevisionReview::AjaxReview';
622 -$wgAjaxExportList[] = 'FlaggedPageView::AjaxBuildDiffHeaderItems';
 622+$wgAjaxExportList[] = 'FlaggablePageView::AjaxBuildDiffHeaderItems';
623623
624624 # Cache update
625625 $wgSpecialPageCacheUpdates['UnreviewedPages'] = 'UnreviewedPages::updateQueryCache';
Index: trunk/extensions/FlaggedRevs/maintenance/purgeReviewablePages.inc
@@ -33,7 +33,7 @@
3434 # Go through and append each purgeable page...
3535 foreach ( $res as $row ) {
3636 $title = Title::newFromRow( $row );
37 - $fa = FlaggedPage::getTitleInstance( $title );
 37+ $fa = FlaggableWikiPage::getTitleInstance( $title );
3838 if ( $fa->isReviewable() ) {
3939 # Need to purge this page - add to list
4040 fwrite( $fileHandle, $title->getPrefixedDBKey() . "\n" );
Index: trunk/extensions/FlaggedRevs/maintenance/updateTracking.inc
@@ -121,7 +121,7 @@
122122 $db->begin();
123123 foreach ( $res as $row ) {
124124 $title = Title::newFromRow( $row );
125 - $article = new FlaggedPage( $title );
 125+ $article = new FlaggableWikiPage( $title );
126126 $oldFrev = FlaggedRevision::newFromStable( $title, FR_MASTER );
127127 $frev = FlaggedRevision::determineStable( $title, FR_MASTER );
128128 # Update fp_stable, fp_quality, and fp_reviewed
Index: trunk/extensions/FlaggedRevs/tests/FlaggedPageTest.php
@@ -23,7 +23,7 @@
2424
2525 public function testPageDataFromTitle() {
2626 $title = Title::makeTitle( NS_MAIN, "somePage" );
27 - $article = new FlaggedPage( $title );
 27+ $article = new FlaggableWikiPage( $title );
2828
2929 $user = $this->user;
3030 $article->doEdit( "Some text to insert", "creating a page", EDIT_NEW, false, $user );
@@ -39,4 +39,4 @@
4040 $this->assertEquals( true, array_key_exists( 'fp_reviewed', $data ),
4141 "data->fp_reviewed field exists" );
4242 }
43 -}
\ No newline at end of file
 43+}
Index: trunk/extensions/FlaggedRevs/dataclasses/FlaggedPageConfig.php
@@ -1,237 +0,0 @@
2 -<?php
3 -/*
4 -* Page stability configuration functions
5 -*/
6 -class FlaggedPageConfig {
7 - /**
8 - * Get visibility settings/restrictions for a page
9 - * @param Title $title, page title
10 - * @param int $flags, FR_MASTER
11 - * @return array (associative) (select,override,autoreview,expiry)
12 - */
13 - public static function getStabilitySettings( Title $title, $flags = 0 ) {
14 - $db = ( $flags & FR_MASTER ) ?
15 - wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
16 - $row = $db->selectRow( 'flaggedpage_config',
17 - self::selectFields(),
18 - array( 'fpc_page_id' => $title->getArticleID() ),
19 - __METHOD__
20 - );
21 - return self::getVisibilitySettingsFromRow( $row );
22 - }
23 -
24 - /**
25 - * @return array basic select fields for FlaggedPageConfig DB row
26 - */
27 - public static function selectFields() {
28 - return array( 'fpc_override', 'fpc_level', 'fpc_expiry' );
29 - }
30 -
31 - /**
32 - * Get page configuration settings from a DB row
33 - */
34 - public static function getVisibilitySettingsFromRow( $row ) {
35 - if ( $row ) {
36 - # This code should be refactored, now that it's being used more generally.
37 - $expiry = Block::decodeExpiry( $row->fpc_expiry );
38 - # Only apply the settings if they haven't expired
39 - if ( !$expiry || $expiry < wfTimestampNow() ) {
40 - $row = null; // expired
41 - self::purgeExpiredConfigurations();
42 - }
43 - }
44 - // Is there a non-expired row?
45 - if ( $row ) {
46 - $level = $row->fpc_level;
47 - if ( !self::isValidRestriction( $row->fpc_level ) ) {
48 - $level = ''; // site default; ignore fpc_level
49 - }
50 - $config = array(
51 - 'override' => $row->fpc_override ? 1 : 0,
52 - 'autoreview' => $level,
53 - 'expiry' => Block::decodeExpiry( $row->fpc_expiry ) // TS_MW
54 - );
55 - # If there are protection levels defined check if this is valid...
56 - if ( FlaggedRevs::useProtectionLevels() ) {
57 - $level = self::getProtectionLevel( $config );
58 - if ( $level == 'invalid' || $level == 'none' ) {
59 - // If 'none', make sure expiry is 'infinity'
60 - $config = self::getDefaultVisibilitySettings(); // revert to default (none)
61 - }
62 - }
63 - } else {
64 - # Return the default config if this page doesn't have its own
65 - $config = self::getDefaultVisibilitySettings();
66 - }
67 - return $config;
68 - }
69 -
70 - /**
71 - * Get default stability configuration settings
72 - * @return array
73 - */
74 - public static function getDefaultVisibilitySettings() {
75 - return array(
76 - # Keep this consistent: 1 => override, 0 => don't
77 - 'override' => FlaggedRevs::isStableShownByDefault() ? 1 : 0,
78 - 'autoreview' => '',
79 - 'expiry' => 'infinity'
80 - );
81 - }
82 -
83 - /**
84 - * Set the stability configuration settings for a page
85 - * @param Title $title
86 - * @param array $config
87 - * @return bool Row changed
88 - */
89 - public static function setStabilitySettings( Title $title, array $config ) {
90 - $dbw = wfGetDB( DB_MASTER );
91 - # If setting to site default values and there is a row then erase it
92 - if ( self::configIsReset( $config ) ) {
93 - $dbw->delete( 'flaggedpage_config',
94 - array( 'fpc_page_id' => $title->getArticleID() ),
95 - __METHOD__
96 - );
97 - $changed = ( $dbw->affectedRows() != 0 ); // did this do anything?
98 - # Otherwise, add/replace row if we are not just setting it to the site default
99 - } else {
100 - $dbExpiry = Block::encodeExpiry( $config['expiry'], $dbw );
101 - # Get current config...
102 - $oldRow = $dbw->selectRow( 'flaggedpage_config',
103 - array( 'fpc_override', 'fpc_level', 'fpc_expiry' ),
104 - array( 'fpc_page_id' => $title->getArticleID() ),
105 - __METHOD__,
106 - 'FOR UPDATE' // lock
107 - );
108 - # Check if this is not the same config as the existing (if any) row
109 - $changed = ( !$oldRow // no previous config
110 - || $oldRow->fpc_override != $config['override'] // ...override changed, or...
111 - || $oldRow->fpc_level != $config['autoreview'] // ...autoreview level changed, or...
112 - || $oldRow->fpc_expiry != $dbExpiry // ...expiry changed
113 - );
114 - # If the new config is different, replace the old row...
115 - if ( $changed ) {
116 - $dbw->replace( 'flaggedpage_config',
117 - array( 'PRIMARY' ),
118 - array(
119 - 'fpc_page_id' => $title->getArticleID(),
120 - 'fpc_select' => -1, // unused
121 - 'fpc_override' => (int)$config['override'],
122 - 'fpc_level' => $config['autoreview'],
123 - 'fpc_expiry' => $dbExpiry
124 - ),
125 - __METHOD__
126 - );
127 - }
128 - }
129 - return $changed;
130 - }
131 -
132 - /**
133 - * Does this config equal the default settings?
134 - * @param array $config
135 - * @return bool
136 - */
137 - public static function configIsReset( array $config ) {
138 - if ( FlaggedRevs::useOnlyIfProtected() ) {
139 - return ( $config['autoreview'] == '' );
140 - } else {
141 - return ( $config['override'] == FlaggedRevs::isStableShownByDefault()
142 - && $config['autoreview'] == '' );
143 - }
144 - }
145 -
146 - /**
147 - * Find what protection level a config is in
148 - * @param array $config
149 - * @return string
150 - */
151 - public static function getProtectionLevel( array $config ) {
152 - if ( !FlaggedRevs::useProtectionLevels() ) {
153 - throw new MWException( '$wgFlaggedRevsProtection is disabled' );
154 - }
155 - $defaultConfig = self::getDefaultVisibilitySettings();
156 - # Check if the page is not protected at all...
157 - if ( $config['override'] == $defaultConfig['override']
158 - && $config['autoreview'] == '' )
159 - {
160 - return "none"; // not protected
161 - }
162 - # All protection levels have 'override' on
163 - if ( $config['override'] ) {
164 - # The levels are defined by the 'autoreview' settings
165 - if ( in_array( $config['autoreview'], FlaggedRevs::getRestrictionLevels() ) ) {
166 - return $config['autoreview'];
167 - }
168 - }
169 - return "invalid";
170 - }
171 -
172 - /**
173 - * Check if an fpc_level value is valid
174 - * @param string $right
175 - */
176 - protected static function isValidRestriction( $right ) {
177 - if ( $right == '' ) {
178 - return true; // no restrictions (none)
179 - }
180 - return in_array( $right, FlaggedRevs::getRestrictionLevels(), true );
181 - }
182 -
183 - /**
184 - * Purge expired restrictions from the flaggedpage_config table.
185 - * The stable version of pages may change and invalidation may be required.
186 - */
187 - public static function purgeExpiredConfigurations() {
188 - if ( wfReadOnly() ) return;
189 - $dbw = wfGetDB( DB_MASTER );
190 - # Find pages with expired configs...
191 - $config = self::getDefaultVisibilitySettings(); // config is to be reset
192 - $encCutoff = $dbw->addQuotes( $dbw->timestamp() );
193 - $ret = $dbw->select(
194 - array( 'flaggedpage_config', 'page' ),
195 - array( 'fpc_page_id', 'page_namespace', 'page_title' ),
196 - array( 'page_id = fpc_page_id', 'fpc_expiry < ' . $encCutoff ),
197 - __METHOD__
198 - // array( 'FOR UPDATE' )
199 - );
200 - # Figured out to do with each page...
201 - $pagesClearConfig = array();
202 - $pagesClearTracking = $titlesClearTracking = array();
203 - foreach ( $ret as $row ) {
204 - # If FlaggedRevs got "turned off" (in protection config)
205 - # for this page, then clear it from the tracking tables...
206 - if ( FlaggedRevs::useOnlyIfProtected() && !$config['override'] ) {
207 - $pagesClearTracking[] = $row->fpc_page_id; // no stable version
208 - $titlesClearTracking[] = Title::newFromRow( $row ); // no stable version
209 - }
210 - $pagesClearConfig[] = $row->fpc_page_id; // page with expired config
211 - }
212 - # Clear the expired config for these pages...
213 - if ( count( $pagesClearConfig ) ) {
214 - $dbw->delete( 'flaggedpage_config',
215 - array( 'fpc_page_id' => $pagesClearConfig, 'fpc_expiry < ' . $encCutoff ),
216 - __METHOD__
217 - );
218 - }
219 - # Clear the tracking rows and update page_touched for the
220 - # pages in $pagesClearConfig that do now have a stable version...
221 - if ( count( $pagesClearTracking ) ) {
222 - FlaggedRevs::clearTrackingRows( $pagesClearTracking );
223 - $dbw->update( 'page',
224 - array( 'page_touched' => $dbw->timestamp() ),
225 - array( 'page_id' => $pagesClearTracking ),
226 - __METHOD__
227 - );
228 - }
229 - # Also, clear their squid caches and purge other pages that use this page.
230 - # NOTE: all of these updates are deferred via $wgDeferredUpdateList.
231 - foreach ( $titlesClearTracking as $title ) {
232 - FlaggedRevs::purgeSquid( $title );
233 - if ( FlaggedRevs::inclusionSetting() == FR_INCLUDES_STABLE ) {
234 - FlaggedRevs::HTMLCacheUpdates( $title ); // purge pages that use this page
235 - }
236 - }
237 - }
238 -}
Index: trunk/extensions/FlaggedRevs/dataclasses/FlaggedPage.php
@@ -1,552 +0,0 @@
2 -<?php
3 -/**
4 - * Class representing a MediaWiki article and history
5 - *
6 - * FlaggedPage::getTitleInstance() is preferred over constructor calls
7 - */
8 -class FlaggedPage extends WikiPage {
9 - /* Process cache variables */
10 - protected $stable = 0;
11 - protected $stableRev = null;
12 - protected $revsArePending = null;
13 - protected $pendingRevCount = null;
14 - protected $pageConfig = null;
15 - protected $syncedInTracking = null;
16 -
17 - protected $file = null; // for file pages
18 -
19 - /**
20 - * Get a FlaggedPage for a given title
21 - * @param Title
22 - * @return FlaggedPage
23 - */
24 - public static function getTitleInstance( Title $title ) {
25 - // Check if there is already an instance on this title
26 - if ( !isset( $title->flaggedRevsArticle ) ) {
27 - $title->flaggedRevsArticle = new self( $title );
28 - }
29 - return $title->flaggedRevsArticle;
30 - }
31 -
32 - /**
33 - * Get a FlaggedPage for a given article
34 - * @param Article
35 - * @return FlaggedPage
36 - */
37 - public static function getArticleInstance( Page $article ) {
38 - return self::getTitleInstance( $article->getTitle() );
39 - }
40 -
41 - /**
42 - * Clear object process cache values
43 - * @return void
44 - */
45 - public function clear() {
46 - $this->stable = 0;
47 - $this->stableRev = null;
48 - $this->revsArePending = null;
49 - $this->pendingRevCount = null;
50 - $this->pageConfig = null;
51 - $this->syncedInTracking = null;
52 - $this->file = null;
53 - parent::clear(); // call super!
54 - }
55 -
56 - /**
57 - * Get the current file version (null if this not a File page)
58 - *
59 - * @return File|null|false
60 - */
61 - public function getFile() {
62 - if ( $this->file === null && $this->mTitle->getNamespace() == NS_FILE ) {
63 - $this->file = wfFindFile( $this->mTitle );
64 - }
65 - return $this->file;
66 - }
67 -
68 - /**
69 - * Is the stable version shown by default for this page?
70 - * @return bool
71 - */
72 - public function isStableShownByDefault() {
73 - if ( !$this->isReviewable() ) {
74 - return false; // no stable versions can exist
75 - }
76 - $config = $this->getStabilitySettings(); // page configuration
77 - return (bool)$config['override'];
78 - }
79 -
80 - /**
81 - * Do edits have to be reviewed before being shown by default (going live)?
82 - * @return bool
83 - */
84 - public function editsRequireReview() {
85 - return (
86 - $this->isReviewable() && // reviewable page
87 - $this->isStableShownByDefault() && // and stable versions override
88 - $this->getStableRev() // and there is a stable version
89 - );
90 - }
91 -
92 - /**
93 - * Are edits to this page currently pending?
94 - * @return bool
95 - */
96 - public function revsArePending() {
97 - if ( !$this->mDataLoaded ) {
98 - $this->loadPageData();
99 - }
100 - return $this->revsArePending;
101 - }
102 -
103 - /**
104 - * Get number of revs since the stable revision
105 - * Note: slower than revsArePending()
106 - * @param int $flags FR_MASTER (be sure to use loadFromDB( FR_MASTER ) if set)
107 - * @return int
108 - */
109 - public function getPendingRevCount( $flags = 0 ) {
110 - global $wgMemc, $wgParserCacheExpireTime;
111 - if ( !$this->mDataLoaded ) {
112 - $this->loadPageData();
113 - }
114 - # Pending count deferred even after page data load
115 - if ( $this->pendingRevCount !== null ) {
116 - return $this->pendingRevCount; // use process cache
117 - }
118 - $srev = $this->getStableRev();
119 - if ( !$srev ) {
120 - return 0; // none
121 - }
122 - $count = null;
123 - $sRevId = $srev->getRevId();
124 - # Try the cache...
125 - $key = wfMemcKey( 'flaggedrevs', 'countPending', $this->getId() );
126 - if ( !( $flags & FR_MASTER ) ) {
127 - $tuple = FlaggedRevs::getMemcValue( $wgMemc->get( $key ), $this );
128 - # Items is cached and newer that page_touched...
129 - if ( $tuple !== false ) {
130 - # Confirm that cache value was made against the same stable rev Id.
131 - # This avoids lengthy cache pollution if $sRevId is outdated.
132 - list( $cRevId, $cPending ) = explode( '-', $tuple, 2 );
133 - if ( $cRevId == $sRevId ) {
134 - $count = (int)$cPending;
135 - }
136 - }
137 - }
138 - # Otherwise, fetch result from DB as needed...
139 - if ( is_null( $count ) ) {
140 - $db = ( $flags & FR_MASTER ) ?
141 - wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
142 - $srevTS = $db->timestamp( $srev->getRevTimestamp() );
143 - $count = $db->selectField( 'revision', 'COUNT(*)',
144 - array( 'rev_page' => $this->getId(),
145 - 'rev_timestamp > ' . $db->addQuotes( $srevTS ) ), // bug 15515
146 - __METHOD__ );
147 - # Save result to cache...
148 - $data = FlaggedRevs::makeMemcObj( "{$sRevId}-{$count}" );
149 - $wgMemc->set( $key, $data, $wgParserCacheExpireTime );
150 - }
151 - $this->pendingRevCount = $count;
152 - return $this->pendingRevCount;
153 - }
154 -
155 - /**
156 - * Checks if the stable version is synced with the current revision
157 - * Note: slower than getPendingRevCount()
158 - * @return bool
159 - */
160 - public function stableVersionIsSynced() {
161 - global $wgMemc, $wgParserCacheExpireTime;
162 - $srev = $this->getStableRev();
163 - if ( !$srev ) {
164 - return true;
165 - }
166 - # Stable text revision must be the same as the current
167 - if ( $this->revsArePending() ) {
168 - return false;
169 - # Stable file revision must be the same as the current
170 - } elseif ( $this->mTitle->getNamespace() == NS_FILE ) {
171 - $file = $this->getFile(); // current upload version
172 - if ( $file && $file->getTimestamp() > $srev->getFileTimestamp() ) {
173 - return false;
174 - }
175 - }
176 - # If using the current version of includes, there is nothing else to check.
177 - if ( FlaggedRevs::inclusionSetting() == FR_INCLUDES_CURRENT ) {
178 - return true; // short-circuit
179 - }
180 - # Try the cache...
181 - $key = wfMemcKey( 'flaggedrevs', 'includesSynced', $this->getId() );
182 - $value = FlaggedRevs::getMemcValue( $wgMemc->get( $key ), $this );
183 - if ( $value === "true" ) {
184 - return true;
185 - } elseif ( $value === "false" ) {
186 - return false;
187 - }
188 - # Since the stable and current revisions have the same text and only outputs,
189 - # the only other things to check for are template and file differences in the output.
190 - # (a) Check if the current output has a newer template/file used
191 - # (b) Check if the stable version has a file/template that was deleted
192 - $synced = ( !$srev->findPendingTemplateChanges()
193 - && !$srev->findPendingFileChanges( 'noForeign' ) );
194 - # Save to cache. This will be updated whenever the page is touched.
195 - $data = FlaggedRevs::makeMemcObj( $synced ? "true" : "false" );
196 - $wgMemc->set( $key, $data, $wgParserCacheExpireTime );
197 -
198 - return $synced;
199 - }
200 -
201 - /**
202 - * Are template/file changes and ONLY template/file changes pending?
203 - * @return bool
204 - */
205 - public function onlyTemplatesOrFilesPending() {
206 - return ( !$this->revsArePending() && !$this->stableVersionIsSynced() );
207 - }
208 -
209 - /**
210 - * Is this page less open than the site defaults?
211 - * @return bool
212 - */
213 - public function isPageLocked() {
214 - return ( !FlaggedRevs::isStableShownByDefault() && $this->isStableShownByDefault() );
215 - }
216 -
217 - /**
218 - * Is this page more open than the site defaults?
219 - * @return bool
220 - */
221 - public function isPageUnlocked() {
222 - return ( FlaggedRevs::isStableShownByDefault() && !$this->isStableShownByDefault() );
223 - }
224 -
225 - /**
226 - * Tags are only shown for unreviewed content and this page is not locked/unlocked?
227 - * @return bool
228 - */
229 - public function lowProfileUI() {
230 - return FlaggedRevs::lowProfileUI() &&
231 - FlaggedRevs::isStableShownByDefault() == $this->isStableShownByDefault();
232 - }
233 -
234 - /**
235 - * Is this article reviewable?
236 - * @return bool
237 - */
238 - public function isReviewable() {
239 - if ( !FlaggedRevs::inReviewNamespace( $this->mTitle ) ) {
240 - return false;
241 - }
242 - # Check if flagging is disabled for this page via config
243 - if ( FlaggedRevs::useOnlyIfProtected() ) {
244 - $config = $this->getStabilitySettings(); // page configuration
245 - return (bool)$config['override']; // stable is default or flagging disabled
246 - }
247 - return true;
248 - }
249 -
250 - /**
251 - * Get the stable revision ID
252 - * @return int
253 - */
254 - public function getStable() {
255 - if ( !FlaggedRevs::inReviewNamespace( $this->mTitle ) ) {
256 - return 0; // short-circuit
257 - }
258 - if ( !$this->mDataLoaded ) {
259 - $this->loadPageData();
260 - }
261 - return (int)$this->stable;
262 - }
263 -
264 - /**
265 - * Get the stable revision
266 - * @return mixed (FlaggedRevision/null)
267 - */
268 - public function getStableRev() {
269 - if ( !FlaggedRevs::inReviewNamespace( $this->mTitle ) ) {
270 - return null; // short-circuit
271 - }
272 - if ( !$this->mDataLoaded ) {
273 - $this->loadPageData();
274 - }
275 - # Stable rev deferred even after page data load
276 - if ( $this->stableRev === null ) {
277 - $srev = FlaggedRevision::newFromTitle( $this->mTitle, $this->stable );
278 - $this->stableRev = $srev ? $srev : false; // cache negative hits too
279 - }
280 - return $this->stableRev ? $this->stableRev : null; // false => null
281 - }
282 -
283 - /**
284 - * Get visiblity restrictions on page
285 - * @return array (select,override)
286 - */
287 - public function getStabilitySettings() {
288 - if ( !$this->mDataLoaded ) {
289 - $this->loadPageData();
290 - }
291 - return $this->pageConfig;
292 - }
293 -
294 - /*
295 - * Get the fp_reviewed value for this page
296 - * @return bool
297 - */
298 - public function syncedInTracking() {
299 - if ( !$this->mDataLoaded ) {
300 - $this->loadPageData();
301 - }
302 - return $this->syncedInTracking;
303 - }
304 -
305 - /**
306 - * Get the newest of the highest rated flagged revisions of this page
307 - * Note: will not return deleted revisions
308 - * @return int
309 - */
310 - public function getBestFlaggedRevId() {
311 - $dbr = wfGetDB( DB_SLAVE );
312 - # Get the highest quality revision (not necessarily this one).
313 - $oldid = $dbr->selectField( array( 'flaggedrevs', 'revision' ),
314 - 'fr_rev_id',
315 - array(
316 - 'fr_page_id' => $this->getId(),
317 - 'rev_page = fr_page_id', // sanity
318 - 'rev_id = fr_rev_id',
319 - 'rev_deleted & ' . Revision::DELETED_TEXT => 0
320 - ),
321 - __METHOD__,
322 - array(
323 - 'ORDER BY' => 'fr_quality DESC, fr_rev_timestamp DESC',
324 - 'USE INDEX' => array( 'flaggedrevs' => 'page_qal_time' )
325 - )
326 - );
327 - return (int)$oldid;
328 - }
329 -
330 - /**
331 - * Updates the fp_reviewed field for this article
332 - * @param bool $synced
333 - */
334 - public function updateSyncStatus( $synced ) {
335 - wfProfileIn( __METHOD__ );
336 - if ( !wfReadOnly() ) {
337 - $dbw = wfGetDB( DB_MASTER );
338 - $dbw->update( 'flaggedpages',
339 - array( 'fp_reviewed' => $synced ? 1 : 0 ),
340 - array( 'fp_page_id' => $this->getID() ),
341 - __METHOD__
342 - );
343 - }
344 - wfProfileOut( __METHOD__ );
345 - }
346 -
347 - /**
348 - * Fetch a page record with the given conditions
349 - * @param $dbr Database object
350 - * @param $conditions Array
351 - * @return mixed Database result resource, or false on failure
352 - */
353 - protected function pageData( $dbr, $conditions ) {
354 - $row = $dbr->selectRow(
355 - array( 'page', 'flaggedpages', 'flaggedpage_config' ),
356 - array_merge(
357 - WikiPage::selectFields(),
358 - FlaggedPageConfig::selectFields(),
359 - array( 'fp_pending_since', 'fp_stable', 'fp_reviewed' ) ),
360 - $conditions,
361 - __METHOD__,
362 - array(),
363 - array(
364 - 'flaggedpages' => array( 'LEFT JOIN', 'fp_page_id = page_id' ),
365 - 'flaggedpage_config' => array( 'LEFT JOIN', 'fpc_page_id = page_id' ) )
366 - );
367 - return $row;
368 - }
369 -
370 - /**
371 - * Set the page field data loaded from some source
372 - * @param $data Database row object or "fromdb"
373 - * @return void
374 - */
375 - public function loadPageData( $data = 'fromdb' ) {
376 - $this->mDataLoaded = true; // sanity
377 - # Fetch data from DB as needed...
378 - if ( $data === 'fromdb' || $data === 'fromdbmaster' ) {
379 - $db = ( $data == 'fromdbmaster' )
380 - ? wfGetDB( DB_MASTER )
381 - : wfGetDB( DB_SLAVE );
382 - $data = $this->pageDataFromTitle( $db, $this->mTitle );
383 - }
384 - # Load in primary page data...
385 - parent::loadPageData( $data /* Row obj */ );
386 - # Load in FlaggedRevs page data...
387 - $this->stable = 0; // 0 => "found nothing"
388 - $this->stableRev = null; // defer this one...
389 - $this->revsArePending = false; // false => "found nothing" or "none pending"
390 - $this->pendingRevCount = null; // defer this one...
391 - $this->pageConfig = FlaggedPageConfig::getDefaultVisibilitySettings(); // default
392 - $this->syncedInTracking = true; // false => "unreviewed" or "synced"
393 - # Load in flaggedrevs Row data if the page exists...(sanity check NS)
394 - if ( $data && FlaggedRevs::inReviewNamespace( $this->mTitle ) ) {
395 - if ( $data->fpc_override !== null ) { // page config row found
396 - $this->pageConfig = FlaggedPageConfig::getVisibilitySettingsFromRow( $data );
397 - }
398 - if ( $data->fp_stable !== null ) { // stable rev found
399 - $this->stable = (int)$data->fp_stable;
400 - $this->revsArePending = ( $data->fp_pending_since !== null ); // revs await review
401 - $this->syncedInTracking = (bool)$data->fp_reviewed;
402 - }
403 - }
404 - }
405 -
406 - /**
407 - * Updates the flagging tracking tables for this page
408 - * @param FlaggedRevision $srev The new stable version
409 - * @param int|null $latest The latest rev ID (optional)
410 - * @return bool Updates were done
411 - */
412 - public function updateStableVersion( FlaggedRevision $srev, $latest = null ) {
413 - $rev = $srev->getRevision();
414 - if ( !$this->exists() || !$rev ) {
415 - return false; // no bogus entries
416 - }
417 - # Get the latest revision ID if not set
418 - if ( !$latest ) {
419 - $latest = $this->mTitle->getLatestRevID( Title::GAID_FOR_UPDATE );
420 - }
421 - $dbw = wfGetDB( DB_MASTER );
422 - # Get the highest quality revision (not necessarily this one)...
423 - if ( $srev->getQuality() === FlaggedRevs::highestReviewTier() ) {
424 - $maxQuality = $srev->getQuality(); // save a query
425 - } else {
426 - $maxQuality = $dbw->selectField( array( 'flaggedrevs', 'revision' ),
427 - 'fr_quality',
428 - array( 'fr_page_id' => $this->getId(),
429 - 'rev_id = fr_rev_id',
430 - 'rev_page = fr_page_id',
431 - 'rev_deleted & ' . Revision::DELETED_TEXT => 0
432 - ),
433 - __METHOD__,
434 - array( 'ORDER BY' => 'fr_quality DESC', 'LIMIT' => 1 )
435 - );
436 - $maxQuality = max( $maxQuality, $srev->getQuality() ); // sanity
437 - }
438 - # Get the timestamp of the first edit after the stable version (if any)...
439 - $nextTimestamp = null;
440 - if ( $rev->getId() != $latest ) {
441 - $timestamp = $dbw->timestamp( $rev->getTimestamp() );
442 - $nextEditTS = $dbw->selectField( 'revision',
443 - 'rev_timestamp',
444 - array(
445 - 'rev_page' => $this->getId(),
446 - "rev_timestamp > " . $dbw->addQuotes( $timestamp ) ),
447 - __METHOD__,
448 - array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 )
449 - );
450 - if ( $nextEditTS ) { // sanity check
451 - $nextTimestamp = $nextEditTS;
452 - }
453 - }
454 - # Get the new page sync status...
455 - $synced = !(
456 - $nextTimestamp !== null || // edits pending
457 - $srev->findPendingTemplateChanges() || // template changes pending
458 - $srev->findPendingFileChanges( 'noForeign' ) // file changes pending
459 - );
460 - # Alter table metadata
461 - $dbw->replace( 'flaggedpages',
462 - array( 'fp_page_id' ),
463 - array(
464 - 'fp_page_id' => $this->getId(),
465 - 'fp_stable' => $rev->getId(),
466 - 'fp_reviewed' => $synced ? 1 : 0,
467 - 'fp_quality' => ( $maxQuality === false ) ? null : $maxQuality,
468 - 'fp_pending_since' => $dbw->timestampOrNull( $nextTimestamp )
469 - ),
470 - __METHOD__
471 - );
472 - # Update pending edit tracking table
473 - self::updatePendingList( $this->getId(), $latest );
474 - return true;
475 - }
476 -
477 - /**
478 - * Updates the flagging tracking tables for this page
479 - * @return void
480 - */
481 - public function clearStableVersion() {
482 - if ( !$this->exists() ) {
483 - return; // nothing to do
484 - }
485 - $dbw = wfGetDB( DB_MASTER );
486 - $dbw->delete( 'flaggedpages',
487 - array( 'fp_page_id' => $this->getId() ), __METHOD__ );
488 - $dbw->delete( 'flaggedpage_pending',
489 - array( 'fpp_page_id' => $this->getId() ), __METHOD__ );
490 - }
491 -
492 - /**
493 - * Updates the flaggedpage_pending table
494 - * @param int $pageId Page ID
495 - * @abstract int $latest Latest revision
496 - * @return void
497 - */
498 - protected static function updatePendingList( $pageId, $latest ) {
499 - $data = array();
500 - $level = FlaggedRevs::highestReviewTier();
501 - # Update pending times for each level, going from highest to lowest
502 - $dbw = wfGetDB( DB_MASTER );
503 - $higherLevelId = 0;
504 - $higherLevelTS = '';
505 - while ( $level >= 0 ) {
506 - # Get the latest revision of this level...
507 - $row = $dbw->selectRow( array( 'flaggedrevs', 'revision' ),
508 - array( 'fr_rev_id', 'rev_timestamp' ),
509 - array( 'fr_page_id' => $pageId,
510 - 'fr_quality' => $level,
511 - 'rev_id = fr_rev_id',
512 - 'rev_page = fr_page_id',
513 - 'rev_deleted & ' . Revision::DELETED_TEXT => 0,
514 - 'rev_id > ' . intval( $higherLevelId )
515 - ),
516 - __METHOD__,
517 - array( 'ORDER BY' => 'fr_rev_id DESC', 'LIMIT' => 1 )
518 - );
519 - # If there is a revision of this level, track it...
520 - # Revisions reviewed to one level count as reviewed
521 - # at the lower levels (i.e. quality -> checked).
522 - if ( $row ) {
523 - $id = $row->fr_rev_id;
524 - $ts = $row->rev_timestamp;
525 - } else {
526 - $id = $higherLevelId; // use previous (quality -> checked)
527 - $ts = $higherLevelTS; // use previous (quality -> checked)
528 - }
529 - # Get edits that actually are pending...
530 - if ( $id && $latest > $id ) {
531 - # Get the timestamp of the edit after this version (if any)
532 - $nextTimestamp = $dbw->selectField( 'revision',
533 - 'rev_timestamp',
534 - array( 'rev_page' => $pageId, "rev_timestamp > " . $dbw->addQuotes( $ts ) ),
535 - __METHOD__,
536 - array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 )
537 - );
538 - $data[] = array(
539 - 'fpp_page_id' => $pageId,
540 - 'fpp_quality' => $level,
541 - 'fpp_rev_id' => $id,
542 - 'fpp_pending_since' => $nextTimestamp
543 - );
544 - $higherLevelId = $id;
545 - $higherLevelTS = $ts;
546 - }
547 - $level--;
548 - }
549 - # Clear any old junk, and insert new rows
550 - $dbw->delete( 'flaggedpage_pending', array( 'fpp_page_id' => $pageId ), __METHOD__ );
551 - $dbw->insert( 'flaggedpage_pending', $data, __METHOD__ );
552 - }
553 -}
Index: trunk/extensions/FlaggedRevs/dataclasses/FRPageConfig.php
@@ -0,0 +1,237 @@
 2+<?php
 3+/*
 4+* Page stability configuration functions
 5+*/
 6+class FRPageConfig {
 7+ /**
 8+ * Get visibility settings/restrictions for a page
 9+ * @param Title $title, page title
 10+ * @param int $flags, FR_MASTER
 11+ * @return array (associative) (select,override,autoreview,expiry)
 12+ */
 13+ public static function getStabilitySettings( Title $title, $flags = 0 ) {
 14+ $db = ( $flags & FR_MASTER ) ?
 15+ wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
 16+ $row = $db->selectRow( 'flaggedpage_config',
 17+ self::selectFields(),
 18+ array( 'fpc_page_id' => $title->getArticleID() ),
 19+ __METHOD__
 20+ );
 21+ return self::getVisibilitySettingsFromRow( $row );
 22+ }
 23+
 24+ /**
 25+ * @return array basic select fields for FRPageConfig DB row
 26+ */
 27+ public static function selectFields() {
 28+ return array( 'fpc_override', 'fpc_level', 'fpc_expiry' );
 29+ }
 30+
 31+ /**
 32+ * Get page configuration settings from a DB row
 33+ */
 34+ public static function getVisibilitySettingsFromRow( $row ) {
 35+ if ( $row ) {
 36+ # This code should be refactored, now that it's being used more generally.
 37+ $expiry = Block::decodeExpiry( $row->fpc_expiry );
 38+ # Only apply the settings if they haven't expired
 39+ if ( !$expiry || $expiry < wfTimestampNow() ) {
 40+ $row = null; // expired
 41+ self::purgeExpiredConfigurations();
 42+ }
 43+ }
 44+ // Is there a non-expired row?
 45+ if ( $row ) {
 46+ $level = $row->fpc_level;
 47+ if ( !self::isValidRestriction( $row->fpc_level ) ) {
 48+ $level = ''; // site default; ignore fpc_level
 49+ }
 50+ $config = array(
 51+ 'override' => $row->fpc_override ? 1 : 0,
 52+ 'autoreview' => $level,
 53+ 'expiry' => Block::decodeExpiry( $row->fpc_expiry ) // TS_MW
 54+ );
 55+ # If there are protection levels defined check if this is valid...
 56+ if ( FlaggedRevs::useProtectionLevels() ) {
 57+ $level = self::getProtectionLevel( $config );
 58+ if ( $level == 'invalid' || $level == 'none' ) {
 59+ // If 'none', make sure expiry is 'infinity'
 60+ $config = self::getDefaultVisibilitySettings(); // revert to default (none)
 61+ }
 62+ }
 63+ } else {
 64+ # Return the default config if this page doesn't have its own
 65+ $config = self::getDefaultVisibilitySettings();
 66+ }
 67+ return $config;
 68+ }
 69+
 70+ /**
 71+ * Get default stability configuration settings
 72+ * @return array
 73+ */
 74+ public static function getDefaultVisibilitySettings() {
 75+ return array(
 76+ # Keep this consistent: 1 => override, 0 => don't
 77+ 'override' => FlaggedRevs::isStableShownByDefault() ? 1 : 0,
 78+ 'autoreview' => '',
 79+ 'expiry' => 'infinity'
 80+ );
 81+ }
 82+
 83+ /**
 84+ * Set the stability configuration settings for a page
 85+ * @param Title $title
 86+ * @param array $config
 87+ * @return bool Row changed
 88+ */
 89+ public static function setStabilitySettings( Title $title, array $config ) {
 90+ $dbw = wfGetDB( DB_MASTER );
 91+ # If setting to site default values and there is a row then erase it
 92+ if ( self::configIsReset( $config ) ) {
 93+ $dbw->delete( 'flaggedpage_config',
 94+ array( 'fpc_page_id' => $title->getArticleID() ),
 95+ __METHOD__
 96+ );
 97+ $changed = ( $dbw->affectedRows() != 0 ); // did this do anything?
 98+ # Otherwise, add/replace row if we are not just setting it to the site default
 99+ } else {
 100+ $dbExpiry = Block::encodeExpiry( $config['expiry'], $dbw );
 101+ # Get current config...
 102+ $oldRow = $dbw->selectRow( 'flaggedpage_config',
 103+ array( 'fpc_override', 'fpc_level', 'fpc_expiry' ),
 104+ array( 'fpc_page_id' => $title->getArticleID() ),
 105+ __METHOD__,
 106+ 'FOR UPDATE' // lock
 107+ );
 108+ # Check if this is not the same config as the existing (if any) row
 109+ $changed = ( !$oldRow // no previous config
 110+ || $oldRow->fpc_override != $config['override'] // ...override changed, or...
 111+ || $oldRow->fpc_level != $config['autoreview'] // ...autoreview level changed, or...
 112+ || $oldRow->fpc_expiry != $dbExpiry // ...expiry changed
 113+ );
 114+ # If the new config is different, replace the old row...
 115+ if ( $changed ) {
 116+ $dbw->replace( 'flaggedpage_config',
 117+ array( 'PRIMARY' ),
 118+ array(
 119+ 'fpc_page_id' => $title->getArticleID(),
 120+ 'fpc_select' => -1, // unused
 121+ 'fpc_override' => (int)$config['override'],
 122+ 'fpc_level' => $config['autoreview'],
 123+ 'fpc_expiry' => $dbExpiry
 124+ ),
 125+ __METHOD__
 126+ );
 127+ }
 128+ }
 129+ return $changed;
 130+ }
 131+
 132+ /**
 133+ * Does this config equal the default settings?
 134+ * @param array $config
 135+ * @return bool
 136+ */
 137+ public static function configIsReset( array $config ) {
 138+ if ( FlaggedRevs::useOnlyIfProtected() ) {
 139+ return ( $config['autoreview'] == '' );
 140+ } else {
 141+ return ( $config['override'] == FlaggedRevs::isStableShownByDefault()
 142+ && $config['autoreview'] == '' );
 143+ }
 144+ }
 145+
 146+ /**
 147+ * Find what protection level a config is in
 148+ * @param array $config
 149+ * @return string
 150+ */
 151+ public static function getProtectionLevel( array $config ) {
 152+ if ( !FlaggedRevs::useProtectionLevels() ) {
 153+ throw new MWException( '$wgFlaggedRevsProtection is disabled' );
 154+ }
 155+ $defaultConfig = self::getDefaultVisibilitySettings();
 156+ # Check if the page is not protected at all...
 157+ if ( $config['override'] == $defaultConfig['override']
 158+ && $config['autoreview'] == '' )
 159+ {
 160+ return "none"; // not protected
 161+ }
 162+ # All protection levels have 'override' on
 163+ if ( $config['override'] ) {
 164+ # The levels are defined by the 'autoreview' settings
 165+ if ( in_array( $config['autoreview'], FlaggedRevs::getRestrictionLevels() ) ) {
 166+ return $config['autoreview'];
 167+ }
 168+ }
 169+ return "invalid";
 170+ }
 171+
 172+ /**
 173+ * Check if an fpc_level value is valid
 174+ * @param string $right
 175+ */
 176+ protected static function isValidRestriction( $right ) {
 177+ if ( $right == '' ) {
 178+ return true; // no restrictions (none)
 179+ }
 180+ return in_array( $right, FlaggedRevs::getRestrictionLevels(), true );
 181+ }
 182+
 183+ /**
 184+ * Purge expired restrictions from the flaggedpage_config table.
 185+ * The stable version of pages may change and invalidation may be required.
 186+ */
 187+ public static function purgeExpiredConfigurations() {
 188+ if ( wfReadOnly() ) return;
 189+ $dbw = wfGetDB( DB_MASTER );
 190+ # Find pages with expired configs...
 191+ $config = self::getDefaultVisibilitySettings(); // config is to be reset
 192+ $encCutoff = $dbw->addQuotes( $dbw->timestamp() );
 193+ $ret = $dbw->select(
 194+ array( 'flaggedpage_config', 'page' ),
 195+ array( 'fpc_page_id', 'page_namespace', 'page_title' ),
 196+ array( 'page_id = fpc_page_id', 'fpc_expiry < ' . $encCutoff ),
 197+ __METHOD__
 198+ // array( 'FOR UPDATE' )
 199+ );
 200+ # Figured out to do with each page...
 201+ $pagesClearConfig = array();
 202+ $pagesClearTracking = $titlesClearTracking = array();
 203+ foreach ( $ret as $row ) {
 204+ # If FlaggedRevs got "turned off" (in protection config)
 205+ # for this page, then clear it from the tracking tables...
 206+ if ( FlaggedRevs::useOnlyIfProtected() && !$config['override'] ) {
 207+ $pagesClearTracking[] = $row->fpc_page_id; // no stable version
 208+ $titlesClearTracking[] = Title::newFromRow( $row ); // no stable version
 209+ }
 210+ $pagesClearConfig[] = $row->fpc_page_id; // page with expired config
 211+ }
 212+ # Clear the expired config for these pages...
 213+ if ( count( $pagesClearConfig ) ) {
 214+ $dbw->delete( 'flaggedpage_config',
 215+ array( 'fpc_page_id' => $pagesClearConfig, 'fpc_expiry < ' . $encCutoff ),
 216+ __METHOD__
 217+ );
 218+ }
 219+ # Clear the tracking rows and update page_touched for the
 220+ # pages in $pagesClearConfig that do now have a stable version...
 221+ if ( count( $pagesClearTracking ) ) {
 222+ FlaggedRevs::clearTrackingRows( $pagesClearTracking );
 223+ $dbw->update( 'page',
 224+ array( 'page_touched' => $dbw->timestamp() ),
 225+ array( 'page_id' => $pagesClearTracking ),
 226+ __METHOD__
 227+ );
 228+ }
 229+ # Also, clear their squid caches and purge other pages that use this page.
 230+ # NOTE: all of these updates are deferred via $wgDeferredUpdateList.
 231+ foreach ( $titlesClearTracking as $title ) {
 232+ FlaggedRevs::purgeSquid( $title );
 233+ if ( FlaggedRevs::inclusionSetting() == FR_INCLUDES_STABLE ) {
 234+ FlaggedRevs::HTMLCacheUpdates( $title ); // purge pages that use this page
 235+ }
 236+ }
 237+ }
 238+}
Property changes on: trunk/extensions/FlaggedRevs/dataclasses/FRPageConfig.php
___________________________________________________________________
Added: svn:eol-style
1239 + native
Index: trunk/extensions/FlaggedRevs/dataclasses/FlaggedRevs.hooks.php
@@ -62,7 +62,7 @@
6363 public static function onTitleMoveComplete(
6464 Title $otitle, Title $ntitle, $user, $pageId
6565 ) {
66 - $fa = FlaggedPage::getTitleInstance( $ntitle );
 66+ $fa = FlaggableWikiPage::getTitleInstance( $ntitle );
6767 $fa->loadPageData( 'fromdbmaster' );
6868 // Re-validate NS/config (new title may not be reviewable)
6969 if ( $fa->isReviewable() ) {
@@ -241,7 +241,7 @@
242242 if ( !FlaggedRevs::inReviewNamespace( $title ) ) {
243243 $ret = '';
244244 } else {
245 - $config = FlaggedPageConfig::getStabilitySettings( $title );
 245+ $config = FRPageConfig::getStabilitySettings( $title );
246246 $ret = $config['autoreview'];
247247 }
248248 }
@@ -308,7 +308,7 @@
309309 if ( !FlaggedRevs::inReviewNamespace( $title ) || !$title->exists() ) {
310310 return true; // extra short-circuit
311311 }
312 - $flaggedArticle = FlaggedPage::getTitleInstance( $title );
 312+ $flaggedArticle = FlaggableWikiPage::getTitleInstance( $title );
313313 # If the draft shows by default anyway, nothing to do...
314314 if ( !$flaggedArticle->isStableShownByDefault() ) {
315315 return true;
@@ -321,7 +321,7 @@
322322 }
323323 # Don't let users patrol reviewable pages (where reviewed <=> patrolled)
324324 } elseif ( $action === 'patrol' || $action === 'autopatrol' ) {
325 - $flaggedArticle = FlaggedPage::getTitleInstance( $title );
 325+ $flaggedArticle = FlaggableWikiPage::getTitleInstance( $title );
326326 # For a page to be patrollable it must not be reviewable.
327327 # Note: normally, edits to non-reviewable, non-patrollable, pages are
328328 # silently marked patrolled automatically. With $wgUseNPPatrol on, the
@@ -333,7 +333,7 @@
334334 # Enforce autoreview/review restrictions
335335 } elseif ( $action === 'autoreview' || $action === 'review' ) {
336336 # Get autoreview restriction settings...
337 - $fa = FlaggedPage::getTitleInstance( $title );
 337+ $fa = FlaggableWikiPage::getTitleInstance( $title );
338338 $config = $fa->getStabilitySettings();
339339 # Convert Sysop -> protect
340340 $right = ( $config['autoreview'] === 'sysop' ) ?
@@ -362,7 +362,7 @@
363363 ) {
364364 global $wgRequest;
365365 # Edit must be non-null, to a reviewable page, with $user set
366 - $fa = FlaggedPage::getArticleInstance( $article );
 366+ $fa = FlaggableWikiPage::getArticleInstance( $article );
367367 $fa->loadPageData( 'fromdbmaster' );
368368 if ( !$rev || !$user || !$fa->isReviewable() ) {
369369 return true;
@@ -543,7 +543,7 @@
544544 if ( !$baseId && !$reviewEdit ) {
545545 return true; // short-circuit
546546 }
547 - $fa = FlaggedPage::getArticleInstance( $article );
 547+ $fa = FlaggableWikiPage::getArticleInstance( $article );
548548 $fa->loadPageData( 'fromdbmaster' );
549549 if ( !$fa->isReviewable() ) {
550550 return true; // page is not reviewable
@@ -608,7 +608,7 @@
609609 if ( empty( $rc->mAttribs['rc_this_oldid'] ) ) {
610610 return true;
611611 }
612 - $fa = FlaggedPage::getTitleInstance( $rc->getTitle() );
 612+ $fa = FlaggableWikiPage::getTitleInstance( $rc->getTitle() );
613613 $fa->loadPageData( 'fromdbmaster' );
614614 // Is the page reviewable?
615615 if ( $fa->isReviewable() ) {
Index: trunk/extensions/FlaggedRevs/dataclasses/FlaggedRevsLog.php
@@ -100,12 +100,12 @@
101101 Title $title, array $config, array $oldConfig, $reason
102102 ) {
103103 $log = new LogPage( 'stable' );
104 - if ( FlaggedPageConfig::configIsReset( $config ) ) {
 104+ if ( FRPageConfig::configIsReset( $config ) ) {
105105 # We are going back to default settings
106106 $log->addEntry( 'reset', $title, $reason );
107107 } else {
108108 # We are changing to non-default settings
109 - $action = ( $oldConfig === FlaggedPageConfig::getDefaultVisibilitySettings() )
 109+ $action = ( $oldConfig === FRPageConfig::getDefaultVisibilitySettings() )
110110 ? 'config' // set a custom configuration
111111 : 'modify'; // modified an existing custom configuration
112112 $log->addEntry( $action, $title, $reason,
Index: trunk/extensions/FlaggedRevs/dataclasses/FlaggedRevs.class.php
@@ -615,7 +615,7 @@
616616 if ( $sv === null ) { // optional
617617 $sv = FlaggedRevision::determineStable( $title, FR_MASTER );
618618 }
619 - $article = new FlaggedPage( $title );
 619+ $article = new FlaggableWikiPage( $title );
620620 if ( !$sv ) {
621621 # Empty flaggedrevs data for this page if there is no stable version
622622 $article->clearStableVersion();
Index: trunk/extensions/FlaggedRevs/dataclasses/FlaggableWikiPage.php
@@ -0,0 +1,552 @@
 2+<?php
 3+/**
 4+ * Class representing a MediaWiki article and history
 5+ *
 6+ * FlaggableWikiPage::getTitleInstance() is preferred over constructor calls
 7+ */
 8+class FlaggableWikiPage extends WikiPage {
 9+ /* Process cache variables */
 10+ protected $stable = 0;
 11+ protected $stableRev = null;
 12+ protected $revsArePending = null;
 13+ protected $pendingRevCount = null;
 14+ protected $pageConfig = null;
 15+ protected $syncedInTracking = null;
 16+
 17+ protected $file = null; // for file pages
 18+
 19+ /**
 20+ * Get a FlaggableWikiPage for a given title
 21+ * @param Title
 22+ * @return FlaggableWikiPage
 23+ */
 24+ public static function getTitleInstance( Title $title ) {
 25+ // Check if there is already an instance on this title
 26+ if ( !isset( $title->flaggedRevsArticle ) ) {
 27+ $title->flaggedRevsArticle = new self( $title );
 28+ }
 29+ return $title->flaggedRevsArticle;
 30+ }
 31+
 32+ /**
 33+ * Get a FlaggableWikiPage for a given article
 34+ * @param Article
 35+ * @return FlaggableWikiPage
 36+ */
 37+ public static function getArticleInstance( Page $article ) {
 38+ return self::getTitleInstance( $article->getTitle() );
 39+ }
 40+
 41+ /**
 42+ * Clear object process cache values
 43+ * @return void
 44+ */
 45+ public function clear() {
 46+ $this->stable = 0;
 47+ $this->stableRev = null;
 48+ $this->revsArePending = null;
 49+ $this->pendingRevCount = null;
 50+ $this->pageConfig = null;
 51+ $this->syncedInTracking = null;
 52+ $this->file = null;
 53+ parent::clear(); // call super!
 54+ }
 55+
 56+ /**
 57+ * Get the current file version (null if this not a File page)
 58+ *
 59+ * @return File|null|false
 60+ */
 61+ public function getFile() {
 62+ if ( $this->file === null && $this->mTitle->getNamespace() == NS_FILE ) {
 63+ $this->file = wfFindFile( $this->mTitle );
 64+ }
 65+ return $this->file;
 66+ }
 67+
 68+ /**
 69+ * Is the stable version shown by default for this page?
 70+ * @return bool
 71+ */
 72+ public function isStableShownByDefault() {
 73+ if ( !$this->isReviewable() ) {
 74+ return false; // no stable versions can exist
 75+ }
 76+ $config = $this->getStabilitySettings(); // page configuration
 77+ return (bool)$config['override'];
 78+ }
 79+
 80+ /**
 81+ * Do edits have to be reviewed before being shown by default (going live)?
 82+ * @return bool
 83+ */
 84+ public function editsRequireReview() {
 85+ return (
 86+ $this->isReviewable() && // reviewable page
 87+ $this->isStableShownByDefault() && // and stable versions override
 88+ $this->getStableRev() // and there is a stable version
 89+ );
 90+ }
 91+
 92+ /**
 93+ * Are edits to this page currently pending?
 94+ * @return bool
 95+ */
 96+ public function revsArePending() {
 97+ if ( !$this->mDataLoaded ) {
 98+ $this->loadPageData();
 99+ }
 100+ return $this->revsArePending;
 101+ }
 102+
 103+ /**
 104+ * Get number of revs since the stable revision
 105+ * Note: slower than revsArePending()
 106+ * @param int $flags FR_MASTER (be sure to use loadFromDB( FR_MASTER ) if set)
 107+ * @return int
 108+ */
 109+ public function getPendingRevCount( $flags = 0 ) {
 110+ global $wgMemc, $wgParserCacheExpireTime;
 111+ if ( !$this->mDataLoaded ) {
 112+ $this->loadPageData();
 113+ }
 114+ # Pending count deferred even after page data load
 115+ if ( $this->pendingRevCount !== null ) {
 116+ return $this->pendingRevCount; // use process cache
 117+ }
 118+ $srev = $this->getStableRev();
 119+ if ( !$srev ) {
 120+ return 0; // none
 121+ }
 122+ $count = null;
 123+ $sRevId = $srev->getRevId();
 124+ # Try the cache...
 125+ $key = wfMemcKey( 'flaggedrevs', 'countPending', $this->getId() );
 126+ if ( !( $flags & FR_MASTER ) ) {
 127+ $tuple = FlaggedRevs::getMemcValue( $wgMemc->get( $key ), $this );
 128+ # Items is cached and newer that page_touched...
 129+ if ( $tuple !== false ) {
 130+ # Confirm that cache value was made against the same stable rev Id.
 131+ # This avoids lengthy cache pollution if $sRevId is outdated.
 132+ list( $cRevId, $cPending ) = explode( '-', $tuple, 2 );
 133+ if ( $cRevId == $sRevId ) {
 134+ $count = (int)$cPending;
 135+ }
 136+ }
 137+ }
 138+ # Otherwise, fetch result from DB as needed...
 139+ if ( is_null( $count ) ) {
 140+ $db = ( $flags & FR_MASTER ) ?
 141+ wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
 142+ $srevTS = $db->timestamp( $srev->getRevTimestamp() );
 143+ $count = $db->selectField( 'revision', 'COUNT(*)',
 144+ array( 'rev_page' => $this->getId(),
 145+ 'rev_timestamp > ' . $db->addQuotes( $srevTS ) ), // bug 15515
 146+ __METHOD__ );
 147+ # Save result to cache...
 148+ $data = FlaggedRevs::makeMemcObj( "{$sRevId}-{$count}" );
 149+ $wgMemc->set( $key, $data, $wgParserCacheExpireTime );
 150+ }
 151+ $this->pendingRevCount = $count;
 152+ return $this->pendingRevCount;
 153+ }
 154+
 155+ /**
 156+ * Checks if the stable version is synced with the current revision
 157+ * Note: slower than getPendingRevCount()
 158+ * @return bool
 159+ */
 160+ public function stableVersionIsSynced() {
 161+ global $wgMemc, $wgParserCacheExpireTime;
 162+ $srev = $this->getStableRev();
 163+ if ( !$srev ) {
 164+ return true;
 165+ }
 166+ # Stable text revision must be the same as the current
 167+ if ( $this->revsArePending() ) {
 168+ return false;
 169+ # Stable file revision must be the same as the current
 170+ } elseif ( $this->mTitle->getNamespace() == NS_FILE ) {
 171+ $file = $this->getFile(); // current upload version
 172+ if ( $file && $file->getTimestamp() > $srev->getFileTimestamp() ) {
 173+ return false;
 174+ }
 175+ }
 176+ # If using the current version of includes, there is nothing else to check.
 177+ if ( FlaggedRevs::inclusionSetting() == FR_INCLUDES_CURRENT ) {
 178+ return true; // short-circuit
 179+ }
 180+ # Try the cache...
 181+ $key = wfMemcKey( 'flaggedrevs', 'includesSynced', $this->getId() );
 182+ $value = FlaggedRevs::getMemcValue( $wgMemc->get( $key ), $this );
 183+ if ( $value === "true" ) {
 184+ return true;
 185+ } elseif ( $value === "false" ) {
 186+ return false;
 187+ }
 188+ # Since the stable and current revisions have the same text and only outputs,
 189+ # the only other things to check for are template and file differences in the output.
 190+ # (a) Check if the current output has a newer template/file used
 191+ # (b) Check if the stable version has a file/template that was deleted
 192+ $synced = ( !$srev->findPendingTemplateChanges()
 193+ && !$srev->findPendingFileChanges( 'noForeign' ) );
 194+ # Save to cache. This will be updated whenever the page is touched.
 195+ $data = FlaggedRevs::makeMemcObj( $synced ? "true" : "false" );
 196+ $wgMemc->set( $key, $data, $wgParserCacheExpireTime );
 197+
 198+ return $synced;
 199+ }
 200+
 201+ /**
 202+ * Are template/file changes and ONLY template/file changes pending?
 203+ * @return bool
 204+ */
 205+ public function onlyTemplatesOrFilesPending() {
 206+ return ( !$this->revsArePending() && !$this->stableVersionIsSynced() );
 207+ }
 208+
 209+ /**
 210+ * Is this page less open than the site defaults?
 211+ * @return bool
 212+ */
 213+ public function isPageLocked() {
 214+ return ( !FlaggedRevs::isStableShownByDefault() && $this->isStableShownByDefault() );
 215+ }
 216+
 217+ /**
 218+ * Is this page more open than the site defaults?
 219+ * @return bool
 220+ */
 221+ public function isPageUnlocked() {
 222+ return ( FlaggedRevs::isStableShownByDefault() && !$this->isStableShownByDefault() );
 223+ }
 224+
 225+ /**
 226+ * Tags are only shown for unreviewed content and this page is not locked/unlocked?
 227+ * @return bool
 228+ */
 229+ public function lowProfileUI() {
 230+ return FlaggedRevs::lowProfileUI() &&
 231+ FlaggedRevs::isStableShownByDefault() == $this->isStableShownByDefault();
 232+ }
 233+
 234+ /**
 235+ * Is this article reviewable?
 236+ * @return bool
 237+ */
 238+ public function isReviewable() {
 239+ if ( !FlaggedRevs::inReviewNamespace( $this->mTitle ) ) {
 240+ return false;
 241+ }
 242+ # Check if flagging is disabled for this page via config
 243+ if ( FlaggedRevs::useOnlyIfProtected() ) {
 244+ $config = $this->getStabilitySettings(); // page configuration
 245+ return (bool)$config['override']; // stable is default or flagging disabled
 246+ }
 247+ return true;
 248+ }
 249+
 250+ /**
 251+ * Get the stable revision ID
 252+ * @return int
 253+ */
 254+ public function getStable() {
 255+ if ( !FlaggedRevs::inReviewNamespace( $this->mTitle ) ) {
 256+ return 0; // short-circuit
 257+ }
 258+ if ( !$this->mDataLoaded ) {
 259+ $this->loadPageData();
 260+ }
 261+ return (int)$this->stable;
 262+ }
 263+
 264+ /**
 265+ * Get the stable revision
 266+ * @return mixed (FlaggedRevision/null)
 267+ */
 268+ public function getStableRev() {
 269+ if ( !FlaggedRevs::inReviewNamespace( $this->mTitle ) ) {
 270+ return null; // short-circuit
 271+ }
 272+ if ( !$this->mDataLoaded ) {
 273+ $this->loadPageData();
 274+ }
 275+ # Stable rev deferred even after page data load
 276+ if ( $this->stableRev === null ) {
 277+ $srev = FlaggedRevision::newFromTitle( $this->mTitle, $this->stable );
 278+ $this->stableRev = $srev ? $srev : false; // cache negative hits too
 279+ }
 280+ return $this->stableRev ? $this->stableRev : null; // false => null
 281+ }
 282+
 283+ /**
 284+ * Get visiblity restrictions on page
 285+ * @return array (select,override)
 286+ */
 287+ public function getStabilitySettings() {
 288+ if ( !$this->mDataLoaded ) {
 289+ $this->loadPageData();
 290+ }
 291+ return $this->pageConfig;
 292+ }
 293+
 294+ /*
 295+ * Get the fp_reviewed value for this page
 296+ * @return bool
 297+ */
 298+ public function syncedInTracking() {
 299+ if ( !$this->mDataLoaded ) {
 300+ $this->loadPageData();
 301+ }
 302+ return $this->syncedInTracking;
 303+ }
 304+
 305+ /**
 306+ * Get the newest of the highest rated flagged revisions of this page
 307+ * Note: will not return deleted revisions
 308+ * @return int
 309+ */
 310+ public function getBestFlaggedRevId() {
 311+ $dbr = wfGetDB( DB_SLAVE );
 312+ # Get the highest quality revision (not necessarily this one).
 313+ $oldid = $dbr->selectField( array( 'flaggedrevs', 'revision' ),
 314+ 'fr_rev_id',
 315+ array(
 316+ 'fr_page_id' => $this->getId(),
 317+ 'rev_page = fr_page_id', // sanity
 318+ 'rev_id = fr_rev_id',
 319+ 'rev_deleted & ' . Revision::DELETED_TEXT => 0
 320+ ),
 321+ __METHOD__,
 322+ array(
 323+ 'ORDER BY' => 'fr_quality DESC, fr_rev_timestamp DESC',
 324+ 'USE INDEX' => array( 'flaggedrevs' => 'page_qal_time' )
 325+ )
 326+ );
 327+ return (int)$oldid;
 328+ }
 329+
 330+ /**
 331+ * Updates the fp_reviewed field for this article
 332+ * @param bool $synced
 333+ */
 334+ public function updateSyncStatus( $synced ) {
 335+ wfProfileIn( __METHOD__ );
 336+ if ( !wfReadOnly() ) {
 337+ $dbw = wfGetDB( DB_MASTER );
 338+ $dbw->update( 'flaggedpages',
 339+ array( 'fp_reviewed' => $synced ? 1 : 0 ),
 340+ array( 'fp_page_id' => $this->getID() ),
 341+ __METHOD__
 342+ );
 343+ }
 344+ wfProfileOut( __METHOD__ );
 345+ }
 346+
 347+ /**
 348+ * Fetch a page record with the given conditions
 349+ * @param $dbr Database object
 350+ * @param $conditions Array
 351+ * @return mixed Database result resource, or false on failure
 352+ */
 353+ protected function pageData( $dbr, $conditions ) {
 354+ $row = $dbr->selectRow(
 355+ array( 'page', 'flaggedpages', 'flaggedpage_config' ),
 356+ array_merge(
 357+ WikiPage::selectFields(),
 358+ FRPageConfig::selectFields(),
 359+ array( 'fp_pending_since', 'fp_stable', 'fp_reviewed' ) ),
 360+ $conditions,
 361+ __METHOD__,
 362+ array(),
 363+ array(
 364+ 'flaggedpages' => array( 'LEFT JOIN', 'fp_page_id = page_id' ),
 365+ 'flaggedpage_config' => array( 'LEFT JOIN', 'fpc_page_id = page_id' ) )
 366+ );
 367+ return $row;
 368+ }
 369+
 370+ /**
 371+ * Set the page field data loaded from some source
 372+ * @param $data Database row object or "fromdb"
 373+ * @return void
 374+ */
 375+ public function loadPageData( $data = 'fromdb' ) {
 376+ $this->mDataLoaded = true; // sanity
 377+ # Fetch data from DB as needed...
 378+ if ( $data === 'fromdb' || $data === 'fromdbmaster' ) {
 379+ $db = ( $data == 'fromdbmaster' )
 380+ ? wfGetDB( DB_MASTER )
 381+ : wfGetDB( DB_SLAVE );
 382+ $data = $this->pageDataFromTitle( $db, $this->mTitle );
 383+ }
 384+ # Load in primary page data...
 385+ parent::loadPageData( $data /* Row obj */ );
 386+ # Load in FlaggedRevs page data...
 387+ $this->stable = 0; // 0 => "found nothing"
 388+ $this->stableRev = null; // defer this one...
 389+ $this->revsArePending = false; // false => "found nothing" or "none pending"
 390+ $this->pendingRevCount = null; // defer this one...
 391+ $this->pageConfig = FRPageConfig::getDefaultVisibilitySettings(); // default
 392+ $this->syncedInTracking = true; // false => "unreviewed" or "synced"
 393+ # Load in flaggedrevs Row data if the page exists...(sanity check NS)
 394+ if ( $data && FlaggedRevs::inReviewNamespace( $this->mTitle ) ) {
 395+ if ( $data->fpc_override !== null ) { // page config row found
 396+ $this->pageConfig = FRPageConfig::getVisibilitySettingsFromRow( $data );
 397+ }
 398+ if ( $data->fp_stable !== null ) { // stable rev found
 399+ $this->stable = (int)$data->fp_stable;
 400+ $this->revsArePending = ( $data->fp_pending_since !== null ); // revs await review
 401+ $this->syncedInTracking = (bool)$data->fp_reviewed;
 402+ }
 403+ }
 404+ }
 405+
 406+ /**
 407+ * Updates the flagging tracking tables for this page
 408+ * @param FlaggedRevision $srev The new stable version
 409+ * @param int|null $latest The latest rev ID (optional)
 410+ * @return bool Updates were done
 411+ */
 412+ public function updateStableVersion( FlaggedRevision $srev, $latest = null ) {
 413+ $rev = $srev->getRevision();
 414+ if ( !$this->exists() || !$rev ) {
 415+ return false; // no bogus entries
 416+ }
 417+ # Get the latest revision ID if not set
 418+ if ( !$latest ) {
 419+ $latest = $this->mTitle->getLatestRevID( Title::GAID_FOR_UPDATE );
 420+ }
 421+ $dbw = wfGetDB( DB_MASTER );
 422+ # Get the highest quality revision (not necessarily this one)...
 423+ if ( $srev->getQuality() === FlaggedRevs::highestReviewTier() ) {
 424+ $maxQuality = $srev->getQuality(); // save a query
 425+ } else {
 426+ $maxQuality = $dbw->selectField( array( 'flaggedrevs', 'revision' ),
 427+ 'fr_quality',
 428+ array( 'fr_page_id' => $this->getId(),
 429+ 'rev_id = fr_rev_id',
 430+ 'rev_page = fr_page_id',
 431+ 'rev_deleted & ' . Revision::DELETED_TEXT => 0
 432+ ),
 433+ __METHOD__,
 434+ array( 'ORDER BY' => 'fr_quality DESC', 'LIMIT' => 1 )
 435+ );
 436+ $maxQuality = max( $maxQuality, $srev->getQuality() ); // sanity
 437+ }
 438+ # Get the timestamp of the first edit after the stable version (if any)...
 439+ $nextTimestamp = null;
 440+ if ( $rev->getId() != $latest ) {
 441+ $timestamp = $dbw->timestamp( $rev->getTimestamp() );
 442+ $nextEditTS = $dbw->selectField( 'revision',
 443+ 'rev_timestamp',
 444+ array(
 445+ 'rev_page' => $this->getId(),
 446+ "rev_timestamp > " . $dbw->addQuotes( $timestamp ) ),
 447+ __METHOD__,
 448+ array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 )
 449+ );
 450+ if ( $nextEditTS ) { // sanity check
 451+ $nextTimestamp = $nextEditTS;
 452+ }
 453+ }
 454+ # Get the new page sync status...
 455+ $synced = !(
 456+ $nextTimestamp !== null || // edits pending
 457+ $srev->findPendingTemplateChanges() || // template changes pending
 458+ $srev->findPendingFileChanges( 'noForeign' ) // file changes pending
 459+ );
 460+ # Alter table metadata
 461+ $dbw->replace( 'flaggedpages',
 462+ array( 'fp_page_id' ),
 463+ array(
 464+ 'fp_page_id' => $this->getId(),
 465+ 'fp_stable' => $rev->getId(),
 466+ 'fp_reviewed' => $synced ? 1 : 0,
 467+ 'fp_quality' => ( $maxQuality === false ) ? null : $maxQuality,
 468+ 'fp_pending_since' => $dbw->timestampOrNull( $nextTimestamp )
 469+ ),
 470+ __METHOD__
 471+ );
 472+ # Update pending edit tracking table
 473+ self::updatePendingList( $this->getId(), $latest );
 474+ return true;
 475+ }
 476+
 477+ /**
 478+ * Updates the flagging tracking tables for this page
 479+ * @return void
 480+ */
 481+ public function clearStableVersion() {
 482+ if ( !$this->exists() ) {
 483+ return; // nothing to do
 484+ }
 485+ $dbw = wfGetDB( DB_MASTER );
 486+ $dbw->delete( 'flaggedpages',
 487+ array( 'fp_page_id' => $this->getId() ), __METHOD__ );
 488+ $dbw->delete( 'flaggedpage_pending',
 489+ array( 'fpp_page_id' => $this->getId() ), __METHOD__ );
 490+ }
 491+
 492+ /**
 493+ * Updates the flaggedpage_pending table
 494+ * @param int $pageId Page ID
 495+ * @abstract int $latest Latest revision
 496+ * @return void
 497+ */
 498+ protected static function updatePendingList( $pageId, $latest ) {
 499+ $data = array();
 500+ $level = FlaggedRevs::highestReviewTier();
 501+ # Update pending times for each level, going from highest to lowest
 502+ $dbw = wfGetDB( DB_MASTER );
 503+ $higherLevelId = 0;
 504+ $higherLevelTS = '';
 505+ while ( $level >= 0 ) {
 506+ # Get the latest revision of this level...
 507+ $row = $dbw->selectRow( array( 'flaggedrevs', 'revision' ),
 508+ array( 'fr_rev_id', 'rev_timestamp' ),
 509+ array( 'fr_page_id' => $pageId,
 510+ 'fr_quality' => $level,
 511+ 'rev_id = fr_rev_id',
 512+ 'rev_page = fr_page_id',
 513+ 'rev_deleted & ' . Revision::DELETED_TEXT => 0,
 514+ 'rev_id > ' . intval( $higherLevelId )
 515+ ),
 516+ __METHOD__,
 517+ array( 'ORDER BY' => 'fr_rev_id DESC', 'LIMIT' => 1 )
 518+ );
 519+ # If there is a revision of this level, track it...
 520+ # Revisions reviewed to one level count as reviewed
 521+ # at the lower levels (i.e. quality -> checked).
 522+ if ( $row ) {
 523+ $id = $row->fr_rev_id;
 524+ $ts = $row->rev_timestamp;
 525+ } else {
 526+ $id = $higherLevelId; // use previous (quality -> checked)
 527+ $ts = $higherLevelTS; // use previous (quality -> checked)
 528+ }
 529+ # Get edits that actually are pending...
 530+ if ( $id && $latest > $id ) {
 531+ # Get the timestamp of the edit after this version (if any)
 532+ $nextTimestamp = $dbw->selectField( 'revision',
 533+ 'rev_timestamp',
 534+ array( 'rev_page' => $pageId, "rev_timestamp > " . $dbw->addQuotes( $ts ) ),
 535+ __METHOD__,
 536+ array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 )
 537+ );
 538+ $data[] = array(
 539+ 'fpp_page_id' => $pageId,
 540+ 'fpp_quality' => $level,
 541+ 'fpp_rev_id' => $id,
 542+ 'fpp_pending_since' => $nextTimestamp
 543+ );
 544+ $higherLevelId = $id;
 545+ $higherLevelTS = $ts;
 546+ }
 547+ $level--;
 548+ }
 549+ # Clear any old junk, and insert new rows
 550+ $dbw->delete( 'flaggedpage_pending', array( 'fpp_page_id' => $pageId ), __METHOD__ );
 551+ $dbw->insert( 'flaggedpage_pending', $data, __METHOD__ );
 552+ }
 553+}
Property changes on: trunk/extensions/FlaggedRevs/dataclasses/FlaggableWikiPage.php
___________________________________________________________________
Added: svn:eol-style
1554 + native
Index: trunk/extensions/FlaggedRevs/dataclasses/FlaggedRevision.php
@@ -205,7 +205,7 @@
206206 # Get visiblity settings to see if page is reviewable...
207207 if ( FlaggedRevs::useOnlyIfProtected() ) {
208208 if ( empty( $config ) ) {
209 - $config = FlaggedPageConfig::getStabilitySettings( $title, $flags );
 209+ $config = FRPageConfig::getStabilitySettings( $title, $flags );
210210 }
211211 if ( !$config['override'] ) {
212212 return null; // page is not reviewable; no stable version
Index: trunk/extensions/FlaggedRevs/api/actions/ApiReview.php
@@ -66,7 +66,7 @@
6767 $form->setDim( $tag, (int)$params['flag_' . $tag] );
6868 }
6969 if ( $form->getAction() === 'approve' ) {
70 - $article = new FlaggedPage( $title );
 70+ $article = new FlaggableWikiPage( $title );
7171 // Get the file version used for File: pages
7272 $file = $article->getFile();
7373 if ( $file ) {
Index: trunk/extensions/FlaggedRevs/api/actions/ApiReviewActivity.php
@@ -49,7 +49,7 @@
5050 }
5151 $title = $newRev->getTitle();
5252
53 - $fa = FlaggedPage::getTitleInstance( $title );
 53+ $fa = FlaggableWikiPage::getTitleInstance( $title );
5454 if ( !$fa->isReviewable() ) {
5555 $this->dieUsage( "Provided page is not reviewable.", 'notreviewable' );
5656 }
Index: trunk/extensions/FlaggedRevs/business/RevisionReviewForm.php
@@ -139,7 +139,7 @@
140140 * @return mixed (true on success, error string on failure)
141141 */
142142 protected function doBuildOnReady() {
143 - $this->article = FlaggedPage::getTitleInstance( $this->page );
 143+ $this->article = FlaggableWikiPage::getTitleInstance( $this->page );
144144 return true;
145145 }
146146
Index: trunk/extensions/FlaggedRevs/business/PageStabilityForm.php
@@ -151,7 +151,7 @@
152152 */
153153 protected function doCheckParameters() {
154154 # Load old config settings from the master
155 - $this->oldConfig = FlaggedPageConfig::getStabilitySettings( $this->page, FR_MASTER );
 155+ $this->oldConfig = FRPageConfig::getStabilitySettings( $this->page, FR_MASTER );
156156 if ( $this->expiryCustom != '' ) {
157157 // Custom expiry takes precedence
158158 $this->expirySelection = 'othertime';
@@ -221,10 +221,10 @@
222222 return 'stabilize_expiry_old';
223223 }
224224 # Update the DB row with the new config...
225 - $changed = FlaggedPageConfig::setStabilitySettings( $this->page, $this->getNewConfig() );
 225+ $changed = FRPageConfig::setStabilitySettings( $this->page, $this->getNewConfig() );
226226 # Log if this actually changed anything...
227227 if ( $changed ) {
228 - $article = new FlaggedPage( $this->page );
 228+ $article = new FlaggableWikiPage( $this->page );
229229 if ( FlaggedRevs::useOnlyIfProtected() ) {
230230 # Config may have changed to allow stable versions, so refresh
231231 # the tracking table to account for any hidden reviewed versions...
@@ -258,7 +258,7 @@
259259 # Apply watchlist checkbox value (may be NULL)
260260 $this->updateWatchlist();
261261 # Take this opportunity to purge out expired configurations
262 - FlaggedPageConfig::purgeExpiredConfigurations();
 262+ FRPageConfig::purgeExpiredConfigurations();
263263 return true;
264264 }
265265
@@ -268,7 +268,7 @@
269269 * (b) Add a null edit like the log entry
270270 * @return Revision
271271 */
272 - protected function updateLogsAndHistory( FlaggedPage $article ) {
 272+ protected function updateLogsAndHistory( FlaggableWikiPage $article ) {
273273 global $wgContLang;
274274 $newConfig = $this->getNewConfig();
275275 $oldConfig = $this->getOldConfig();
@@ -278,7 +278,7 @@
279279 FlaggedRevsLog::updateStabilityLog( $this->page, $newConfig, $oldConfig, $reason );
280280
281281 # Build null-edit comment...<action: reason [settings] (expiry)>
282 - if ( FlaggedPageConfig::configIsReset( $newConfig ) ) {
 282+ if ( FRPageConfig::configIsReset( $newConfig ) ) {
283283 $type = "stable-logentry-reset";
284284 $settings = ''; // no level, expiry info
285285 } else {
@@ -319,7 +319,7 @@
320320 throw new MWException( __CLASS__ . " input fields not set yet.\n");
321321 }
322322 if ( $this->oldConfig === array() && $this->page ) {
323 - $this->oldConfig = FlaggedPageConfig::getStabilitySettings( $this->page );
 323+ $this->oldConfig = FRPageConfig::getStabilitySettings( $this->page );
324324 }
325325 return $this->oldConfig;
326326 }
@@ -406,7 +406,7 @@
407407 $oldConfig = $this->getOldConfig();
408408 if ( isset( $wgFlaggedRevsProtectQuota ) // quota exists
409409 && $this->autoreview != '' // and we are protecting
410 - && FlaggedPageConfig::getProtectionLevel( $oldConfig ) == 'none' ) // unprotected
 410+ && FRPageConfig::getProtectionLevel( $oldConfig ) == 'none' ) // unprotected
411411 {
412412 $dbw = wfGetDB( DB_MASTER );
413413 $count = $dbw->selectField( 'flaggedpage_config', 'COUNT(*)', '', __METHOD__ );
@@ -415,7 +415,7 @@
416416 }
417417 }
418418 # Autoreview only when protecting currently unprotected pages
419 - $this->reviewThis = ( FlaggedPageConfig::getProtectionLevel( $oldConfig ) == 'none' );
 419+ $this->reviewThis = ( FRPageConfig::getProtectionLevel( $oldConfig ) == 'none' );
420420 # Autoreview restriction => use stable
421421 # No autoreview restriction => site default
422422 $this->override = ( $this->autoreview != '' )
@@ -426,7 +426,7 @@
427427 'override' => $this->override,
428428 'autoreview' => $this->autoreview
429429 );
430 - if ( FlaggedPageConfig::getProtectionLevel( $newConfig ) == 'invalid' ) {
 430+ if ( FRPageConfig::getProtectionLevel( $newConfig ) == 'invalid' ) {
431431 return 'stabilize_invalid_level'; // double-check configuration
432432 }
433433 # Check autoreview restriction setting
Index: trunk/extensions/FlaggedRevs/presentation/FlaggedPageView.php
@@ -1,1890 +0,0 @@
2 -<?php
3 -/**
4 - * Class representing a web view of a MediaWiki page
5 - */
6 -class FlaggedPageView extends ContextSource {
7 - protected $out = null;
8 - protected $article = null;
9 -
10 - protected $diffRevs = null; // assoc array of old and new Revisions for diffs
11 - protected $oldRevIncludes = null; // ( array of templates, array of file)
12 - protected $isReviewableDiff = false;
13 - protected $isDiffFromStable = false;
14 - protected $isMultiPageDiff = false;
15 - protected $reviewNotice = '';
16 - protected $diffNoticeBox = '';
17 - protected $diffIncChangeBox = '';
18 - protected $reviewFormRev = false;
19 -
20 - protected $loaded = false;
21 -
22 - protected static $instance = null;
23 -
24 - /*
25 - * Get the FlaggedPageView for this request
26 - */
27 - public static function singleton() {
28 - if ( self::$instance == null ) {
29 - self::$instance = new self();
30 - }
31 - return self::$instance;
32 - }
33 - protected function __construct() { }
34 - protected function __clone() { }
35 -
36 - /*
37 - * Clear the FlaggedPageView for this request.
38 - * Only needed when page redirection changes the environment.
39 - */
40 - public function clear() {
41 - self::$instance = null;
42 - }
43 -
44 - /*
45 - * Load the global FlaggedPage instance
46 - */
47 - protected function load() {
48 - if ( !$this->loaded ) {
49 - $this->loaded = true;
50 - $this->article = self::globalArticleInstance();
51 - if ( $this->article == null ) {
52 - throw new MWException( 'FlaggedPageView has no context article!' );
53 - }
54 - $this->out = $this->getOutput(); // convenience
55 - }
56 - }
57 -
58 - /**
59 - * Get the FlaggedPage instance associated with $wgTitle,
60 - * or false if there isn't such a title
61 - * @return FlaggedPage|null
62 - */
63 - public static function globalArticleInstance() {
64 - $title = RequestContext::getMain()->getTitle();
65 - if ( $title ) {
66 - return FlaggedPage::getTitleInstance( $title );
67 - }
68 - return null;
69 - }
70 -
71 - /*
72 - * Check if the old and new diff revs are set for this page view
73 - * @return bool
74 - */
75 - public function diffRevsAreSet() {
76 - return (bool)$this->diffRevs;
77 - }
78 -
79 - /**
80 - * Is this web response for a request to view a page where both:
81 - * (a) no specific page version was requested via URL params
82 - * (b) a stable version exists and is to be displayed
83 - * This factors in site/page config, user preferences, and web request params.
84 - * @return bool
85 - */
86 - protected function showingStableAsDefault() {
87 - $request = $this->getRequest();
88 - $reqUser = $this->getUser();
89 - $this->load();
90 - # This only applies to viewing the default version of pages...
91 - if ( !$this->isDefaultPageView( $request ) ) {
92 - return false;
93 - # ...and the page must be reviewable and have a stable version
94 - } elseif ( !$this->article->getStableRev() ) {
95 - return false;
96 - }
97 - # Check user preferences ("show stable by default?")
98 - $pref = (int)$reqUser->getOption( 'flaggedrevsstable' );
99 - if ( $pref == FR_SHOW_STABLE_ALWAYS ) {
100 - return true;
101 - } elseif ( $pref == FR_SHOW_STABLE_NEVER ) {
102 - return false;
103 - }
104 - # Viewer may be in a group that sees the draft by default
105 - if ( $this->userViewsDraftByDefault( $reqUser ) ) {
106 - return false;
107 - }
108 - # Does the stable version override the draft?
109 - $config = $this->article->getStabilitySettings();
110 - return (bool)$config['override'];
111 - }
112 -
113 - /**
114 - * Is this web response for a request to view a page where both:
115 - * (a) the stable version of a page was requested (?stable=1)
116 - * (b) the stable version exists and is to be displayed
117 - * @return bool
118 - */
119 - protected function showingStableByRequest() {
120 - $request = $this->getRequest();
121 - $this->load();
122 - # Are we explicity requesting the stable version?
123 - if ( $request->getIntOrNull( 'stable' ) === 1 ) {
124 - # This only applies to viewing a version of the page...
125 - if ( !$this->isPageView( $request ) ) {
126 - return false;
127 - # ...with no version parameters other than ?stable=1...
128 - } elseif ( $request->getVal( 'oldid' ) || $request->getVal( 'stableid' ) ) {
129 - return false; // over-determined
130 - # ...and the page must be reviewable and have a stable version
131 - } elseif ( !$this->article->getStableRev() ) {
132 - return false;
133 - }
134 - return true; // show stable version
135 - }
136 - return false;
137 - }
138 -
139 - /**
140 - * Is this web response for a request to view a page
141 - * where a stable version exists and is to be displayed
142 - * @return bool
143 - */
144 - public function showingStable() {
145 - return $this->showingStableByRequest() || $this->showingStableAsDefault();
146 - }
147 -
148 - /**
149 - * Should this be using a simple icon-based UI?
150 - * Check the user's preferences first, using the site settings as the default.
151 - * @return bool
152 - */
153 - public function useSimpleUI() {
154 - global $wgSimpleFlaggedRevsUI;
155 - $reqUser = $this->getUser();
156 - return $reqUser->getOption( 'flaggedrevssimpleui', intval( $wgSimpleFlaggedRevsUI ) );
157 - }
158 -
159 - /**
160 - * Should this user see the draft revision of pages by default?
161 - * @param $user User
162 - * @return bool
163 - */
164 - protected function userViewsDraftByDefault( $user ) {
165 - global $wgFlaggedRevsExceptions;
166 - # Check user preferences ("show stable by default?")
167 - if ( $user->getOption( 'flaggedrevsstable' ) ) {
168 - return false;
169 - }
170 - # Viewer sees current by default (editors, insiders, ect...) ?
171 - foreach ( $wgFlaggedRevsExceptions as $group ) {
172 - if ( $group == 'user' ) {
173 - if ( $user->getId() ) {
174 - return true;
175 - }
176 - } elseif ( in_array( $group, $user->getGroups() ) ) {
177 - return true;
178 - }
179 - }
180 - return false;
181 - }
182 -
183 - /**
184 - * Is this a view page action (including diffs)?
185 - * @param $request WebRequest
186 - * @return bool
187 - */
188 - protected function isPageViewOrDiff( WebRequest $request ) {
189 - global $mediaWiki;
190 - $action = isset( $mediaWiki )
191 - ? $mediaWiki->getAction( $request )
192 - : $request->getVal( 'action', 'view' ); // cli
193 - return self::isViewAction( $action );
194 - }
195 -
196 - /**
197 - * Is this a view page action (not including diffs)?
198 - * @param $request WebRequest
199 - * @return bool
200 - */
201 - protected function isPageView( WebRequest $request ) {
202 - return $this->isPageViewOrDiff( $request )
203 - && $request->getVal( 'diff' ) === null;
204 - }
205 -
206 - /**
207 - * Is this a web request to just *view* the *default* version of a page?
208 - * @param $request WebRequest
209 - * @return bool
210 - */
211 - protected function isDefaultPageView( WebRequest $request ) {
212 - global $mediaWiki;
213 - $action = isset( $mediaWiki )
214 - ? $mediaWiki->getAction( $request )
215 - : $request->getVal( 'action', 'view' ); // cli
216 - return ( self::isViewAction( $action )
217 - && $request->getVal( 'oldid' ) === null
218 - && $request->getVal( 'stable' ) === null
219 - && $request->getVal( 'stableid' ) === null
220 - && $request->getVal( 'diff' ) === null
221 - );
222 - }
223 -
224 - /**
225 - * Is this a view page action?
226 - * @param $action string from MediaWiki::getAction()
227 - * @return bool
228 - */
229 - protected static function isViewAction( $action ) {
230 - return ( $action == 'view' || $action == 'purge' || $action == 'render' );
231 - }
232 -
233 - /**
234 - * Output review notice
235 - */
236 - public function displayTag() {
237 - $this->load();
238 - // Sanity check that this is a reviewable page
239 - if ( $this->article->isReviewable() ) {
240 - $this->out->appendSubtitle( $this->reviewNotice );
241 - }
242 - return true;
243 - }
244 -
245 - /**
246 - * Add a stable link when viewing old versions of an article that
247 - * have been reviewed. (e.g. for &oldid=x urls)
248 - */
249 - public function addStableLink() {
250 - $request = $this->getRequest();
251 - $this->load();
252 - if ( !$this->article->isReviewable() || !$request->getVal( 'oldid' ) ) {
253 - return true;
254 - }
255 - # We may have nav links like "direction=prev&oldid=x"
256 - $revID = $this->getOldIDFromRequest();
257 - $frev = FlaggedRevision::newFromTitle( $this->article->getTitle(), $revID );
258 - # Give a notice if this rev ID corresponds to a reviewed version...
259 - if ( $frev ) {
260 - $time = $this->getLang()->date( $frev->getTimestamp(), true );
261 - $flags = $frev->getTags();
262 - $quality = FlaggedRevs::isQuality( $flags );
263 - $msg = $quality ? 'revreview-quality-source' : 'revreview-basic-source';
264 - $tag = wfMsgExt( $msg, 'parseinline', $frev->getRevId(), $time );
265 - # Hide clutter
266 - if ( !$this->useSimpleUI() && !empty( $flags ) ) {
267 - $tag .= FlaggedRevsXML::ratingToggle() .
268 - "<div id='mw-fr-revisiondetails' style='display:block;'>" .
269 - wfMsgHtml( 'revreview-oldrating' ) .
270 - FlaggedRevsXML::addTagRatings( $flags ) . '</div>';
271 - }
272 - $css = 'flaggedrevs_notice plainlinks noprint';
273 - $tag = "<div id='mw-fr-revisiontag-old' class='$css'>$tag</div>";
274 - $this->out->addHTML( $tag );
275 - }
276 - return true;
277 - }
278 -
279 - /**
280 - * @return mixed int/false/null
281 - */
282 - protected function getRequestedStableId() {
283 - $request = $this->getRequest();
284 - $reqId = $request->getVal( 'stableid' );
285 - if ( $reqId === "best" ) {
286 - $reqId = $this->article->getBestFlaggedRevId();
287 - }
288 - return $reqId;
289 - }
290 -
291 - /**
292 - * Replaces a page with the last stable version if possible
293 - * Adds stable version status/info tags and notes
294 - * Adds a quick review form on the bottom if needed
295 - */
296 - public function setPageContent( &$outputDone, &$useParserCache ) {
297 - $request = $this->getRequest();
298 - $this->load();
299 - # Only trigger on page views with no oldid=x param
300 - if ( !$this->isPageView( $request ) || $request->getVal( 'oldid' ) ) {
301 - return true;
302 - # Only trigger for reviewable pages that exist
303 - } elseif ( !$this->article->exists() || !$this->article->isReviewable() ) {
304 - return true;
305 - }
306 - $tag = '';
307 - $old = $stable = false;
308 - # Check the newest stable version.
309 - $srev = $this->article->getStableRev();
310 - $stableId = $srev ? $srev->getRevId() : 0;
311 - $frev = $srev; // $frev is the revision we are looking at
312 - # Check for any explicitly requested reviewed version (stableid=X)...
313 - $reqId = $this->getRequestedStableId();
314 - if ( $reqId ) {
315 - if ( !$stableId ) {
316 - $reqId = false; // must be invalid
317 - # Treat requesting the stable version by ID as &stable=1
318 - } elseif ( $reqId != $stableId ) {
319 - $old = true; // old reviewed version requested by ID
320 - $frev = FlaggedRevision::newFromTitle( $this->article->getTitle(), $reqId );
321 - if ( !$frev ) {
322 - $reqId = false; // invalid ID given
323 - }
324 - } else {
325 - $stable = true; // stable version requested by ID
326 - }
327 - }
328 - // $reqId is null if nothing requested, false if invalid
329 - if ( $reqId === false ) {
330 - $this->out->addWikiText( wfMsg( 'revreview-invalid' ) );
331 - $this->out->returnToMain( false, $this->article->getTitle() );
332 - # Tell MW that parser output is done
333 - $outputDone = true;
334 - $useParserCache = false;
335 - return true;
336 - }
337 - // Is the page config altered?
338 - $prot = FlaggedRevsXML::lockStatusIcon( $this->article );
339 - // Is there no stable version?
340 - if ( !$frev ) {
341 - # Add "no reviewed version" tag, but not for printable output
342 - $this->showUnreviewedPage( $tag, $prot );
343 - return true;
344 - }
345 - # Get flags and date
346 - $flags = $frev->getTags();
347 - # Get quality level
348 - $quality = FlaggedRevs::isQuality( $flags );
349 - $pristine = FlaggedRevs::isPristine( $flags );
350 - // Looking at some specific old stable revision ("&stableid=x")
351 - // set to override given the relevant conditions. If the user is
352 - // requesting the stable revision ("&stableid=x"), defer to override
353 - // behavior below, since it is the same as ("&stable=1").
354 - if ( $old ) {
355 - # Tell MW that parser output is done by setting $outputDone
356 - $outputDone = $this->showOldReviewedVersion( $frev, $tag, $prot );
357 - $useParserCache = false;
358 - // Stable version requested by ID or relevant conditions met to
359 - // to override page view with the stable version.
360 - } elseif ( $stable || $this->showingStable() ) {
361 - # Tell MW that parser output is done by setting $outputDone
362 - $outputDone = $this->showStableVersion( $srev, $tag, $prot );
363 - $useParserCache = false;
364 - // Looking at some specific old revision (&oldid=x) or if FlaggedRevs is not
365 - // set to override given the relevant conditions (like &stable=0) or there
366 - // is no stable version.
367 - } else {
368 - $this->showDraftVersion( $srev, $tag, $prot );
369 - }
370 - # Some checks for which tag CSS to use
371 - if ( $this->useSimpleUI() ) {
372 - $tagClass = 'flaggedrevs_short';
373 - } elseif ( $pristine ) {
374 - $tagClass = 'flaggedrevs_pristine';
375 - } elseif ( $quality ) {
376 - $tagClass = 'flaggedrevs_quality';
377 - } else {
378 - $tagClass = 'flaggedrevs_basic';
379 - }
380 - # Wrap tag contents in a div
381 - if ( $tag != '' ) {
382 - $css = "{$tagClass} plainlinks noprint";
383 - $notice = "<div id=\"mw-fr-revisiontag\" class=\"{$css}\">{$tag}</div>\n";
384 - $this->reviewNotice .= $notice;
385 - }
386 - return true;
387 - }
388 -
389 - /**
390 - * If the page has a stable version and it shows by default,
391 - * tell search crawlers to index only that version of the page.
392 - * Also index the draft as well if they are synced (bug 27173).
393 - * However, any URL with ?stableid=x should not be indexed (as with ?oldid=x).
394 - */
395 - public function setRobotPolicy() {
396 - $request = $this->getRequest();
397 - if ( $this->article->getStableRev() && $this->article->isStableShownByDefault() ) {
398 - if ( $this->showingStable() ) {
399 - return; // stable version - index this
400 - } elseif ( !$request->getVal( 'stableid' )
401 - && $this->out->getRevisionId() == $this->article->getStable()
402 - && $this->article->stableVersionIsSynced() )
403 - {
404 - return; // draft that is synced with the stable version - index this
405 - }
406 - $this->out->setRobotPolicy( 'noindex,nofollow' ); // don't index this version
407 - }
408 - }
409 -
410 - /**
411 - * @param $tag review box/bar info
412 - * @param $prot protection notice
413 - * Tag output function must be called by caller
414 - */
415 - protected function showUnreviewedPage( $tag, $prot ) {
416 - if ( $this->out->isPrintable() ) {
417 - return; // all this function does is add notices; don't show them
418 - }
419 - $icon = FlaggedRevsXML::draftStatusIcon();
420 - // Simple icon-based UI
421 - if ( $this->useSimpleUI() ) {
422 - $tag .= $prot . $icon . wfMsgExt( 'revreview-quick-none', 'parseinline' );
423 - $css = "flaggedrevs_short plainlinks noprint";
424 - $this->reviewNotice .= "<div id='mw-fr-revisiontag' class='$css'>$tag</div>";
425 - // Standard UI
426 - } else {
427 - $css = 'flaggedrevs_notice plainlinks noprint';
428 - $tag = "<div id='mw-fr-revisiontag' class='$css'>" .
429 - $prot . $icon . wfMsgExt( 'revreview-noflagged', 'parseinline' ) .
430 - "</div>";
431 - $this->reviewNotice .= $tag;
432 - }
433 - }
434 -
435 - /**
436 - * Tag output function must be called by caller
437 - * Parser cache control deferred to caller
438 - * @param $srev stable version
439 - * @param $tag review box/bar info
440 - * @param $prot protection notice icon
441 - * @return void
442 - */
443 - protected function showDraftVersion( FlaggedRevision $srev, &$tag, $prot ) {
444 - $request = $this->getRequest();
445 - $reqUser = $this->getUser();
446 - $this->load();
447 - if ( $this->out->isPrintable() ) {
448 - return; // all this function does is add notices; don't show them
449 - }
450 - $flags = $srev->getTags();
451 - $time = $this->getLang()->date( $srev->getTimestamp(), true );
452 - # Get quality level
453 - $quality = FlaggedRevs::isQuality( $flags );
454 - # Get stable version sync status
455 - $synced = $this->article->stableVersionIsSynced();
456 - if ( $synced ) { // draft == stable
457 - $diffToggle = ''; // no diff to show
458 - } else { // draft != stable
459 - # The user may want the diff (via prefs)
460 - $diffToggle = $this->getTopDiffToggle( $srev, $quality );
461 - if ( $diffToggle != '' ) $diffToggle = " $diffToggle";
462 - # Make sure there is always a notice bar when viewing the draft.
463 - if ( $this->useSimpleUI() ) { // we already one for detailed UI
464 - $this->setPendingNotice( $srev, $diffToggle );
465 - }
466 - }
467 - # Give a "your edit is pending" notice to newer users if
468 - # an unreviewed edit was completed...
469 - if ( $request->getVal( 'shownotice' )
470 - && $this->article->getUserText( Revision::RAW ) == $reqUser->getName()
471 - && $this->article->revsArePending()
472 - && !$reqUser->isAllowed( 'review' ) )
473 - {
474 - $revsSince = $this->article->getPendingRevCount();
475 - $pending = $prot;
476 - if ( $this->showRatingIcon() ) {
477 - $pending .= FlaggedRevsXML::draftStatusIcon();
478 - }
479 - $pending .= wfMsgExt( 'revreview-edited',
480 - 'parseinline', $srev->getRevId(), $revsSince );
481 - $anchor = $request->getVal( 'fromsection' );
482 - if ( $anchor != null ) {
483 - $section = str_replace( '_', ' ', $anchor ); // prettify
484 - $pending .= wfMsgExt( 'revreview-edited-section', 'parse', $anchor, $section );
485 - }
486 - # Notice should always use subtitle
487 - $this->reviewNotice = "<div id='mw-fr-reviewnotice' " .
488 - "class='flaggedrevs_preview plainlinks'>$pending</div>";
489 - # Otherwise, construct some tagging info for non-printable outputs.
490 - # Also, if low profile UI is enabled and the page is synced, skip the tag.
491 - # Note: the "your edit is pending" notice has all this info, so we never add both.
492 - } elseif ( !( $this->article->lowProfileUI() && $synced ) ) {
493 - $revsSince = $this->article->getPendingRevCount();
494 - // Simple icon-based UI
495 - if ( $this->useSimpleUI() ) {
496 - if ( !$reqUser->getId() ) {
497 - $msgHTML = ''; // Anons just see simple icons
498 - } elseif ( $synced ) {
499 - $msg = $quality
500 - ? 'revreview-quick-quality-same'
501 - : 'revreview-quick-basic-same';
502 - $msgHTML = wfMsgExt( $msg, 'parseinline',
503 - $srev->getRevId(), $revsSince );
504 - } else {
505 - $msg = $quality
506 - ? 'revreview-quick-see-quality'
507 - : 'revreview-quick-see-basic';
508 - $msgHTML = wfMsgExt( $msg, 'parseinline',
509 - $srev->getRevId(), $revsSince );
510 - }
511 - $icon = '';
512 - # For protection based configs, show lock only if it's not redundant.
513 - if ( $this->showRatingIcon() ) {
514 - $icon = $synced
515 - ? FlaggedRevsXML::stableStatusIcon( $quality )
516 - : FlaggedRevsXML::draftStatusIcon();
517 - }
518 - $msgHTML = $prot . $icon . $msgHTML;
519 - $tag .= FlaggedRevsXML::prettyRatingBox( $srev, $msgHTML,
520 - $revsSince, 'draft', $synced, false );
521 - // Standard UI
522 - } else {
523 - if ( $synced ) {
524 - if ( $quality ) {
525 - $msg = 'revreview-quality-same';
526 - } else {
527 - $msg = 'revreview-basic-same';
528 - }
529 - $msgHTML = wfMsgExt( $msg, 'parseinline',
530 - $srev->getRevId(), $time, $revsSince );
531 - } else {
532 - $msg = $quality
533 - ? 'revreview-newest-quality'
534 - : 'revreview-newest-basic';
535 - $msg .= ( $revsSince == 0 ) ? '-i' : '';
536 - $msgHTML = wfMsgExt( $msg, 'parseinline',
537 - $srev->getRevId(), $time, $revsSince );
538 - }
539 - $icon = $synced
540 - ? FlaggedRevsXML::stableStatusIcon( $quality )
541 - : FlaggedRevsXML::draftStatusIcon();
542 - $tag .= $prot . $icon . $msgHTML . $diffToggle;
543 - }
544 - }
545 - }
546 -
547 - /**
548 - * Tag output function must be called by caller
549 - * Parser cache control deferred to caller
550 - * @param $srev stable version
551 - * @param $frev selected flagged revision
552 - * @param $tag review box/bar info
553 - * @param $prot protection notice icon
554 - * @return ParserOutput
555 - */
556 - protected function showOldReviewedVersion( FlaggedRevision $frev, &$tag, $prot ) {
557 - $reqUser = $this->getUser();
558 - $this->load();
559 - $flags = $frev->getTags();
560 - $time = $this->getLang()->date( $frev->getTimestamp(), true );
561 - # Set display revision ID
562 - $this->out->setRevisionId( $frev->getRevId() );
563 - # Get quality level
564 - $quality = FlaggedRevs::isQuality( $flags );
565 -
566 - # Construct some tagging for non-printable outputs. Note that the pending
567 - # notice has all this info already, so don't do this if we added that already.
568 - if ( !$this->out->isPrintable() ) {
569 - // Simple icon-based UI
570 - if ( $this->useSimpleUI() ) {
571 - $icon = '';
572 - # For protection based configs, show lock only if it's not redundant.
573 - if ( $this->showRatingIcon() ) {
574 - $icon = FlaggedRevsXML::stableStatusIcon( $quality );
575 - }
576 - $revsSince = $this->article->getPendingRevCount();
577 - if ( !$reqUser->getId() ) {
578 - $msgHTML = ''; // Anons just see simple icons
579 - } else {
580 - $msg = $quality
581 - ? 'revreview-quick-quality-old'
582 - : 'revreview-quick-basic-old';
583 - $msgHTML = wfMsgExt( $msg, 'parseinline', $frev->getRevId(), $revsSince );
584 - }
585 - $msgHTML = $prot . $icon . $msgHTML;
586 - $tag = FlaggedRevsXML::prettyRatingBox( $frev, $msgHTML,
587 - $revsSince, 'oldstable', false /*synced*/ );
588 - // Standard UI
589 - } else {
590 - $icon = FlaggedRevsXML::stableStatusIcon( $quality );
591 - $msg = $quality
592 - ? 'revreview-quality-old'
593 - : 'revreview-basic-old';
594 - $tag = $prot . $icon;
595 - $tag .= wfMsgExt( $msg, 'parseinline', $frev->getRevId(), $time );
596 - # Hide clutter
597 - if ( !empty( $flags ) ) {
598 - $tag .= FlaggedRevsXML::ratingToggle();
599 - $tag .= "<div id='mw-fr-revisiondetails' style='display:block;'>" .
600 - wfMsgHtml( 'revreview-oldrating' ) .
601 - FlaggedRevsXML::addTagRatings( $flags ) . '</div>';
602 - }
603 - }
604 - }
605 -
606 - $text = $frev->getRevText();
607 - # Get the new stable parser output...
608 - $pOpts = $this->article->makeParserOptions( $reqUser );
609 - $parserOut = FlaggedRevs::parseStableText(
610 - $this->article->getTitle(), $text, $frev->getRevId(), $pOpts );
611 -
612 - # Parse and output HTML
613 - $redirHtml = $this->getRedirectHtml( $text );
614 - if ( $redirHtml == '' ) { // page is not a redirect...
615 - # Add the stable output to the page view
616 - $this->out->addParserOutput( $parserOut );
617 - } else { // page is a redirect...
618 - $this->out->addHtml( $redirHtml );
619 - # Add output to set categories, displaytitle, etc.
620 - $this->out->addParserOutputNoText( $parserOut );
621 - }
622 -
623 - return $parserOut;
624 - }
625 -
626 - /**
627 - * Tag output function must be called by caller
628 - * Parser cache control deferred to caller
629 - * @param $srev stable version
630 - * @param $tag review box/bar info
631 - * @param $prot protection notice
632 - * @return ParserOutput
633 - */
634 - protected function showStableVersion( FlaggedRevision $srev, &$tag, $prot ) {
635 - $reqUser = $this->getUser();
636 - $this->load();
637 - $flags = $srev->getTags();
638 - $time = $this->getLang()->date( $srev->getTimestamp(), true );
639 - # Set display revision ID
640 - $this->out->setRevisionId( $srev->getRevId() );
641 - # Get quality level
642 - $quality = FlaggedRevs::isQuality( $flags );
643 -
644 - $synced = $this->article->stableVersionIsSynced();
645 - # Construct some tagging
646 - if ( !$this->out->isPrintable() && !( $this->article->lowProfileUI() && $synced ) ) {
647 - $revsSince = $this->article->getPendingRevCount();
648 - // Simple icon-based UI
649 - if ( $this->useSimpleUI() ) {
650 - $icon = '';
651 - # For protection based configs, show lock only if it's not redundant.
652 - if ( $this->showRatingIcon() ) {
653 - $icon = FlaggedRevsXML::stableStatusIcon( $quality );
654 - }
655 - if ( !$reqUser->getId() ) {
656 - $msgHTML = ''; // Anons just see simple icons
657 - } else {
658 - $msg = $quality
659 - ? 'revreview-quick-quality'
660 - : 'revreview-quick-basic';
661 - # Uses messages 'revreview-quick-quality-same', 'revreview-quick-basic-same'
662 - $msg = $synced ? "{$msg}-same" : $msg;
663 - $msgHTML = wfMsgExt( $msg, 'parseinline',
664 - $srev->getRevId(), $revsSince );
665 - }
666 - $msgHTML = $prot . $icon . $msgHTML;
667 - $tag = FlaggedRevsXML::prettyRatingBox( $srev, $msgHTML,
668 - $revsSince, 'stable', $synced );
669 - // Standard UI
670 - } else {
671 - $icon = FlaggedRevsXML::stableStatusIcon( $quality );
672 - $msg = $quality ? 'revreview-quality' : 'revreview-basic';
673 - if ( $synced ) {
674 - # uses messages 'revreview-quality-same', 'revreview-basic-same'
675 - $msg .= '-same';
676 - } elseif ( $revsSince == 0 ) {
677 - # uses messages 'revreview-quality-i', 'revreview-basic-i'
678 - $msg .= '-i';
679 - }
680 - $tag = $prot . $icon;
681 - $tag .= wfMsgExt( $msg, 'parseinline', $srev->getRevId(), $time, $revsSince );
682 - if ( !empty( $flags ) ) {
683 - $tag .= FlaggedRevsXML::ratingToggle();
684 - $tag .= "<div id='mw-fr-revisiondetails' style='display:block;'>" .
685 - FlaggedRevsXML::addTagRatings( $flags ) . '</div>';
686 - }
687 - }
688 - }
689 -
690 - # Get parsed stable version and output HTML
691 - $pOpts = $this->article->makeParserOptions( $reqUser );
692 - $parserCache = FRParserCacheStable::singleton();
693 - $parserOut = $parserCache->get( $this->article, $pOpts );
694 -
695 - # Do not use the parser cache if it lacks mImageTimeKeys and there is a
696 - # chance that a review form will be added to this page (which requires the versions).
697 - $canReview = $this->article->getTitle()->userCan( 'review' );
698 - if ( $parserOut && ( !$canReview || FlaggedRevs::parserOutputIsVersioned( $parserOut ) ) ) {
699 - # Cache hit. Note that redirects are not cached.
700 - $this->out->addParserOutput( $parserOut );
701 - } else {
702 - $text = $srev->getRevText();
703 - # Get the new stable parser output...
704 - $parserOut = FlaggedRevs::parseStableText(
705 - $this->article->getTitle(), $text, $srev->getRevId(), $pOpts );
706 -
707 - $redirHtml = $this->getRedirectHtml( $text );
708 - if ( $redirHtml == '' ) { // page is not a redirect...
709 - # Update the stable version cache
710 - $parserCache->save( $parserOut, $this->article, $pOpts );
711 - # Add the stable output to the page view
712 - $this->out->addParserOutput( $parserOut );
713 - } else { // page is a redirect...
714 - $this->out->addHtml( $redirHtml );
715 - # Add output to set categories, displaytitle, etc.
716 - $this->out->addParserOutputNoText( $parserOut );
717 - }
718 - # Update the stable version dependancies
719 - FlaggedRevs::updateStableOnlyDeps( $this->article, $parserOut );
720 - }
721 -
722 - # Update page sync status for tracking purposes.
723 - # NOTE: avoids master hits and doesn't have to be perfect for what it does
724 - if ( $this->article->syncedInTracking() != $synced ) {
725 - if ( wfGetLB()->safeGetLag( wfGetDB( DB_SLAVE ) ) <= 5 ) { // avoid write-delay cycles
726 - $this->article->updateSyncStatus( $synced );
727 - }
728 - }
729 -
730 - return $parserOut;
731 - }
732 -
733 - // Get fancy redirect arrow and link HTML
734 - protected function getRedirectHtml( $text ) {
735 - $rTargets = Title::newFromRedirectArray( $text );
736 - if ( $rTargets ) {
737 - $article = new Article( $this->article->getTitle() );
738 - return $article->viewRedirect( $rTargets );
739 - }
740 - return '';
741 - }
742 -
743 - // Show icons for draft/stable/old reviewed versions
744 - protected function showRatingIcon() {
745 - if ( FlaggedRevs::useOnlyIfProtected() ) {
746 - // If there is only one quality level and we have tabs to know
747 - // which version we are looking at, then just use the lock icon...
748 - return FlaggedRevs::qualityVersions();
749 - }
750 - return true;
751 - }
752 -
753 - /**
754 - * Get collapsible diff-to-stable html to add to the review notice as needed
755 - * @param FlaggedRevision $srev, stable version
756 - * @param bool $quality, revision is quality
757 - * @return string, the html line (either "" or "<diff toggle><diff div>")
758 - */
759 - protected function getTopDiffToggle( FlaggedRevision $srev, $quality ) {
760 - $reqUser = $this->getUser();
761 - $this->load();
762 - if ( !$reqUser->getBoolOption( 'flaggedrevsviewdiffs' ) ) {
763 - return false; // nothing to do here
764 - }
765 - # Diff should only show for the draft
766 - $oldid = $this->getOldIDFromRequest();
767 - $latest = $this->article->getLatest();
768 - if ( $oldid && $oldid != $latest ) {
769 - return false; // not viewing the draft
770 - }
771 - $revsSince = $this->article->getPendingRevCount();
772 - if ( !$revsSince ) {
773 - return false; // no pending changes
774 - }
775 - $title = $this->article->getTitle(); // convenience
776 - # Review status of left diff revision...
777 - $leftNote = $quality
778 - ? 'revreview-hist-quality'
779 - : 'revreview-hist-basic';
780 - $lClass = FlaggedRevsXML::getQualityColor( (int)$quality );
781 - $leftNote = "<span class='$lClass'>[" . wfMsgHtml( $leftNote ) . "]</span>";
782 - # Review status of right diff revision...
783 - $rClass = FlaggedRevsXML::getQualityColor( false );
784 - $rightNote = "<span class='$rClass'>[" .
785 - wfMsgHtml( 'revreview-hist-pending' ) . "]</span>";
786 - # Get the actual body of the diff...
787 - $diffEngine = new DifferenceEngine( $title, $srev->getRevId(), $latest );
788 - $diffBody = $diffEngine->getDiffBody();
789 - if ( strlen( $diffBody ) > 0 ) {
790 - $nEdits = $revsSince - 1; // full diff-to-stable, no need for query
791 - if ( $nEdits ) {
792 - $limit = 100;
793 - $nUsers = $title->countAuthorsBetween( $srev->getRevId(), $latest, $limit );
794 - $multiNotice = DifferenceEngine::intermediateEditsMsg( $nEdits, $nUsers, $limit );
795 - } else {
796 - $multiNotice = '';
797 - }
798 - $diffEngine->showDiffStyle(); // add CSS
799 - $this->isDiffFromStable = true; // alter default review form tags
800 - return
801 - FlaggedRevsXML::diffToggle() .
802 - "<div id='mw-fr-stablediff'>\n" .
803 - self::getFormattedDiff( $diffBody, $multiNotice, $leftNote, $rightNote ) .
804 - "</div>\n";
805 - }
806 - return '';
807 - }
808 -
809 - // $n number of in-between revs
810 - protected static function getFormattedDiff(
811 - $diffBody, $multiNotice, $leftStatus, $rightStatus
812 - ) {
813 - if ( $multiNotice != '' ) {
814 - $multiNotice = "<tr><td colspan='4' align='center' class='diff-multi'>" .
815 - $multiNotice . "</td></tr>";
816 - }
817 - return
818 - "<table border='0' width='98%' cellpadding='0' cellspacing='4' class='diff'>" .
819 - "<col class='diff-marker' />" .
820 - "<col class='diff-content' />" .
821 - "<col class='diff-marker' />" .
822 - "<col class='diff-content' />" .
823 - "<tr>" .
824 - "<td colspan='2' width='50%' align='center' class='diff-otitle'><b>" .
825 - $leftStatus . "</b></td>" .
826 - "<td colspan='2' width='50%' align='center' class='diff-ntitle'><b>" .
827 - $rightStatus . "</b></td>" .
828 - "</tr>" .
829 - $multiNotice .
830 - $diffBody .
831 - "</table>";
832 - }
833 -
834 - /**
835 - * Get the normal and display files for the underlying ImagePage.
836 - * If the a stable version needs to be displayed, this will set $normalFile
837 - * to the current version, and $displayFile to the desired version.
838 - *
839 - * If no stable version is required, the reference parameters will not be set
840 - *
841 - * Depends on $request
842 - */
843 - public function imagePageFindFile( &$normalFile, &$displayFile ) {
844 - $request = $this->getRequest();
845 - $this->load();
846 - # Determine timestamp. A reviewed version may have explicitly been requested...
847 - $frev = null;
848 - $time = false;
849 - $reqId = $request->getVal( 'stableid' );
850 - if ( $reqId ) {
851 - $frev = FlaggedRevision::newFromTitle( $this->article->getTitle(), $reqId );
852 - } elseif ( $this->showingStable() ) {
853 - $frev = $this->article->getStableRev();
854 - }
855 - if ( $frev ) {
856 - $time = $frev->getFileTimestamp();
857 - // B/C, may be stored in associated image version metadata table
858 - // @TODO: remove, updateTracking.php does this
859 - if ( !$time ) {
860 - $dbr = wfGetDB( DB_SLAVE );
861 - $time = $dbr->selectField( 'flaggedimages',
862 - 'fi_img_timestamp',
863 - array( 'fi_rev_id' => $frev->getRevId(),
864 - 'fi_name' => $this->article->getTitle()->getDBkey() ),
865 - __METHOD__
866 - );
867 - $time = trim( $time ); // remove garbage
868 - $time = $time ? wfTimestamp( TS_MW, $time ) : false;
869 - }
870 - }
871 - if ( !$time ) {
872 - # Try request parameter
873 - $time = $request->getVal( 'filetimestamp', false );
874 - }
875 -
876 - if ( !$time ) {
877 - return; // Use the default behaviour
878 - }
879 -
880 - $title = $this->article->getTitle();
881 - $displayFile = wfFindFile( $title, array( 'time' => $time ) );
882 - # If none found, try current
883 - if ( !$displayFile ) {
884 - wfDebug( __METHOD__ . ": {$title->getPrefixedDBkey()}: $time not found, using current\n" );
885 - $displayFile = wfFindFile( $title );
886 - # If none found, use a valid local placeholder
887 - if ( !$displayFile ) {
888 - $displayFile = wfLocalFile( $title ); // fallback to current
889 - }
890 - $normalFile = $displayFile;
891 - # If found, set $normalFile
892 - } else {
893 - wfDebug( __METHOD__ . ": {$title->getPrefixedDBkey()}: using timestamp $time\n" );
894 - $normalFile = wfFindFile( $title );
895 - }
896 - }
897 -
898 - /**
899 - * Adds stable version tags to page when viewing history
900 - */
901 - public function addToHistView() {
902 - $this->load();
903 - # Add a notice if there are pending edits...
904 - $srev = $this->article->getStableRev();
905 - if ( $srev && $this->article->revsArePending() ) {
906 - $revsSince = $this->article->getPendingRevCount();
907 - $tag = "<div id='mw-fr-revisiontag-edit' class='flaggedrevs_notice plainlinks'>" .
908 - FlaggedRevsXML::lockStatusIcon( $this->article ) . # flag protection icon as needed
909 - FlaggedRevsXML::pendingEditNotice( $this->article, $srev, $revsSince ) . "</div>";
910 - $this->out->addHTML( $tag );
911 - }
912 - return true;
913 - }
914 -
915 - /**
916 - * Adds stable version tags to page when editing
917 - */
918 - public function addToEditView( EditPage $editPage ) {
919 - global $wgParser;
920 - $reqUser = $this->getUser();
921 - $this->load();
922 - # Must be reviewable. UI may be limited to unobtrusive patrolling system.
923 - if ( !$this->article->isReviewable() ) {
924 - return true;
925 - }
926 - $items = array();
927 - # Show stabilization log
928 - $log = $this->stabilityLogNotice();
929 - if ( $log ) $items[] = $log;
930 - # Check the newest stable version
931 - $frev = $this->article->getStableRev();
932 - if ( $frev ) {
933 - $quality = $frev->getQuality();
934 - # Find out revision id of base version
935 - $latestId = $this->article->getLatest();
936 - $revId = $editPage->oldid ? $editPage->oldid : $latestId;
937 - # Let new users know about review procedure a tag.
938 - # If the log excerpt was shown this is redundant.
939 - if ( !$log && !$reqUser->getId() && $this->article->isStableShownByDefault() ) {
940 - $items[] = wfMsgExt( 'revreview-editnotice', 'parseinline' );
941 - }
942 - # Add a notice if there are pending edits...
943 - if ( $this->article->revsArePending() ) {
944 - $revsSince = $this->article->getPendingRevCount();
945 - $items[] = FlaggedRevsXML::pendingEditNotice( $this->article, $frev, $revsSince );
946 - }
947 - # Show diff to stable, to make things less confusing.
948 - # This can be disabled via user preferences and other conditions...
949 - if ( $frev->getRevId() < $latestId // changes were made
950 - && $reqUser->getBoolOption( 'flaggedrevseditdiffs' ) // not disable via prefs
951 - && $revId == $latestId // only for current rev
952 - && $editPage->section != 'new' // not for new sections
953 - && $editPage->formtype != 'diff' // not "show changes"
954 - ) {
955 - # Left diff side...
956 - $leftNote = $quality
957 - ? 'revreview-hist-quality'
958 - : 'revreview-hist-basic';
959 - $lClass = FlaggedRevsXML::getQualityColor( (int)$quality );
960 - $leftNote = "<span class='$lClass'>[" .
961 - wfMsgHtml( $leftNote ) . "]</span>";
962 - # Right diff side...
963 - $rClass = FlaggedRevsXML::getQualityColor( false );
964 - $rightNote = "<span class='$rClass'>[" .
965 - wfMsgHtml( 'revreview-hist-pending' ) . "]</span>";
966 - # Get the stable version source
967 - $text = $frev->getRevText();
968 - # Are we editing a section?
969 - $section = ( $editPage->section == "" ) ?
970 - false : intval( $editPage->section );
971 - if ( $section !== false ) {
972 - $text = $wgParser->getSection( $text, $section );
973 - }
974 - if ( $text !== false && strcmp( $text, $editPage->textbox1 ) !== 0 ) {
975 - $diffEngine = new DifferenceEngine( $this->article->getTitle() );
976 - $diffBody = $diffEngine->generateDiffBody( $text, $editPage->textbox1 );
977 - $diffHtml =
978 - wfMsgExt( 'review-edit-diff', 'parseinline' ) . ' ' .
979 - FlaggedRevsXML::diffToggle() .
980 - "<div id='mw-fr-stablediff'>" .
981 - self::getFormattedDiff( $diffBody, '', $leftNote, $rightNote ) .
982 - "</div>\n";
983 - $items[] = $diffHtml;
984 - $diffEngine->showDiffStyle(); // add CSS
985 - }
986 - }
987 - # Output items
988 - if ( count( $items ) ) {
989 - $html = "<table class='flaggedrevs_editnotice plainlinks'>";
990 - foreach ( $items as $item ) {
991 - $html .= '<tr><td>' . $item . '</td></tr>';
992 - }
993 - $html .= '</table>';
994 - $this->out->addHTML( $html );
995 - }
996 - }
997 - return true;
998 - }
999 -
1000 - protected function stabilityLogNotice() {
1001 - $this->load();
1002 - $s = '';
1003 - # Only for pages manually made to be stable...
1004 - if ( $this->article->isPageLocked() ) {
1005 - $s = wfMsgExt( 'revreview-locked', 'parseinline' );
1006 - $s .= ' ' . FlaggedRevsXML::logDetailsToggle();
1007 - $s .= FlaggedRevsXML::stabilityLogExcerpt( $this->article );
1008 - # ...or unstable
1009 - } elseif ( $this->article->isPageUnlocked() ) {
1010 - $s = wfMsgExt( 'revreview-unlocked', 'parseinline' );
1011 - $s .= ' ' . FlaggedRevsXML::logDetailsToggle();
1012 - $s .= FlaggedRevsXML::stabilityLogExcerpt( $this->article );
1013 - }
1014 - return $s;
1015 - }
1016 -
1017 - public function addToNoSuchSection( EditPage $editPage, &$s ) {
1018 - $this->load();
1019 - $srev = $this->article->getStableRev();
1020 - # Add notice for users that may have clicked "edit" for a
1021 - # section in the stable version that isn't in the draft.
1022 - if ( $srev && $this->article->revsArePending() ) {
1023 - $revsSince = $this->article->getPendingRevCount();
1024 - if ( $revsSince ) {
1025 - $s .= "<div class='flaggedrevs_editnotice plainlinks'>" .
1026 - wfMsgExt( 'revreview-pending-nosection', 'parseinline',
1027 - $srev->getRevId(), $revsSince ) . "</div>";
1028 - }
1029 - }
1030 - return true;
1031 - }
1032 -
1033 - /**
1034 - * Add unreviewed pages links
1035 - */
1036 - public function addToCategoryView() {
1037 - $reqUser = $this->getUser();
1038 - $this->load();
1039 - if ( !$reqUser->isAllowed( 'review' ) ) {
1040 - return true;
1041 - }
1042 - if ( !FlaggedRevs::useOnlyIfProtected() ) {
1043 - # Add links to lists of unreviewed pages and pending changes in this category
1044 - $category = $this->article->getTitle()->getText();
1045 - $this->out->appendSubtitle(
1046 - Html::rawElement(
1047 - 'span',
1048 - array( 'class' => 'plainlinks', 'id' => 'mw-fr-category-oldreviewed' ),
1049 - wfMsgExt( 'flaggedrevs-categoryview', 'parseinline', urlencode( $category ) )
1050 - )
1051 - );
1052 - }
1053 - return true;
1054 - }
1055 -
1056 - /**
1057 - * Add review form to pages when necessary on a regular page view (action=view).
1058 - * If $output is an OutputPage then this prepends the form onto it.
1059 - * If $output is a string then this appends the review form to it.
1060 - * @param mixed string|OutputPage
1061 - */
1062 - public function addReviewForm( &$output ) {
1063 - $request = $this->getRequest();
1064 - $reqUser = $this->getUser();
1065 - $this->load();
1066 - if ( $this->out->isPrintable() ) {
1067 - return false; // Must be on non-printable output
1068 - }
1069 - # User must have review rights
1070 - if ( !$reqUser->isAllowed( 'review' ) ) {
1071 - return true;
1072 - }
1073 - # Page must exist and be reviewable
1074 - if ( !$this->article->exists() || !$this->article->isReviewable() ) {
1075 - return true;
1076 - }
1077 - # Must be a page view action...
1078 - if ( !$this->isPageViewOrDiff( $request ) ) {
1079 - return true;
1080 - }
1081 - # Get the revision being displayed
1082 - $rev = false;
1083 - if ( $this->reviewFormRev ) {
1084 - $rev = $this->reviewFormRev; // $newRev for diffs stored here
1085 - } elseif ( $this->out->getRevisionId() ) {
1086 - $rev = Revision::newFromId( $this->out->getRevisionId() );
1087 - }
1088 - # Build the review form as needed
1089 - if ( $rev && ( !$this->diffRevs || $this->isReviewableDiff ) ) {
1090 - $form = new RevisionReviewFormUI( $this->getContext(), $this->article, $rev );
1091 - # Default tags and existence of "reject" button depend on context
1092 - if ( $this->diffRevs ) {
1093 - $form->setDiffPriorRev( $this->diffRevs['old'] );
1094 - }
1095 - # Review notice box goes in top of form
1096 - $form->setTopNotice( $this->diffNoticeBox );
1097 - $form->setBottomNotice( $this->diffIncChangeBox );
1098 -
1099 - # Set the file version we are viewing (for File: pages)
1100 - $form->setFileVersion( $this->out->getFileVersion() );
1101 - # $wgOut may not already have the inclusion IDs, such as for diffonly=1.
1102 - # fr_unversionedIncludes indicates that ParserOutput added to $wgOut lacked inclusion IDs.
1103 - # If they're lacking, then we use getRevIncludes() to get the draft inclusion versions.
1104 - # Note: showStableVersion() already makes sure that $wgOut has the stable inclusion versions.
1105 - if ( $this->out->getRevisionId() == $rev->getId() && empty( $this->out->fr_unversionedIncludes ) ) {
1106 - $tmpVers = $this->out->getTemplateIds();
1107 - $fileVers = $this->out->getFileSearchOptions();
1108 - } elseif ( $this->oldRevIncludes ) { // e.g. diffonly=1, stable diff
1109 - # We may have already fetched the inclusion IDs to get the template/file changes.
1110 - list( $tmpVers, $fileVers ) = $this->oldRevIncludes; // reuse
1111 - } else { // e.g. diffonly=1, other diffs
1112 - # $wgOut may not already have the inclusion IDs, such as for diffonly=1.
1113 - # RevisionReviewForm will fetch them as needed however.
1114 - list( $tmpVers, $fileVers ) =
1115 - FRInclusionCache::getRevIncludes( $this->article, $rev, $reqUser );
1116 - }
1117 - $form->setIncludeVersions( $tmpVers, $fileVers );
1118 -
1119 - list( $html, $status ) = $form->getHtml();
1120 - # Diff action: place the form at the top of the page
1121 - if ( $output instanceof OutputPage ) {
1122 - $output->prependHTML( $html );
1123 - # View action: place the form at the bottom of the page
1124 - } else {
1125 - $output .= $html;
1126 - }
1127 - }
1128 - return true;
1129 - }
1130 -
1131 - /**
1132 - * Add link to stable version setting to protection form
1133 - */
1134 - public function addStabilizationLink() {
1135 - $request = $this->getRequest();
1136 - $this->load();
1137 - if ( FlaggedRevs::useProtectionLevels() ) {
1138 - return true; // simple custom levels set for action=protect
1139 - }
1140 - # Check only if the title is reviewable
1141 - if ( !FlaggedRevs::inReviewNamespace( $this->article->getTitle() ) ) {
1142 - return true;
1143 - }
1144 - $action = $request->getVal( 'action', 'view' );
1145 - if ( $action == 'protect' || $action == 'unprotect' ) {
1146 - $title = SpecialPage::getTitleFor( 'Stabilization' );
1147 - # Give a link to the page to configure the stable version
1148 - $frev = $this->article->getStableRev();
1149 - if ( $frev && $frev->getRevId() == $this->article->getLatest() ) {
1150 - $this->out->prependHTML( "<span class='plainlinks'>" .
1151 - wfMsgExt( 'revreview-visibility-synced', 'parseinline',
1152 - $title->getPrefixedText() ) . "</span>" );
1153 - } elseif ( $frev ) {
1154 - $this->out->prependHTML( "<span class='plainlinks'>" .
1155 - wfMsgExt( 'revreview-visibility-outdated', 'parseinline',
1156 - $title->getPrefixedText() ) . "</span>" );
1157 - } else {
1158 - $this->out->prependHTML( "<span class='plainlinks'>" .
1159 - wfMsgExt( 'revreview-visibility-nostable', 'parseinline',
1160 - $title->getPrefixedText() ) . "</span>" );
1161 - }
1162 - }
1163 - return true;
1164 - }
1165 -
1166 - /**
1167 - * Modify an array of action links, as used by SkinTemplateNavigation and
1168 - * SkinTemplateTabs, to inlude flagged revs UI elements
1169 - */
1170 - public function setActionTabs( $skin, array &$actions ) {
1171 - $reqUser = $this->getUser();
1172 - $this->load();
1173 - if ( FlaggedRevs::useProtectionLevels() ) {
1174 - return true; // simple custom levels set for action=protect
1175 - }
1176 - $title = $this->article->getTitle()->getSubjectPage();
1177 - if ( !FlaggedRevs::inReviewNamespace( $title ) ) {
1178 - return true; // Only reviewable pages need these tabs
1179 - }
1180 - // Check if we should show a stabilization tab
1181 - if (
1182 - !$this->article->getTitle()->isTalkPage() &&
1183 - is_array( $actions ) &&
1184 - !isset( $actions['protect'] ) &&
1185 - !isset( $actions['unprotect'] ) &&
1186 - $reqUser->isAllowed( 'stablesettings' ) &&
1187 - $title->exists() )
1188 - {
1189 - $stableTitle = SpecialPage::getTitleFor( 'Stabilization' );
1190 - // Add the tab
1191 - $actions['default'] = array(
1192 - 'class' => false,
1193 - 'text' => wfMsg( 'stabilization-tab' ),
1194 - 'href' => $stableTitle->getLocalUrl( 'page=' . $title->getPrefixedUrl() )
1195 - );
1196 - }
1197 - return true;
1198 - }
1199 -
1200 - /**
1201 - * Modify an array of tab links to include flagged revs UI elements
1202 - * @param string $type ('flat' for SkinTemplateTabs, 'nav' for SkinTemplateNavigation)
1203 - */
1204 - public function setViewTabs( Skin $skin, array &$views, $type ) {
1205 - $this->load();
1206 - if ( !FlaggedRevs::inReviewNamespace( $this->article->getTitle() ) ) {
1207 - return true; // short-circuit for non-reviewable pages
1208 - }
1209 - # Hack for bug 16734 (some actions update and view all at once)
1210 - if ( $this->pageWriteOpRequested() && wfGetDB( DB_MASTER )->doneWrites() ) {
1211 - # Tabs need to reflect the new stable version so users actually
1212 - # see the results of their action (i.e. "delete"/"rollback")
1213 - $this->article->loadPageData( 'fromdbmaster' );
1214 - }
1215 - $srev = $this->article->getStableRev();
1216 - if ( !$srev ) {
1217 - return true; // No stable revision exists
1218 - }
1219 - $synced = $this->article->stableVersionIsSynced();
1220 - $pendingEdits = !$synced && $this->article->isStableShownByDefault();
1221 - // Set the edit tab names as needed...
1222 - if ( $pendingEdits ) {
1223 - if ( isset( $views['edit'] ) ) {
1224 - $views['edit']['text'] = wfMsg( 'revreview-edit' );
1225 - }
1226 - if ( isset( $views['viewsource'] ) ) {
1227 - $views['viewsource']['text'] = wfMsg( 'revreview-source' );
1228 - }
1229 - }
1230 - # Add "pending changes" tab if the page is not synced
1231 - if ( !$synced ) {
1232 - $this->addDraftTab( $views, $srev, $type );
1233 - }
1234 - return true;
1235 - }
1236 -
1237 - // Add "pending changes" tab and set tab selection CSS
1238 - protected function addDraftTab( array &$views, FlaggedRevision $srev, $type ) {
1239 - $request = $this->getRequest();
1240 - $title = $this->article->getTitle(); // convenience
1241 - $tabs = array(
1242 - 'read' => array( // view stable
1243 - 'text' => '', // unused
1244 - 'href' => $title->getLocalUrl( 'stable=1' ),
1245 - 'class' => ''
1246 - ),
1247 - 'draft' => array( // view draft
1248 - 'text' => wfMsg( 'revreview-current' ),
1249 - 'href' => $title->getLocalUrl( 'stable=0&redirect=no' ),
1250 - 'class' => 'collapsible'
1251 - ),
1252 - );
1253 - // Set tab selection CSS
1254 - if ( $this->showingStable() || $request->getVal( 'stableid' ) ) {
1255 - // We are looking a the stable version or an old reviewed one
1256 - $tabs['read']['class'] = 'selected';
1257 - } elseif ( $this->isPageViewOrDiff( $request ) ) {
1258 - $ts = null;
1259 - if ( $this->out->getRevisionId() ) { // @TODO: avoid same query in Skin.php
1260 - $ts = ( $this->out->getRevisionId() == $this->article->getLatest() )
1261 - ? $this->article->getTimestamp() // skip query
1262 - : Revision::getTimestampFromId( $title, $this->out->getRevisionId() );
1263 - }
1264 - // Are we looking at a pending revision?
1265 - if ( $ts > $srev->getRevTimestamp() ) { // bug 15515
1266 - $tabs['draft']['class'] .= ' selected';
1267 - // Are there *just* pending template/file changes.
1268 - } elseif ( $this->article->onlyTemplatesOrFilesPending()
1269 - && $this->out->getRevisionId() == $this->article->getStable() )
1270 - {
1271 - $tabs['draft']['class'] .= ' selected';
1272 - // Otherwise, fallback to regular tab behavior
1273 - } else {
1274 - $tabs['read']['class'] = 'selected';
1275 - }
1276 - }
1277 - $newViews = array();
1278 - // Rebuild tabs array. Deals with Monobook vs Vector differences.
1279 - if ( $type == 'nav' ) { // Vector et al
1280 - foreach ( $views as $tabAction => $data ) {
1281 - // The 'view' tab. Make it go to the stable version...
1282 - if ( $tabAction == 'view' ) {
1283 - // 'view' for content page; make it go to the stable version
1284 - $newViews[$tabAction]['text'] = $data['text']; // keep tab name
1285 - $newViews[$tabAction]['href'] = $tabs['read']['href'];
1286 - $newViews[$tabAction]['class'] = $tabs['read']['class'];
1287 - // All other tabs...
1288 - } else {
1289 - // Add 'draft' tab to content page to the left of 'edit'...
1290 - if ( $tabAction == 'edit' || $tabAction == 'viewsource' ) {
1291 - $newViews['current'] = $tabs['draft'];
1292 - }
1293 - $newViews[$tabAction] = $data;
1294 - }
1295 - }
1296 - } elseif ( $type == 'flat' ) { // MonoBook et al
1297 - $first = true;
1298 - foreach ( $views as $tabAction => $data ) {
1299 - // The first tab ('page'). Make it go to the stable version...
1300 - if ( $first ) {
1301 - $first = false;
1302 - $newViews[$tabAction]['text'] = $data['text']; // keep tab name
1303 - $newViews[$tabAction]['href'] = $tabs['read']['href'];
1304 - $newViews[$tabAction]['class'] = $data['class']; // keep tab class
1305 - // All other tabs...
1306 - } else {
1307 - // Add 'draft' tab to content page to the left of 'edit'...
1308 - if ( $tabAction == 'edit' || $tabAction == 'viewsource' ) {
1309 - $newViews['current'] = $tabs['draft'];
1310 - }
1311 - $newViews[$tabAction] = $data;
1312 - }
1313 - }
1314 - }
1315 - // Replaces old tabs with new tabs
1316 - $views = $newViews;
1317 - }
1318 -
1319 - /**
1320 - * Check if a flaggedrevs relevant write op was done this page view
1321 - * @return bool
1322 - */
1323 - protected function pageWriteOpRequested() {
1324 - $request = $this->getRequest();
1325 - # Hack for bug 16734 (some actions update and view all at once)
1326 - $action = $request->getVal( 'action' );
1327 - if ( $action === 'rollback' ) {
1328 - return true;
1329 - } elseif ( $action === 'delete' && $request->wasPosted() ) {
1330 - return true;
1331 - }
1332 - return false;
1333 - }
1334 -
1335 - protected function getOldIDFromRequest() {
1336 - $article = new Article( $this->article->getTitle() );
1337 - return $article->getOldIDFromRequest();
1338 - }
1339 -
1340 - /**
1341 - * Adds a notice saying that this revision is pending review
1342 - * @param FlaggedRevision $srev The stable version
1343 - * @param string $diffToggle either "" or " <diff toggle><diff div>"
1344 - * @return void
1345 - */
1346 - public function setPendingNotice( FlaggedRevision $srev, $diffToggle = '' ) {
1347 - $this->load();
1348 - $time = $this->getLang()->date( $srev->getTimestamp(), true );
1349 - $revsSince = $this->article->getPendingRevCount();
1350 - $msg = $srev->getQuality()
1351 - ? 'revreview-newest-quality'
1352 - : 'revreview-newest-basic';
1353 - $msg .= ( $revsSince == 0 ) ? '-i' : '';
1354 - # Add bar msg to the top of the page...
1355 - $css = 'flaggedrevs_preview plainlinks';
1356 - $msgHTML = wfMsgExt( $msg, 'parseinline', $srev->getRevId(), $time, $revsSince );
1357 - $this->reviewNotice .= "<div id='mw-fr-reviewnotice' class='$css'>" .
1358 - "$msgHTML$diffToggle</div>";
1359 - }
1360 -
1361 - /**
1362 - * When viewing a diff:
1363 - * (a) Add the review form to the top of the page
1364 - * (b) Mark off which versions are checked or not
1365 - * (c) When comparing the stable revision to the current:
1366 - * (i) Show a tag with some explanation for the diff
1367 - * (ii) List any template/file changes pending review
1368 - */
1369 - public function addToDiffView( $diff, $oldRev, $newRev ) {
1370 - global $wgMemc, $wgParserCacheExpireTime;
1371 - $request = $this->getRequest();
1372 - $reqUser = $this->getUser();
1373 - $this->load();
1374 - # Exempt printer-friendly output
1375 - if ( $this->out->isPrintable() ) {
1376 - return true;
1377 - # Multi-page diffs are useless and misbehave (bug 19327). Sanity check $newRev.
1378 - } elseif ( $this->isMultiPageDiff || !$newRev ) {
1379 - return true;
1380 - # Page must be reviewable.
1381 - } elseif ( !$this->article->isReviewable() ) {
1382 - return true;
1383 - }
1384 - $srev = $this->article->getStableRev();
1385 - # Check if this is a diff-to-stable. If so:
1386 - # (a) prompt reviewers to review the changes
1387 - # (b) list template/file changes if only includes are pending
1388 - if ( $srev
1389 - && $this->isDiffFromStable
1390 - && !$this->article->stableVersionIsSynced() ) // pending changes
1391 - {
1392 - $changeText = '';
1393 - $this->reviewFormRev = $newRev;
1394 - $changeList = array();
1395 - # Page not synced only due to includes?
1396 - if ( !$this->article->revsArePending() ) {
1397 - # Add a list of links to each changed template...
1398 - $changeList = self::fetchTemplateChanges( $srev );
1399 - # Add a list of links to each changed file...
1400 - $changeList = array_merge( $changeList, self::fetchFileChanges( $srev ) );
1401 - # Correct bad cache which said they were not synced...
1402 - if ( !count( $changeList ) ) {
1403 - $key = wfMemcKey( 'flaggedrevs', 'includesSynced', $this->article->getId() );
1404 - $data = FlaggedRevs::makeMemcObj( "true" );
1405 - $wgMemc->set( $key, $data, $wgParserCacheExpireTime );
1406 - }
1407 - # Otherwise, check for includes pending on top of edits pending...
1408 - } else {
1409 - $incs = FRInclusionCache::getRevIncludes( $this->article, $newRev, $reqUser );
1410 - $this->oldRevIncludes = $incs; // process cache
1411 - # Add a list of links to each changed template...
1412 - $changeList = self::fetchTemplateChanges( $srev, $incs[0] );
1413 - # Add a list of links to each changed file...
1414 - $changeList = array_merge( $changeList, self::fetchFileChanges( $srev, $incs[1] ) );
1415 - }
1416 - # If there are pending revs or templates/files changes, notify the user...
1417 - if ( $this->article->revsArePending() || count( $changeList ) ) {
1418 - # If the user can review then prompt them to review them...
1419 - if ( $reqUser->isAllowed( 'review' ) ) {
1420 - // Reviewer just edited...
1421 - if ( $request->getInt( 'shownotice' )
1422 - && $newRev->isCurrent()
1423 - && $newRev->getRawUserText() == $reqUser->getName() )
1424 - {
1425 - $title = $this->article->getTitle(); // convenience
1426 - // @TODO: make diff class cache this
1427 - $n = $title->countRevisionsBetween( $oldRev, $newRev );
1428 - if ( $n ) {
1429 - $msg = 'revreview-update-edited-prev'; // previous pending edits
1430 - } else {
1431 - $msg = 'revreview-update-edited'; // just couldn't autoreview
1432 - }
1433 - // All other cases...
1434 - } else {
1435 - $msg = 'revreview-update'; // generic "please review" notice...
1436 - }
1437 - $this->diffNoticeBox = wfMsgExt( $msg, 'parse' ); // add as part of form
1438 - }
1439 - # Add include change list...
1440 - if ( count( $changeList ) ) { // just inclusion changes
1441 - $changeText .= "<p>" .
1442 - wfMsgExt( 'revreview-update-includes', 'parseinline' ) .
1443 - '&#160;' . implode( ', ', $changeList ) . "</p>\n";
1444 - }
1445 - }
1446 - # template/file change list
1447 - if ( $changeText != '' ) {
1448 - if ( $reqUser->isAllowed( 'review' ) ) {
1449 - $this->diffIncChangeBox = "<p>$changeText</p>";
1450 - } else {
1451 - $css = 'flaggedrevs_diffnotice plainlinks';
1452 - $this->out->addHTML(
1453 - "<div id='mw-fr-difftostable' class='$css'>$changeText</div>\n"
1454 - );
1455 - }
1456 - }
1457 - }
1458 - # Add a link to diff from stable to current as needed.
1459 - # Show review status of the diff revision(s). Uses a <table>.
1460 - $this->out->addHTML(
1461 - '<div id="mw-fr-diff-headeritems">' .
1462 - self::diffLinkAndMarkers( $this->article, $oldRev, $newRev ) .
1463 - '</div>'
1464 - );
1465 - return true;
1466 - }
1467 -
1468 - // get new diff header items for in-place AJAX page review
1469 - public static function AjaxBuildDiffHeaderItems() {
1470 - $args = func_get_args(); // <oldid, newid>
1471 - if ( count( $args ) >= 2 ) {
1472 - $oldid = (int)$args[0];
1473 - $newid = (int)$args[1];
1474 - $oldRev = Revision::newFromId( $oldid );
1475 - $newRev = Revision::newFromId( $newid );
1476 - if ( $newRev && $newRev->getTitle() ) {
1477 - $fa = FlaggedPage::getTitleInstance( $newRev->getTitle() );
1478 - return self::diffLinkAndMarkers( $fa, $oldRev, $newRev );
1479 - }
1480 - }
1481 - return '';
1482 - }
1483 -
1484 - /**
1485 - * (a) Add a link to diff from stable to current as needed
1486 - * (b) Show review status of the diff revision(s). Uses a <table>.
1487 - * Note: used by ajax function to rebuild diff page
1488 - */
1489 - public static function diffLinkAndMarkers( FlaggedPage $article, $oldRev, $newRev ) {
1490 - $s = '<form id="mw-fr-diff-dataform">';
1491 - $s .= Html::hidden( 'oldid', $oldRev ? $oldRev->getId() : 0 );
1492 - $s .= Html::hidden( 'newid', $newRev ? $newRev->getId() : 0 );
1493 - $s .= "</form>\n";
1494 - if ( $newRev ) { // sanity check
1495 - $s .= self::diffToStableLink( $article, $oldRev, $newRev );
1496 - $s .= self::diffReviewMarkers( $article, $oldRev, $newRev );
1497 - }
1498 - return $s;
1499 - }
1500 -
1501 - /**
1502 - * Add a link to diff-to-stable for reviewable pages
1503 - */
1504 - protected static function diffToStableLink(
1505 - FlaggedPage $article, $oldRev, Revision $newRev
1506 - ) {
1507 - $srev = $article->getStableRev();
1508 - if ( !$srev ) {
1509 - return ''; // nothing to do
1510 - }
1511 - $review = '';
1512 - # Is this already the full diff-to-stable?
1513 - $fullStableDiff = $newRev->isCurrent()
1514 - && self::isDiffToStable( $srev, $oldRev, $newRev );
1515 - # Make a link to the full diff-to-stable if:
1516 - # (a) Actual revs are pending and (b) We are not viewing the full diff-to-stable
1517 - if ( $article->revsArePending() && !$fullStableDiff ) {
1518 - $review = Linker::linkKnown(
1519 - $article->getTitle(),
1520 - wfMsgHtml( 'review-diff2stable' ),
1521 - array(),
1522 - array( 'oldid' => $srev->getRevId(), 'diff' => 'cur' ) + FlaggedRevs::diffOnlyCGI()
1523 - );
1524 - $review = wfMsgHtml( 'parentheses', $review );
1525 - $review = "<div class='fr-diff-to-stable' align='center'>$review</div>";
1526 - }
1527 - return $review;
1528 - }
1529 -
1530 - /**
1531 - * Add [checked version] and such to left and right side of diff
1532 - */
1533 - protected static function diffReviewMarkers( FlaggedPage $article, $oldRev, $newRev ) {
1534 - $table = '';
1535 - $srev = $article->getStableRev();
1536 - # Diff between two revisions
1537 - if ( $oldRev && $newRev ) {
1538 - list( $msg, $class ) = self::getDiffRevMsgAndClass( $oldRev, $srev );
1539 - $table .= "<table class='fr-diff-ratings'><tr>";
1540 - $table .= "<td width='50%' align='center'>";
1541 - $table .= "<span class='$class'>[" .
1542 - wfMsgHtml( $msg ) . "]</span>";
1543 -
1544 - list( $msg, $class ) = self::getDiffRevMsgAndClass( $newRev, $srev );
1545 - $table .= "</td><td width='50%' align='center'>";
1546 - $table .= "<span class='$class'>[" .
1547 - wfMsgHtml( $msg ) . "]</span>";
1548 -
1549 - $table .= "</td></tr></table>\n";
1550 - # New page "diffs" - just one rev
1551 - } elseif ( $newRev ) {
1552 - list( $msg, $class ) = self::getDiffRevMsgAndClass( $newRev, $srev );
1553 - $table .= "<table class='fr-diff-ratings'>";
1554 - $table .= "<tr><td align='center'><span class='$class'>";
1555 - $table .= '[' . wfMsgHtml( $msg ) . ']';
1556 - $table .= "</span></td></tr></table>\n";
1557 - }
1558 - return $table;
1559 - }
1560 -
1561 - protected static function getDiffRevMsgAndClass(
1562 - Revision $rev, FlaggedRevision $srev = null
1563 - ) {
1564 - $tier = FlaggedRevision::getRevQuality( $rev->getPage(), $rev->getId() );
1565 - if ( $tier !== false ) {
1566 - $msg = $tier
1567 - ? 'revreview-hist-quality'
1568 - : 'revreview-hist-basic';
1569 - } else {
1570 - $msg = ( $srev && $rev->getTimestamp() > $srev->getRevTimestamp() ) // bug 15515
1571 - ? 'revreview-hist-pending'
1572 - : 'revreview-hist-draft';
1573 - }
1574 - $css = FlaggedRevsXML::getQualityColor( $tier );
1575 - return array( $msg, $css );
1576 - }
1577 -
1578 - // Fetch template changes for a reviewed revision since review
1579 - // @return array
1580 - protected static function fetchTemplateChanges( FlaggedRevision $frev, $newTemplates = null ) {
1581 - $diffLinks = array();
1582 - if ( $newTemplates === null ) {
1583 - $changes = $frev->findPendingTemplateChanges();
1584 - } else {
1585 - $changes = $frev->findTemplateChanges( $newTemplates );
1586 - }
1587 - foreach ( $changes as $tuple ) {
1588 - list( $title, $revIdStable, $hasStable ) = $tuple;
1589 - $link = Linker::linkKnown(
1590 - $title,
1591 - htmlspecialchars( $title->getPrefixedText() ),
1592 - array(),
1593 - array( 'diff' => 'cur', 'oldid' => $revIdStable ) );
1594 - if ( !$hasStable ) {
1595 - $link = "<strong>$link</strong>";
1596 - }
1597 - $diffLinks[] = $link;
1598 - }
1599 - return $diffLinks;
1600 - }
1601 -
1602 - // Fetch file changes for a reviewed revision since review
1603 - // @return array
1604 - protected static function fetchFileChanges( FlaggedRevision $frev, $newFiles = null ) {
1605 - $diffLinks = array();
1606 - if ( $newFiles === null ) {
1607 - $changes = $frev->findPendingFileChanges( 'noForeign' );
1608 - } else {
1609 - $changes = $frev->findFileChanges( $newFiles, 'noForeign' );
1610 - }
1611 - foreach ( $changes as $tuple ) {
1612 - list( $title, $revIdStable, $hasStable ) = $tuple;
1613 - // @TODO: change when MW has file diffs
1614 - $link = Linker::link( $title, htmlspecialchars( $title->getPrefixedText() ) );
1615 - if ( !$hasStable ) {
1616 - $link = "<strong>$link</strong>";
1617 - }
1618 - $diffLinks[] = $link;
1619 - }
1620 - return $diffLinks;
1621 - }
1622 -
1623 - /**
1624 - * Set $this->isDiffFromStable and $this->isMultiPageDiff fields
1625 - * Note: $oldRev could be false
1626 - */
1627 - public function setViewFlags( $diff, $oldRev, $newRev ) {
1628 - $this->load();
1629 - // We only want valid diffs that actually make sense...
1630 - if ( $newRev && $oldRev && $newRev->getTimestamp() >= $oldRev->getTimestamp() ) {
1631 - // Is this a diff between two pages?
1632 - if ( $newRev->getPage() != $oldRev->getPage() ) {
1633 - $this->isMultiPageDiff = true;
1634 - // Is there a stable version?
1635 - } elseif ( $this->article->isReviewable() ) {
1636 - $srev = $this->article->getStableRev();
1637 - // Is this a diff of a draft rev against the stable rev?
1638 - if ( self::isDiffToStable( $srev, $oldRev, $newRev ) ) {
1639 - $this->isDiffFromStable = true;
1640 - $this->isReviewableDiff = true;
1641 - // Is this a diff of a draft rev against a reviewed rev?
1642 - } elseif (
1643 - FlaggedRevision::newFromTitle( $diff->getTitle(), $oldRev->getId() ) ||
1644 - FlaggedRevision::newFromTitle( $diff->getTitle(), $newRev->getId() )
1645 - ) {
1646 - $this->isReviewableDiff = true;
1647 - }
1648 - }
1649 - $this->diffRevs = array( 'old' => $oldRev, 'new' => $newRev );
1650 - }
1651 - return true;
1652 - }
1653 -
1654 - // Is a diff from $oldRev to $newRev a diff-to-stable?
1655 - protected static function isDiffToStable( $srev, $oldRev, $newRev ) {
1656 - return ( $srev && $oldRev && $newRev
1657 - && $oldRev->getPage() == $newRev->getPage() // no multipage diffs
1658 - && $oldRev->getId() == $srev->getRevId()
1659 - && $newRev->getTimestamp() >= $oldRev->getTimestamp() // no backwards diffs
1660 - );
1661 - }
1662 -
1663 - /**
1664 - * Redirect users out to review the changes to the stable version.
1665 - * Only for people who can review and for pages that have a stable version.
1666 - */
1667 - public function injectPostEditURLParams( &$sectionAnchor, &$extraQuery ) {
1668 - $reqUser = $this->getUser();
1669 - $this->load();
1670 - $this->article->loadPageData( 'fromdbmaster' );
1671 - # Get the stable version from the master
1672 - $frev = $this->article->getStableRev();
1673 - if ( !$frev || !$this->article->revsArePending() ) {
1674 - return true; // only for pages with pending edits
1675 - }
1676 - $params = array();
1677 - // If the edit was not autoreviewed, and the user can actually make a
1678 - // new stable version, then go to the diff...
1679 - if ( $frev->userCanSetFlags( $reqUser ) ) {
1680 - $params += array( 'oldid' => $frev->getRevId(), 'diff' => 'cur', 'shownotice' => 1 );
1681 - $params += FlaggedRevs::diffOnlyCGI();
1682 - // ...otherwise, go to the draft revision after completing an edit.
1683 - // This allows for users to immediately see their changes.
1684 - } else {
1685 - $params += array( 'stable' => 0 );
1686 - // Show a notice at the top of the page for non-reviewers...
1687 - if ( !$reqUser->isAllowed( 'review' ) && $this->article->isStableShownByDefault() ) {
1688 - $params += array( 'shownotice' => 1 );
1689 - if ( $sectionAnchor ) {
1690 - // Pass a section parameter in the URL as needed to add a link to
1691 - // the "your changes are pending" box on the top of the page...
1692 - $section = str_replace(
1693 - array( ':' , '.' ), array( '%3A', '%' ), // hack: reverse encoding
1694 - substr( $sectionAnchor, 1 ) // remove the '#'
1695 - );
1696 - $params += array( 'fromsection' => $section );
1697 - $sectionAnchor = ''; // go to the top of the page to see notice
1698 - }
1699 - }
1700 - }
1701 - if ( $extraQuery !== '' ) {
1702 - $extraQuery .= '&';
1703 - }
1704 - $extraQuery .= wfArrayToCGI( $params ); // note: EditPage will add initial "&"
1705 - return true;
1706 - }
1707 -
1708 - /**
1709 - * If submitting the edit will leave it pending, then change the button text
1710 - * Note: interacts with 'review pending changes' checkbox
1711 - * @TODO: would be nice if hook passed in button attribs, not XML
1712 - */
1713 - public function changeSaveButton( EditPage $editPage, array &$buttons ) {
1714 - if ( !$this->editWillRequireReview( $editPage ) ) {
1715 - return true; // edit will go live or be reviewed on save
1716 - }
1717 - if ( extension_loaded( 'domxml' ) ) {
1718 - wfDebug( "Warning: you have the obsolete domxml extension for PHP. Please remove it!\n" );
1719 - return true; # PECL extension conflicts with the core DOM extension (see bug 13770)
1720 - } elseif ( isset( $buttons['save'] ) && extension_loaded( 'dom' ) ) {
1721 - $dom = new DOMDocument();
1722 - $dom->loadXML( $buttons['save'] ); // load button XML from hook
1723 - foreach ( $dom->getElementsByTagName( 'input' ) as $input ) { // one <input>
1724 - $input->setAttribute( 'value', wfMsg( 'revreview-submitedit' ) );
1725 - $input->setAttribute( 'title', // keep accesskey
1726 - wfMsgExt( 'revreview-submitedit-title', 'parsemag' ) .
1727 - ' [' . wfMsg( 'accesskey-save' ) . ']' );
1728 - # Change submit button text & title
1729 - $buttons['save'] = $dom->saveXML( $dom->documentElement );
1730 - }
1731 - }
1732 - return true;
1733 - }
1734 -
1735 - /**
1736 - * If this edit will not go live on submit (accounting for wpReviewEdit)
1737 - * @param EditPage $editPage
1738 - * @return bool
1739 - */
1740 - protected function editWillRequireReview( EditPage $editPage ) {
1741 - $request = $this->getRequest();
1742 - $title = $this->article->getTitle(); // convenience
1743 - if ( !$this->editRequiresReview( $editPage ) ) {
1744 - return false; // edit will go live immediatly
1745 - } elseif ( $request->getCheck( 'wpReviewEdit' ) && $title->userCan( 'review' ) ) {
1746 - return false; // edit checked off to be reviewed on save
1747 - }
1748 - return true; // edit needs review
1749 - }
1750 -
1751 - /**
1752 - * If this edit will not go live on submit unless wpReviewEdit is checked
1753 - * @param EditPage $editPage
1754 - * @return bool
1755 - */
1756 - protected function editRequiresReview( EditPage $editPage ) {
1757 - if ( !$this->article->editsRequireReview() ) {
1758 - return false; // edits go live immediatly
1759 - } elseif ( $this->editWillBeAutoreviewed( $editPage ) ) {
1760 - return false; // edit will be autoreviewed anyway
1761 - }
1762 - return true; // edit needs review
1763 - }
1764 -
1765 - /**
1766 - * If this edit will be auto-reviewed on submit
1767 - * Note: checking wpReviewEdit does not count as auto-reviewed
1768 - * @param EditPage $editPage
1769 - * @return bool
1770 - */
1771 - protected function editWillBeAutoreviewed( EditPage $editPage ) {
1772 - $title = $this->article->getTitle(); // convenience
1773 - if ( !$this->article->isReviewable() ) {
1774 - return false;
1775 - }
1776 - if ( $title->userCan( 'autoreview' ) ) {
1777 - if ( FlaggedRevs::autoReviewNewPages() && !$this->article->exists() ) {
1778 - return true; // edit will be autoreviewed
1779 - }
1780 - if ( !isset( $editPage->fr_baseFRev ) ) {
1781 - $baseRevId = self::getBaseRevId( $editPage );
1782 - $editPage->fr_baseFRev = FlaggedRevision::newFromTitle( $title, $baseRevId );
1783 - }
1784 - if ( $editPage->fr_baseFRev ) {
1785 - return true; // edit will be autoreviewed
1786 - }
1787 - }
1788 - return false; // edit won't be autoreviewed
1789 - }
1790 -
1791 - /**
1792 - * Add a "review pending changes" checkbox to the edit form iff:
1793 - * (a) there are currently any revisions pending (bug 16713)
1794 - * (b) this is an unreviewed page (bug 23970)
1795 - */
1796 - public function addReviewCheck( EditPage $editPage, array &$checkboxes, &$tabindex ) {
1797 - $request = $this->getRequest();
1798 - $title = $this->article->getTitle(); // convenience
1799 - if ( !$this->article->isReviewable() || !$title->userCan( 'review' ) ) {
1800 - return true; // not needed
1801 - } elseif ( $this->editWillBeAutoreviewed( $editPage ) ) {
1802 - return true; // edit will be auto-reviewed
1803 - }
1804 - if ( self::getBaseRevId( $editPage ) == $this->article->getLatest() ) {
1805 - # For pages with either no stable version, or an outdated one, let
1806 - # the user decide if he/she wants it reviewed on the spot. One might
1807 - # do this if he/she just saw the diff-to-stable and *then* decided to edit.
1808 - # Note: check not shown when editing old revisions, which is confusing.
1809 - $checkbox = Xml::check(
1810 - 'wpReviewEdit',
1811 - $request->getCheck( 'wpReviewEdit' ),
1812 - array( 'tabindex' => ++$tabindex, 'id' => 'wpReviewEdit' )
1813 - );
1814 - $attribs = array( 'for' => 'wpReviewEdit' );
1815 - // For reviewed pages...
1816 - if ( $this->article->getStable() ) {
1817 - // For pending changes...
1818 - if ( $this->article->revsArePending() ) {
1819 - $n = $this->article->getPendingRevCount();
1820 - $attribs['title'] = wfMsg( 'revreview-check-flag-p-title' );
1821 - $labelMsg = wfMsgExt( 'revreview-check-flag-p', 'parseinline', $n );
1822 - // For just the user's changes...
1823 - } else {
1824 - $attribs['title'] = wfMsgExt( 'revreview-check-flag-y-title', 'parsemag' );
1825 - $labelMsg = wfMsgExt( 'revreview-check-flag-y', 'parseinline' );
1826 - }
1827 - // For unreviewed pages...
1828 - } else {
1829 - $attribs['title'] = wfMsg( 'revreview-check-flag-u-title' );
1830 - $labelMsg = wfMsgExt( 'revreview-check-flag-u', 'parseinline' );
1831 - }
1832 - $label = Xml::element( 'label', $attribs, $labelMsg );
1833 - $checkboxes['reviewed'] = $checkbox . '&#160;' . $label;
1834 - }
1835 - return true;
1836 - }
1837 -
1838 - /**
1839 - * (a) Add a hidden field that has the rev ID the text is based off.
1840 - * (b) If an edit was undone, add a hidden field that has the rev ID of that edit.
1841 - * Needed for autoreview and user stats (for autopromote).
1842 - * Note: baseRevId trusted for Reviewers - text checked for others.
1843 - */
1844 - public function addRevisionIDField( EditPage $editPage, OutputPage $out ) {
1845 - $this->load();
1846 - $revId = self::getBaseRevId( $editPage );
1847 - $out->addHTML( "\n" . Html::hidden( 'baseRevId', $revId ) );
1848 - $out->addHTML( "\n" . Html::hidden( 'undidRev',
1849 - empty( $editPage->undidRev ) ? 0 : $editPage->undidRev )
1850 - );
1851 - return true;
1852 - }
1853 -
1854 - /**
1855 - * Guess the rev ID the text of this form is based off
1856 - * Note: baseRevId trusted for Reviewers - check text for others.
1857 - * @return int
1858 - */
1859 - protected static function getBaseRevId( EditPage $editPage ) {
1860 - $request = FlaggedPageView::singleton()->getRequest();
1861 - if ( !isset( $editPage->fr_baseRevId ) ) {
1862 - $article = $editPage->getArticle(); // convenience
1863 - $latestId = $article->getLatest(); // current rev
1864 - $undo = $request->getIntOrNull( 'undo' );
1865 - # Undoing consecutive top edits...
1866 - if ( $undo && $undo === $latestId ) {
1867 - # Treat this like a revert to a base revision.
1868 - # We are undoing all edits *after* some rev ID (undoafter).
1869 - # If undoafter is not given, then it is the previous rev ID.
1870 - $revId = $request->getInt( 'undoafter',
1871 - $article->getTitle()->getPreviousRevisionID( $latestId, Title::GAID_FOR_UPDATE ) );
1872 - # Undoing other edits...
1873 - } elseif ( $undo ) {
1874 - $revId = $latestId; // current rev is the base rev
1875 - # Other edits...
1876 - } else {
1877 - # If we are editing via oldid=X, then use that rev ID.
1878 - # Otherwise, check if the client specified the ID (bug 23098).
1879 - $revId = $article->getOldID()
1880 - ? $article->getOldID()
1881 - : $request->getInt( 'baseRevId' ); // e.g. "show changes"/"preview"
1882 - }
1883 - # Zero oldid => draft revision
1884 - if ( !$revId ) {
1885 - $revId = $latestId;
1886 - }
1887 - $editPage->fr_baseRevId = $revId;
1888 - }
1889 - return $editPage->fr_baseRevId;
1890 - }
1891 -}
Index: trunk/extensions/FlaggedRevs/presentation/FlaggedRevsUI.hooks.php
@@ -129,7 +129,7 @@
130130 return true; // don't double-load
131131 }
132132 $loadedModules = true;
133 - $fa = FlaggedPageView::globalArticleInstance();
 133+ $fa = FlaggablePageView::globalArticleInstance();
134134 # Try to only add to relevant pages
135135 if ( !$fa || !$fa->isReviewable() ) {
136136 return true;
@@ -149,7 +149,7 @@
150150 $rTags = FlaggedRevs::getJSTagParams();
151151 $globalVars['wgFlaggedRevsParams'] = $rTags;
152152 # Get page-specific meta-data
153 - $fa = FlaggedPageView::globalArticleInstance();
 153+ $fa = FlaggablePageView::globalArticleInstance();
154154 # Try to only add to relevant pages
155155 if ( $fa && $fa->isReviewable() ) {
156156 $frev = $fa->getStableRev();
@@ -183,7 +183,7 @@
184184 */
185185 public static function onBeforePageDisplay( &$out, &$skin ) {
186186 if ( $out->getTitle()->getNamespace() != NS_SPECIAL ) {
187 - $view = FlaggedPageView::singleton();
 187+ $view = FlaggablePageView::singleton();
188188 $view->addStabilizationLink(); // link on protect form
189189 $view->displayTag(); // show notice bar/icon in subtitle
190190 if ( $out->isArticleRelated() ) {
@@ -273,7 +273,7 @@
274274 }
275275
276276 public static function onImagePageFindFile( $imagePage, &$normalFile, &$displayFile ) {
277 - $view = FlaggedPageView::singleton();
 277+ $view = FlaggablePageView::singleton();
278278 $view->imagePageFindFile( $normalFile, $displayFile );
279279 return true;
280280 }
@@ -285,8 +285,8 @@
286286 // *sigh*...skip, dealt with in setNavigation()
287287 return true;
288288 }
289 - if ( FlaggedPageView::globalArticleInstance() != null ) {
290 - $view = FlaggedPageView::singleton();
 289+ if ( FlaggablePageView::globalArticleInstance() != null ) {
 290+ $view = FlaggablePageView::singleton();
291291 $view->setActionTabs( $skin, $contentActions );
292292 $view->setViewTabs( $skin, $contentActions, 'flat' );
293293 }
@@ -295,8 +295,8 @@
296296
297297 // Vector et al: $links is all the tabs (2 levels)
298298 public static function onSkinTemplateNavigation( Skin $skin, array &$links ) {
299 - if ( FlaggedPageView::globalArticleInstance() != null ) {
300 - $view = FlaggedPageView::singleton();
 299+ if ( FlaggablePageView::globalArticleInstance() != null ) {
 300+ $view = FlaggablePageView::singleton();
301301 $view->setActionTabs( $skin, $links['actions'] );
302302 $view->setViewTabs( $skin, $links['views'], 'nav' );
303303 }
@@ -304,7 +304,7 @@
305305 }
306306
307307 public static function onArticleViewHeader( &$article, &$outputDone, &$useParserCache ) {
308 - $view = FlaggedPageView::singleton();
 308+ $view = FlaggablePageView::singleton();
309309 $view->addStableLink( $outputDone, $useParserCache );
310310 $view->setPageContent( $outputDone, $useParserCache );
311311 return true;
@@ -314,7 +314,7 @@
315315 Title $title, WebRequest $request, &$ignoreRedirect, &$target, Article &$article
316316 ) {
317317 global $wgMemc, $wgParserCacheExpireTime;
318 - $fa = FlaggedPage::getTitleInstance( $title ); // on $wgTitle
 318+ $fa = FlaggableWikiPage::getTitleInstance( $title ); // on $wgTitle
319319 if ( !$fa->isReviewable() ) {
320320 return true; // nothing to do
321321 }
@@ -324,7 +324,7 @@
325325 return true;
326326 }
327327 $srev = $fa->getStableRev();
328 - $view = FlaggedPageView::singleton();
 328+ $view = FlaggablePageView::singleton();
329329 # Check if we are viewing an unsynced stable version...
330330 if ( $srev && $view->showingStable() && $srev->getRevId() != $article->getLatest() ) {
331331 # Check the stable redirect properties from the cache...
@@ -357,31 +357,31 @@
358358 }
359359
360360 public static function addToEditView( &$editPage ) {
361 - $view = FlaggedPageView::singleton();
 361+ $view = FlaggablePageView::singleton();
362362 $view->addToEditView( $editPage );
363363 return true;
364364 }
365365
366366 public static function onBeforeEditButtons( &$editPage, &$buttons ) {
367 - $view = FlaggedPageView::singleton();
 367+ $view = FlaggablePageView::singleton();
368368 $view->changeSaveButton( $editPage, $buttons );
369369 return true;
370370 }
371371
372372 public static function onNoSuchSection( &$editPage, &$s ) {
373 - $view = FlaggedPageView::singleton();
 373+ $view = FlaggablePageView::singleton();
374374 $view->addToNoSuchSection( $editPage, $s );
375375 return true;
376376 }
377377
378378 public static function addToHistView( &$article ) {
379 - $view = FlaggedPageView::singleton();
 379+ $view = FlaggablePageView::singleton();
380380 $view->addToHistView();
381381 return true;
382382 }
383383
384384 public static function onCategoryPageView( &$category ) {
385 - $view = FlaggedPageView::singleton();
 385+ $view = FlaggablePageView::singleton();
386386 $view->addToCategoryView();
387387 return true;
388388 }
@@ -389,9 +389,9 @@
390390 public static function onSkinAfterContent( &$data ) {
391391 global $wgOut;
392392 if ( $wgOut->isArticleRelated()
393 - && FlaggedPageView::globalArticleInstance() != null )
 393+ && FlaggablePageView::globalArticleInstance() != null )
394394 {
395 - $view = FlaggedPageView::singleton();
 395+ $view = FlaggablePageView::singleton();
396396 // Only use this hook if we want to append the form.
397397 // We *prepend* the form for diffs, so skip that case here.
398398 if ( !$view->diffRevsAreSet() ) {
@@ -410,7 +410,7 @@
411411 }
412412
413413 public static function addToHistQuery( HistoryPager $pager, array &$queryInfo ) {
414 - $flaggedArticle = FlaggedPage::getTitleInstance( $pager->getTitle() );
 414+ $flaggedArticle = FlaggableWikiPage::getTitleInstance( $pager->getTitle() );
415415 # Non-content pages cannot be validated. Stable version must exist.
416416 if ( $flaggedArticle->isReviewable() && $flaggedArticle->getStableRev() ) {
417417 # Highlight flaggedrevs
@@ -436,7 +436,7 @@
437437 if ( !$file->isLocal() ) {
438438 return true; // local files only
439439 }
440 - $flaggedArticle = FlaggedPage::getTitleInstance( $file->getTitle() );
 440+ $flaggedArticle = FlaggableWikiPage::getTitleInstance( $file->getTitle() );
441441 # Non-content pages cannot be validated. Stable version must exist.
442442 if ( $flaggedArticle->isReviewable() && $flaggedArticle->getStableRev() ) {
443443 $tables[] = 'flaggedrevs';
@@ -501,7 +501,7 @@
502502 }
503503
504504 public static function addToHistLine( HistoryPager $history, $row, &$s, &$liClasses ) {
505 - $fa = FlaggedPage::getTitleInstance( $history->getTitle() );
 505+ $fa = FlaggableWikiPage::getTitleInstance( $history->getTitle() );
506506 if ( !$fa->isReviewable() ) {
507507 return true; // nothing to do here
508508 }
@@ -656,8 +656,8 @@
657657 }
658658
659659 public static function injectPostEditURLParams( $article, &$sectionAnchor, &$extraQuery ) {
660 - if ( FlaggedPageView::globalArticleInstance() != null ) {
661 - $view = FlaggedPageView::singleton();
 660+ if ( FlaggablePageView::globalArticleInstance() != null ) {
 661+ $view = FlaggablePageView::singleton();
662662 $view->injectPostEditURLParams( $sectionAnchor, $extraQuery );
663663 }
664664 return true;
@@ -677,20 +677,20 @@
678678
679679 public static function onDiffViewHeader( $diff, $oldRev, $newRev ) {
680680 self::injectStyleAndJS();
681 - $view = FlaggedPageView::singleton();
 681+ $view = FlaggablePageView::singleton();
682682 $view->setViewFlags( $diff, $oldRev, $newRev );
683683 $view->addToDiffView( $diff, $oldRev, $newRev );
684684 return true;
685685 }
686686
687687 public static function addRevisionIDField( $editPage, $out ) {
688 - $view = FlaggedPageView::singleton();
 688+ $view = FlaggablePageView::singleton();
689689 $view->addRevisionIDField( $editPage, $out );
690690 return true;
691691 }
692692
693693 public static function addReviewCheck( $editPage, &$checkboxes, &$tabindex ) {
694 - $view = FlaggedPageView::singleton();
 694+ $view = FlaggablePageView::singleton();
695695 $view->addReviewCheck( $editPage, $checkboxes, $tabindex );
696696 return true;
697697 }
@@ -743,12 +743,12 @@
744744 array() : array( 'disabled' => 'disabled' );
745745
746746 # Get the current config/expiry
747 - $config = FlaggedPageConfig::getStabilitySettings( $article->getTitle(), FR_MASTER );
 747+ $config = FRPageConfig::getStabilitySettings( $article->getTitle(), FR_MASTER );
748748 $oldExpirySelect = ( $config['expiry'] == 'infinity' ) ? 'infinite' : 'existing';
749749
750750 # Load requested restriction level, default to current level...
751751 $restriction = $wgRequest->getVal( 'mwStabilityLevel',
752 - FlaggedPageConfig::getProtectionLevel( $config ) );
 752+ FRPageConfig::getProtectionLevel( $config ) );
753753 # Load the requested expiry time (dropdown)
754754 $expirySelect = $wgRequest->getVal( 'mwStabilizeExpirySelection', $oldExpirySelect );
755755 # Load the requested expiry time (field)
Index: trunk/extensions/FlaggedRevs/presentation/FlaggedRevsXML.php
@@ -371,7 +371,7 @@
372372
373373 /*
374374 * Creates CSS lock icon if page is locked/unlocked
375 - * @param FlaggedPage $flaggedArticle
 375+ * @param FlaggableWikiPage $flaggedArticle
376376 * @return string
377377 */
378378 public static function lockStatusIcon( $flaggedArticle ) {
@@ -388,7 +388,7 @@
389389 }
390390
391391 /*
392 - * @param FlaggedPage $flaggedArticle
 392+ * @param FlaggableWikiPage $flaggedArticle
393393 * @param FlaggedRevision $frev
394394 * @param int $revsSince
395395 * @return string
Index: trunk/extensions/FlaggedRevs/presentation/specialpages/reports/ConfiguredPages_body.php
@@ -66,7 +66,7 @@
6767 }
6868 # Purge expired entries on one in every 10 queries
6969 if ( !mt_rand( 0, 10 ) ) {
70 - FlaggedPageConfig::purgeExpiredConfigurations();
 70+ FRPageConfig::purgeExpiredConfigurations();
7171 }
7272 }
7373
Index: trunk/extensions/FlaggedRevs/presentation/specialpages/reports/StablePages_body.php
@@ -64,7 +64,7 @@
6565 }
6666 # Purge expired entries on one in every 10 queries
6767 if ( !mt_rand( 0, 10 ) ) {
68 - FlaggedPageConfig::purgeExpiredConfigurations();
 68+ FRPageConfig::purgeExpiredConfigurations();
6969 }
7070 }
7171
Index: trunk/extensions/FlaggedRevs/presentation/FlaggablePageView.php
@@ -0,0 +1,1890 @@
 2+<?php
 3+/**
 4+ * Class representing a web view of a MediaWiki page
 5+ */
 6+class FlaggablePageView extends ContextSource {
 7+ protected $out = null;
 8+ protected $article = null;
 9+
 10+ protected $diffRevs = null; // assoc array of old and new Revisions for diffs
 11+ protected $oldRevIncludes = null; // ( array of templates, array of file)
 12+ protected $isReviewableDiff = false;
 13+ protected $isDiffFromStable = false;
 14+ protected $isMultiPageDiff = false;
 15+ protected $reviewNotice = '';
 16+ protected $diffNoticeBox = '';
 17+ protected $diffIncChangeBox = '';
 18+ protected $reviewFormRev = false;
 19+
 20+ protected $loaded = false;
 21+
 22+ protected static $instance = null;
 23+
 24+ /*
 25+ * Get the FlaggablePageView for this request
 26+ */
 27+ public static function singleton() {
 28+ if ( self::$instance == null ) {
 29+ self::$instance = new self();
 30+ }
 31+ return self::$instance;
 32+ }
 33+ protected function __construct() { }
 34+ protected function __clone() { }
 35+
 36+ /*
 37+ * Clear the FlaggablePageView for this request.
 38+ * Only needed when page redirection changes the environment.
 39+ */
 40+ public function clear() {
 41+ self::$instance = null;
 42+ }
 43+
 44+ /*
 45+ * Load the global FlaggableWikiPage instance
 46+ */
 47+ protected function load() {
 48+ if ( !$this->loaded ) {
 49+ $this->loaded = true;
 50+ $this->article = self::globalArticleInstance();
 51+ if ( $this->article == null ) {
 52+ throw new MWException( 'FlaggablePageView has no context article!' );
 53+ }
 54+ $this->out = $this->getOutput(); // convenience
 55+ }
 56+ }
 57+
 58+ /**
 59+ * Get the FlaggableWikiPage instance associated with $wgTitle,
 60+ * or false if there isn't such a title
 61+ * @return FlaggableWikiPage|null
 62+ */
 63+ public static function globalArticleInstance() {
 64+ $title = RequestContext::getMain()->getTitle();
 65+ if ( $title ) {
 66+ return FlaggableWikiPage::getTitleInstance( $title );
 67+ }
 68+ return null;
 69+ }
 70+
 71+ /*
 72+ * Check if the old and new diff revs are set for this page view
 73+ * @return bool
 74+ */
 75+ public function diffRevsAreSet() {
 76+ return (bool)$this->diffRevs;
 77+ }
 78+
 79+ /**
 80+ * Is this web response for a request to view a page where both:
 81+ * (a) no specific page version was requested via URL params
 82+ * (b) a stable version exists and is to be displayed
 83+ * This factors in site/page config, user preferences, and web request params.
 84+ * @return bool
 85+ */
 86+ protected function showingStableAsDefault() {
 87+ $request = $this->getRequest();
 88+ $reqUser = $this->getUser();
 89+ $this->load();
 90+ # This only applies to viewing the default version of pages...
 91+ if ( !$this->isDefaultPageView( $request ) ) {
 92+ return false;
 93+ # ...and the page must be reviewable and have a stable version
 94+ } elseif ( !$this->article->getStableRev() ) {
 95+ return false;
 96+ }
 97+ # Check user preferences ("show stable by default?")
 98+ $pref = (int)$reqUser->getOption( 'flaggedrevsstable' );
 99+ if ( $pref == FR_SHOW_STABLE_ALWAYS ) {
 100+ return true;
 101+ } elseif ( $pref == FR_SHOW_STABLE_NEVER ) {
 102+ return false;
 103+ }
 104+ # Viewer may be in a group that sees the draft by default
 105+ if ( $this->userViewsDraftByDefault( $reqUser ) ) {
 106+ return false;
 107+ }
 108+ # Does the stable version override the draft?
 109+ $config = $this->article->getStabilitySettings();
 110+ return (bool)$config['override'];
 111+ }
 112+
 113+ /**
 114+ * Is this web response for a request to view a page where both:
 115+ * (a) the stable version of a page was requested (?stable=1)
 116+ * (b) the stable version exists and is to be displayed
 117+ * @return bool
 118+ */
 119+ protected function showingStableByRequest() {
 120+ $request = $this->getRequest();
 121+ $this->load();
 122+ # Are we explicity requesting the stable version?
 123+ if ( $request->getIntOrNull( 'stable' ) === 1 ) {
 124+ # This only applies to viewing a version of the page...
 125+ if ( !$this->isPageView( $request ) ) {
 126+ return false;
 127+ # ...with no version parameters other than ?stable=1...
 128+ } elseif ( $request->getVal( 'oldid' ) || $request->getVal( 'stableid' ) ) {
 129+ return false; // over-determined
 130+ # ...and the page must be reviewable and have a stable version
 131+ } elseif ( !$this->article->getStableRev() ) {
 132+ return false;
 133+ }
 134+ return true; // show stable version
 135+ }
 136+ return false;
 137+ }
 138+
 139+ /**
 140+ * Is this web response for a request to view a page
 141+ * where a stable version exists and is to be displayed
 142+ * @return bool
 143+ */
 144+ public function showingStable() {
 145+ return $this->showingStableByRequest() || $this->showingStableAsDefault();
 146+ }
 147+
 148+ /**
 149+ * Should this be using a simple icon-based UI?
 150+ * Check the user's preferences first, using the site settings as the default.
 151+ * @return bool
 152+ */
 153+ public function useSimpleUI() {
 154+ global $wgSimpleFlaggedRevsUI;
 155+ $reqUser = $this->getUser();
 156+ return $reqUser->getOption( 'flaggedrevssimpleui', intval( $wgSimpleFlaggedRevsUI ) );
 157+ }
 158+
 159+ /**
 160+ * Should this user see the draft revision of pages by default?
 161+ * @param $user User
 162+ * @return bool
 163+ */
 164+ protected function userViewsDraftByDefault( $user ) {
 165+ global $wgFlaggedRevsExceptions;
 166+ # Check user preferences ("show stable by default?")
 167+ if ( $user->getOption( 'flaggedrevsstable' ) ) {
 168+ return false;
 169+ }
 170+ # Viewer sees current by default (editors, insiders, ect...) ?
 171+ foreach ( $wgFlaggedRevsExceptions as $group ) {
 172+ if ( $group == 'user' ) {
 173+ if ( $user->getId() ) {
 174+ return true;
 175+ }
 176+ } elseif ( in_array( $group, $user->getGroups() ) ) {
 177+ return true;
 178+ }
 179+ }
 180+ return false;
 181+ }
 182+
 183+ /**
 184+ * Is this a view page action (including diffs)?
 185+ * @param $request WebRequest
 186+ * @return bool
 187+ */
 188+ protected function isPageViewOrDiff( WebRequest $request ) {
 189+ global $mediaWiki;
 190+ $action = isset( $mediaWiki )
 191+ ? $mediaWiki->getAction( $request )
 192+ : $request->getVal( 'action', 'view' ); // cli
 193+ return self::isViewAction( $action );
 194+ }
 195+
 196+ /**
 197+ * Is this a view page action (not including diffs)?
 198+ * @param $request WebRequest
 199+ * @return bool
 200+ */
 201+ protected function isPageView( WebRequest $request ) {
 202+ return $this->isPageViewOrDiff( $request )
 203+ && $request->getVal( 'diff' ) === null;
 204+ }
 205+
 206+ /**
 207+ * Is this a web request to just *view* the *default* version of a page?
 208+ * @param $request WebRequest
 209+ * @return bool
 210+ */
 211+ protected function isDefaultPageView( WebRequest $request ) {
 212+ global $mediaWiki;
 213+ $action = isset( $mediaWiki )
 214+ ? $mediaWiki->getAction( $request )
 215+ : $request->getVal( 'action', 'view' ); // cli
 216+ return ( self::isViewAction( $action )
 217+ && $request->getVal( 'oldid' ) === null
 218+ && $request->getVal( 'stable' ) === null
 219+ && $request->getVal( 'stableid' ) === null
 220+ && $request->getVal( 'diff' ) === null
 221+ );
 222+ }
 223+
 224+ /**
 225+ * Is this a view page action?
 226+ * @param $action string from MediaWiki::getAction()
 227+ * @return bool
 228+ */
 229+ protected static function isViewAction( $action ) {
 230+ return ( $action == 'view' || $action == 'purge' || $action == 'render' );
 231+ }
 232+
 233+ /**
 234+ * Output review notice
 235+ */
 236+ public function displayTag() {
 237+ $this->load();
 238+ // Sanity check that this is a reviewable page
 239+ if ( $this->article->isReviewable() ) {
 240+ $this->out->appendSubtitle( $this->reviewNotice );
 241+ }
 242+ return true;
 243+ }
 244+
 245+ /**
 246+ * Add a stable link when viewing old versions of an article that
 247+ * have been reviewed. (e.g. for &oldid=x urls)
 248+ */
 249+ public function addStableLink() {
 250+ $request = $this->getRequest();
 251+ $this->load();
 252+ if ( !$this->article->isReviewable() || !$request->getVal( 'oldid' ) ) {
 253+ return true;
 254+ }
 255+ # We may have nav links like "direction=prev&oldid=x"
 256+ $revID = $this->getOldIDFromRequest();
 257+ $frev = FlaggedRevision::newFromTitle( $this->article->getTitle(), $revID );
 258+ # Give a notice if this rev ID corresponds to a reviewed version...
 259+ if ( $frev ) {
 260+ $time = $this->getLang()->date( $frev->getTimestamp(), true );
 261+ $flags = $frev->getTags();
 262+ $quality = FlaggedRevs::isQuality( $flags );
 263+ $msg = $quality ? 'revreview-quality-source' : 'revreview-basic-source';
 264+ $tag = wfMsgExt( $msg, 'parseinline', $frev->getRevId(), $time );
 265+ # Hide clutter
 266+ if ( !$this->useSimpleUI() && !empty( $flags ) ) {
 267+ $tag .= FlaggedRevsXML::ratingToggle() .
 268+ "<div id='mw-fr-revisiondetails' style='display:block;'>" .
 269+ wfMsgHtml( 'revreview-oldrating' ) .
 270+ FlaggedRevsXML::addTagRatings( $flags ) . '</div>';
 271+ }
 272+ $css = 'flaggedrevs_notice plainlinks noprint';
 273+ $tag = "<div id='mw-fr-revisiontag-old' class='$css'>$tag</div>";
 274+ $this->out->addHTML( $tag );
 275+ }
 276+ return true;
 277+ }
 278+
 279+ /**
 280+ * @return mixed int/false/null
 281+ */
 282+ protected function getRequestedStableId() {
 283+ $request = $this->getRequest();
 284+ $reqId = $request->getVal( 'stableid' );
 285+ if ( $reqId === "best" ) {
 286+ $reqId = $this->article->getBestFlaggedRevId();
 287+ }
 288+ return $reqId;
 289+ }
 290+
 291+ /**
 292+ * Replaces a page with the last stable version if possible
 293+ * Adds stable version status/info tags and notes
 294+ * Adds a quick review form on the bottom if needed
 295+ */
 296+ public function setPageContent( &$outputDone, &$useParserCache ) {
 297+ $request = $this->getRequest();
 298+ $this->load();
 299+ # Only trigger on page views with no oldid=x param
 300+ if ( !$this->isPageView( $request ) || $request->getVal( 'oldid' ) ) {
 301+ return true;
 302+ # Only trigger for reviewable pages that exist
 303+ } elseif ( !$this->article->exists() || !$this->article->isReviewable() ) {
 304+ return true;
 305+ }
 306+ $tag = '';
 307+ $old = $stable = false;
 308+ # Check the newest stable version.
 309+ $srev = $this->article->getStableRev();
 310+ $stableId = $srev ? $srev->getRevId() : 0;
 311+ $frev = $srev; // $frev is the revision we are looking at
 312+ # Check for any explicitly requested reviewed version (stableid=X)...
 313+ $reqId = $this->getRequestedStableId();
 314+ if ( $reqId ) {
 315+ if ( !$stableId ) {
 316+ $reqId = false; // must be invalid
 317+ # Treat requesting the stable version by ID as &stable=1
 318+ } elseif ( $reqId != $stableId ) {
 319+ $old = true; // old reviewed version requested by ID
 320+ $frev = FlaggedRevision::newFromTitle( $this->article->getTitle(), $reqId );
 321+ if ( !$frev ) {
 322+ $reqId = false; // invalid ID given
 323+ }
 324+ } else {
 325+ $stable = true; // stable version requested by ID
 326+ }
 327+ }
 328+ // $reqId is null if nothing requested, false if invalid
 329+ if ( $reqId === false ) {
 330+ $this->out->addWikiText( wfMsg( 'revreview-invalid' ) );
 331+ $this->out->returnToMain( false, $this->article->getTitle() );
 332+ # Tell MW that parser output is done
 333+ $outputDone = true;
 334+ $useParserCache = false;
 335+ return true;
 336+ }
 337+ // Is the page config altered?
 338+ $prot = FlaggedRevsXML::lockStatusIcon( $this->article );
 339+ // Is there no stable version?
 340+ if ( !$frev ) {
 341+ # Add "no reviewed version" tag, but not for printable output
 342+ $this->showUnreviewedPage( $tag, $prot );
 343+ return true;
 344+ }
 345+ # Get flags and date
 346+ $flags = $frev->getTags();
 347+ # Get quality level
 348+ $quality = FlaggedRevs::isQuality( $flags );
 349+ $pristine = FlaggedRevs::isPristine( $flags );
 350+ // Looking at some specific old stable revision ("&stableid=x")
 351+ // set to override given the relevant conditions. If the user is
 352+ // requesting the stable revision ("&stableid=x"), defer to override
 353+ // behavior below, since it is the same as ("&stable=1").
 354+ if ( $old ) {
 355+ # Tell MW that parser output is done by setting $outputDone
 356+ $outputDone = $this->showOldReviewedVersion( $frev, $tag, $prot );
 357+ $useParserCache = false;
 358+ // Stable version requested by ID or relevant conditions met to
 359+ // to override page view with the stable version.
 360+ } elseif ( $stable || $this->showingStable() ) {
 361+ # Tell MW that parser output is done by setting $outputDone
 362+ $outputDone = $this->showStableVersion( $srev, $tag, $prot );
 363+ $useParserCache = false;
 364+ // Looking at some specific old revision (&oldid=x) or if FlaggedRevs is not
 365+ // set to override given the relevant conditions (like &stable=0) or there
 366+ // is no stable version.
 367+ } else {
 368+ $this->showDraftVersion( $srev, $tag, $prot );
 369+ }
 370+ # Some checks for which tag CSS to use
 371+ if ( $this->useSimpleUI() ) {
 372+ $tagClass = 'flaggedrevs_short';
 373+ } elseif ( $pristine ) {
 374+ $tagClass = 'flaggedrevs_pristine';
 375+ } elseif ( $quality ) {
 376+ $tagClass = 'flaggedrevs_quality';
 377+ } else {
 378+ $tagClass = 'flaggedrevs_basic';
 379+ }
 380+ # Wrap tag contents in a div
 381+ if ( $tag != '' ) {
 382+ $css = "{$tagClass} plainlinks noprint";
 383+ $notice = "<div id=\"mw-fr-revisiontag\" class=\"{$css}\">{$tag}</div>\n";
 384+ $this->reviewNotice .= $notice;
 385+ }
 386+ return true;
 387+ }
 388+
 389+ /**
 390+ * If the page has a stable version and it shows by default,
 391+ * tell search crawlers to index only that version of the page.
 392+ * Also index the draft as well if they are synced (bug 27173).
 393+ * However, any URL with ?stableid=x should not be indexed (as with ?oldid=x).
 394+ */
 395+ public function setRobotPolicy() {
 396+ $request = $this->getRequest();
 397+ if ( $this->article->getStableRev() && $this->article->isStableShownByDefault() ) {
 398+ if ( $this->showingStable() ) {
 399+ return; // stable version - index this
 400+ } elseif ( !$request->getVal( 'stableid' )
 401+ && $this->out->getRevisionId() == $this->article->getStable()
 402+ && $this->article->stableVersionIsSynced() )
 403+ {
 404+ return; // draft that is synced with the stable version - index this
 405+ }
 406+ $this->out->setRobotPolicy( 'noindex,nofollow' ); // don't index this version
 407+ }
 408+ }
 409+
 410+ /**
 411+ * @param $tag review box/bar info
 412+ * @param $prot protection notice
 413+ * Tag output function must be called by caller
 414+ */
 415+ protected function showUnreviewedPage( $tag, $prot ) {
 416+ if ( $this->out->isPrintable() ) {
 417+ return; // all this function does is add notices; don't show them
 418+ }
 419+ $icon = FlaggedRevsXML::draftStatusIcon();
 420+ // Simple icon-based UI
 421+ if ( $this->useSimpleUI() ) {
 422+ $tag .= $prot . $icon . wfMsgExt( 'revreview-quick-none', 'parseinline' );
 423+ $css = "flaggedrevs_short plainlinks noprint";
 424+ $this->reviewNotice .= "<div id='mw-fr-revisiontag' class='$css'>$tag</div>";
 425+ // Standard UI
 426+ } else {
 427+ $css = 'flaggedrevs_notice plainlinks noprint';
 428+ $tag = "<div id='mw-fr-revisiontag' class='$css'>" .
 429+ $prot . $icon . wfMsgExt( 'revreview-noflagged', 'parseinline' ) .
 430+ "</div>";
 431+ $this->reviewNotice .= $tag;
 432+ }
 433+ }
 434+
 435+ /**
 436+ * Tag output function must be called by caller
 437+ * Parser cache control deferred to caller
 438+ * @param $srev stable version
 439+ * @param $tag review box/bar info
 440+ * @param $prot protection notice icon
 441+ * @return void
 442+ */
 443+ protected function showDraftVersion( FlaggedRevision $srev, &$tag, $prot ) {
 444+ $request = $this->getRequest();
 445+ $reqUser = $this->getUser();
 446+ $this->load();
 447+ if ( $this->out->isPrintable() ) {
 448+ return; // all this function does is add notices; don't show them
 449+ }
 450+ $flags = $srev->getTags();
 451+ $time = $this->getLang()->date( $srev->getTimestamp(), true );
 452+ # Get quality level
 453+ $quality = FlaggedRevs::isQuality( $flags );
 454+ # Get stable version sync status
 455+ $synced = $this->article->stableVersionIsSynced();
 456+ if ( $synced ) { // draft == stable
 457+ $diffToggle = ''; // no diff to show
 458+ } else { // draft != stable
 459+ # The user may want the diff (via prefs)
 460+ $diffToggle = $this->getTopDiffToggle( $srev, $quality );
 461+ if ( $diffToggle != '' ) $diffToggle = " $diffToggle";
 462+ # Make sure there is always a notice bar when viewing the draft.
 463+ if ( $this->useSimpleUI() ) { // we already one for detailed UI
 464+ $this->setPendingNotice( $srev, $diffToggle );
 465+ }
 466+ }
 467+ # Give a "your edit is pending" notice to newer users if
 468+ # an unreviewed edit was completed...
 469+ if ( $request->getVal( 'shownotice' )
 470+ && $this->article->getUserText( Revision::RAW ) == $reqUser->getName()
 471+ && $this->article->revsArePending()
 472+ && !$reqUser->isAllowed( 'review' ) )
 473+ {
 474+ $revsSince = $this->article->getPendingRevCount();
 475+ $pending = $prot;
 476+ if ( $this->showRatingIcon() ) {
 477+ $pending .= FlaggedRevsXML::draftStatusIcon();
 478+ }
 479+ $pending .= wfMsgExt( 'revreview-edited',
 480+ 'parseinline', $srev->getRevId(), $revsSince );
 481+ $anchor = $request->getVal( 'fromsection' );
 482+ if ( $anchor != null ) {
 483+ $section = str_replace( '_', ' ', $anchor ); // prettify
 484+ $pending .= wfMsgExt( 'revreview-edited-section', 'parse', $anchor, $section );
 485+ }
 486+ # Notice should always use subtitle
 487+ $this->reviewNotice = "<div id='mw-fr-reviewnotice' " .
 488+ "class='flaggedrevs_preview plainlinks'>$pending</div>";
 489+ # Otherwise, construct some tagging info for non-printable outputs.
 490+ # Also, if low profile UI is enabled and the page is synced, skip the tag.
 491+ # Note: the "your edit is pending" notice has all this info, so we never add both.
 492+ } elseif ( !( $this->article->lowProfileUI() && $synced ) ) {
 493+ $revsSince = $this->article->getPendingRevCount();
 494+ // Simple icon-based UI
 495+ if ( $this->useSimpleUI() ) {
 496+ if ( !$reqUser->getId() ) {
 497+ $msgHTML = ''; // Anons just see simple icons
 498+ } elseif ( $synced ) {
 499+ $msg = $quality
 500+ ? 'revreview-quick-quality-same'
 501+ : 'revreview-quick-basic-same';
 502+ $msgHTML = wfMsgExt( $msg, 'parseinline',
 503+ $srev->getRevId(), $revsSince );
 504+ } else {
 505+ $msg = $quality
 506+ ? 'revreview-quick-see-quality'
 507+ : 'revreview-quick-see-basic';
 508+ $msgHTML = wfMsgExt( $msg, 'parseinline',
 509+ $srev->getRevId(), $revsSince );
 510+ }
 511+ $icon = '';
 512+ # For protection based configs, show lock only if it's not redundant.
 513+ if ( $this->showRatingIcon() ) {
 514+ $icon = $synced
 515+ ? FlaggedRevsXML::stableStatusIcon( $quality )
 516+ : FlaggedRevsXML::draftStatusIcon();
 517+ }
 518+ $msgHTML = $prot . $icon . $msgHTML;
 519+ $tag .= FlaggedRevsXML::prettyRatingBox( $srev, $msgHTML,
 520+ $revsSince, 'draft', $synced, false );
 521+ // Standard UI
 522+ } else {
 523+ if ( $synced ) {
 524+ if ( $quality ) {
 525+ $msg = 'revreview-quality-same';
 526+ } else {
 527+ $msg = 'revreview-basic-same';
 528+ }
 529+ $msgHTML = wfMsgExt( $msg, 'parseinline',
 530+ $srev->getRevId(), $time, $revsSince );
 531+ } else {
 532+ $msg = $quality
 533+ ? 'revreview-newest-quality'
 534+ : 'revreview-newest-basic';
 535+ $msg .= ( $revsSince == 0 ) ? '-i' : '';
 536+ $msgHTML = wfMsgExt( $msg, 'parseinline',
 537+ $srev->getRevId(), $time, $revsSince );
 538+ }
 539+ $icon = $synced
 540+ ? FlaggedRevsXML::stableStatusIcon( $quality )
 541+ : FlaggedRevsXML::draftStatusIcon();
 542+ $tag .= $prot . $icon . $msgHTML . $diffToggle;
 543+ }
 544+ }
 545+ }
 546+
 547+ /**
 548+ * Tag output function must be called by caller
 549+ * Parser cache control deferred to caller
 550+ * @param $srev stable version
 551+ * @param $frev selected flagged revision
 552+ * @param $tag review box/bar info
 553+ * @param $prot protection notice icon
 554+ * @return ParserOutput
 555+ */
 556+ protected function showOldReviewedVersion( FlaggedRevision $frev, &$tag, $prot ) {
 557+ $reqUser = $this->getUser();
 558+ $this->load();
 559+ $flags = $frev->getTags();
 560+ $time = $this->getLang()->date( $frev->getTimestamp(), true );
 561+ # Set display revision ID
 562+ $this->out->setRevisionId( $frev->getRevId() );
 563+ # Get quality level
 564+ $quality = FlaggedRevs::isQuality( $flags );
 565+
 566+ # Construct some tagging for non-printable outputs. Note that the pending
 567+ # notice has all this info already, so don't do this if we added that already.
 568+ if ( !$this->out->isPrintable() ) {
 569+ // Simple icon-based UI
 570+ if ( $this->useSimpleUI() ) {
 571+ $icon = '';
 572+ # For protection based configs, show lock only if it's not redundant.
 573+ if ( $this->showRatingIcon() ) {
 574+ $icon = FlaggedRevsXML::stableStatusIcon( $quality );
 575+ }
 576+ $revsSince = $this->article->getPendingRevCount();
 577+ if ( !$reqUser->getId() ) {
 578+ $msgHTML = ''; // Anons just see simple icons
 579+ } else {
 580+ $msg = $quality
 581+ ? 'revreview-quick-quality-old'
 582+ : 'revreview-quick-basic-old';
 583+ $msgHTML = wfMsgExt( $msg, 'parseinline', $frev->getRevId(), $revsSince );
 584+ }
 585+ $msgHTML = $prot . $icon . $msgHTML;
 586+ $tag = FlaggedRevsXML::prettyRatingBox( $frev, $msgHTML,
 587+ $revsSince, 'oldstable', false /*synced*/ );
 588+ // Standard UI
 589+ } else {
 590+ $icon = FlaggedRevsXML::stableStatusIcon( $quality );
 591+ $msg = $quality
 592+ ? 'revreview-quality-old'
 593+ : 'revreview-basic-old';
 594+ $tag = $prot . $icon;
 595+ $tag .= wfMsgExt( $msg, 'parseinline', $frev->getRevId(), $time );
 596+ # Hide clutter
 597+ if ( !empty( $flags ) ) {
 598+ $tag .= FlaggedRevsXML::ratingToggle();
 599+ $tag .= "<div id='mw-fr-revisiondetails' style='display:block;'>" .
 600+ wfMsgHtml( 'revreview-oldrating' ) .
 601+ FlaggedRevsXML::addTagRatings( $flags ) . '</div>';
 602+ }
 603+ }
 604+ }
 605+
 606+ $text = $frev->getRevText();
 607+ # Get the new stable parser output...
 608+ $pOpts = $this->article->makeParserOptions( $reqUser );
 609+ $parserOut = FlaggedRevs::parseStableText(
 610+ $this->article->getTitle(), $text, $frev->getRevId(), $pOpts );
 611+
 612+ # Parse and output HTML
 613+ $redirHtml = $this->getRedirectHtml( $text );
 614+ if ( $redirHtml == '' ) { // page is not a redirect...
 615+ # Add the stable output to the page view
 616+ $this->out->addParserOutput( $parserOut );
 617+ } else { // page is a redirect...
 618+ $this->out->addHtml( $redirHtml );
 619+ # Add output to set categories, displaytitle, etc.
 620+ $this->out->addParserOutputNoText( $parserOut );
 621+ }
 622+
 623+ return $parserOut;
 624+ }
 625+
 626+ /**
 627+ * Tag output function must be called by caller
 628+ * Parser cache control deferred to caller
 629+ * @param $srev stable version
 630+ * @param $tag review box/bar info
 631+ * @param $prot protection notice
 632+ * @return ParserOutput
 633+ */
 634+ protected function showStableVersion( FlaggedRevision $srev, &$tag, $prot ) {
 635+ $reqUser = $this->getUser();
 636+ $this->load();
 637+ $flags = $srev->getTags();
 638+ $time = $this->getLang()->date( $srev->getTimestamp(), true );
 639+ # Set display revision ID
 640+ $this->out->setRevisionId( $srev->getRevId() );
 641+ # Get quality level
 642+ $quality = FlaggedRevs::isQuality( $flags );
 643+
 644+ $synced = $this->article->stableVersionIsSynced();
 645+ # Construct some tagging
 646+ if ( !$this->out->isPrintable() && !( $this->article->lowProfileUI() && $synced ) ) {
 647+ $revsSince = $this->article->getPendingRevCount();
 648+ // Simple icon-based UI
 649+ if ( $this->useSimpleUI() ) {
 650+ $icon = '';
 651+ # For protection based configs, show lock only if it's not redundant.
 652+ if ( $this->showRatingIcon() ) {
 653+ $icon = FlaggedRevsXML::stableStatusIcon( $quality );
 654+ }
 655+ if ( !$reqUser->getId() ) {
 656+ $msgHTML = ''; // Anons just see simple icons
 657+ } else {
 658+ $msg = $quality
 659+ ? 'revreview-quick-quality'
 660+ : 'revreview-quick-basic';
 661+ # Uses messages 'revreview-quick-quality-same', 'revreview-quick-basic-same'
 662+ $msg = $synced ? "{$msg}-same" : $msg;
 663+ $msgHTML = wfMsgExt( $msg, 'parseinline',
 664+ $srev->getRevId(), $revsSince );
 665+ }
 666+ $msgHTML = $prot . $icon . $msgHTML;
 667+ $tag = FlaggedRevsXML::prettyRatingBox( $srev, $msgHTML,
 668+ $revsSince, 'stable', $synced );
 669+ // Standard UI
 670+ } else {
 671+ $icon = FlaggedRevsXML::stableStatusIcon( $quality );
 672+ $msg = $quality ? 'revreview-quality' : 'revreview-basic';
 673+ if ( $synced ) {
 674+ # uses messages 'revreview-quality-same', 'revreview-basic-same'
 675+ $msg .= '-same';
 676+ } elseif ( $revsSince == 0 ) {
 677+ # uses messages 'revreview-quality-i', 'revreview-basic-i'
 678+ $msg .= '-i';
 679+ }
 680+ $tag = $prot . $icon;
 681+ $tag .= wfMsgExt( $msg, 'parseinline', $srev->getRevId(), $time, $revsSince );
 682+ if ( !empty( $flags ) ) {
 683+ $tag .= FlaggedRevsXML::ratingToggle();
 684+ $tag .= "<div id='mw-fr-revisiondetails' style='display:block;'>" .
 685+ FlaggedRevsXML::addTagRatings( $flags ) . '</div>';
 686+ }
 687+ }
 688+ }
 689+
 690+ # Get parsed stable version and output HTML
 691+ $pOpts = $this->article->makeParserOptions( $reqUser );
 692+ $parserCache = FRParserCacheStable::singleton();
 693+ $parserOut = $parserCache->get( $this->article, $pOpts );
 694+
 695+ # Do not use the parser cache if it lacks mImageTimeKeys and there is a
 696+ # chance that a review form will be added to this page (which requires the versions).
 697+ $canReview = $this->article->getTitle()->userCan( 'review' );
 698+ if ( $parserOut && ( !$canReview || FlaggedRevs::parserOutputIsVersioned( $parserOut ) ) ) {
 699+ # Cache hit. Note that redirects are not cached.
 700+ $this->out->addParserOutput( $parserOut );
 701+ } else {
 702+ $text = $srev->getRevText();
 703+ # Get the new stable parser output...
 704+ $parserOut = FlaggedRevs::parseStableText(
 705+ $this->article->getTitle(), $text, $srev->getRevId(), $pOpts );
 706+
 707+ $redirHtml = $this->getRedirectHtml( $text );
 708+ if ( $redirHtml == '' ) { // page is not a redirect...
 709+ # Update the stable version cache
 710+ $parserCache->save( $parserOut, $this->article, $pOpts );
 711+ # Add the stable output to the page view
 712+ $this->out->addParserOutput( $parserOut );
 713+ } else { // page is a redirect...
 714+ $this->out->addHtml( $redirHtml );
 715+ # Add output to set categories, displaytitle, etc.
 716+ $this->out->addParserOutputNoText( $parserOut );
 717+ }
 718+ # Update the stable version dependancies
 719+ FlaggedRevs::updateStableOnlyDeps( $this->article, $parserOut );
 720+ }
 721+
 722+ # Update page sync status for tracking purposes.
 723+ # NOTE: avoids master hits and doesn't have to be perfect for what it does
 724+ if ( $this->article->syncedInTracking() != $synced ) {
 725+ if ( wfGetLB()->safeGetLag( wfGetDB( DB_SLAVE ) ) <= 5 ) { // avoid write-delay cycles
 726+ $this->article->updateSyncStatus( $synced );
 727+ }
 728+ }
 729+
 730+ return $parserOut;
 731+ }
 732+
 733+ // Get fancy redirect arrow and link HTML
 734+ protected function getRedirectHtml( $text ) {
 735+ $rTargets = Title::newFromRedirectArray( $text );
 736+ if ( $rTargets ) {
 737+ $article = new Article( $this->article->getTitle() );
 738+ return $article->viewRedirect( $rTargets );
 739+ }
 740+ return '';
 741+ }
 742+
 743+ // Show icons for draft/stable/old reviewed versions
 744+ protected function showRatingIcon() {
 745+ if ( FlaggedRevs::useOnlyIfProtected() ) {
 746+ // If there is only one quality level and we have tabs to know
 747+ // which version we are looking at, then just use the lock icon...
 748+ return FlaggedRevs::qualityVersions();
 749+ }
 750+ return true;
 751+ }
 752+
 753+ /**
 754+ * Get collapsible diff-to-stable html to add to the review notice as needed
 755+ * @param FlaggedRevision $srev, stable version
 756+ * @param bool $quality, revision is quality
 757+ * @return string, the html line (either "" or "<diff toggle><diff div>")
 758+ */
 759+ protected function getTopDiffToggle( FlaggedRevision $srev, $quality ) {
 760+ $reqUser = $this->getUser();
 761+ $this->load();
 762+ if ( !$reqUser->getBoolOption( 'flaggedrevsviewdiffs' ) ) {
 763+ return false; // nothing to do here
 764+ }
 765+ # Diff should only show for the draft
 766+ $oldid = $this->getOldIDFromRequest();
 767+ $latest = $this->article->getLatest();
 768+ if ( $oldid && $oldid != $latest ) {
 769+ return false; // not viewing the draft
 770+ }
 771+ $revsSince = $this->article->getPendingRevCount();
 772+ if ( !$revsSince ) {
 773+ return false; // no pending changes
 774+ }
 775+ $title = $this->article->getTitle(); // convenience
 776+ # Review status of left diff revision...
 777+ $leftNote = $quality
 778+ ? 'revreview-hist-quality'
 779+ : 'revreview-hist-basic';
 780+ $lClass = FlaggedRevsXML::getQualityColor( (int)$quality );
 781+ $leftNote = "<span class='$lClass'>[" . wfMsgHtml( $leftNote ) . "]</span>";
 782+ # Review status of right diff revision...
 783+ $rClass = FlaggedRevsXML::getQualityColor( false );
 784+ $rightNote = "<span class='$rClass'>[" .
 785+ wfMsgHtml( 'revreview-hist-pending' ) . "]</span>";
 786+ # Get the actual body of the diff...
 787+ $diffEngine = new DifferenceEngine( $title, $srev->getRevId(), $latest );
 788+ $diffBody = $diffEngine->getDiffBody();
 789+ if ( strlen( $diffBody ) > 0 ) {
 790+ $nEdits = $revsSince - 1; // full diff-to-stable, no need for query
 791+ if ( $nEdits ) {
 792+ $limit = 100;
 793+ $nUsers = $title->countAuthorsBetween( $srev->getRevId(), $latest, $limit );
 794+ $multiNotice = DifferenceEngine::intermediateEditsMsg( $nEdits, $nUsers, $limit );
 795+ } else {
 796+ $multiNotice = '';
 797+ }
 798+ $diffEngine->showDiffStyle(); // add CSS
 799+ $this->isDiffFromStable = true; // alter default review form tags
 800+ return
 801+ FlaggedRevsXML::diffToggle() .
 802+ "<div id='mw-fr-stablediff'>\n" .
 803+ self::getFormattedDiff( $diffBody, $multiNotice, $leftNote, $rightNote ) .
 804+ "</div>\n";
 805+ }
 806+ return '';
 807+ }
 808+
 809+ // $n number of in-between revs
 810+ protected static function getFormattedDiff(
 811+ $diffBody, $multiNotice, $leftStatus, $rightStatus
 812+ ) {
 813+ if ( $multiNotice != '' ) {
 814+ $multiNotice = "<tr><td colspan='4' align='center' class='diff-multi'>" .
 815+ $multiNotice . "</td></tr>";
 816+ }
 817+ return
 818+ "<table border='0' width='98%' cellpadding='0' cellspacing='4' class='diff'>" .
 819+ "<col class='diff-marker' />" .
 820+ "<col class='diff-content' />" .
 821+ "<col class='diff-marker' />" .
 822+ "<col class='diff-content' />" .
 823+ "<tr>" .
 824+ "<td colspan='2' width='50%' align='center' class='diff-otitle'><b>" .
 825+ $leftStatus . "</b></td>" .
 826+ "<td colspan='2' width='50%' align='center' class='diff-ntitle'><b>" .
 827+ $rightStatus . "</b></td>" .
 828+ "</tr>" .
 829+ $multiNotice .
 830+ $diffBody .
 831+ "</table>";
 832+ }
 833+
 834+ /**
 835+ * Get the normal and display files for the underlying ImagePage.
 836+ * If the a stable version needs to be displayed, this will set $normalFile
 837+ * to the current version, and $displayFile to the desired version.
 838+ *
 839+ * If no stable version is required, the reference parameters will not be set
 840+ *
 841+ * Depends on $request
 842+ */
 843+ public function imagePageFindFile( &$normalFile, &$displayFile ) {
 844+ $request = $this->getRequest();
 845+ $this->load();
 846+ # Determine timestamp. A reviewed version may have explicitly been requested...
 847+ $frev = null;
 848+ $time = false;
 849+ $reqId = $request->getVal( 'stableid' );
 850+ if ( $reqId ) {
 851+ $frev = FlaggedRevision::newFromTitle( $this->article->getTitle(), $reqId );
 852+ } elseif ( $this->showingStable() ) {
 853+ $frev = $this->article->getStableRev();
 854+ }
 855+ if ( $frev ) {
 856+ $time = $frev->getFileTimestamp();
 857+ // B/C, may be stored in associated image version metadata table
 858+ // @TODO: remove, updateTracking.php does this
 859+ if ( !$time ) {
 860+ $dbr = wfGetDB( DB_SLAVE );
 861+ $time = $dbr->selectField( 'flaggedimages',
 862+ 'fi_img_timestamp',
 863+ array( 'fi_rev_id' => $frev->getRevId(),
 864+ 'fi_name' => $this->article->getTitle()->getDBkey() ),
 865+ __METHOD__
 866+ );
 867+ $time = trim( $time ); // remove garbage
 868+ $time = $time ? wfTimestamp( TS_MW, $time ) : false;
 869+ }
 870+ }
 871+ if ( !$time ) {
 872+ # Try request parameter
 873+ $time = $request->getVal( 'filetimestamp', false );
 874+ }
 875+
 876+ if ( !$time ) {
 877+ return; // Use the default behaviour
 878+ }
 879+
 880+ $title = $this->article->getTitle();
 881+ $displayFile = wfFindFile( $title, array( 'time' => $time ) );
 882+ # If none found, try current
 883+ if ( !$displayFile ) {
 884+ wfDebug( __METHOD__ . ": {$title->getPrefixedDBkey()}: $time not found, using current\n" );
 885+ $displayFile = wfFindFile( $title );
 886+ # If none found, use a valid local placeholder
 887+ if ( !$displayFile ) {
 888+ $displayFile = wfLocalFile( $title ); // fallback to current
 889+ }
 890+ $normalFile = $displayFile;
 891+ # If found, set $normalFile
 892+ } else {
 893+ wfDebug( __METHOD__ . ": {$title->getPrefixedDBkey()}: using timestamp $time\n" );
 894+ $normalFile = wfFindFile( $title );
 895+ }
 896+ }
 897+
 898+ /**
 899+ * Adds stable version tags to page when viewing history
 900+ */
 901+ public function addToHistView() {
 902+ $this->load();
 903+ # Add a notice if there are pending edits...
 904+ $srev = $this->article->getStableRev();
 905+ if ( $srev && $this->article->revsArePending() ) {
 906+ $revsSince = $this->article->getPendingRevCount();
 907+ $tag = "<div id='mw-fr-revisiontag-edit' class='flaggedrevs_notice plainlinks'>" .
 908+ FlaggedRevsXML::lockStatusIcon( $this->article ) . # flag protection icon as needed
 909+ FlaggedRevsXML::pendingEditNotice( $this->article, $srev, $revsSince ) . "</div>";
 910+ $this->out->addHTML( $tag );
 911+ }
 912+ return true;
 913+ }
 914+
 915+ /**
 916+ * Adds stable version tags to page when editing
 917+ */
 918+ public function addToEditView( EditPage $editPage ) {
 919+ global $wgParser;
 920+ $reqUser = $this->getUser();
 921+ $this->load();
 922+ # Must be reviewable. UI may be limited to unobtrusive patrolling system.
 923+ if ( !$this->article->isReviewable() ) {
 924+ return true;
 925+ }
 926+ $items = array();
 927+ # Show stabilization log
 928+ $log = $this->stabilityLogNotice();
 929+ if ( $log ) $items[] = $log;
 930+ # Check the newest stable version
 931+ $frev = $this->article->getStableRev();
 932+ if ( $frev ) {
 933+ $quality = $frev->getQuality();
 934+ # Find out revision id of base version
 935+ $latestId = $this->article->getLatest();
 936+ $revId = $editPage->oldid ? $editPage->oldid : $latestId;
 937+ # Let new users know about review procedure a tag.
 938+ # If the log excerpt was shown this is redundant.
 939+ if ( !$log && !$reqUser->getId() && $this->article->isStableShownByDefault() ) {
 940+ $items[] = wfMsgExt( 'revreview-editnotice', 'parseinline' );
 941+ }
 942+ # Add a notice if there are pending edits...
 943+ if ( $this->article->revsArePending() ) {
 944+ $revsSince = $this->article->getPendingRevCount();
 945+ $items[] = FlaggedRevsXML::pendingEditNotice( $this->article, $frev, $revsSince );
 946+ }
 947+ # Show diff to stable, to make things less confusing.
 948+ # This can be disabled via user preferences and other conditions...
 949+ if ( $frev->getRevId() < $latestId // changes were made
 950+ && $reqUser->getBoolOption( 'flaggedrevseditdiffs' ) // not disable via prefs
 951+ && $revId == $latestId // only for current rev
 952+ && $editPage->section != 'new' // not for new sections
 953+ && $editPage->formtype != 'diff' // not "show changes"
 954+ ) {
 955+ # Left diff side...
 956+ $leftNote = $quality
 957+ ? 'revreview-hist-quality'
 958+ : 'revreview-hist-basic';
 959+ $lClass = FlaggedRevsXML::getQualityColor( (int)$quality );
 960+ $leftNote = "<span class='$lClass'>[" .
 961+ wfMsgHtml( $leftNote ) . "]</span>";
 962+ # Right diff side...
 963+ $rClass = FlaggedRevsXML::getQualityColor( false );
 964+ $rightNote = "<span class='$rClass'>[" .
 965+ wfMsgHtml( 'revreview-hist-pending' ) . "]</span>";
 966+ # Get the stable version source
 967+ $text = $frev->getRevText();
 968+ # Are we editing a section?
 969+ $section = ( $editPage->section == "" ) ?
 970+ false : intval( $editPage->section );
 971+ if ( $section !== false ) {
 972+ $text = $wgParser->getSection( $text, $section );
 973+ }
 974+ if ( $text !== false && strcmp( $text, $editPage->textbox1 ) !== 0 ) {
 975+ $diffEngine = new DifferenceEngine( $this->article->getTitle() );
 976+ $diffBody = $diffEngine->generateDiffBody( $text, $editPage->textbox1 );
 977+ $diffHtml =
 978+ wfMsgExt( 'review-edit-diff', 'parseinline' ) . ' ' .
 979+ FlaggedRevsXML::diffToggle() .
 980+ "<div id='mw-fr-stablediff'>" .
 981+ self::getFormattedDiff( $diffBody, '', $leftNote, $rightNote ) .
 982+ "</div>\n";
 983+ $items[] = $diffHtml;
 984+ $diffEngine->showDiffStyle(); // add CSS
 985+ }
 986+ }
 987+ # Output items
 988+ if ( count( $items ) ) {
 989+ $html = "<table class='flaggedrevs_editnotice plainlinks'>";
 990+ foreach ( $items as $item ) {
 991+ $html .= '<tr><td>' . $item . '</td></tr>';
 992+ }
 993+ $html .= '</table>';
 994+ $this->out->addHTML( $html );
 995+ }
 996+ }
 997+ return true;
 998+ }
 999+
 1000+ protected function stabilityLogNotice() {
 1001+ $this->load();
 1002+ $s = '';
 1003+ # Only for pages manually made to be stable...
 1004+ if ( $this->article->isPageLocked() ) {
 1005+ $s = wfMsgExt( 'revreview-locked', 'parseinline' );
 1006+ $s .= ' ' . FlaggedRevsXML::logDetailsToggle();
 1007+ $s .= FlaggedRevsXML::stabilityLogExcerpt( $this->article );
 1008+ # ...or unstable
 1009+ } elseif ( $this->article->isPageUnlocked() ) {
 1010+ $s = wfMsgExt( 'revreview-unlocked', 'parseinline' );
 1011+ $s .= ' ' . FlaggedRevsXML::logDetailsToggle();
 1012+ $s .= FlaggedRevsXML::stabilityLogExcerpt( $this->article );
 1013+ }
 1014+ return $s;
 1015+ }
 1016+
 1017+ public function addToNoSuchSection( EditPage $editPage, &$s ) {
 1018+ $this->load();
 1019+ $srev = $this->article->getStableRev();
 1020+ # Add notice for users that may have clicked "edit" for a
 1021+ # section in the stable version that isn't in the draft.
 1022+ if ( $srev && $this->article->revsArePending() ) {
 1023+ $revsSince = $this->article->getPendingRevCount();
 1024+ if ( $revsSince ) {
 1025+ $s .= "<div class='flaggedrevs_editnotice plainlinks'>" .
 1026+ wfMsgExt( 'revreview-pending-nosection', 'parseinline',
 1027+ $srev->getRevId(), $revsSince ) . "</div>";
 1028+ }
 1029+ }
 1030+ return true;
 1031+ }
 1032+
 1033+ /**
 1034+ * Add unreviewed pages links
 1035+ */
 1036+ public function addToCategoryView() {
 1037+ $reqUser = $this->getUser();
 1038+ $this->load();
 1039+ if ( !$reqUser->isAllowed( 'review' ) ) {
 1040+ return true;
 1041+ }
 1042+ if ( !FlaggedRevs::useOnlyIfProtected() ) {
 1043+ # Add links to lists of unreviewed pages and pending changes in this category
 1044+ $category = $this->article->getTitle()->getText();
 1045+ $this->out->appendSubtitle(
 1046+ Html::rawElement(
 1047+ 'span',
 1048+ array( 'class' => 'plainlinks', 'id' => 'mw-fr-category-oldreviewed' ),
 1049+ wfMsgExt( 'flaggedrevs-categoryview', 'parseinline', urlencode( $category ) )
 1050+ )
 1051+ );
 1052+ }
 1053+ return true;
 1054+ }
 1055+
 1056+ /**
 1057+ * Add review form to pages when necessary on a regular page view (action=view).
 1058+ * If $output is an OutputPage then this prepends the form onto it.
 1059+ * If $output is a string then this appends the review form to it.
 1060+ * @param mixed string|OutputPage
 1061+ */
 1062+ public function addReviewForm( &$output ) {
 1063+ $request = $this->getRequest();
 1064+ $reqUser = $this->getUser();
 1065+ $this->load();
 1066+ if ( $this->out->isPrintable() ) {
 1067+ return false; // Must be on non-printable output
 1068+ }
 1069+ # User must have review rights
 1070+ if ( !$reqUser->isAllowed( 'review' ) ) {
 1071+ return true;
 1072+ }
 1073+ # Page must exist and be reviewable
 1074+ if ( !$this->article->exists() || !$this->article->isReviewable() ) {
 1075+ return true;
 1076+ }
 1077+ # Must be a page view action...
 1078+ if ( !$this->isPageViewOrDiff( $request ) ) {
 1079+ return true;
 1080+ }
 1081+ # Get the revision being displayed
 1082+ $rev = false;
 1083+ if ( $this->reviewFormRev ) {
 1084+ $rev = $this->reviewFormRev; // $newRev for diffs stored here
 1085+ } elseif ( $this->out->getRevisionId() ) {
 1086+ $rev = Revision::newFromId( $this->out->getRevisionId() );
 1087+ }
 1088+ # Build the review form as needed
 1089+ if ( $rev && ( !$this->diffRevs || $this->isReviewableDiff ) ) {
 1090+ $form = new RevisionReviewFormUI( $this->getContext(), $this->article, $rev );
 1091+ # Default tags and existence of "reject" button depend on context
 1092+ if ( $this->diffRevs ) {
 1093+ $form->setDiffPriorRev( $this->diffRevs['old'] );
 1094+ }
 1095+ # Review notice box goes in top of form
 1096+ $form->setTopNotice( $this->diffNoticeBox );
 1097+ $form->setBottomNotice( $this->diffIncChangeBox );
 1098+
 1099+ # Set the file version we are viewing (for File: pages)
 1100+ $form->setFileVersion( $this->out->getFileVersion() );
 1101+ # $wgOut may not already have the inclusion IDs, such as for diffonly=1.
 1102+ # fr_unversionedIncludes indicates that ParserOutput added to $wgOut lacked inclusion IDs.
 1103+ # If they're lacking, then we use getRevIncludes() to get the draft inclusion versions.
 1104+ # Note: showStableVersion() already makes sure that $wgOut has the stable inclusion versions.
 1105+ if ( $this->out->getRevisionId() == $rev->getId() && empty( $this->out->fr_unversionedIncludes ) ) {
 1106+ $tmpVers = $this->out->getTemplateIds();
 1107+ $fileVers = $this->out->getFileSearchOptions();
 1108+ } elseif ( $this->oldRevIncludes ) { // e.g. diffonly=1, stable diff
 1109+ # We may have already fetched the inclusion IDs to get the template/file changes.
 1110+ list( $tmpVers, $fileVers ) = $this->oldRevIncludes; // reuse
 1111+ } else { // e.g. diffonly=1, other diffs
 1112+ # $wgOut may not already have the inclusion IDs, such as for diffonly=1.
 1113+ # RevisionReviewForm will fetch them as needed however.
 1114+ list( $tmpVers, $fileVers ) =
 1115+ FRInclusionCache::getRevIncludes( $this->article, $rev, $reqUser );
 1116+ }
 1117+ $form->setIncludeVersions( $tmpVers, $fileVers );
 1118+
 1119+ list( $html, $status ) = $form->getHtml();
 1120+ # Diff action: place the form at the top of the page
 1121+ if ( $output instanceof OutputPage ) {
 1122+ $output->prependHTML( $html );
 1123+ # View action: place the form at the bottom of the page
 1124+ } else {
 1125+ $output .= $html;
 1126+ }
 1127+ }
 1128+ return true;
 1129+ }
 1130+
 1131+ /**
 1132+ * Add link to stable version setting to protection form
 1133+ */
 1134+ public function addStabilizationLink() {
 1135+ $request = $this->getRequest();
 1136+ $this->load();
 1137+ if ( FlaggedRevs::useProtectionLevels() ) {
 1138+ return true; // simple custom levels set for action=protect
 1139+ }
 1140+ # Check only if the title is reviewable
 1141+ if ( !FlaggedRevs::inReviewNamespace( $this->article->getTitle() ) ) {
 1142+ return true;
 1143+ }
 1144+ $action = $request->getVal( 'action', 'view' );
 1145+ if ( $action == 'protect' || $action == 'unprotect' ) {
 1146+ $title = SpecialPage::getTitleFor( 'Stabilization' );
 1147+ # Give a link to the page to configure the stable version
 1148+ $frev = $this->article->getStableRev();
 1149+ if ( $frev && $frev->getRevId() == $this->article->getLatest() ) {
 1150+ $this->out->prependHTML( "<span class='plainlinks'>" .
 1151+ wfMsgExt( 'revreview-visibility-synced', 'parseinline',
 1152+ $title->getPrefixedText() ) . "</span>" );
 1153+ } elseif ( $frev ) {
 1154+ $this->out->prependHTML( "<span class='plainlinks'>" .
 1155+ wfMsgExt( 'revreview-visibility-outdated', 'parseinline',
 1156+ $title->getPrefixedText() ) . "</span>" );
 1157+ } else {
 1158+ $this->out->prependHTML( "<span class='plainlinks'>" .
 1159+ wfMsgExt( 'revreview-visibility-nostable', 'parseinline',
 1160+ $title->getPrefixedText() ) . "</span>" );
 1161+ }
 1162+ }
 1163+ return true;
 1164+ }
 1165+
 1166+ /**
 1167+ * Modify an array of action links, as used by SkinTemplateNavigation and
 1168+ * SkinTemplateTabs, to inlude flagged revs UI elements
 1169+ */
 1170+ public function setActionTabs( $skin, array &$actions ) {
 1171+ $reqUser = $this->getUser();
 1172+ $this->load();
 1173+ if ( FlaggedRevs::useProtectionLevels() ) {
 1174+ return true; // simple custom levels set for action=protect
 1175+ }
 1176+ $title = $this->article->getTitle()->getSubjectPage();
 1177+ if ( !FlaggedRevs::inReviewNamespace( $title ) ) {
 1178+ return true; // Only reviewable pages need these tabs
 1179+ }
 1180+ // Check if we should show a stabilization tab
 1181+ if (
 1182+ !$this->article->getTitle()->isTalkPage() &&
 1183+ is_array( $actions ) &&
 1184+ !isset( $actions['protect'] ) &&
 1185+ !isset( $actions['unprotect'] ) &&
 1186+ $reqUser->isAllowed( 'stablesettings' ) &&
 1187+ $title->exists() )
 1188+ {
 1189+ $stableTitle = SpecialPage::getTitleFor( 'Stabilization' );
 1190+ // Add the tab
 1191+ $actions['default'] = array(
 1192+ 'class' => false,
 1193+ 'text' => wfMsg( 'stabilization-tab' ),
 1194+ 'href' => $stableTitle->getLocalUrl( 'page=' . $title->getPrefixedUrl() )
 1195+ );
 1196+ }
 1197+ return true;
 1198+ }
 1199+
 1200+ /**
 1201+ * Modify an array of tab links to include flagged revs UI elements
 1202+ * @param string $type ('flat' for SkinTemplateTabs, 'nav' for SkinTemplateNavigation)
 1203+ */
 1204+ public function setViewTabs( Skin $skin, array &$views, $type ) {
 1205+ $this->load();
 1206+ if ( !FlaggedRevs::inReviewNamespace( $this->article->getTitle() ) ) {
 1207+ return true; // short-circuit for non-reviewable pages
 1208+ }
 1209+ # Hack for bug 16734 (some actions update and view all at once)
 1210+ if ( $this->pageWriteOpRequested() && wfGetDB( DB_MASTER )->doneWrites() ) {
 1211+ # Tabs need to reflect the new stable version so users actually
 1212+ # see the results of their action (i.e. "delete"/"rollback")
 1213+ $this->article->loadPageData( 'fromdbmaster' );
 1214+ }
 1215+ $srev = $this->article->getStableRev();
 1216+ if ( !$srev ) {
 1217+ return true; // No stable revision exists
 1218+ }
 1219+ $synced = $this->article->stableVersionIsSynced();
 1220+ $pendingEdits = !$synced && $this->article->isStableShownByDefault();
 1221+ // Set the edit tab names as needed...
 1222+ if ( $pendingEdits ) {
 1223+ if ( isset( $views['edit'] ) ) {
 1224+ $views['edit']['text'] = wfMsg( 'revreview-edit' );
 1225+ }
 1226+ if ( isset( $views['viewsource'] ) ) {
 1227+ $views['viewsource']['text'] = wfMsg( 'revreview-source' );
 1228+ }
 1229+ }
 1230+ # Add "pending changes" tab if the page is not synced
 1231+ if ( !$synced ) {
 1232+ $this->addDraftTab( $views, $srev, $type );
 1233+ }
 1234+ return true;
 1235+ }
 1236+
 1237+ // Add "pending changes" tab and set tab selection CSS
 1238+ protected function addDraftTab( array &$views, FlaggedRevision $srev, $type ) {
 1239+ $request = $this->getRequest();
 1240+ $title = $this->article->getTitle(); // convenience
 1241+ $tabs = array(
 1242+ 'read' => array( // view stable
 1243+ 'text' => '', // unused
 1244+ 'href' => $title->getLocalUrl( 'stable=1' ),
 1245+ 'class' => ''
 1246+ ),
 1247+ 'draft' => array( // view draft
 1248+ 'text' => wfMsg( 'revreview-current' ),
 1249+ 'href' => $title->getLocalUrl( 'stable=0&redirect=no' ),
 1250+ 'class' => 'collapsible'
 1251+ ),
 1252+ );
 1253+ // Set tab selection CSS
 1254+ if ( $this->showingStable() || $request->getVal( 'stableid' ) ) {
 1255+ // We are looking a the stable version or an old reviewed one
 1256+ $tabs['read']['class'] = 'selected';
 1257+ } elseif ( $this->isPageViewOrDiff( $request ) ) {
 1258+ $ts = null;
 1259+ if ( $this->out->getRevisionId() ) { // @TODO: avoid same query in Skin.php
 1260+ $ts = ( $this->out->getRevisionId() == $this->article->getLatest() )
 1261+ ? $this->article->getTimestamp() // skip query
 1262+ : Revision::getTimestampFromId( $title, $this->out->getRevisionId() );
 1263+ }
 1264+ // Are we looking at a pending revision?
 1265+ if ( $ts > $srev->getRevTimestamp() ) { // bug 15515
 1266+ $tabs['draft']['class'] .= ' selected';
 1267+ // Are there *just* pending template/file changes.
 1268+ } elseif ( $this->article->onlyTemplatesOrFilesPending()
 1269+ && $this->out->getRevisionId() == $this->article->getStable() )
 1270+ {
 1271+ $tabs['draft']['class'] .= ' selected';
 1272+ // Otherwise, fallback to regular tab behavior
 1273+ } else {
 1274+ $tabs['read']['class'] = 'selected';
 1275+ }
 1276+ }
 1277+ $newViews = array();
 1278+ // Rebuild tabs array. Deals with Monobook vs Vector differences.
 1279+ if ( $type == 'nav' ) { // Vector et al
 1280+ foreach ( $views as $tabAction => $data ) {
 1281+ // The 'view' tab. Make it go to the stable version...
 1282+ if ( $tabAction == 'view' ) {
 1283+ // 'view' for content page; make it go to the stable version
 1284+ $newViews[$tabAction]['text'] = $data['text']; // keep tab name
 1285+ $newViews[$tabAction]['href'] = $tabs['read']['href'];
 1286+ $newViews[$tabAction]['class'] = $tabs['read']['class'];
 1287+ // All other tabs...
 1288+ } else {
 1289+ // Add 'draft' tab to content page to the left of 'edit'...
 1290+ if ( $tabAction == 'edit' || $tabAction == 'viewsource' ) {
 1291+ $newViews['current'] = $tabs['draft'];
 1292+ }
 1293+ $newViews[$tabAction] = $data;
 1294+ }
 1295+ }
 1296+ } elseif ( $type == 'flat' ) { // MonoBook et al
 1297+ $first = true;
 1298+ foreach ( $views as $tabAction => $data ) {
 1299+ // The first tab ('page'). Make it go to the stable version...
 1300+ if ( $first ) {
 1301+ $first = false;
 1302+ $newViews[$tabAction]['text'] = $data['text']; // keep tab name
 1303+ $newViews[$tabAction]['href'] = $tabs['read']['href'];
 1304+ $newViews[$tabAction]['class'] = $data['class']; // keep tab class
 1305+ // All other tabs...
 1306+ } else {
 1307+ // Add 'draft' tab to content page to the left of 'edit'...
 1308+ if ( $tabAction == 'edit' || $tabAction == 'viewsource' ) {
 1309+ $newViews['current'] = $tabs['draft'];
 1310+ }
 1311+ $newViews[$tabAction] = $data;
 1312+ }
 1313+ }
 1314+ }
 1315+ // Replaces old tabs with new tabs
 1316+ $views = $newViews;
 1317+ }
 1318+
 1319+ /**
 1320+ * Check if a flaggedrevs relevant write op was done this page view
 1321+ * @return bool
 1322+ */
 1323+ protected function pageWriteOpRequested() {
 1324+ $request = $this->getRequest();
 1325+ # Hack for bug 16734 (some actions update and view all at once)
 1326+ $action = $request->getVal( 'action' );
 1327+ if ( $action === 'rollback' ) {
 1328+ return true;
 1329+ } elseif ( $action === 'delete' && $request->wasPosted() ) {
 1330+ return true;
 1331+ }
 1332+ return false;
 1333+ }
 1334+
 1335+ protected function getOldIDFromRequest() {
 1336+ $article = new Article( $this->article->getTitle() );
 1337+ return $article->getOldIDFromRequest();
 1338+ }
 1339+
 1340+ /**
 1341+ * Adds a notice saying that this revision is pending review
 1342+ * @param FlaggedRevision $srev The stable version
 1343+ * @param string $diffToggle either "" or " <diff toggle><diff div>"
 1344+ * @return void
 1345+ */
 1346+ public function setPendingNotice( FlaggedRevision $srev, $diffToggle = '' ) {
 1347+ $this->load();
 1348+ $time = $this->getLang()->date( $srev->getTimestamp(), true );
 1349+ $revsSince = $this->article->getPendingRevCount();
 1350+ $msg = $srev->getQuality()
 1351+ ? 'revreview-newest-quality'
 1352+ : 'revreview-newest-basic';
 1353+ $msg .= ( $revsSince == 0 ) ? '-i' : '';
 1354+ # Add bar msg to the top of the page...
 1355+ $css = 'flaggedrevs_preview plainlinks';
 1356+ $msgHTML = wfMsgExt( $msg, 'parseinline', $srev->getRevId(), $time, $revsSince );
 1357+ $this->reviewNotice .= "<div id='mw-fr-reviewnotice' class='$css'>" .
 1358+ "$msgHTML$diffToggle</div>";
 1359+ }
 1360+
 1361+ /**
 1362+ * When viewing a diff:
 1363+ * (a) Add the review form to the top of the page
 1364+ * (b) Mark off which versions are checked or not
 1365+ * (c) When comparing the stable revision to the current:
 1366+ * (i) Show a tag with some explanation for the diff
 1367+ * (ii) List any template/file changes pending review
 1368+ */
 1369+ public function addToDiffView( $diff, $oldRev, $newRev ) {
 1370+ global $wgMemc, $wgParserCacheExpireTime;
 1371+ $request = $this->getRequest();
 1372+ $reqUser = $this->getUser();
 1373+ $this->load();
 1374+ # Exempt printer-friendly output
 1375+ if ( $this->out->isPrintable() ) {
 1376+ return true;
 1377+ # Multi-page diffs are useless and misbehave (bug 19327). Sanity check $newRev.
 1378+ } elseif ( $this->isMultiPageDiff || !$newRev ) {
 1379+ return true;
 1380+ # Page must be reviewable.
 1381+ } elseif ( !$this->article->isReviewable() ) {
 1382+ return true;
 1383+ }
 1384+ $srev = $this->article->getStableRev();
 1385+ # Check if this is a diff-to-stable. If so:
 1386+ # (a) prompt reviewers to review the changes
 1387+ # (b) list template/file changes if only includes are pending
 1388+ if ( $srev
 1389+ && $this->isDiffFromStable
 1390+ && !$this->article->stableVersionIsSynced() ) // pending changes
 1391+ {
 1392+ $changeText = '';
 1393+ $this->reviewFormRev = $newRev;
 1394+ $changeList = array();
 1395+ # Page not synced only due to includes?
 1396+ if ( !$this->article->revsArePending() ) {
 1397+ # Add a list of links to each changed template...
 1398+ $changeList = self::fetchTemplateChanges( $srev );
 1399+ # Add a list of links to each changed file...
 1400+ $changeList = array_merge( $changeList, self::fetchFileChanges( $srev ) );
 1401+ # Correct bad cache which said they were not synced...
 1402+ if ( !count( $changeList ) ) {
 1403+ $key = wfMemcKey( 'flaggedrevs', 'includesSynced', $this->article->getId() );
 1404+ $data = FlaggedRevs::makeMemcObj( "true" );
 1405+ $wgMemc->set( $key, $data, $wgParserCacheExpireTime );
 1406+ }
 1407+ # Otherwise, check for includes pending on top of edits pending...
 1408+ } else {
 1409+ $incs = FRInclusionCache::getRevIncludes( $this->article, $newRev, $reqUser );
 1410+ $this->oldRevIncludes = $incs; // process cache
 1411+ # Add a list of links to each changed template...
 1412+ $changeList = self::fetchTemplateChanges( $srev, $incs[0] );
 1413+ # Add a list of links to each changed file...
 1414+ $changeList = array_merge( $changeList, self::fetchFileChanges( $srev, $incs[1] ) );
 1415+ }
 1416+ # If there are pending revs or templates/files changes, notify the user...
 1417+ if ( $this->article->revsArePending() || count( $changeList ) ) {
 1418+ # If the user can review then prompt them to review them...
 1419+ if ( $reqUser->isAllowed( 'review' ) ) {
 1420+ // Reviewer just edited...
 1421+ if ( $request->getInt( 'shownotice' )
 1422+ && $newRev->isCurrent()
 1423+ && $newRev->getRawUserText() == $reqUser->getName() )
 1424+ {
 1425+ $title = $this->article->getTitle(); // convenience
 1426+ // @TODO: make diff class cache this
 1427+ $n = $title->countRevisionsBetween( $oldRev, $newRev );
 1428+ if ( $n ) {
 1429+ $msg = 'revreview-update-edited-prev'; // previous pending edits
 1430+ } else {
 1431+ $msg = 'revreview-update-edited'; // just couldn't autoreview
 1432+ }
 1433+ // All other cases...
 1434+ } else {
 1435+ $msg = 'revreview-update'; // generic "please review" notice...
 1436+ }
 1437+ $this->diffNoticeBox = wfMsgExt( $msg, 'parse' ); // add as part of form
 1438+ }
 1439+ # Add include change list...
 1440+ if ( count( $changeList ) ) { // just inclusion changes
 1441+ $changeText .= "<p>" .
 1442+ wfMsgExt( 'revreview-update-includes', 'parseinline' ) .
 1443+ '&#160;' . implode( ', ', $changeList ) . "</p>\n";
 1444+ }
 1445+ }
 1446+ # template/file change list
 1447+ if ( $changeText != '' ) {
 1448+ if ( $reqUser->isAllowed( 'review' ) ) {
 1449+ $this->diffIncChangeBox = "<p>$changeText</p>";
 1450+ } else {
 1451+ $css = 'flaggedrevs_diffnotice plainlinks';
 1452+ $this->out->addHTML(
 1453+ "<div id='mw-fr-difftostable' class='$css'>$changeText</div>\n"
 1454+ );
 1455+ }
 1456+ }
 1457+ }
 1458+ # Add a link to diff from stable to current as needed.
 1459+ # Show review status of the diff revision(s). Uses a <table>.
 1460+ $this->out->addHTML(
 1461+ '<div id="mw-fr-diff-headeritems">' .
 1462+ self::diffLinkAndMarkers( $this->article, $oldRev, $newRev ) .
 1463+ '</div>'
 1464+ );
 1465+ return true;
 1466+ }
 1467+
 1468+ // get new diff header items for in-place AJAX page review
 1469+ public static function AjaxBuildDiffHeaderItems() {
 1470+ $args = func_get_args(); // <oldid, newid>
 1471+ if ( count( $args ) >= 2 ) {
 1472+ $oldid = (int)$args[0];
 1473+ $newid = (int)$args[1];
 1474+ $oldRev = Revision::newFromId( $oldid );
 1475+ $newRev = Revision::newFromId( $newid );
 1476+ if ( $newRev && $newRev->getTitle() ) {
 1477+ $fa = FlaggableWikiPage::getTitleInstance( $newRev->getTitle() );
 1478+ return self::diffLinkAndMarkers( $fa, $oldRev, $newRev );
 1479+ }
 1480+ }
 1481+ return '';
 1482+ }
 1483+
 1484+ /**
 1485+ * (a) Add a link to diff from stable to current as needed
 1486+ * (b) Show review status of the diff revision(s). Uses a <table>.
 1487+ * Note: used by ajax function to rebuild diff page
 1488+ */
 1489+ public static function diffLinkAndMarkers( FlaggableWikiPage $article, $oldRev, $newRev ) {
 1490+ $s = '<form id="mw-fr-diff-dataform">';
 1491+ $s .= Html::hidden( 'oldid', $oldRev ? $oldRev->getId() : 0 );
 1492+ $s .= Html::hidden( 'newid', $newRev ? $newRev->getId() : 0 );
 1493+ $s .= "</form>\n";
 1494+ if ( $newRev ) { // sanity check
 1495+ $s .= self::diffToStableLink( $article, $oldRev, $newRev );
 1496+ $s .= self::diffReviewMarkers( $article, $oldRev, $newRev );
 1497+ }
 1498+ return $s;
 1499+ }
 1500+
 1501+ /**
 1502+ * Add a link to diff-to-stable for reviewable pages
 1503+ */
 1504+ protected static function diffToStableLink(
 1505+ FlaggableWikiPage $article, $oldRev, Revision $newRev
 1506+ ) {
 1507+ $srev = $article->getStableRev();
 1508+ if ( !$srev ) {
 1509+ return ''; // nothing to do
 1510+ }
 1511+ $review = '';
 1512+ # Is this already the full diff-to-stable?
 1513+ $fullStableDiff = $newRev->isCurrent()
 1514+ && self::isDiffToStable( $srev, $oldRev, $newRev );
 1515+ # Make a link to the full diff-to-stable if:
 1516+ # (a) Actual revs are pending and (b) We are not viewing the full diff-to-stable
 1517+ if ( $article->revsArePending() && !$fullStableDiff ) {
 1518+ $review = Linker::linkKnown(
 1519+ $article->getTitle(),
 1520+ wfMsgHtml( 'review-diff2stable' ),
 1521+ array(),
 1522+ array( 'oldid' => $srev->getRevId(), 'diff' => 'cur' ) + FlaggedRevs::diffOnlyCGI()
 1523+ );
 1524+ $review = wfMsgHtml( 'parentheses', $review );
 1525+ $review = "<div class='fr-diff-to-stable' align='center'>$review</div>";
 1526+ }
 1527+ return $review;
 1528+ }
 1529+
 1530+ /**
 1531+ * Add [checked version] and such to left and right side of diff
 1532+ */
 1533+ protected static function diffReviewMarkers( FlaggableWikiPage $article, $oldRev, $newRev ) {
 1534+ $table = '';
 1535+ $srev = $article->getStableRev();
 1536+ # Diff between two revisions
 1537+ if ( $oldRev && $newRev ) {
 1538+ list( $msg, $class ) = self::getDiffRevMsgAndClass( $oldRev, $srev );
 1539+ $table .= "<table class='fr-diff-ratings'><tr>";
 1540+ $table .= "<td width='50%' align='center'>";
 1541+ $table .= "<span class='$class'>[" .
 1542+ wfMsgHtml( $msg ) . "]</span>";
 1543+
 1544+ list( $msg, $class ) = self::getDiffRevMsgAndClass( $newRev, $srev );
 1545+ $table .= "</td><td width='50%' align='center'>";
 1546+ $table .= "<span class='$class'>[" .
 1547+ wfMsgHtml( $msg ) . "]</span>";
 1548+
 1549+ $table .= "</td></tr></table>\n";
 1550+ # New page "diffs" - just one rev
 1551+ } elseif ( $newRev ) {
 1552+ list( $msg, $class ) = self::getDiffRevMsgAndClass( $newRev, $srev );
 1553+ $table .= "<table class='fr-diff-ratings'>";
 1554+ $table .= "<tr><td align='center'><span class='$class'>";
 1555+ $table .= '[' . wfMsgHtml( $msg ) . ']';
 1556+ $table .= "</span></td></tr></table>\n";
 1557+ }
 1558+ return $table;
 1559+ }
 1560+
 1561+ protected static function getDiffRevMsgAndClass(
 1562+ Revision $rev, FlaggedRevision $srev = null
 1563+ ) {
 1564+ $tier = FlaggedRevision::getRevQuality( $rev->getPage(), $rev->getId() );
 1565+ if ( $tier !== false ) {
 1566+ $msg = $tier
 1567+ ? 'revreview-hist-quality'
 1568+ : 'revreview-hist-basic';
 1569+ } else {
 1570+ $msg = ( $srev && $rev->getTimestamp() > $srev->getRevTimestamp() ) // bug 15515
 1571+ ? 'revreview-hist-pending'
 1572+ : 'revreview-hist-draft';
 1573+ }
 1574+ $css = FlaggedRevsXML::getQualityColor( $tier );
 1575+ return array( $msg, $css );
 1576+ }
 1577+
 1578+ // Fetch template changes for a reviewed revision since review
 1579+ // @return array
 1580+ protected static function fetchTemplateChanges( FlaggedRevision $frev, $newTemplates = null ) {
 1581+ $diffLinks = array();
 1582+ if ( $newTemplates === null ) {
 1583+ $changes = $frev->findPendingTemplateChanges();
 1584+ } else {
 1585+ $changes = $frev->findTemplateChanges( $newTemplates );
 1586+ }
 1587+ foreach ( $changes as $tuple ) {
 1588+ list( $title, $revIdStable, $hasStable ) = $tuple;
 1589+ $link = Linker::linkKnown(
 1590+ $title,
 1591+ htmlspecialchars( $title->getPrefixedText() ),
 1592+ array(),
 1593+ array( 'diff' => 'cur', 'oldid' => $revIdStable ) );
 1594+ if ( !$hasStable ) {
 1595+ $link = "<strong>$link</strong>";
 1596+ }
 1597+ $diffLinks[] = $link;
 1598+ }
 1599+ return $diffLinks;
 1600+ }
 1601+
 1602+ // Fetch file changes for a reviewed revision since review
 1603+ // @return array
 1604+ protected static function fetchFileChanges( FlaggedRevision $frev, $newFiles = null ) {
 1605+ $diffLinks = array();
 1606+ if ( $newFiles === null ) {
 1607+ $changes = $frev->findPendingFileChanges( 'noForeign' );
 1608+ } else {
 1609+ $changes = $frev->findFileChanges( $newFiles, 'noForeign' );
 1610+ }
 1611+ foreach ( $changes as $tuple ) {
 1612+ list( $title, $revIdStable, $hasStable ) = $tuple;
 1613+ // @TODO: change when MW has file diffs
 1614+ $link = Linker::link( $title, htmlspecialchars( $title->getPrefixedText() ) );
 1615+ if ( !$hasStable ) {
 1616+ $link = "<strong>$link</strong>";
 1617+ }
 1618+ $diffLinks[] = $link;
 1619+ }
 1620+ return $diffLinks;
 1621+ }
 1622+
 1623+ /**
 1624+ * Set $this->isDiffFromStable and $this->isMultiPageDiff fields
 1625+ * Note: $oldRev could be false
 1626+ */
 1627+ public function setViewFlags( $diff, $oldRev, $newRev ) {
 1628+ $this->load();
 1629+ // We only want valid diffs that actually make sense...
 1630+ if ( $newRev && $oldRev && $newRev->getTimestamp() >= $oldRev->getTimestamp() ) {
 1631+ // Is this a diff between two pages?
 1632+ if ( $newRev->getPage() != $oldRev->getPage() ) {
 1633+ $this->isMultiPageDiff = true;
 1634+ // Is there a stable version?
 1635+ } elseif ( $this->article->isReviewable() ) {
 1636+ $srev = $this->article->getStableRev();
 1637+ // Is this a diff of a draft rev against the stable rev?
 1638+ if ( self::isDiffToStable( $srev, $oldRev, $newRev ) ) {
 1639+ $this->isDiffFromStable = true;
 1640+ $this->isReviewableDiff = true;
 1641+ // Is this a diff of a draft rev against a reviewed rev?
 1642+ } elseif (
 1643+ FlaggedRevision::newFromTitle( $diff->getTitle(), $oldRev->getId() ) ||
 1644+ FlaggedRevision::newFromTitle( $diff->getTitle(), $newRev->getId() )
 1645+ ) {
 1646+ $this->isReviewableDiff = true;
 1647+ }
 1648+ }
 1649+ $this->diffRevs = array( 'old' => $oldRev, 'new' => $newRev );
 1650+ }
 1651+ return true;
 1652+ }
 1653+
 1654+ // Is a diff from $oldRev to $newRev a diff-to-stable?
 1655+ protected static function isDiffToStable( $srev, $oldRev, $newRev ) {
 1656+ return ( $srev && $oldRev && $newRev
 1657+ && $oldRev->getPage() == $newRev->getPage() // no multipage diffs
 1658+ && $oldRev->getId() == $srev->getRevId()
 1659+ && $newRev->getTimestamp() >= $oldRev->getTimestamp() // no backwards diffs
 1660+ );
 1661+ }
 1662+
 1663+ /**
 1664+ * Redirect users out to review the changes to the stable version.
 1665+ * Only for people who can review and for pages that have a stable version.
 1666+ */
 1667+ public function injectPostEditURLParams( &$sectionAnchor, &$extraQuery ) {
 1668+ $reqUser = $this->getUser();
 1669+ $this->load();
 1670+ $this->article->loadPageData( 'fromdbmaster' );
 1671+ # Get the stable version from the master
 1672+ $frev = $this->article->getStableRev();
 1673+ if ( !$frev || !$this->article->revsArePending() ) {
 1674+ return true; // only for pages with pending edits
 1675+ }
 1676+ $params = array();
 1677+ // If the edit was not autoreviewed, and the user can actually make a
 1678+ // new stable version, then go to the diff...
 1679+ if ( $frev->userCanSetFlags( $reqUser ) ) {
 1680+ $params += array( 'oldid' => $frev->getRevId(), 'diff' => 'cur', 'shownotice' => 1 );
 1681+ $params += FlaggedRevs::diffOnlyCGI();
 1682+ // ...otherwise, go to the draft revision after completing an edit.
 1683+ // This allows for users to immediately see their changes.
 1684+ } else {
 1685+ $params += array( 'stable' => 0 );
 1686+ // Show a notice at the top of the page for non-reviewers...
 1687+ if ( !$reqUser->isAllowed( 'review' ) && $this->article->isStableShownByDefault() ) {
 1688+ $params += array( 'shownotice' => 1 );
 1689+ if ( $sectionAnchor ) {
 1690+ // Pass a section parameter in the URL as needed to add a link to
 1691+ // the "your changes are pending" box on the top of the page...
 1692+ $section = str_replace(
 1693+ array( ':' , '.' ), array( '%3A', '%' ), // hack: reverse encoding
 1694+ substr( $sectionAnchor, 1 ) // remove the '#'
 1695+ );
 1696+ $params += array( 'fromsection' => $section );
 1697+ $sectionAnchor = ''; // go to the top of the page to see notice
 1698+ }
 1699+ }
 1700+ }
 1701+ if ( $extraQuery !== '' ) {
 1702+ $extraQuery .= '&';
 1703+ }
 1704+ $extraQuery .= wfArrayToCGI( $params ); // note: EditPage will add initial "&"
 1705+ return true;
 1706+ }
 1707+
 1708+ /**
 1709+ * If submitting the edit will leave it pending, then change the button text
 1710+ * Note: interacts with 'review pending changes' checkbox
 1711+ * @TODO: would be nice if hook passed in button attribs, not XML
 1712+ */
 1713+ public function changeSaveButton( EditPage $editPage, array &$buttons ) {
 1714+ if ( !$this->editWillRequireReview( $editPage ) ) {
 1715+ return true; // edit will go live or be reviewed on save
 1716+ }
 1717+ if ( extension_loaded( 'domxml' ) ) {
 1718+ wfDebug( "Warning: you have the obsolete domxml extension for PHP. Please remove it!\n" );
 1719+ return true; # PECL extension conflicts with the core DOM extension (see bug 13770)
 1720+ } elseif ( isset( $buttons['save'] ) && extension_loaded( 'dom' ) ) {
 1721+ $dom = new DOMDocument();
 1722+ $dom->loadXML( $buttons['save'] ); // load button XML from hook
 1723+ foreach ( $dom->getElementsByTagName( 'input' ) as $input ) { // one <input>
 1724+ $input->setAttribute( 'value', wfMsg( 'revreview-submitedit' ) );
 1725+ $input->setAttribute( 'title', // keep accesskey
 1726+ wfMsgExt( 'revreview-submitedit-title', 'parsemag' ) .
 1727+ ' [' . wfMsg( 'accesskey-save' ) . ']' );
 1728+ # Change submit button text & title
 1729+ $buttons['save'] = $dom->saveXML( $dom->documentElement );
 1730+ }
 1731+ }
 1732+ return true;
 1733+ }
 1734+
 1735+ /**
 1736+ * If this edit will not go live on submit (accounting for wpReviewEdit)
 1737+ * @param EditPage $editPage
 1738+ * @return bool
 1739+ */
 1740+ protected function editWillRequireReview( EditPage $editPage ) {
 1741+ $request = $this->getRequest();
 1742+ $title = $this->article->getTitle(); // convenience
 1743+ if ( !$this->editRequiresReview( $editPage ) ) {
 1744+ return false; // edit will go live immediatly
 1745+ } elseif ( $request->getCheck( 'wpReviewEdit' ) && $title->userCan( 'review' ) ) {
 1746+ return false; // edit checked off to be reviewed on save
 1747+ }
 1748+ return true; // edit needs review
 1749+ }
 1750+
 1751+ /**
 1752+ * If this edit will not go live on submit unless wpReviewEdit is checked
 1753+ * @param EditPage $editPage
 1754+ * @return bool
 1755+ */
 1756+ protected function editRequiresReview( EditPage $editPage ) {
 1757+ if ( !$this->article->editsRequireReview() ) {
 1758+ return false; // edits go live immediatly
 1759+ } elseif ( $this->editWillBeAutoreviewed( $editPage ) ) {
 1760+ return false; // edit will be autoreviewed anyway
 1761+ }
 1762+ return true; // edit needs review
 1763+ }
 1764+
 1765+ /**
 1766+ * If this edit will be auto-reviewed on submit
 1767+ * Note: checking wpReviewEdit does not count as auto-reviewed
 1768+ * @param EditPage $editPage
 1769+ * @return bool
 1770+ */
 1771+ protected function editWillBeAutoreviewed( EditPage $editPage ) {
 1772+ $title = $this->article->getTitle(); // convenience
 1773+ if ( !$this->article->isReviewable() ) {
 1774+ return false;
 1775+ }
 1776+ if ( $title->userCan( 'autoreview' ) ) {
 1777+ if ( FlaggedRevs::autoReviewNewPages() && !$this->article->exists() ) {
 1778+ return true; // edit will be autoreviewed
 1779+ }
 1780+ if ( !isset( $editPage->fr_baseFRev ) ) {
 1781+ $baseRevId = self::getBaseRevId( $editPage );
 1782+ $editPage->fr_baseFRev = FlaggedRevision::newFromTitle( $title, $baseRevId );
 1783+ }
 1784+ if ( $editPage->fr_baseFRev ) {
 1785+ return true; // edit will be autoreviewed
 1786+ }
 1787+ }
 1788+ return false; // edit won't be autoreviewed
 1789+ }
 1790+
 1791+ /**
 1792+ * Add a "review pending changes" checkbox to the edit form iff:
 1793+ * (a) there are currently any revisions pending (bug 16713)
 1794+ * (b) this is an unreviewed page (bug 23970)
 1795+ */
 1796+ public function addReviewCheck( EditPage $editPage, array &$checkboxes, &$tabindex ) {
 1797+ $request = $this->getRequest();
 1798+ $title = $this->article->getTitle(); // convenience
 1799+ if ( !$this->article->isReviewable() || !$title->userCan( 'review' ) ) {
 1800+ return true; // not needed
 1801+ } elseif ( $this->editWillBeAutoreviewed( $editPage ) ) {
 1802+ return true; // edit will be auto-reviewed
 1803+ }
 1804+ if ( self::getBaseRevId( $editPage ) == $this->article->getLatest() ) {
 1805+ # For pages with either no stable version, or an outdated one, let
 1806+ # the user decide if he/she wants it reviewed on the spot. One might
 1807+ # do this if he/she just saw the diff-to-stable and *then* decided to edit.
 1808+ # Note: check not shown when editing old revisions, which is confusing.
 1809+ $checkbox = Xml::check(
 1810+ 'wpReviewEdit',
 1811+ $request->getCheck( 'wpReviewEdit' ),
 1812+ array( 'tabindex' => ++$tabindex, 'id' => 'wpReviewEdit' )
 1813+ );
 1814+ $attribs = array( 'for' => 'wpReviewEdit' );
 1815+ // For reviewed pages...
 1816+ if ( $this->article->getStable() ) {
 1817+ // For pending changes...
 1818+ if ( $this->article->revsArePending() ) {
 1819+ $n = $this->article->getPendingRevCount();
 1820+ $attribs['title'] = wfMsg( 'revreview-check-flag-p-title' );
 1821+ $labelMsg = wfMsgExt( 'revreview-check-flag-p', 'parseinline', $n );
 1822+ // For just the user's changes...
 1823+ } else {
 1824+ $attribs['title'] = wfMsgExt( 'revreview-check-flag-y-title', 'parsemag' );
 1825+ $labelMsg = wfMsgExt( 'revreview-check-flag-y', 'parseinline' );
 1826+ }
 1827+ // For unreviewed pages...
 1828+ } else {
 1829+ $attribs['title'] = wfMsg( 'revreview-check-flag-u-title' );
 1830+ $labelMsg = wfMsgExt( 'revreview-check-flag-u', 'parseinline' );
 1831+ }
 1832+ $label = Xml::element( 'label', $attribs, $labelMsg );
 1833+ $checkboxes['reviewed'] = $checkbox . '&#160;' . $label;
 1834+ }
 1835+ return true;
 1836+ }
 1837+
 1838+ /**
 1839+ * (a) Add a hidden field that has the rev ID the text is based off.
 1840+ * (b) If an edit was undone, add a hidden field that has the rev ID of that edit.
 1841+ * Needed for autoreview and user stats (for autopromote).
 1842+ * Note: baseRevId trusted for Reviewers - text checked for others.
 1843+ */
 1844+ public function addRevisionIDField( EditPage $editPage, OutputPage $out ) {
 1845+ $this->load();
 1846+ $revId = self::getBaseRevId( $editPage );
 1847+ $out->addHTML( "\n" . Html::hidden( 'baseRevId', $revId ) );
 1848+ $out->addHTML( "\n" . Html::hidden( 'undidRev',
 1849+ empty( $editPage->undidRev ) ? 0 : $editPage->undidRev )
 1850+ );
 1851+ return true;
 1852+ }
 1853+
 1854+ /**
 1855+ * Guess the rev ID the text of this form is based off
 1856+ * Note: baseRevId trusted for Reviewers - check text for others.
 1857+ * @return int
 1858+ */
 1859+ protected static function getBaseRevId( EditPage $editPage ) {
 1860+ $request = FlaggablePageView::singleton()->getRequest();
 1861+ if ( !isset( $editPage->fr_baseRevId ) ) {
 1862+ $article = $editPage->getArticle(); // convenience
 1863+ $latestId = $article->getLatest(); // current rev
 1864+ $undo = $request->getIntOrNull( 'undo' );
 1865+ # Undoing consecutive top edits...
 1866+ if ( $undo && $undo === $latestId ) {
 1867+ # Treat this like a revert to a base revision.
 1868+ # We are undoing all edits *after* some rev ID (undoafter).
 1869+ # If undoafter is not given, then it is the previous rev ID.
 1870+ $revId = $request->getInt( 'undoafter',
 1871+ $article->getTitle()->getPreviousRevisionID( $latestId, Title::GAID_FOR_UPDATE ) );
 1872+ # Undoing other edits...
 1873+ } elseif ( $undo ) {
 1874+ $revId = $latestId; // current rev is the base rev
 1875+ # Other edits...
 1876+ } else {
 1877+ # If we are editing via oldid=X, then use that rev ID.
 1878+ # Otherwise, check if the client specified the ID (bug 23098).
 1879+ $revId = $article->getOldID()
 1880+ ? $article->getOldID()
 1881+ : $request->getInt( 'baseRevId' ); // e.g. "show changes"/"preview"
 1882+ }
 1883+ # Zero oldid => draft revision
 1884+ if ( !$revId ) {
 1885+ $revId = $latestId;
 1886+ }
 1887+ $editPage->fr_baseRevId = $revId;
 1888+ }
 1889+ return $editPage->fr_baseRevId;
 1890+ }
 1891+}
Property changes on: trunk/extensions/FlaggedRevs/presentation/FlaggablePageView.php
___________________________________________________________________
Added: svn:eol-style
11892 + native
Index: trunk/extensions/FlaggedRevs/presentation/RevisionReviewFormUI.php
@@ -16,10 +16,10 @@
1717 /**
1818 * Generates a brief review form for a page
1919 * @param RequestContext $context
20 - * @param FlaggedPage $article
 20+ * @param FlaggableWikiPage $article
2121 * @param Revision $rev
2222 */
23 - public function __construct( IContextSource $context, FlaggedPage $article, Revision $rev ) {
 23+ public function __construct( IContextSource $context, FlaggableWikiPage $article, Revision $rev ) {
2424 $this->user = $context->getUser();
2525 $this->request = $context->getRequest();
2626 $this->article = $article;
Index: trunk/extensions/FlaggedRevs/presentation/modules/review.js
@@ -272,7 +272,7 @@
273273 requestArgs.push( diffUIParams.getElementsByTagName('input')[0].value );
274274 requestArgs.push( diffUIParams.getElementsByTagName('input')[1].value );
275275 // Send encoded function plus all arguments...
276 - var url_pars = '?action=ajax&rs=FlaggedPageView::AjaxBuildDiffHeaderItems';
 276+ var url_pars = '?action=ajax&rs=FlaggablePageView::AjaxBuildDiffHeaderItems';
277277 for( var i=0; i<requestArgs.length; i++ ) {
278278 url_pars += '&rsargs[]=' + encodeURIComponent(requestArgs[i]);
279279 }

Follow-up revisions

RevisionCommit summaryAuthorDate
r104036FlaggedPageTest -> FlaggablePageTest...hashar14:24, 23 November 2011

Comments

#Comment by Hashar (talk | contribs)   14:25, 23 November 2011

test class renamed in follow up.

Status & tagging log