Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
56.76% |
105 / 185 |
|
83.33% |
5 / 6 |
CRAP | |
0.00% |
0 / 1 |
DeleteLogFormatter | |
56.76% |
105 / 185 |
|
83.33% |
5 / 6 |
340.49 | |
0.00% |
0 / 1 |
getMessageKey | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
5 | |||
getMessageParameters | |
100.00% |
41 / 41 |
|
100.00% |
1 / 1 |
18 | |||
parseBitField | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getActionLinks | |
0.00% |
0 / 80 |
|
0.00% |
0 / 1 |
420 | |||
getParametersForApi | |
100.00% |
47 / 47 |
|
100.00% |
1 / 1 |
12 | |||
formatParametersForApi | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Formatter for delete log entries. |
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 | * @author Niklas Laxström |
22 | * @license GPL-2.0-or-later |
23 | * @since 1.22 |
24 | */ |
25 | |
26 | use MediaWiki\Revision\RevisionRecord; |
27 | use MediaWiki\SpecialPage\SpecialPage; |
28 | |
29 | /** |
30 | * This class formats delete log entries. |
31 | * |
32 | * @since 1.19 |
33 | */ |
34 | class DeleteLogFormatter extends LogFormatter { |
35 | /** @var array|null */ |
36 | private $parsedParametersDeleteLog; |
37 | |
38 | /** |
39 | * @inheritDoc |
40 | */ |
41 | protected function getMessageKey() { |
42 | $key = parent::getMessageKey(); |
43 | if ( in_array( $this->entry->getSubtype(), [ 'event', 'revision' ] ) ) { |
44 | if ( count( $this->getMessageParameters() ) < 5 ) { |
45 | // Messages: logentry-delete-event-legacy, logentry-delete-revision-legacy, |
46 | // logentry-suppress-event-legacy, logentry-suppress-revision-legacy |
47 | return "$key-legacy"; |
48 | } |
49 | } elseif ( $this->entry->getSubtype() === 'restore' ) { |
50 | $rawParams = $this->entry->getParameters(); |
51 | if ( !isset( $rawParams[':assoc:count'] ) ) { |
52 | // Message: logentry-delete-restore-nocount |
53 | return $key . '-nocount'; |
54 | } |
55 | } |
56 | |
57 | return $key; |
58 | } |
59 | |
60 | /** |
61 | * @inheritDoc |
62 | */ |
63 | protected function getMessageParameters() { |
64 | if ( $this->parsedParametersDeleteLog !== null ) { |
65 | return $this->parsedParametersDeleteLog; |
66 | } |
67 | |
68 | $params = parent::getMessageParameters(); |
69 | $subtype = $this->entry->getSubtype(); |
70 | if ( in_array( $subtype, [ 'event', 'revision' ] ) ) { |
71 | // $params[3] here is 'revision' or 'archive' for page revisions, 'oldimage' or |
72 | // 'filearchive' for file versions, or a comma-separated list of log_ids for log |
73 | // entries. $subtype here is 'revision' for page revisions and file |
74 | // versions, or 'event' for log entries. |
75 | if ( |
76 | ( $subtype === 'event' && count( $params ) === 6 ) |
77 | || ( |
78 | $subtype === 'revision' && isset( $params[3] ) && count( $params ) === 7 |
79 | && in_array( $params[3], [ 'revision', 'archive', 'oldimage', 'filearchive' ] ) |
80 | ) |
81 | ) { |
82 | // See RevDelList::getLogParams()/RevDelLogList::getLogParams() |
83 | $paramStart = $subtype === 'revision' ? 4 : 3; |
84 | |
85 | $old = $this->parseBitField( $params[$paramStart + 1] ); |
86 | $new = $this->parseBitField( $params[$paramStart + 2] ); |
87 | [ $hid, $unhid, $extra ] = RevisionDeleter::getChanges( $new, $old ); |
88 | $changes = []; |
89 | // messages used: revdelete-content-hid, revdelete-summary-hid, revdelete-uname-hid |
90 | foreach ( $hid as $v ) { |
91 | $changes[] = $this->msg( "$v-hid" )->plain(); |
92 | } |
93 | // messages used: revdelete-content-unhid, revdelete-summary-unhid, |
94 | // revdelete-uname-unhid |
95 | foreach ( $unhid as $v ) { |
96 | $changes[] = $this->msg( "$v-unhid" )->plain(); |
97 | } |
98 | foreach ( $extra as $v ) { |
99 | $changes[] = $this->msg( $v )->plain(); |
100 | } |
101 | $changeText = $this->context->getLanguage()->listToText( $changes ); |
102 | |
103 | $newParams = array_slice( $params, 0, 3 ); |
104 | $newParams[3] = $changeText; |
105 | $ids = is_array( $params[$paramStart] ) |
106 | ? $params[$paramStart] |
107 | : explode( ',', $params[$paramStart] ); |
108 | $newParams[4] = $this->context->getLanguage()->formatNum( count( $ids ) ); |
109 | |
110 | $this->parsedParametersDeleteLog = $newParams; |
111 | return $this->parsedParametersDeleteLog; |
112 | } else { |
113 | $this->parsedParametersDeleteLog = array_slice( $params, 0, 3 ); |
114 | return $this->parsedParametersDeleteLog; |
115 | } |
116 | } elseif ( $subtype === 'restore' ) { |
117 | $rawParams = $this->entry->getParameters(); |
118 | if ( isset( $rawParams[':assoc:count'] ) ) { |
119 | $countList = []; |
120 | foreach ( $rawParams[':assoc:count'] as $type => $count ) { |
121 | if ( $count ) { |
122 | // Messages: restore-count-revisions, restore-count-files |
123 | $countList[] = $this->context->msg( 'restore-count-' . $type ) |
124 | ->numParams( $count )->plain(); |
125 | } |
126 | } |
127 | $params[3] = $this->context->getLanguage()->listToText( $countList ); |
128 | } |
129 | } |
130 | |
131 | $this->parsedParametersDeleteLog = $params; |
132 | return $this->parsedParametersDeleteLog; |
133 | } |
134 | |
135 | protected function parseBitField( $string ) { |
136 | // Input is like ofield=2134 or just the number |
137 | if ( strpos( $string, 'field=' ) === 1 ) { |
138 | [ , $field ] = explode( '=', $string ); |
139 | |
140 | return (int)$field; |
141 | } else { |
142 | return (int)$string; |
143 | } |
144 | } |
145 | |
146 | public function getActionLinks() { |
147 | $linkRenderer = $this->getLinkRenderer(); |
148 | if ( !$this->context->getAuthority()->isAllowed( 'deletedhistory' ) |
149 | || $this->entry->isDeleted( LogPage::DELETED_ACTION ) |
150 | ) { |
151 | return ''; |
152 | } |
153 | |
154 | switch ( $this->entry->getSubtype() ) { |
155 | case 'delete': // Show undelete link |
156 | case 'delete_redir': |
157 | case 'delete_redir2': |
158 | if ( $this->context->getAuthority()->isAllowed( 'undelete' ) ) { |
159 | $message = 'undeletelink'; |
160 | } else { |
161 | $message = 'undeleteviewlink'; |
162 | } |
163 | $revert = $linkRenderer->makeKnownLink( |
164 | SpecialPage::getTitleFor( 'Undelete' ), |
165 | $this->msg( $message )->text(), |
166 | [], |
167 | [ 'target' => $this->entry->getTarget()->getPrefixedDBkey() ] |
168 | ); |
169 | |
170 | return $this->msg( 'parentheses' )->rawParams( $revert )->escaped(); |
171 | |
172 | case 'revision': // If an edit was hidden from a page give a review link to the history |
173 | $params = $this->extractParameters(); |
174 | if ( !isset( $params[3] ) || !isset( $params[4] ) ) { |
175 | return ''; |
176 | } |
177 | |
178 | // Different revision types use different URL params... |
179 | $key = $params[3]; |
180 | // This is a array or CSV of the IDs |
181 | $ids = is_array( $params[4] ) |
182 | ? $params[4] |
183 | : explode( ',', $params[4] ); |
184 | |
185 | $links = []; |
186 | |
187 | // If there's only one item, we can show a diff link |
188 | if ( count( $ids ) == 1 ) { |
189 | // Live revision diffs... |
190 | if ( $key == 'oldid' || $key == 'revision' ) { |
191 | $links[] = $linkRenderer->makeKnownLink( |
192 | $this->entry->getTarget(), |
193 | $this->msg( 'diff' )->text(), |
194 | [], |
195 | [ |
196 | 'diff' => intval( $ids[0] ), |
197 | 'unhide' => 1 |
198 | ] |
199 | ); |
200 | // Deleted revision diffs... |
201 | } elseif ( $key == 'artimestamp' || $key == 'archive' ) { |
202 | $links[] = $linkRenderer->makeKnownLink( |
203 | SpecialPage::getTitleFor( 'Undelete' ), |
204 | $this->msg( 'diff' )->text(), |
205 | [], |
206 | [ |
207 | 'target' => $this->entry->getTarget()->getPrefixedDBkey(), |
208 | 'diff' => 'prev', |
209 | 'timestamp' => $ids[0] |
210 | ] |
211 | ); |
212 | } |
213 | } |
214 | |
215 | // View/modify link... |
216 | $links[] = $linkRenderer->makeKnownLink( |
217 | SpecialPage::getTitleFor( 'Revisiondelete' ), |
218 | $this->msg( 'revdel-restore' )->text(), |
219 | [], |
220 | [ |
221 | 'target' => $this->entry->getTarget()->getPrefixedText(), |
222 | 'type' => $key, |
223 | 'ids' => implode( ',', $ids ), |
224 | ] |
225 | ); |
226 | |
227 | return $this->msg( 'parentheses' )->rawParams( |
228 | $this->context->getLanguage()->pipeList( $links ) )->escaped(); |
229 | |
230 | case 'event': // Hidden log items, give review link |
231 | $params = $this->extractParameters(); |
232 | if ( !isset( $params[3] ) ) { |
233 | return ''; |
234 | } |
235 | // This is a CSV of the IDs |
236 | $query = $params[3]; |
237 | if ( is_array( $query ) ) { |
238 | $query = implode( ',', $query ); |
239 | } |
240 | // Link to each hidden object ID, $params[1] is the url param |
241 | $revert = $linkRenderer->makeKnownLink( |
242 | SpecialPage::getTitleFor( 'Revisiondelete' ), |
243 | $this->msg( 'revdel-restore' )->text(), |
244 | [], |
245 | [ |
246 | 'target' => $this->entry->getTarget()->getPrefixedText(), |
247 | 'type' => 'logging', |
248 | 'ids' => $query |
249 | ] |
250 | ); |
251 | |
252 | return $this->msg( 'parentheses' )->rawParams( $revert )->escaped(); |
253 | default: |
254 | return ''; |
255 | } |
256 | } |
257 | |
258 | protected function getParametersForApi() { |
259 | $entry = $this->entry; |
260 | $params = []; |
261 | |
262 | $subtype = $this->entry->getSubtype(); |
263 | if ( in_array( $subtype, [ 'event', 'revision' ] ) ) { |
264 | $rawParams = $entry->getParameters(); |
265 | if ( $subtype === 'event' ) { |
266 | array_unshift( $rawParams, 'logging' ); |
267 | } |
268 | |
269 | static $map = [ |
270 | '4::type', |
271 | '5::ids', |
272 | '6::ofield', |
273 | '7::nfield', |
274 | '4::ids' => '5::ids', |
275 | '5::ofield' => '6::ofield', |
276 | '6::nfield' => '7::nfield', |
277 | ]; |
278 | foreach ( $map as $index => $key ) { |
279 | if ( isset( $rawParams[$index] ) ) { |
280 | $rawParams[$key] = $rawParams[$index]; |
281 | unset( $rawParams[$index] ); |
282 | } |
283 | } |
284 | |
285 | if ( !is_array( $rawParams['5::ids'] ) ) { |
286 | $rawParams['5::ids'] = explode( ',', $rawParams['5::ids'] ); |
287 | } |
288 | |
289 | $params = [ |
290 | '::type' => $rawParams['4::type'], |
291 | ':array:ids' => $rawParams['5::ids'], |
292 | ]; |
293 | |
294 | static $fields = [ |
295 | RevisionRecord::DELETED_TEXT => 'content', |
296 | RevisionRecord::DELETED_COMMENT => 'comment', |
297 | RevisionRecord::DELETED_USER => 'user', |
298 | RevisionRecord::DELETED_RESTRICTED => 'restricted', |
299 | ]; |
300 | |
301 | if ( isset( $rawParams['6::ofield'] ) ) { |
302 | $old = $this->parseBitField( $rawParams['6::ofield'] ); |
303 | $params[':assoc:old'] = [ 'bitmask' => $old ]; |
304 | foreach ( $fields as $bit => $key ) { |
305 | $params[':assoc:old'][$key] = (bool)( $old & $bit ); |
306 | } |
307 | } |
308 | if ( isset( $rawParams['7::nfield'] ) ) { |
309 | $new = $this->parseBitField( $rawParams['7::nfield'] ); |
310 | $params[':assoc:new'] = [ 'bitmask' => $new ]; |
311 | foreach ( $fields as $bit => $key ) { |
312 | $params[':assoc:new'][$key] = (bool)( $new & $bit ); |
313 | } |
314 | } |
315 | } elseif ( $subtype === 'restore' ) { |
316 | $rawParams = $entry->getParameters(); |
317 | if ( isset( $rawParams[':assoc:count'] ) ) { |
318 | $params[':assoc:count'] = $rawParams[':assoc:count']; |
319 | } |
320 | } |
321 | |
322 | return $params; |
323 | } |
324 | |
325 | public function formatParametersForApi() { |
326 | $ret = parent::formatParametersForApi(); |
327 | if ( isset( $ret['ids'] ) ) { |
328 | ApiResult::setIndexedTagName( $ret['ids'], 'id' ); |
329 | } |
330 | return $ret; |
331 | } |
332 | } |