DeleteLogFormatter   B
last analyzed

Complexity

Total Complexity 44

Size/Duplication

Total Lines 257
Duplicated Lines 2.72 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
dl 7
loc 257
rs 8.3396
c 0
b 0
f 0
wmc 44
lcom 1
cbo 11

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getMessageKey() 0 12 3
A parseBitField() 0 10 2
D getActionLinks() 0 109 17
B getParametersForApi() 0 54 7
A formatParametersForApi() 7 7 2
C getMessageParameters() 0 58 13

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like DeleteLogFormatter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DeleteLogFormatter, and based on these observations, apply Extract Interface, too.

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 http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
23
 * @since 1.22
24
 */
25
26
/**
27
 * This class formats delete log entries.
28
 *
29
 * @since 1.19
30
 */
31
class DeleteLogFormatter extends LogFormatter {
32
	protected function getMessageKey() {
33
		$key = parent::getMessageKey();
34
		if ( in_array( $this->entry->getSubtype(), [ 'event', 'revision' ] ) ) {
35
			if ( count( $this->getMessageParameters() ) < 5 ) {
36
				// Messages: logentry-delete-event-legacy, logentry-delete-revision-legacy,
37
				// logentry-suppress-event-legacy, logentry-suppress-revision-legacy
38
				return "$key-legacy";
39
			}
40
		}
41
42
		return $key;
43
	}
44
45
	protected function getMessageParameters() {
46
		if ( isset( $this->parsedParametersDeleteLog ) ) {
47
			return $this->parsedParametersDeleteLog;
0 ignored issues
show
Bug introduced by
The property parsedParametersDeleteLog does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
48
		}
49
50
		$params = parent::getMessageParameters();
51
		$subtype = $this->entry->getSubtype();
52
		if ( in_array( $subtype, [ 'event', 'revision' ] ) ) {
53
			// $params[3] here is 'revision' or 'archive' for page revisions, 'oldimage' or
54
			// 'filearchive' for file versions, or a comma-separated list of log_ids for log
55
			// entries. $subtype here is 'revision' for page revisions and file
56
			// versions, or 'event' for log entries.
57
			if (
58
				( $subtype === 'event' && count( $params ) === 6 )
59
				|| (
60
					$subtype === 'revision' && isset( $params[3] )
61
					&& in_array( $params[3], [ 'revision', 'archive', 'oldimage', 'filearchive' ] )
62
				)
63
			) {
64
				// See RevDelList::getLogParams()/RevDelLogList::getLogParams()
65
				$paramStart = $subtype === 'revision' ? 4 : 3;
66
67
				$old = $this->parseBitField( $params[$paramStart + 1] );
68
				$new = $this->parseBitField( $params[$paramStart + 2] );
69
				list( $hid, $unhid, $extra ) = RevisionDeleter::getChanges( $new, $old );
70
				$changes = [];
71
				// messages used: revdelete-content-hid, revdelete-summary-hid, revdelete-uname-hid
72
				foreach ( $hid as $v ) {
73
					$changes[] = $this->msg( "$v-hid" )->plain();
74
				}
75
				// messages used: revdelete-content-unhid, revdelete-summary-unhid,
76
				// revdelete-uname-unhid
77
				foreach ( $unhid as $v ) {
78
					$changes[] = $this->msg( "$v-unhid" )->plain();
79
				}
80
				foreach ( $extra as $v ) {
81
					$changes[] = $this->msg( $v )->plain();
82
				}
83
				$changeText = $this->context->getLanguage()->listToText( $changes );
84
85
				$newParams = array_slice( $params, 0, 3 );
86
				$newParams[3] = $changeText;
87
				$ids = is_array( $params[$paramStart] )
88
					? $params[$paramStart]
89
					: explode( ',', $params[$paramStart] );
90
				$newParams[4] = $this->context->getLanguage()->formatNum( count( $ids ) );
91
92
				$this->parsedParametersDeleteLog = $newParams;
93
				return $this->parsedParametersDeleteLog;
94
			} else {
95
				$this->parsedParametersDeleteLog = array_slice( $params, 0, 3 );
96
				return $this->parsedParametersDeleteLog;
97
			}
98
		}
99
100
		$this->parsedParametersDeleteLog = $params;
101
		return $this->parsedParametersDeleteLog;
102
	}
103
104
	protected function parseBitField( $string ) {
105
		// Input is like ofield=2134 or just the number
106
		if ( strpos( $string, 'field=' ) === 1 ) {
107
			list( , $field ) = explode( '=', $string );
108
109
			return (int)$field;
110
		} else {
111
			return (int)$string;
112
		}
113
	}
114
115
	public function getActionLinks() {
116
		$user = $this->context->getUser();
117
		if ( !$user->isAllowed( 'deletedhistory' )
118
			|| $this->entry->isDeleted( LogPage::DELETED_ACTION )
119
		) {
120
			return '';
121
		}
122
123
		switch ( $this->entry->getSubtype() ) {
124
			case 'delete': // Show undelete link
125
				if ( $user->isAllowed( 'undelete' ) ) {
126
					$message = 'undeletelink';
127
				} else {
128
					$message = 'undeleteviewlink';
129
				}
130
				$revert = Linker::linkKnown(
131
					SpecialPage::getTitleFor( 'Undelete' ),
132
					$this->msg( $message )->escaped(),
133
					[],
134
					[ 'target' => $this->entry->getTarget()->getPrefixedDBkey() ]
135
				);
136
137
				return $this->msg( 'parentheses' )->rawParams( $revert )->escaped();
138
139
			case 'revision': // If an edit was hidden from a page give a review link to the history
140
				$params = $this->extractParameters();
141
				if ( !isset( $params[3] ) || !isset( $params[4] ) ) {
142
					return '';
143
				}
144
145
				// Different revision types use different URL params...
146
				$key = $params[3];
147
				// This is a array or CSV of the IDs
148
				$ids = is_array( $params[4] )
149
					? $params[4]
150
					: explode( ',', $params[4] );
151
152
				$links = [];
153
154
				// If there's only one item, we can show a diff link
155
				if ( count( $ids ) == 1 ) {
156
					// Live revision diffs...
157
					if ( $key == 'oldid' || $key == 'revision' ) {
158
						$links[] = Linker::linkKnown(
159
							$this->entry->getTarget(),
160
							$this->msg( 'diff' )->escaped(),
161
							[],
162
							[
163
								'diff' => intval( $ids[0] ),
164
								'unhide' => 1
165
							]
166
						);
167
						// Deleted revision diffs...
168
					} elseif ( $key == 'artimestamp' || $key == 'archive' ) {
169
						$links[] = Linker::linkKnown(
170
							SpecialPage::getTitleFor( 'Undelete' ),
171
							$this->msg( 'diff' )->escaped(),
172
							[],
173
							[
174
								'target' => $this->entry->getTarget()->getPrefixedDBkey(),
175
								'diff' => 'prev',
176
								'timestamp' => $ids[0]
177
							]
178
						);
179
					}
180
				}
181
182
				// View/modify link...
183
				$links[] = Linker::linkKnown(
184
					SpecialPage::getTitleFor( 'Revisiondelete' ),
185
					$this->msg( 'revdel-restore' )->escaped(),
186
					[],
187
					[
188
						'target' => $this->entry->getTarget()->getPrefixedText(),
189
						'type' => $key,
190
						'ids' => implode( ',', $ids ),
191
					]
192
				);
193
194
				return $this->msg( 'parentheses' )->rawParams(
195
					$this->context->getLanguage()->pipeList( $links ) )->escaped();
196
197
			case 'event': // Hidden log items, give review link
198
				$params = $this->extractParameters();
199
				if ( !isset( $params[3] ) ) {
200
					return '';
201
				}
202
				// This is a CSV of the IDs
203
				$query = $params[3];
204
				if ( is_array( $query ) ) {
205
					$query = implode( ',', $query );
206
				}
207
				// Link to each hidden object ID, $params[1] is the url param
208
				$revert = Linker::linkKnown(
209
					SpecialPage::getTitleFor( 'Revisiondelete' ),
210
					$this->msg( 'revdel-restore' )->escaped(),
211
					[],
212
					[
213
						'target' => $this->entry->getTarget()->getPrefixedText(),
214
						'type' => 'logging',
215
						'ids' => $query
216
					]
217
				);
218
219
				return $this->msg( 'parentheses' )->rawParams( $revert )->escaped();
220
			default:
221
				return '';
222
		}
223
	}
224
225
	protected function getParametersForApi() {
226
		$entry = $this->entry;
227
		$params = [];
228
229
		$subtype = $this->entry->getSubtype();
230
		if ( in_array( $subtype, [ 'event', 'revision' ] ) ) {
231
			$rawParams = $entry->getParameters();
232
			if ( $subtype === 'event' ) {
233
				array_unshift( $rawParams, 'logging' );
234
			}
235
236
			static $map = [
237
				'4::type',
238
				'5::ids',
239
				'6::ofield',
240
				'7::nfield',
241
				'4::ids' => '5::ids',
242
				'5::ofield' => '6::ofield',
243
				'6::nfield' => '7::nfield',
244
			];
245
			foreach ( $map as $index => $key ) {
246
				if ( isset( $rawParams[$index] ) ) {
247
					$rawParams[$key] = $rawParams[$index];
248
					unset( $rawParams[$index] );
249
				}
250
			}
251
252
			$old = $this->parseBitField( $rawParams['6::ofield'] );
253
			$new = $this->parseBitField( $rawParams['7::nfield'] );
254
			if ( !is_array( $rawParams['5::ids'] ) ) {
255
				$rawParams['5::ids'] = explode( ',', $rawParams['5::ids'] );
256
			}
257
258
			$params = [
259
				'::type' => $rawParams['4::type'],
260
				':array:ids' => $rawParams['5::ids'],
261
				':assoc:old' => [ 'bitmask' => $old ],
262
				':assoc:new' => [ 'bitmask' => $new ],
263
			];
264
265
			static $fields = [
266
				Revision::DELETED_TEXT => 'content',
267
				Revision::DELETED_COMMENT => 'comment',
268
				Revision::DELETED_USER => 'user',
269
				Revision::DELETED_RESTRICTED => 'restricted',
270
			];
271
			foreach ( $fields as $bit => $key ) {
272
				$params[':assoc:old'][$key] = (bool)( $old & $bit );
273
				$params[':assoc:new'][$key] = (bool)( $new & $bit );
274
			}
275
		}
276
277
		return $params;
278
	}
279
280 View Code Duplication
	public function formatParametersForApi() {
281
		$ret = parent::formatParametersForApi();
282
		if ( isset( $ret['ids'] ) ) {
283
			ApiResult::setIndexedTagName( $ret['ids'], 'id' );
284
		}
285
		return $ret;
286
	}
287
}
288