Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 58 |
|
0.00% |
0 / 10 |
CRAP | |
0.00% |
0 / 1 |
RevisionDeleter | |
0.00% |
0 / 58 |
|
0.00% |
0 / 10 |
600 | |
0.00% |
0 / 1 |
getTypes | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCanonicalTypeName | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
12 | |||
createList | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
6 | |||
checkItem | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
12 | |||
getChanges | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
12 | |||
getRelationType | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getRestriction | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getRevdelConstant | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
suggestTarget | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
extractBitfield | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
20 |
1 | <?php |
2 | /** |
3 | * Revision/log/file deletion backend |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | * @ingroup RevisionDelete |
22 | */ |
23 | |
24 | use MediaWiki\Context\IContextSource; |
25 | use MediaWiki\MediaWikiServices; |
26 | use MediaWiki\Page\PageIdentity; |
27 | use MediaWiki\Revision\RevisionRecord; |
28 | use MediaWiki\Title\Title; |
29 | |
30 | /** |
31 | * General controller for RevDel, used by both SpecialRevisiondelete and |
32 | * ApiRevisionDelete. |
33 | * @ingroup RevisionDelete |
34 | */ |
35 | class RevisionDeleter { |
36 | /** |
37 | * List of known revdel types, with their corresponding ObjectFactory spec to |
38 | * create the relevant class. All specs need to include DBLoadBalancerFactory, |
39 | * which is used in the base RevDelList class |
40 | */ |
41 | private const ALLOWED_TYPES = [ |
42 | 'revision' => [ |
43 | 'class' => RevDelRevisionList::class, |
44 | 'services' => [ |
45 | 'DBLoadBalancerFactory', |
46 | 'HookContainer', |
47 | 'HtmlCacheUpdater', |
48 | 'RevisionStore', |
49 | ], |
50 | ], |
51 | 'archive' => [ |
52 | 'class' => RevDelArchiveList::class, |
53 | 'services' => [ |
54 | 'DBLoadBalancerFactory', |
55 | 'HookContainer', |
56 | 'HtmlCacheUpdater', |
57 | 'RevisionStore', |
58 | ], |
59 | ], |
60 | 'oldimage' => [ |
61 | 'class' => RevDelFileList::class, |
62 | 'services' => [ |
63 | 'DBLoadBalancerFactory', |
64 | 'HtmlCacheUpdater', |
65 | 'RepoGroup', |
66 | ], |
67 | ], |
68 | 'filearchive' => [ |
69 | 'class' => RevDelArchivedFileList::class, |
70 | 'services' => [ |
71 | 'DBLoadBalancerFactory', |
72 | 'HtmlCacheUpdater', |
73 | 'RepoGroup', |
74 | ], |
75 | ], |
76 | 'logging' => [ |
77 | 'class' => RevDelLogList::class, |
78 | 'services' => [ |
79 | 'DBLoadBalancerFactory', |
80 | 'CommentStore', |
81 | 'LogFormatterFactory', |
82 | ], |
83 | ], |
84 | ]; |
85 | |
86 | /** Type map to support old log entries */ |
87 | private const DEPRECATED_TYPE_MAP = [ |
88 | 'oldid' => 'revision', |
89 | 'artimestamp' => 'archive', |
90 | 'oldimage' => 'oldimage', |
91 | 'fileid' => 'filearchive', |
92 | 'logid' => 'logging', |
93 | ]; |
94 | |
95 | /** |
96 | * Lists the valid possible types for revision deletion. |
97 | * |
98 | * @since 1.22 |
99 | * @return array |
100 | */ |
101 | public static function getTypes() { |
102 | return array_keys( self::ALLOWED_TYPES ); |
103 | } |
104 | |
105 | /** |
106 | * Gets the canonical type name, if any. |
107 | * |
108 | * @since 1.22 |
109 | * @param string $typeName |
110 | * @return string|null |
111 | */ |
112 | public static function getCanonicalTypeName( $typeName ) { |
113 | if ( isset( self::DEPRECATED_TYPE_MAP[$typeName] ) ) { |
114 | $typeName = self::DEPRECATED_TYPE_MAP[$typeName]; |
115 | } |
116 | return isset( self::ALLOWED_TYPES[$typeName] ) ? $typeName : null; |
117 | } |
118 | |
119 | /** |
120 | * Instantiate the appropriate list class for a given list of IDs. |
121 | * |
122 | * @since 1.22 |
123 | * @param string $typeName RevDel type, see RevisionDeleter::getTypes() |
124 | * @param IContextSource $context |
125 | * @param PageIdentity $page |
126 | * @param array $ids |
127 | * @return RevDelList |
128 | */ |
129 | public static function createList( $typeName, IContextSource $context, PageIdentity $page, array $ids ) { |
130 | $typeName = self::getCanonicalTypeName( $typeName ); |
131 | if ( !$typeName ) { |
132 | throw new InvalidArgumentException( __METHOD__ . ": Unknown RevDel type '$typeName'" ); |
133 | } |
134 | $spec = self::ALLOWED_TYPES[$typeName]; |
135 | $objectFactory = MediaWikiServices::getInstance()->getObjectFactory(); |
136 | |
137 | // ObjectFactory::createObject accepts an array, not just a callable (phan bug) |
138 | // @phan-suppress-next-line PhanTypeInvalidCallableArrayKey |
139 | return $objectFactory->createObject( |
140 | $spec, |
141 | [ |
142 | 'extraArgs' => [ $context, $page, $ids ], |
143 | 'assertClass' => RevDelList::class, |
144 | ] |
145 | ); |
146 | } |
147 | |
148 | /** |
149 | * Checks for a change in the bitfield for a certain option and updates the |
150 | * provided array accordingly. |
151 | * |
152 | * @param string $desc Description to add to the array if the option was |
153 | * enabled / disabled. |
154 | * @param int $field The bitmask describing the single option. |
155 | * @param int $diff The xor of the old and new bitfields. |
156 | * @param int $new The new bitfield |
157 | * @param array &$arr The array to update. |
158 | */ |
159 | protected static function checkItem( $desc, $field, $diff, $new, &$arr ) { |
160 | if ( $diff & $field ) { |
161 | $arr[( $new & $field ) ? 0 : 1][] = $desc; |
162 | } |
163 | } |
164 | |
165 | /** |
166 | * Gets an array of message keys describing the changes made to the |
167 | * visibility of the revision. |
168 | * |
169 | * If the resulting array is $arr, then $arr[0] will contain an array of |
170 | * keys describing the items that were hidden, $arr[1] will contain |
171 | * an array of keys describing the items that were unhidden, and $arr[2] |
172 | * will contain an array with a single message key, which can be one of |
173 | * "revdelete-restricted", "revdelete-unrestricted" indicating (un)suppression |
174 | * or null to indicate nothing in particular. |
175 | * You can turn the keys in $arr[0] and $arr[1] into message keys by |
176 | * appending -hid and -unhid to the keys respectively. |
177 | * |
178 | * @param int $n The new bitfield. |
179 | * @param int $o The old bitfield. |
180 | * @return array An array as described above. |
181 | * @since 1.19 public |
182 | */ |
183 | public static function getChanges( $n, $o ) { |
184 | $diff = $n ^ $o; |
185 | $ret = [ 0 => [], 1 => [], 2 => [] ]; |
186 | // Build bitfield changes in language |
187 | self::checkItem( 'revdelete-content', |
188 | RevisionRecord::DELETED_TEXT, $diff, $n, $ret ); |
189 | self::checkItem( 'revdelete-summary', |
190 | RevisionRecord::DELETED_COMMENT, $diff, $n, $ret ); |
191 | self::checkItem( 'revdelete-uname', |
192 | RevisionRecord::DELETED_USER, $diff, $n, $ret ); |
193 | // Restriction application to sysops |
194 | if ( $diff & RevisionRecord::DELETED_RESTRICTED ) { |
195 | if ( $n & RevisionRecord::DELETED_RESTRICTED ) { |
196 | $ret[2][] = 'revdelete-restricted'; |
197 | } else { |
198 | $ret[2][] = 'revdelete-unrestricted'; |
199 | } |
200 | } |
201 | return $ret; |
202 | } |
203 | |
204 | /** Get DB field name for URL param... |
205 | * Future code for other things may also track |
206 | * other types of revision-specific changes. |
207 | * @param string $typeName |
208 | * @return string|null One of log_id/rev_id/fa_id/ar_timestamp/oi_archive_name |
209 | */ |
210 | public static function getRelationType( $typeName ) { |
211 | $typeName = self::getCanonicalTypeName( $typeName ); |
212 | if ( !$typeName ) { |
213 | return null; |
214 | } |
215 | return call_user_func( [ self::ALLOWED_TYPES[$typeName]['class'], 'getRelationType' ] ); |
216 | } |
217 | |
218 | /** |
219 | * Get the user right required for the RevDel type |
220 | * @since 1.22 |
221 | * @param string $typeName |
222 | * @return string|null User right |
223 | */ |
224 | public static function getRestriction( $typeName ) { |
225 | $typeName = self::getCanonicalTypeName( $typeName ); |
226 | if ( !$typeName ) { |
227 | return null; |
228 | } |
229 | return call_user_func( [ self::ALLOWED_TYPES[$typeName]['class'], 'getRestriction' ] ); |
230 | } |
231 | |
232 | /** |
233 | * Get the revision deletion constant for the RevDel type |
234 | * @since 1.22 |
235 | * @param string $typeName |
236 | * @return int|null RevDel constant |
237 | */ |
238 | public static function getRevdelConstant( $typeName ) { |
239 | $typeName = self::getCanonicalTypeName( $typeName ); |
240 | if ( !$typeName ) { |
241 | return null; |
242 | } |
243 | return call_user_func( [ self::ALLOWED_TYPES[$typeName]['class'], 'getRevdelConstant' ] ); |
244 | } |
245 | |
246 | /** |
247 | * Suggest a target for the revision deletion |
248 | * @since 1.22 |
249 | * @param string $typeName |
250 | * @param Title|null $target User-supplied target |
251 | * @param array $ids |
252 | * @return Title|null |
253 | */ |
254 | public static function suggestTarget( $typeName, $target, array $ids ) { |
255 | $typeName = self::getCanonicalTypeName( $typeName ); |
256 | if ( !$typeName ) { |
257 | return $target; |
258 | } |
259 | return call_user_func( |
260 | [ self::ALLOWED_TYPES[$typeName]['class'], 'suggestTarget' ], |
261 | $target, |
262 | $ids |
263 | ); |
264 | } |
265 | |
266 | /** |
267 | * Put together a rev_deleted bitfield |
268 | * @since 1.22 |
269 | * @param array $bitPars ExtractBitParams() params |
270 | * @param int $oldfield Current bitfield |
271 | * @return int |
272 | */ |
273 | public static function extractBitfield( array $bitPars, $oldfield ) { |
274 | // Build the actual new rev_deleted bitfield |
275 | $newBits = 0; |
276 | foreach ( $bitPars as $const => $val ) { |
277 | if ( $val == 1 ) { |
278 | $newBits |= $const; // $const is the *_deleted const |
279 | } elseif ( $val == -1 ) { |
280 | $newBits |= ( $oldfield & $const ); // use existing |
281 | } |
282 | } |
283 | return $newBits; |
284 | } |
285 | } |