Index: trunk/extensions/FlaggedRevs/FlaggedRevs.php |
— | — | @@ -238,7 +238,7 @@ |
239 | 239 | $wgAutoloadClasses['FlaggedRevs'] = $accessDir . 'FlaggedRevs.class.php'; |
240 | 240 | $wgAutoloadClasses['FRUserCounters'] = $accessDir . 'FRUserCounters.php'; |
241 | 241 | $wgAutoloadClasses['FRUserActivity'] = $accessDir . 'FRUserActivity.php'; |
242 | | -$wgAutoloadClasses['FlaggedPageConfig'] = $accessDir . 'FlaggedPageConfig.php'; |
| 242 | +$wgAutoloadClasses['FRPageConfig'] = $accessDir . 'FRPageConfig.php'; |
243 | 243 | $wgAutoloadClasses['FlaggedRevsLog'] = $accessDir . 'FlaggedRevsLog.php'; |
244 | 244 | $wgAutoloadClasses['FRInclusionCache'] = $accessDir . 'FRInclusionCache.php'; |
245 | 245 | $wgAutoloadClasses['FlaggedRevsStats'] = $accessDir . 'FlaggedRevsStats.php'; |
— | — | @@ -248,7 +248,7 @@ |
249 | 249 | $wgAutoloadClasses['FRSquidUpdate'] = $accessDir . 'FRExtraCacheUpdate.php'; |
250 | 250 | $wgAutoloadClasses['FRDependencyUpdate'] = $accessDir . 'FRDependencyUpdate.php'; |
251 | 251 | $wgAutoloadClasses['FRInclusionManager'] = $accessDir . 'FRInclusionManager.php'; |
252 | | -$wgAutoloadClasses['FlaggedPage'] = $accessDir . 'FlaggedPage.php'; |
| 252 | +$wgAutoloadClasses['FlaggableWikiPage'] = $accessDir . 'FlaggableWikiPage.php'; |
253 | 253 | $wgAutoloadClasses['FlaggedRevision'] = $accessDir . 'FlaggedRevision.php'; |
254 | 254 | $wgAutoloadClasses['FRParserCacheStable'] = $accessDir . 'FRParserCacheStable.php'; |
255 | 255 | |
— | — | @@ -267,7 +267,7 @@ |
268 | 268 | $wgAutoloadClasses['PageStabilityProtectForm'] = $dir . 'business/PageStabilityForm.php'; |
269 | 269 | |
270 | 270 | # Presentation classes... |
271 | | -$wgAutoloadClasses['FlaggedPageView'] = $dir . 'presentation/FlaggedPageView.php'; |
| 271 | +$wgAutoloadClasses['FlaggablePageView'] = $dir . 'presentation/FlaggablePageView.php'; |
272 | 272 | $wgAutoloadClasses['FlaggedRevsLogView'] = $dir . 'presentation/FlaggedRevsLogView.php'; |
273 | 273 | $wgAutoloadClasses['FlaggedRevsXML'] = $dir . 'presentation/FlaggedRevsXML.php'; |
274 | 274 | $wgAutoloadClasses['RevisionReviewFormUI'] = $dir . 'presentation/RevisionReviewFormUI.php'; |
— | — | @@ -618,7 +618,7 @@ |
619 | 619 | |
620 | 620 | # AJAX functions |
621 | 621 | $wgAjaxExportList[] = 'RevisionReview::AjaxReview'; |
622 | | -$wgAjaxExportList[] = 'FlaggedPageView::AjaxBuildDiffHeaderItems'; |
| 622 | +$wgAjaxExportList[] = 'FlaggablePageView::AjaxBuildDiffHeaderItems'; |
623 | 623 | |
624 | 624 | # Cache update |
625 | 625 | $wgSpecialPageCacheUpdates['UnreviewedPages'] = 'UnreviewedPages::updateQueryCache'; |
Index: trunk/extensions/FlaggedRevs/maintenance/purgeReviewablePages.inc |
— | — | @@ -33,7 +33,7 @@ |
34 | 34 | # Go through and append each purgeable page... |
35 | 35 | foreach ( $res as $row ) { |
36 | 36 | $title = Title::newFromRow( $row ); |
37 | | - $fa = FlaggedPage::getTitleInstance( $title ); |
| 37 | + $fa = FlaggableWikiPage::getTitleInstance( $title ); |
38 | 38 | if ( $fa->isReviewable() ) { |
39 | 39 | # Need to purge this page - add to list |
40 | 40 | fwrite( $fileHandle, $title->getPrefixedDBKey() . "\n" ); |
Index: trunk/extensions/FlaggedRevs/maintenance/updateTracking.inc |
— | — | @@ -121,7 +121,7 @@ |
122 | 122 | $db->begin(); |
123 | 123 | foreach ( $res as $row ) { |
124 | 124 | $title = Title::newFromRow( $row ); |
125 | | - $article = new FlaggedPage( $title ); |
| 125 | + $article = new FlaggableWikiPage( $title ); |
126 | 126 | $oldFrev = FlaggedRevision::newFromStable( $title, FR_MASTER ); |
127 | 127 | $frev = FlaggedRevision::determineStable( $title, FR_MASTER ); |
128 | 128 | # Update fp_stable, fp_quality, and fp_reviewed |
Index: trunk/extensions/FlaggedRevs/tests/FlaggedPageTest.php |
— | — | @@ -23,7 +23,7 @@ |
24 | 24 | |
25 | 25 | public function testPageDataFromTitle() { |
26 | 26 | $title = Title::makeTitle( NS_MAIN, "somePage" ); |
27 | | - $article = new FlaggedPage( $title ); |
| 27 | + $article = new FlaggableWikiPage( $title ); |
28 | 28 | |
29 | 29 | $user = $this->user; |
30 | 30 | $article->doEdit( "Some text to insert", "creating a page", EDIT_NEW, false, $user ); |
— | — | @@ -39,4 +39,4 @@ |
40 | 40 | $this->assertEquals( true, array_key_exists( 'fp_reviewed', $data ), |
41 | 41 | "data->fp_reviewed field exists" ); |
42 | 42 | } |
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 |
1 | 239 | + native |
Index: trunk/extensions/FlaggedRevs/dataclasses/FlaggedRevs.hooks.php |
— | — | @@ -62,7 +62,7 @@ |
63 | 63 | public static function onTitleMoveComplete( |
64 | 64 | Title $otitle, Title $ntitle, $user, $pageId |
65 | 65 | ) { |
66 | | - $fa = FlaggedPage::getTitleInstance( $ntitle ); |
| 66 | + $fa = FlaggableWikiPage::getTitleInstance( $ntitle ); |
67 | 67 | $fa->loadPageData( 'fromdbmaster' ); |
68 | 68 | // Re-validate NS/config (new title may not be reviewable) |
69 | 69 | if ( $fa->isReviewable() ) { |
— | — | @@ -241,7 +241,7 @@ |
242 | 242 | if ( !FlaggedRevs::inReviewNamespace( $title ) ) { |
243 | 243 | $ret = ''; |
244 | 244 | } else { |
245 | | - $config = FlaggedPageConfig::getStabilitySettings( $title ); |
| 245 | + $config = FRPageConfig::getStabilitySettings( $title ); |
246 | 246 | $ret = $config['autoreview']; |
247 | 247 | } |
248 | 248 | } |
— | — | @@ -308,7 +308,7 @@ |
309 | 309 | if ( !FlaggedRevs::inReviewNamespace( $title ) || !$title->exists() ) { |
310 | 310 | return true; // extra short-circuit |
311 | 311 | } |
312 | | - $flaggedArticle = FlaggedPage::getTitleInstance( $title ); |
| 312 | + $flaggedArticle = FlaggableWikiPage::getTitleInstance( $title ); |
313 | 313 | # If the draft shows by default anyway, nothing to do... |
314 | 314 | if ( !$flaggedArticle->isStableShownByDefault() ) { |
315 | 315 | return true; |
— | — | @@ -321,7 +321,7 @@ |
322 | 322 | } |
323 | 323 | # Don't let users patrol reviewable pages (where reviewed <=> patrolled) |
324 | 324 | } elseif ( $action === 'patrol' || $action === 'autopatrol' ) { |
325 | | - $flaggedArticle = FlaggedPage::getTitleInstance( $title ); |
| 325 | + $flaggedArticle = FlaggableWikiPage::getTitleInstance( $title ); |
326 | 326 | # For a page to be patrollable it must not be reviewable. |
327 | 327 | # Note: normally, edits to non-reviewable, non-patrollable, pages are |
328 | 328 | # silently marked patrolled automatically. With $wgUseNPPatrol on, the |
— | — | @@ -333,7 +333,7 @@ |
334 | 334 | # Enforce autoreview/review restrictions |
335 | 335 | } elseif ( $action === 'autoreview' || $action === 'review' ) { |
336 | 336 | # Get autoreview restriction settings... |
337 | | - $fa = FlaggedPage::getTitleInstance( $title ); |
| 337 | + $fa = FlaggableWikiPage::getTitleInstance( $title ); |
338 | 338 | $config = $fa->getStabilitySettings(); |
339 | 339 | # Convert Sysop -> protect |
340 | 340 | $right = ( $config['autoreview'] === 'sysop' ) ? |
— | — | @@ -362,7 +362,7 @@ |
363 | 363 | ) { |
364 | 364 | global $wgRequest; |
365 | 365 | # Edit must be non-null, to a reviewable page, with $user set |
366 | | - $fa = FlaggedPage::getArticleInstance( $article ); |
| 366 | + $fa = FlaggableWikiPage::getArticleInstance( $article ); |
367 | 367 | $fa->loadPageData( 'fromdbmaster' ); |
368 | 368 | if ( !$rev || !$user || !$fa->isReviewable() ) { |
369 | 369 | return true; |
— | — | @@ -543,7 +543,7 @@ |
544 | 544 | if ( !$baseId && !$reviewEdit ) { |
545 | 545 | return true; // short-circuit |
546 | 546 | } |
547 | | - $fa = FlaggedPage::getArticleInstance( $article ); |
| 547 | + $fa = FlaggableWikiPage::getArticleInstance( $article ); |
548 | 548 | $fa->loadPageData( 'fromdbmaster' ); |
549 | 549 | if ( !$fa->isReviewable() ) { |
550 | 550 | return true; // page is not reviewable |
— | — | @@ -608,7 +608,7 @@ |
609 | 609 | if ( empty( $rc->mAttribs['rc_this_oldid'] ) ) { |
610 | 610 | return true; |
611 | 611 | } |
612 | | - $fa = FlaggedPage::getTitleInstance( $rc->getTitle() ); |
| 612 | + $fa = FlaggableWikiPage::getTitleInstance( $rc->getTitle() ); |
613 | 613 | $fa->loadPageData( 'fromdbmaster' ); |
614 | 614 | // Is the page reviewable? |
615 | 615 | if ( $fa->isReviewable() ) { |
Index: trunk/extensions/FlaggedRevs/dataclasses/FlaggedRevsLog.php |
— | — | @@ -100,12 +100,12 @@ |
101 | 101 | Title $title, array $config, array $oldConfig, $reason |
102 | 102 | ) { |
103 | 103 | $log = new LogPage( 'stable' ); |
104 | | - if ( FlaggedPageConfig::configIsReset( $config ) ) { |
| 104 | + if ( FRPageConfig::configIsReset( $config ) ) { |
105 | 105 | # We are going back to default settings |
106 | 106 | $log->addEntry( 'reset', $title, $reason ); |
107 | 107 | } else { |
108 | 108 | # We are changing to non-default settings |
109 | | - $action = ( $oldConfig === FlaggedPageConfig::getDefaultVisibilitySettings() ) |
| 109 | + $action = ( $oldConfig === FRPageConfig::getDefaultVisibilitySettings() ) |
110 | 110 | ? 'config' // set a custom configuration |
111 | 111 | : 'modify'; // modified an existing custom configuration |
112 | 112 | $log->addEntry( $action, $title, $reason, |
Index: trunk/extensions/FlaggedRevs/dataclasses/FlaggedRevs.class.php |
— | — | @@ -615,7 +615,7 @@ |
616 | 616 | if ( $sv === null ) { // optional |
617 | 617 | $sv = FlaggedRevision::determineStable( $title, FR_MASTER ); |
618 | 618 | } |
619 | | - $article = new FlaggedPage( $title ); |
| 619 | + $article = new FlaggableWikiPage( $title ); |
620 | 620 | if ( !$sv ) { |
621 | 621 | # Empty flaggedrevs data for this page if there is no stable version |
622 | 622 | $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 |
1 | 554 | + native |
Index: trunk/extensions/FlaggedRevs/dataclasses/FlaggedRevision.php |
— | — | @@ -205,7 +205,7 @@ |
206 | 206 | # Get visiblity settings to see if page is reviewable... |
207 | 207 | if ( FlaggedRevs::useOnlyIfProtected() ) { |
208 | 208 | if ( empty( $config ) ) { |
209 | | - $config = FlaggedPageConfig::getStabilitySettings( $title, $flags ); |
| 209 | + $config = FRPageConfig::getStabilitySettings( $title, $flags ); |
210 | 210 | } |
211 | 211 | if ( !$config['override'] ) { |
212 | 212 | return null; // page is not reviewable; no stable version |
Index: trunk/extensions/FlaggedRevs/api/actions/ApiReview.php |
— | — | @@ -66,7 +66,7 @@ |
67 | 67 | $form->setDim( $tag, (int)$params['flag_' . $tag] ); |
68 | 68 | } |
69 | 69 | if ( $form->getAction() === 'approve' ) { |
70 | | - $article = new FlaggedPage( $title ); |
| 70 | + $article = new FlaggableWikiPage( $title ); |
71 | 71 | // Get the file version used for File: pages |
72 | 72 | $file = $article->getFile(); |
73 | 73 | if ( $file ) { |
Index: trunk/extensions/FlaggedRevs/api/actions/ApiReviewActivity.php |
— | — | @@ -49,7 +49,7 @@ |
50 | 50 | } |
51 | 51 | $title = $newRev->getTitle(); |
52 | 52 | |
53 | | - $fa = FlaggedPage::getTitleInstance( $title ); |
| 53 | + $fa = FlaggableWikiPage::getTitleInstance( $title ); |
54 | 54 | if ( !$fa->isReviewable() ) { |
55 | 55 | $this->dieUsage( "Provided page is not reviewable.", 'notreviewable' ); |
56 | 56 | } |
Index: trunk/extensions/FlaggedRevs/business/RevisionReviewForm.php |
— | — | @@ -139,7 +139,7 @@ |
140 | 140 | * @return mixed (true on success, error string on failure) |
141 | 141 | */ |
142 | 142 | protected function doBuildOnReady() { |
143 | | - $this->article = FlaggedPage::getTitleInstance( $this->page ); |
| 143 | + $this->article = FlaggableWikiPage::getTitleInstance( $this->page ); |
144 | 144 | return true; |
145 | 145 | } |
146 | 146 | |
Index: trunk/extensions/FlaggedRevs/business/PageStabilityForm.php |
— | — | @@ -151,7 +151,7 @@ |
152 | 152 | */ |
153 | 153 | protected function doCheckParameters() { |
154 | 154 | # 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 ); |
156 | 156 | if ( $this->expiryCustom != '' ) { |
157 | 157 | // Custom expiry takes precedence |
158 | 158 | $this->expirySelection = 'othertime'; |
— | — | @@ -221,10 +221,10 @@ |
222 | 222 | return 'stabilize_expiry_old'; |
223 | 223 | } |
224 | 224 | # Update the DB row with the new config... |
225 | | - $changed = FlaggedPageConfig::setStabilitySettings( $this->page, $this->getNewConfig() ); |
| 225 | + $changed = FRPageConfig::setStabilitySettings( $this->page, $this->getNewConfig() ); |
226 | 226 | # Log if this actually changed anything... |
227 | 227 | if ( $changed ) { |
228 | | - $article = new FlaggedPage( $this->page ); |
| 228 | + $article = new FlaggableWikiPage( $this->page ); |
229 | 229 | if ( FlaggedRevs::useOnlyIfProtected() ) { |
230 | 230 | # Config may have changed to allow stable versions, so refresh |
231 | 231 | # the tracking table to account for any hidden reviewed versions... |
— | — | @@ -258,7 +258,7 @@ |
259 | 259 | # Apply watchlist checkbox value (may be NULL) |
260 | 260 | $this->updateWatchlist(); |
261 | 261 | # Take this opportunity to purge out expired configurations |
262 | | - FlaggedPageConfig::purgeExpiredConfigurations(); |
| 262 | + FRPageConfig::purgeExpiredConfigurations(); |
263 | 263 | return true; |
264 | 264 | } |
265 | 265 | |
— | — | @@ -268,7 +268,7 @@ |
269 | 269 | * (b) Add a null edit like the log entry |
270 | 270 | * @return Revision |
271 | 271 | */ |
272 | | - protected function updateLogsAndHistory( FlaggedPage $article ) { |
| 272 | + protected function updateLogsAndHistory( FlaggableWikiPage $article ) { |
273 | 273 | global $wgContLang; |
274 | 274 | $newConfig = $this->getNewConfig(); |
275 | 275 | $oldConfig = $this->getOldConfig(); |
— | — | @@ -278,7 +278,7 @@ |
279 | 279 | FlaggedRevsLog::updateStabilityLog( $this->page, $newConfig, $oldConfig, $reason ); |
280 | 280 | |
281 | 281 | # Build null-edit comment...<action: reason [settings] (expiry)> |
282 | | - if ( FlaggedPageConfig::configIsReset( $newConfig ) ) { |
| 282 | + if ( FRPageConfig::configIsReset( $newConfig ) ) { |
283 | 283 | $type = "stable-logentry-reset"; |
284 | 284 | $settings = ''; // no level, expiry info |
285 | 285 | } else { |
— | — | @@ -319,7 +319,7 @@ |
320 | 320 | throw new MWException( __CLASS__ . " input fields not set yet.\n"); |
321 | 321 | } |
322 | 322 | if ( $this->oldConfig === array() && $this->page ) { |
323 | | - $this->oldConfig = FlaggedPageConfig::getStabilitySettings( $this->page ); |
| 323 | + $this->oldConfig = FRPageConfig::getStabilitySettings( $this->page ); |
324 | 324 | } |
325 | 325 | return $this->oldConfig; |
326 | 326 | } |
— | — | @@ -406,7 +406,7 @@ |
407 | 407 | $oldConfig = $this->getOldConfig(); |
408 | 408 | if ( isset( $wgFlaggedRevsProtectQuota ) // quota exists |
409 | 409 | && $this->autoreview != '' // and we are protecting |
410 | | - && FlaggedPageConfig::getProtectionLevel( $oldConfig ) == 'none' ) // unprotected |
| 410 | + && FRPageConfig::getProtectionLevel( $oldConfig ) == 'none' ) // unprotected |
411 | 411 | { |
412 | 412 | $dbw = wfGetDB( DB_MASTER ); |
413 | 413 | $count = $dbw->selectField( 'flaggedpage_config', 'COUNT(*)', '', __METHOD__ ); |
— | — | @@ -415,7 +415,7 @@ |
416 | 416 | } |
417 | 417 | } |
418 | 418 | # Autoreview only when protecting currently unprotected pages |
419 | | - $this->reviewThis = ( FlaggedPageConfig::getProtectionLevel( $oldConfig ) == 'none' ); |
| 419 | + $this->reviewThis = ( FRPageConfig::getProtectionLevel( $oldConfig ) == 'none' ); |
420 | 420 | # Autoreview restriction => use stable |
421 | 421 | # No autoreview restriction => site default |
422 | 422 | $this->override = ( $this->autoreview != '' ) |
— | — | @@ -426,7 +426,7 @@ |
427 | 427 | 'override' => $this->override, |
428 | 428 | 'autoreview' => $this->autoreview |
429 | 429 | ); |
430 | | - if ( FlaggedPageConfig::getProtectionLevel( $newConfig ) == 'invalid' ) { |
| 430 | + if ( FRPageConfig::getProtectionLevel( $newConfig ) == 'invalid' ) { |
431 | 431 | return 'stabilize_invalid_level'; // double-check configuration |
432 | 432 | } |
433 | 433 | # 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 | | - ' ' . 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 . ' ' . $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 @@ |
130 | 130 | return true; // don't double-load |
131 | 131 | } |
132 | 132 | $loadedModules = true; |
133 | | - $fa = FlaggedPageView::globalArticleInstance(); |
| 133 | + $fa = FlaggablePageView::globalArticleInstance(); |
134 | 134 | # Try to only add to relevant pages |
135 | 135 | if ( !$fa || !$fa->isReviewable() ) { |
136 | 136 | return true; |
— | — | @@ -149,7 +149,7 @@ |
150 | 150 | $rTags = FlaggedRevs::getJSTagParams(); |
151 | 151 | $globalVars['wgFlaggedRevsParams'] = $rTags; |
152 | 152 | # Get page-specific meta-data |
153 | | - $fa = FlaggedPageView::globalArticleInstance(); |
| 153 | + $fa = FlaggablePageView::globalArticleInstance(); |
154 | 154 | # Try to only add to relevant pages |
155 | 155 | if ( $fa && $fa->isReviewable() ) { |
156 | 156 | $frev = $fa->getStableRev(); |
— | — | @@ -183,7 +183,7 @@ |
184 | 184 | */ |
185 | 185 | public static function onBeforePageDisplay( &$out, &$skin ) { |
186 | 186 | if ( $out->getTitle()->getNamespace() != NS_SPECIAL ) { |
187 | | - $view = FlaggedPageView::singleton(); |
| 187 | + $view = FlaggablePageView::singleton(); |
188 | 188 | $view->addStabilizationLink(); // link on protect form |
189 | 189 | $view->displayTag(); // show notice bar/icon in subtitle |
190 | 190 | if ( $out->isArticleRelated() ) { |
— | — | @@ -273,7 +273,7 @@ |
274 | 274 | } |
275 | 275 | |
276 | 276 | public static function onImagePageFindFile( $imagePage, &$normalFile, &$displayFile ) { |
277 | | - $view = FlaggedPageView::singleton(); |
| 277 | + $view = FlaggablePageView::singleton(); |
278 | 278 | $view->imagePageFindFile( $normalFile, $displayFile ); |
279 | 279 | return true; |
280 | 280 | } |
— | — | @@ -285,8 +285,8 @@ |
286 | 286 | // *sigh*...skip, dealt with in setNavigation() |
287 | 287 | return true; |
288 | 288 | } |
289 | | - if ( FlaggedPageView::globalArticleInstance() != null ) { |
290 | | - $view = FlaggedPageView::singleton(); |
| 289 | + if ( FlaggablePageView::globalArticleInstance() != null ) { |
| 290 | + $view = FlaggablePageView::singleton(); |
291 | 291 | $view->setActionTabs( $skin, $contentActions ); |
292 | 292 | $view->setViewTabs( $skin, $contentActions, 'flat' ); |
293 | 293 | } |
— | — | @@ -295,8 +295,8 @@ |
296 | 296 | |
297 | 297 | // Vector et al: $links is all the tabs (2 levels) |
298 | 298 | 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(); |
301 | 301 | $view->setActionTabs( $skin, $links['actions'] ); |
302 | 302 | $view->setViewTabs( $skin, $links['views'], 'nav' ); |
303 | 303 | } |
— | — | @@ -304,7 +304,7 @@ |
305 | 305 | } |
306 | 306 | |
307 | 307 | public static function onArticleViewHeader( &$article, &$outputDone, &$useParserCache ) { |
308 | | - $view = FlaggedPageView::singleton(); |
| 308 | + $view = FlaggablePageView::singleton(); |
309 | 309 | $view->addStableLink( $outputDone, $useParserCache ); |
310 | 310 | $view->setPageContent( $outputDone, $useParserCache ); |
311 | 311 | return true; |
— | — | @@ -314,7 +314,7 @@ |
315 | 315 | Title $title, WebRequest $request, &$ignoreRedirect, &$target, Article &$article |
316 | 316 | ) { |
317 | 317 | global $wgMemc, $wgParserCacheExpireTime; |
318 | | - $fa = FlaggedPage::getTitleInstance( $title ); // on $wgTitle |
| 318 | + $fa = FlaggableWikiPage::getTitleInstance( $title ); // on $wgTitle |
319 | 319 | if ( !$fa->isReviewable() ) { |
320 | 320 | return true; // nothing to do |
321 | 321 | } |
— | — | @@ -324,7 +324,7 @@ |
325 | 325 | return true; |
326 | 326 | } |
327 | 327 | $srev = $fa->getStableRev(); |
328 | | - $view = FlaggedPageView::singleton(); |
| 328 | + $view = FlaggablePageView::singleton(); |
329 | 329 | # Check if we are viewing an unsynced stable version... |
330 | 330 | if ( $srev && $view->showingStable() && $srev->getRevId() != $article->getLatest() ) { |
331 | 331 | # Check the stable redirect properties from the cache... |
— | — | @@ -357,31 +357,31 @@ |
358 | 358 | } |
359 | 359 | |
360 | 360 | public static function addToEditView( &$editPage ) { |
361 | | - $view = FlaggedPageView::singleton(); |
| 361 | + $view = FlaggablePageView::singleton(); |
362 | 362 | $view->addToEditView( $editPage ); |
363 | 363 | return true; |
364 | 364 | } |
365 | 365 | |
366 | 366 | public static function onBeforeEditButtons( &$editPage, &$buttons ) { |
367 | | - $view = FlaggedPageView::singleton(); |
| 367 | + $view = FlaggablePageView::singleton(); |
368 | 368 | $view->changeSaveButton( $editPage, $buttons ); |
369 | 369 | return true; |
370 | 370 | } |
371 | 371 | |
372 | 372 | public static function onNoSuchSection( &$editPage, &$s ) { |
373 | | - $view = FlaggedPageView::singleton(); |
| 373 | + $view = FlaggablePageView::singleton(); |
374 | 374 | $view->addToNoSuchSection( $editPage, $s ); |
375 | 375 | return true; |
376 | 376 | } |
377 | 377 | |
378 | 378 | public static function addToHistView( &$article ) { |
379 | | - $view = FlaggedPageView::singleton(); |
| 379 | + $view = FlaggablePageView::singleton(); |
380 | 380 | $view->addToHistView(); |
381 | 381 | return true; |
382 | 382 | } |
383 | 383 | |
384 | 384 | public static function onCategoryPageView( &$category ) { |
385 | | - $view = FlaggedPageView::singleton(); |
| 385 | + $view = FlaggablePageView::singleton(); |
386 | 386 | $view->addToCategoryView(); |
387 | 387 | return true; |
388 | 388 | } |
— | — | @@ -389,9 +389,9 @@ |
390 | 390 | public static function onSkinAfterContent( &$data ) { |
391 | 391 | global $wgOut; |
392 | 392 | if ( $wgOut->isArticleRelated() |
393 | | - && FlaggedPageView::globalArticleInstance() != null ) |
| 393 | + && FlaggablePageView::globalArticleInstance() != null ) |
394 | 394 | { |
395 | | - $view = FlaggedPageView::singleton(); |
| 395 | + $view = FlaggablePageView::singleton(); |
396 | 396 | // Only use this hook if we want to append the form. |
397 | 397 | // We *prepend* the form for diffs, so skip that case here. |
398 | 398 | if ( !$view->diffRevsAreSet() ) { |
— | — | @@ -410,7 +410,7 @@ |
411 | 411 | } |
412 | 412 | |
413 | 413 | public static function addToHistQuery( HistoryPager $pager, array &$queryInfo ) { |
414 | | - $flaggedArticle = FlaggedPage::getTitleInstance( $pager->getTitle() ); |
| 414 | + $flaggedArticle = FlaggableWikiPage::getTitleInstance( $pager->getTitle() ); |
415 | 415 | # Non-content pages cannot be validated. Stable version must exist. |
416 | 416 | if ( $flaggedArticle->isReviewable() && $flaggedArticle->getStableRev() ) { |
417 | 417 | # Highlight flaggedrevs |
— | — | @@ -436,7 +436,7 @@ |
437 | 437 | if ( !$file->isLocal() ) { |
438 | 438 | return true; // local files only |
439 | 439 | } |
440 | | - $flaggedArticle = FlaggedPage::getTitleInstance( $file->getTitle() ); |
| 440 | + $flaggedArticle = FlaggableWikiPage::getTitleInstance( $file->getTitle() ); |
441 | 441 | # Non-content pages cannot be validated. Stable version must exist. |
442 | 442 | if ( $flaggedArticle->isReviewable() && $flaggedArticle->getStableRev() ) { |
443 | 443 | $tables[] = 'flaggedrevs'; |
— | — | @@ -501,7 +501,7 @@ |
502 | 502 | } |
503 | 503 | |
504 | 504 | public static function addToHistLine( HistoryPager $history, $row, &$s, &$liClasses ) { |
505 | | - $fa = FlaggedPage::getTitleInstance( $history->getTitle() ); |
| 505 | + $fa = FlaggableWikiPage::getTitleInstance( $history->getTitle() ); |
506 | 506 | if ( !$fa->isReviewable() ) { |
507 | 507 | return true; // nothing to do here |
508 | 508 | } |
— | — | @@ -656,8 +656,8 @@ |
657 | 657 | } |
658 | 658 | |
659 | 659 | 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(); |
662 | 662 | $view->injectPostEditURLParams( $sectionAnchor, $extraQuery ); |
663 | 663 | } |
664 | 664 | return true; |
— | — | @@ -677,20 +677,20 @@ |
678 | 678 | |
679 | 679 | public static function onDiffViewHeader( $diff, $oldRev, $newRev ) { |
680 | 680 | self::injectStyleAndJS(); |
681 | | - $view = FlaggedPageView::singleton(); |
| 681 | + $view = FlaggablePageView::singleton(); |
682 | 682 | $view->setViewFlags( $diff, $oldRev, $newRev ); |
683 | 683 | $view->addToDiffView( $diff, $oldRev, $newRev ); |
684 | 684 | return true; |
685 | 685 | } |
686 | 686 | |
687 | 687 | public static function addRevisionIDField( $editPage, $out ) { |
688 | | - $view = FlaggedPageView::singleton(); |
| 688 | + $view = FlaggablePageView::singleton(); |
689 | 689 | $view->addRevisionIDField( $editPage, $out ); |
690 | 690 | return true; |
691 | 691 | } |
692 | 692 | |
693 | 693 | public static function addReviewCheck( $editPage, &$checkboxes, &$tabindex ) { |
694 | | - $view = FlaggedPageView::singleton(); |
| 694 | + $view = FlaggablePageView::singleton(); |
695 | 695 | $view->addReviewCheck( $editPage, $checkboxes, $tabindex ); |
696 | 696 | return true; |
697 | 697 | } |
— | — | @@ -743,12 +743,12 @@ |
744 | 744 | array() : array( 'disabled' => 'disabled' ); |
745 | 745 | |
746 | 746 | # Get the current config/expiry |
747 | | - $config = FlaggedPageConfig::getStabilitySettings( $article->getTitle(), FR_MASTER ); |
| 747 | + $config = FRPageConfig::getStabilitySettings( $article->getTitle(), FR_MASTER ); |
748 | 748 | $oldExpirySelect = ( $config['expiry'] == 'infinity' ) ? 'infinite' : 'existing'; |
749 | 749 | |
750 | 750 | # Load requested restriction level, default to current level... |
751 | 751 | $restriction = $wgRequest->getVal( 'mwStabilityLevel', |
752 | | - FlaggedPageConfig::getProtectionLevel( $config ) ); |
| 752 | + FRPageConfig::getProtectionLevel( $config ) ); |
753 | 753 | # Load the requested expiry time (dropdown) |
754 | 754 | $expirySelect = $wgRequest->getVal( 'mwStabilizeExpirySelection', $oldExpirySelect ); |
755 | 755 | # Load the requested expiry time (field) |
Index: trunk/extensions/FlaggedRevs/presentation/FlaggedRevsXML.php |
— | — | @@ -371,7 +371,7 @@ |
372 | 372 | |
373 | 373 | /* |
374 | 374 | * Creates CSS lock icon if page is locked/unlocked |
375 | | - * @param FlaggedPage $flaggedArticle |
| 375 | + * @param FlaggableWikiPage $flaggedArticle |
376 | 376 | * @return string |
377 | 377 | */ |
378 | 378 | public static function lockStatusIcon( $flaggedArticle ) { |
— | — | @@ -388,7 +388,7 @@ |
389 | 389 | } |
390 | 390 | |
391 | 391 | /* |
392 | | - * @param FlaggedPage $flaggedArticle |
| 392 | + * @param FlaggableWikiPage $flaggedArticle |
393 | 393 | * @param FlaggedRevision $frev |
394 | 394 | * @param int $revsSince |
395 | 395 | * @return string |
Index: trunk/extensions/FlaggedRevs/presentation/specialpages/reports/ConfiguredPages_body.php |
— | — | @@ -66,7 +66,7 @@ |
67 | 67 | } |
68 | 68 | # Purge expired entries on one in every 10 queries |
69 | 69 | if ( !mt_rand( 0, 10 ) ) { |
70 | | - FlaggedPageConfig::purgeExpiredConfigurations(); |
| 70 | + FRPageConfig::purgeExpiredConfigurations(); |
71 | 71 | } |
72 | 72 | } |
73 | 73 | |
Index: trunk/extensions/FlaggedRevs/presentation/specialpages/reports/StablePages_body.php |
— | — | @@ -64,7 +64,7 @@ |
65 | 65 | } |
66 | 66 | # Purge expired entries on one in every 10 queries |
67 | 67 | if ( !mt_rand( 0, 10 ) ) { |
68 | | - FlaggedPageConfig::purgeExpiredConfigurations(); |
| 68 | + FRPageConfig::purgeExpiredConfigurations(); |
69 | 69 | } |
70 | 70 | } |
71 | 71 | |
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 | + ' ' . 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 . ' ' . $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 |
1 | 1892 | + native |
Index: trunk/extensions/FlaggedRevs/presentation/RevisionReviewFormUI.php |
— | — | @@ -16,10 +16,10 @@ |
17 | 17 | /** |
18 | 18 | * Generates a brief review form for a page |
19 | 19 | * @param RequestContext $context |
20 | | - * @param FlaggedPage $article |
| 20 | + * @param FlaggableWikiPage $article |
21 | 21 | * @param Revision $rev |
22 | 22 | */ |
23 | | - public function __construct( IContextSource $context, FlaggedPage $article, Revision $rev ) { |
| 23 | + public function __construct( IContextSource $context, FlaggableWikiPage $article, Revision $rev ) { |
24 | 24 | $this->user = $context->getUser(); |
25 | 25 | $this->request = $context->getRequest(); |
26 | 26 | $this->article = $article; |
Index: trunk/extensions/FlaggedRevs/presentation/modules/review.js |
— | — | @@ -272,7 +272,7 @@ |
273 | 273 | requestArgs.push( diffUIParams.getElementsByTagName('input')[0].value ); |
274 | 274 | requestArgs.push( diffUIParams.getElementsByTagName('input')[1].value ); |
275 | 275 | // Send encoded function plus all arguments... |
276 | | - var url_pars = '?action=ajax&rs=FlaggedPageView::AjaxBuildDiffHeaderItems'; |
| 276 | + var url_pars = '?action=ajax&rs=FlaggablePageView::AjaxBuildDiffHeaderItems'; |
277 | 277 | for( var i=0; i<requestArgs.length; i++ ) { |
278 | 278 | url_pars += '&rsargs[]=' + encodeURIComponent(requestArgs[i]); |
279 | 279 | } |