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 | ], |
82 | ], |
83 | ]; |
84 | |
85 | /** Type map to support old log entries */ |
86 | private const DEPRECATED_TYPE_MAP = [ |
87 | 'oldid' => 'revision', |
88 | 'artimestamp' => 'archive', |
89 | 'oldimage' => 'oldimage', |
90 | 'fileid' => 'filearchive', |
91 | 'logid' => 'logging', |
92 | ]; |
93 | |
94 | /** |
95 | * Lists the valid possible types for revision deletion. |
96 | * |
97 | * @since 1.22 |
98 | * @return array |
99 | */ |
100 | public static function getTypes() { |
101 | return array_keys( self::ALLOWED_TYPES ); |
102 | } |
103 | |
104 | /** |
105 | * Gets the canonical type name, if any. |
106 | * |
107 | * @since 1.22 |
108 | * @param string $typeName |
109 | * @return string|null |
110 | */ |
111 | public static function getCanonicalTypeName( $typeName ) { |
112 | if ( isset( self::DEPRECATED_TYPE_MAP[$typeName] ) ) { |
113 | $typeName = self::DEPRECATED_TYPE_MAP[$typeName]; |
114 | } |
115 | return isset( self::ALLOWED_TYPES[$typeName] ) ? $typeName : null; |
116 | } |
117 | |
118 | /** |
119 | * Instantiate the appropriate list class for a given list of IDs. |
120 | * |
121 | * @since 1.22 |
122 | * @param string $typeName RevDel type, see RevisionDeleter::getTypes() |
123 | * @param IContextSource $context |
124 | * @param PageIdentity $page |
125 | * @param array $ids |
126 | * @return RevDelList |
127 | */ |
128 | public static function createList( $typeName, IContextSource $context, PageIdentity $page, array $ids ) { |
129 | $typeName = self::getCanonicalTypeName( $typeName ); |
130 | if ( !$typeName ) { |
131 | throw new InvalidArgumentException( __METHOD__ . ": Unknown RevDel type '$typeName'" ); |
132 | } |
133 | $spec = self::ALLOWED_TYPES[$typeName]; |
134 | $objectFactory = MediaWikiServices::getInstance()->getObjectFactory(); |
135 | |
136 | // ObjectFactory::createObject accepts an array, not just a callable (phan bug) |
137 | // @phan-suppress-next-line PhanTypeInvalidCallableArrayKey |
138 | return $objectFactory->createObject( |
139 | $spec, |
140 | [ |
141 | 'extraArgs' => [ $context, $page, $ids ], |
142 | 'assertClass' => RevDelList::class, |
143 | ] |
144 | ); |
145 | } |
146 | |
147 | /** |
148 | * Checks for a change in the bitfield for a certain option and updates the |
149 | * provided array accordingly. |
150 | * |
151 | * @param string $desc Description to add to the array if the option was |
152 | * enabled / disabled. |
153 | * @param int $field The bitmask describing the single option. |
154 | * @param int $diff The xor of the old and new bitfields. |
155 | * @param int $new The new bitfield |
156 | * @param array &$arr The array to update. |
157 | */ |
158 | protected static function checkItem( $desc, $field, $diff, $new, &$arr ) { |
159 | if ( $diff & $field ) { |
160 | $arr[( $new & $field ) ? 0 : 1][] = $desc; |
161 | } |
162 | } |
163 | |
164 | /** |
165 | * Gets an array of message keys describing the changes made to the |
166 | * visibility of the revision. |
167 | * |
168 | * If the resulting array is $arr, then $arr[0] will contain an array of |
169 | * keys describing the items that were hidden, $arr[1] will contain |
170 | * an array of keys describing the items that were unhidden, and $arr[2] |
171 | * will contain an array with a single message key, which can be one of |
172 | * "revdelete-restricted", "revdelete-unrestricted" indicating (un)suppression |
173 | * or null to indicate nothing in particular. |
174 | * You can turn the keys in $arr[0] and $arr[1] into message keys by |
175 | * appending -hid and -unhid to the keys respectively. |
176 | * |
177 | * @param int $n The new bitfield. |
178 | * @param int $o The old bitfield. |
179 | * @return array An array as described above. |
180 | * @since 1.19 public |
181 | */ |
182 | public static function getChanges( $n, $o ) { |
183 | $diff = $n ^ $o; |
184 | $ret = [ 0 => [], 1 => [], 2 => [] ]; |
185 | // Build bitfield changes in language |
186 | self::checkItem( 'revdelete-content', |
187 | RevisionRecord::DELETED_TEXT, $diff, $n, $ret ); |
188 | self::checkItem( 'revdelete-summary', |
189 | RevisionRecord::DELETED_COMMENT, $diff, $n, $ret ); |
190 | self::checkItem( 'revdelete-uname', |
191 | RevisionRecord::DELETED_USER, $diff, $n, $ret ); |
192 | // Restriction application to sysops |
193 | if ( $diff & RevisionRecord::DELETED_RESTRICTED ) { |
194 | if ( $n & RevisionRecord::DELETED_RESTRICTED ) { |
195 | $ret[2][] = 'revdelete-restricted'; |
196 | } else { |
197 | $ret[2][] = 'revdelete-unrestricted'; |
198 | } |
199 | } |
200 | return $ret; |
201 | } |
202 | |
203 | /** Get DB field name for URL param... |
204 | * Future code for other things may also track |
205 | * other types of revision-specific changes. |
206 | * @param string $typeName |
207 | * @return string|null One of log_id/rev_id/fa_id/ar_timestamp/oi_archive_name |
208 | */ |
209 | public static function getRelationType( $typeName ) { |
210 | $typeName = self::getCanonicalTypeName( $typeName ); |
211 | if ( !$typeName ) { |
212 | return null; |
213 | } |
214 | return call_user_func( [ self::ALLOWED_TYPES[$typeName]['class'], 'getRelationType' ] ); |
215 | } |
216 | |
217 | /** |
218 | * Get the user right required for the RevDel type |
219 | * @since 1.22 |
220 | * @param string $typeName |
221 | * @return string|null User right |
222 | */ |
223 | public static function getRestriction( $typeName ) { |
224 | $typeName = self::getCanonicalTypeName( $typeName ); |
225 | if ( !$typeName ) { |
226 | return null; |
227 | } |
228 | return call_user_func( [ self::ALLOWED_TYPES[$typeName]['class'], 'getRestriction' ] ); |
229 | } |
230 | |
231 | /** |
232 | * Get the revision deletion constant for the RevDel type |
233 | * @since 1.22 |
234 | * @param string $typeName |
235 | * @return int|null RevDel constant |
236 | */ |
237 | public static function getRevdelConstant( $typeName ) { |
238 | $typeName = self::getCanonicalTypeName( $typeName ); |
239 | if ( !$typeName ) { |
240 | return null; |
241 | } |
242 | return call_user_func( [ self::ALLOWED_TYPES[$typeName]['class'], 'getRevdelConstant' ] ); |
243 | } |
244 | |
245 | /** |
246 | * Suggest a target for the revision deletion |
247 | * @since 1.22 |
248 | * @param string $typeName |
249 | * @param Title|null $target User-supplied target |
250 | * @param array $ids |
251 | * @return Title|null |
252 | */ |
253 | public static function suggestTarget( $typeName, $target, array $ids ) { |
254 | $typeName = self::getCanonicalTypeName( $typeName ); |
255 | if ( !$typeName ) { |
256 | return $target; |
257 | } |
258 | return call_user_func( |
259 | [ self::ALLOWED_TYPES[$typeName]['class'], 'suggestTarget' ], |
260 | $target, |
261 | $ids |
262 | ); |
263 | } |
264 | |
265 | /** |
266 | * Put together a rev_deleted bitfield |
267 | * @since 1.22 |
268 | * @param array $bitPars ExtractBitParams() params |
269 | * @param int $oldfield Current bitfield |
270 | * @return int |
271 | */ |
272 | public static function extractBitfield( array $bitPars, $oldfield ) { |
273 | // Build the actual new rev_deleted bitfield |
274 | $newBits = 0; |
275 | foreach ( $bitPars as $const => $val ) { |
276 | if ( $val == 1 ) { |
277 | $newBits |= $const; // $const is the *_deleted const |
278 | } elseif ( $val == -1 ) { |
279 | $newBits |= ( $oldfield & $const ); // use existing |
280 | } |
281 | } |
282 | return $newBits; |
283 | } |
284 | } |