Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

ElggWidget   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 285
Duplicated Lines 0 %

Test Coverage

Coverage 76.58%

Importance

Changes 0
Metric Value
dl 0
loc 285
ccs 85
cts 111
cp 0.7658
rs 8.2769
c 0
b 0
f 0
wmc 41

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getContext() 0 2 1
A initializeAttributes() 0 4 1
A setContext() 0 2 1
A __isset() 0 12 3
D move() 0 85 14
C saveSettings() 0 28 7
A getTitle() 0 7 3
A __get() 0 13 3
A __unset() 0 12 3
B __set() 0 18 5

How to fix   Complexity   

Complex Class

Complex classes like ElggWidget 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.

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 ElggWidget, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * \ElggWidget
5
 *
6
 * Stores metadata in private settings rather than as \ElggMetadata
7
 *
8
 * @package    Elgg.Core
9
 * @subpackage Widgets
10
 *
11
 * @property-read string $handler internal, do not use
12
 * @property-read string $column  internal, do not use
13
 * @property-read string $order   internal, do not use
14
 * @property-read string $context internal, do not use
15
 */
16
class ElggWidget extends \ElggObject {
17
18
	/**
19
	 * Set subtype to widget.
20
	 *
21
	 * @return void
22
	 */
23 43
	protected function initializeAttributes() {
24 43
		parent::initializeAttributes();
25
26 43
		$this->attributes['subtype'] = "widget";
27 43
	}
28
29
	/**
30
	 * Get a value from attributes, metadata or private settings
31
	 *
32
	 * @param string $name The name of the value
33
	 * @return mixed
34
	 */
35 43
	public function __get($name) {
36
		// See if its in our base attribute
37 43
		if (array_key_exists($name, $this->attributes)) {
38 43
			return $this->attributes[$name];
39
		}
40
		
41
	
42
		// object title and description are stored as metadata
43 33
		if (in_array($name, ['title', 'description'])) {
44 23
			return parent::__get($name);
45
		}
46
47 19
		return $this->getPrivateSetting($name);
48
	}
49
50
	/**
51
	 * Set an attribute, metadata or private setting value
52
	 *
53
	 * @param string $name  The name of the value to set
54
	 * @param mixed  $value The value to set
55
	 * @return void
56
	 */
57 28
	public function __set($name, $value) {
58 28
		if (array_key_exists($name, $this->attributes)) {
59
			// Check that we're not trying to change the guid!
60 28
			if ((array_key_exists('guid', $this->attributes)) && ($name == 'guid')) {
61 1
				return;
62
			}
63
64 28
			$this->attributes[$name] = $value;
65 28
			return;
66
		}
67
		
68
		// object title and description are stored as metadata
69 28
		if (in_array($name, ['title', 'description'])) {
70 22
			parent::__set($name, $value);
71 22
			return;
72
		}
73
		
74 28
		$this->setPrivateSetting($name, $value);
75 28
	}
76
77
	/**
78
	 * Unset a property from private settings or attribute.
79
	 *
80
	 * @see \ElggEntity->__unset
81
	 *
82
	 * @param string $name The name of the attribute or metadata.
83
	 *
84
	 * @return void
85
	 * @since 2.2.0
86
	 */
87 3
	public function __unset($name) {
88 3
		if (array_key_exists($name, $this->attributes)) {
89
			parent::__unset($name);
90
		}
91
		
92
		// object title and description are stored as metadata
93 3
		if (in_array($name, ['title', 'description'])) {
94 2
			parent::__unset($name);
95 2
			return;
96
		}
97
		
98 1
		$this->removePrivateSetting($name);
99 1
	}
100
	
101
	/**
102
	 * Test if property is set either as an attribute, metadata or private setting
103
	 *
104
	 * @tip Use isset($entity->property)
105
	 *
106
	 * @see \ElggEntity->__isset
107
	 *
108
	 * @param string $name The name of the attribute or private setting.
109
	 *
110
	 * @return bool
111
	 * @since 2.2.0
112
	 */
113 43
	public function __isset($name) {
114 43
		if (array_key_exists($name, $this->attributes)) {
115 43
			return parent::__isset($name);
116
		}
117
		
118
		// object title and description are stored as metadata
119 2
		if (in_array($name, ['title', 'description'])) {
120 1
			return parent::__isset($name);
121
		}
122
		
123 1
		$private_setting = $this->getPrivateSetting($name);
124 1
		return !is_null($private_setting);
125
	}
126
127
	/**
128
	 * Set the widget context
129
	 *
130
	 * @param string $context The widget context
131
	 * @return bool
132
	 * @since 1.8.0
133
	 */
134 1
	public function setContext($context) {
135 1
		return $this->setPrivateSetting('context', $context);
136
	}
137
138
	/**
139
	 * Get the widget context
140
	 *
141
	 * @return string
142
	 * @since 1.8.0
143
	 */
144 11
	public function getContext() {
145 11
		return (string) $this->getPrivateSetting('context');
146
	}
147
148
	/**
149
	 * Get the title of the widget
150
	 *
151
	 * @return string
152
	 * @since 1.8.0
153
	 */
154 3
	public function getTitle() {
155 3
		$title = $this->title;
156 3
		if (!$title) {
157 3
			$container = $this->getContainerEntity() ? : null;
158 3
			$title = _elgg_services()->widgets->getNameById($this->handler, $this->getContext(), $container);
159
		}
160 3
		return $title;
161
	}
162
163
	/**
164
	 * Move the widget
165
	 *
166
	 * @param int $column The widget column
167
	 * @param int $rank   Zero-based rank from the top of the column
168
	 * @return void
169
	 * @since 1.8.0
170
	 */
171 8
	public function move($column, $rank) {
172
		$options = [
173 8
			'type' => 'object',
174 8
			'subtype' => 'widget',
175 8
			'container_guid' => $this->container_guid,
176
			'limit' => false,
177
			'private_setting_name_value_pairs' => [
178 8
				['name' => 'context', 'value' => $this->getContext()],
179 8
				['name' => 'column', 'value' => $column]
180
			]
181
		];
182 8
		$widgets = elgg_get_entities($options);
183 8
		if (!$widgets) {
184 8
			$this->column = (int) $column;
1 ignored issue
show
Bug introduced by Ismayil Khayredinov
The property column is declared read-only in ElggWidget.
Loading history...
185 8
			$this->order = 0;
1 ignored issue
show
Bug introduced by Cash Costello
The property order is declared read-only in ElggWidget.
Loading history...
186 8
			return;
187
		}
188
189 4
		usort($widgets, function($a, $b) {return (int) $a->order > (int) $b->order;
0 ignored issues
show
Bug introduced by Ismayil Khayredinov
It seems like $widgets can also be of type integer; however, parameter $array of usort() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

189
		usort(/** @scrutinizer ignore-type */ $widgets, function($a, $b) {return (int) $a->order > (int) $b->order;
Loading history...
190
191 4
		});
192
193
		// remove widgets from inactive plugins
194 4
		$widget_types = elgg_get_widget_types([
195 4
			'context' => $this->context,
196 4
			'container' => $this->getContainerEntity(),
197
		]);
198 4
		$inactive_widgets = [];
199 4
		foreach ($widgets as $index => $widget) {
200 4
			if (!array_key_exists($widget->handler, $widget_types)) {
201
				$inactive_widgets[] = $widget;
202 4
				unset($widgets[$index]);
203
			}
204
		}
205
206 4
		$bottom_rank = count($widgets);
0 ignored issues
show
Bug introduced by Jeroen Dalsem
It seems like $widgets can also be of type integer; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

206
		$bottom_rank = count(/** @scrutinizer ignore-type */ $widgets);
Loading history...
207 4
		if ($column == $this->column) {
208
			$bottom_rank--;
209
		}
210
		
211 4
		if ($rank == 0) {
212
			// top of the column
213
			$this->order = reset($widgets)->order - 10;
0 ignored issues
show
Bug introduced by Cash Costello
It seems like $widgets can also be of type integer; however, parameter $array of reset() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

213
			$this->order = reset(/** @scrutinizer ignore-type */ $widgets)->order - 10;
Loading history...
214 4
		} elseif ($rank == $bottom_rank) {
215
			// bottom of the column of active widgets
216 4
			$this->order = end($widgets)->order + 10;
0 ignored issues
show
Bug introduced by Cash Costello
It seems like $widgets can also be of type integer; however, parameter $array of end() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

216
			$this->order = end(/** @scrutinizer ignore-type */ $widgets)->order + 10;
Loading history...
217
		} else {
218
			// reorder widgets
219
220
			// remove the widget that's being moved from the array
221
			foreach ($widgets as $index => $widget) {
222
				if ($widget->guid == $this->guid) {
223
					unset($widgets[$index]);
224
				}
225
			}
226
227
			// split the array in two and recombine with the moved widget in middle
228
			$before = array_slice($widgets, 0, $rank);
0 ignored issues
show
Bug introduced by Cash Costello
It seems like $widgets can also be of type integer; however, parameter $array of array_slice() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

228
			$before = array_slice(/** @scrutinizer ignore-type */ $widgets, 0, $rank);
Loading history...
229
			array_push($before, $this);
230
			$after = array_slice($widgets, $rank);
231
			$widgets = array_merge($before, $after);
232
			ksort($widgets);
233
			$order = 0;
234
			foreach ($widgets as $widget) {
235
				$widget->order = $order;
236
				$order += 10;
237
			}
238
		}
239
240
		// put inactive widgets at the bottom
241 4
		if ($inactive_widgets) {
242
			$bottom = 0;
243
			foreach ($widgets as $widget) {
244
				if ($widget->order > $bottom) {
245
					$bottom = $widget->order;
246
				}
247
			}
248
			$bottom += 10;
249
			foreach ($inactive_widgets as $widget) {
250
				$widget->order = $bottom;
251
				$bottom += 10;
252
			}
253
		}
254
255 4
		$this->column = $column;
256 4
	}
257
258
	/**
259
	 * Saves the widget's settings
260
	 *
261
	 * Plugins can override the save mechanism using the plugin hook:
262
	 * 'widget_settings', <widget handler identifier>. The widget and
263
	 * the parameters are passed. The plugin hook handler should return
264
	 * true to indicate that it has successfully saved the settings.
265
	 *
266
	 * @warning The values in the parameter array cannot be arrays
267
	 *
268
	 * @param array $params An array of name => value parameters
269
	 *
270
	 * @return bool
271
	 * @since 1.8.0
272
	 */
273 1
	public function saveSettings($params) {
274 1
		if (!$this->canEdit()) {
275 1
			return false;
276
		}
277
278
		// plugin hook handlers should return true to indicate the settings have
279
		// been saved so that default code does not run
280
		$hook_params = [
281 1
			'widget' => $this,
282 1
			'params' => $params
283
		];
284 1
		if (_elgg_services()->hooks->trigger('widget_settings', $this->handler, $hook_params, false) === true) {
285
			return true;
286
		}
287
288 1
		if (is_array($params) && count($params) > 0) {
289 1
			foreach ($params as $name => $value) {
290 1
				if (is_array($value)) {
291
					// private settings cannot handle arrays
292
					return false;
293
				} else {
294 1
					$this->$name = $value;
295
				}
296
			}
297 1
			$this->save();
298
		}
299
300 1
		return true;
301
	}
302
}
303