Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 125 |
|
0.00% |
0 / 8 |
CRAP | |
0.00% |
0 / 1 |
FRPageConfig | |
0.00% |
0 / 125 |
|
0.00% |
0 / 8 |
1722 | |
0.00% |
0 / 1 |
getStabilitySettings | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 | |||
getVisibilitySettingsFromRow | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
110 | |||
getDefaultVisibilitySettings | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
setStabilitySettings | |
0.00% |
0 / 36 |
|
0.00% |
0 / 1 |
56 | |||
configIsReset | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
getProtectionLevel | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
42 | |||
isValidRestriction | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
purgeExpiredConfigurations | |
0.00% |
0 / 38 |
|
0.00% |
0 / 1 |
90 |
1 | <?php |
2 | |
3 | use MediaWiki\MediaWikiServices; |
4 | use MediaWiki\Title\Title; |
5 | |
6 | /** |
7 | * Page stability configuration functions |
8 | */ |
9 | class FRPageConfig { |
10 | /** |
11 | * Get visibility settings/restrictions for a page |
12 | * @param Title $title page title |
13 | * @param int $flags One of the IDBAccessObject::READ_… constants |
14 | * @return array [ 'override' => int, 'autoreview' => string, 'expiry' => string ] |
15 | */ |
16 | public static function getStabilitySettings( Title $title, $flags = 0 ) { |
17 | if ( $flags & IDBAccessObject::READ_LATEST ) { |
18 | $db = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase(); |
19 | } else { |
20 | $db = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase(); |
21 | } |
22 | $row = $db->newSelectQueryBuilder() |
23 | ->select( [ 'fpc_override', 'fpc_level', 'fpc_expiry' ] ) |
24 | ->from( 'flaggedpage_config' ) |
25 | ->where( [ 'fpc_page_id' => $title->getArticleID() ] ) |
26 | ->caller( __METHOD__ ) |
27 | ->fetchRow(); |
28 | return self::getVisibilitySettingsFromRow( $row ); |
29 | } |
30 | |
31 | /** |
32 | * Get page configuration settings from a DB row |
33 | * @param stdClass|false $row |
34 | * @return array [ 'override' => int, 'autoreview' => string, 'expiry' => string ] |
35 | */ |
36 | public static function getVisibilitySettingsFromRow( $row ) { |
37 | $expiry = false; |
38 | if ( $row ) { |
39 | $expiry = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase() |
40 | ->decodeExpiry( $row->fpc_expiry ); |
41 | # Only apply the settings if they haven't expired |
42 | if ( !$expiry || $expiry < wfTimestampNow() ) { |
43 | $row = null; // expired |
44 | } |
45 | } |
46 | // Is there a non-expired row? |
47 | if ( !$row ) { |
48 | # Return the default config if this page doesn't have its own |
49 | return self::getDefaultVisibilitySettings(); |
50 | } |
51 | |
52 | $level = self::isValidRestriction( $row->fpc_level ) ? $row->fpc_level : ''; |
53 | $config = [ |
54 | 'override' => $row->fpc_override ? 1 : 0, |
55 | 'autoreview' => $level, |
56 | 'expiry' => $expiry // TS_MW |
57 | ]; |
58 | # If there are protection levels defined check if this is valid... |
59 | if ( FlaggedRevs::useProtectionLevels() ) { |
60 | $level = self::getProtectionLevel( $config ); |
61 | if ( $level == 'invalid' || $level == 'none' ) { |
62 | // If 'none', make sure expiry is 'infinity' |
63 | return self::getDefaultVisibilitySettings(); // revert to default (none) |
64 | } |
65 | } |
66 | return $config; |
67 | } |
68 | |
69 | /** |
70 | * Get default stability configuration settings |
71 | * @return array |
72 | */ |
73 | public static function getDefaultVisibilitySettings() { |
74 | return [ |
75 | # Keep this consistent: 1 => override, 0 => don't |
76 | 'override' => FlaggedRevs::isStableShownByDefault() ? 1 : 0, |
77 | 'autoreview' => '', |
78 | 'expiry' => 'infinity' |
79 | ]; |
80 | } |
81 | |
82 | /** |
83 | * Set the stability configuration settings for a page |
84 | * @param Title $title |
85 | * @param array $config |
86 | * @return bool Row changed |
87 | */ |
88 | public static function setStabilitySettings( Title $title, array $config ) { |
89 | $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase(); |
90 | |
91 | # Purge expired entries on one in every 10 queries |
92 | if ( !mt_rand( 0, 10 ) ) { |
93 | self::purgeExpiredConfigurations(); |
94 | } |
95 | # If setting to site default values and there is a row then erase it |
96 | if ( self::configIsReset( $config ) ) { |
97 | $dbw->newDeleteQueryBuilder() |
98 | ->deleteFrom( 'flaggedpage_config' ) |
99 | ->where( [ 'fpc_page_id' => $title->getArticleID() ] ) |
100 | ->caller( __METHOD__ ) |
101 | ->execute(); |
102 | $changed = ( $dbw->affectedRows() > 0 ); // did this do anything? |
103 | # Otherwise, add/replace row if we are not just setting it to the site default |
104 | } else { |
105 | $dbExpiry = $dbw->encodeExpiry( $config['expiry'] ); |
106 | # Get current config... |
107 | $oldRow = $dbw->newSelectQueryBuilder() |
108 | ->select( [ 'fpc_override', 'fpc_level', 'fpc_expiry' ] ) |
109 | ->from( 'flaggedpage_config' ) |
110 | ->where( [ 'fpc_page_id' => $title->getArticleID() ] ) |
111 | ->forUpdate() // lock |
112 | ->caller( __METHOD__ ) |
113 | ->fetchRow(); |
114 | # Check if this is not the same config as the existing (if any) row |
115 | $changed = ( !$oldRow // no previous config |
116 | || $oldRow->fpc_override != $config['override'] // ...override changed, or... |
117 | || $oldRow->fpc_level != $config['autoreview'] // ...autoreview level changed, or... |
118 | || $oldRow->fpc_expiry != $dbExpiry // ...expiry changed |
119 | ); |
120 | # If the new config is different, replace the old row... |
121 | if ( $changed ) { |
122 | $dbw->newReplaceQueryBuilder() |
123 | ->replaceInto( 'flaggedpage_config' ) |
124 | ->uniqueIndexFields( 'fpc_page_id' ) |
125 | ->row( [ |
126 | 'fpc_page_id' => $title->getArticleID(), |
127 | 'fpc_override' => (int)$config['override'], |
128 | 'fpc_level' => $config['autoreview'], |
129 | 'fpc_expiry' => $dbExpiry |
130 | ] ) |
131 | ->caller( __METHOD__ ) |
132 | ->execute(); |
133 | } |
134 | } |
135 | return $changed; |
136 | } |
137 | |
138 | /** |
139 | * Does this config equal the default settings? |
140 | * @param array $config |
141 | * @return bool |
142 | */ |
143 | public static function configIsReset( array $config ) { |
144 | if ( FlaggedRevs::useOnlyIfProtected() ) { |
145 | return ( $config['autoreview'] == '' ); |
146 | } else { |
147 | return ( $config['override'] == FlaggedRevs::isStableShownByDefault() |
148 | && $config['autoreview'] == '' ); |
149 | } |
150 | } |
151 | |
152 | /** |
153 | * Find what protection level a config is in |
154 | * @param array $config |
155 | * @return string |
156 | */ |
157 | public static function getProtectionLevel( array $config ) { |
158 | if ( !FlaggedRevs::useProtectionLevels() ) { |
159 | throw new LogicException( '$wgFlaggedRevsProtection is disabled' ); |
160 | } |
161 | $defaultConfig = self::getDefaultVisibilitySettings(); |
162 | # Check if the page is not protected at all... |
163 | if ( $config['override'] == $defaultConfig['override'] |
164 | && $config['autoreview'] == '' |
165 | ) { |
166 | return "none"; // not protected |
167 | } |
168 | # All protection levels have 'override' on |
169 | if ( $config['override'] ) { |
170 | # The levels are defined by the 'autoreview' settings |
171 | if ( in_array( $config['autoreview'], FlaggedRevs::getRestrictionLevels() ) ) { |
172 | return $config['autoreview']; |
173 | } |
174 | } |
175 | return "invalid"; |
176 | } |
177 | |
178 | /** |
179 | * Check if an fpc_level value is valid |
180 | * @param string $right |
181 | * @return bool |
182 | */ |
183 | private static function isValidRestriction( $right ) { |
184 | if ( $right == '' ) { |
185 | return true; // no restrictions (none) |
186 | } |
187 | return in_array( $right, FlaggedRevs::getRestrictionLevels(), true ); |
188 | } |
189 | |
190 | /** |
191 | * Purge expired restrictions from the flaggedpage_config table. |
192 | * The stable version of pages may change and invalidation may be required. |
193 | */ |
194 | private static function purgeExpiredConfigurations() { |
195 | if ( MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() ) { |
196 | return; |
197 | } |
198 | $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase(); |
199 | |
200 | # Find pages with expired configs... |
201 | $config = self::getDefaultVisibilitySettings(); // config is to be reset |
202 | $cutoff = $dbw->timestamp(); |
203 | $ret = $dbw->newSelectQueryBuilder() |
204 | ->select( [ 'fpc_page_id', 'page_namespace', 'page_title' ] ) |
205 | ->from( 'flaggedpage_config' ) |
206 | ->join( 'page', null, 'page_id = fpc_page_id' ) |
207 | ->where( $dbw->expr( 'fpc_expiry', '<', $cutoff ) ) |
208 | ->caller( __METHOD__ ) |
209 | ->fetchResultSet(); |
210 | # Figured out to do with each page... |
211 | $pagesClearConfig = []; |
212 | $pagesClearTracking = []; |
213 | $titlesClearTracking = []; |
214 | foreach ( $ret as $row ) { |
215 | # If FlaggedRevs got "turned off" (in protection config) |
216 | # for this page, then clear it from the tracking tables... |
217 | if ( FlaggedRevs::useOnlyIfProtected() && !$config['override'] ) { |
218 | $pagesClearTracking[] = $row->fpc_page_id; // no stable version |
219 | $titlesClearTracking[] = Title::newFromRow( $row ); // no stable version |
220 | } |
221 | $pagesClearConfig[] = $row->fpc_page_id; // page with expired config |
222 | } |
223 | # Clear the expired config for these pages... |
224 | if ( count( $pagesClearConfig ) ) { |
225 | $dbw->newDeleteQueryBuilder() |
226 | ->deleteFrom( 'flaggedpage_config' ) |
227 | ->where( [ 'fpc_page_id' => $pagesClearConfig, $dbw->expr( 'fpc_expiry', '<', $cutoff ) ] ) |
228 | ->caller( __METHOD__ ) |
229 | ->execute(); |
230 | } |
231 | # Clear the tracking rows and update page_touched for the |
232 | # pages in $pagesClearConfig that do now have a stable version... |
233 | if ( count( $pagesClearTracking ) ) { |
234 | FlaggedRevs::clearTrackingRows( $pagesClearTracking ); |
235 | $dbw->newUpdateQueryBuilder() |
236 | ->update( 'page' ) |
237 | ->set( [ 'page_touched' => $dbw->timestamp() ] ) |
238 | ->where( [ 'page_id' => $pagesClearTracking ] ) |
239 | ->caller( __METHOD__ ) |
240 | ->execute(); |
241 | } |
242 | # Also, clear their squid caches and purge other pages that use this page. |
243 | # NOTE: all of these updates are deferred via DeferredUpdates |
244 | foreach ( $titlesClearTracking as $title ) { |
245 | FlaggedRevs::purgeMediaWikiHtmlCdn( $title ); |
246 | if ( FlaggedRevs::inclusionSetting() == FR_INCLUDES_STABLE ) { |
247 | FlaggedRevs::updateHtmlCaches( $title ); // purge pages that use this page |
248 | } |
249 | } |
250 | } |
251 | } |