Completed
Pull Request — patch_1-1-4 (#3210)
by Emanuele
12:56
created

CoreFeatures_Controller   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 447
Duplicated Lines 2.68 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 28.7%

Importance

Changes 0
Metric Value
dl 12
loc 447
rs 6.4799
c 0
b 0
f 0
ccs 66
cts 230
cp 0.287
wmc 54
lcom 1
cbo 3

8 Methods

Rating   Name   Duplication   Size   Complexity  
A action_index() 0 5 1
B action_features() 0 67 7
C settings() 0 148 10
B _getModulesConfig() 12 38 7
A config_vars() 0 14 3
A loadGeneralSettingParameters() 0 23 3
C _save_core_features() 0 59 16
B _prepare_corefeatures() 0 24 7

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

1
<?php
2
3
/**
4
 * This controller allows to choose features to activated and deactivate them.
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * This file contains code covered by:
11
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
12
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
13
 *
14
 * @version 1.1
15
 *
16
 */
17
18
/**
19
 * This class takes care of the Core Features admin screen.
20
 *
21
 * What it does:
22
 *
23
 * - It sets up the context, initializes the features info for display
24
 * - updates the settings for enabled/disabled core features as requested.
25
 * - loads in module core features
26
 *
27
 * @package CoreFeatures
28
 */
29
class CoreFeatures_Controller extends Action_Controller
30
{
31
	/**
32
	 * Default handler.
33
	 *
34
	 * @see Action_Controller::action_index()
35
	 */
36
	public function action_index()
37
	{
38
		// just delegate to our preferred default
39
		return $this->action_features();
40
	}
41
42
	/**
43
	 * This is an overall control panel enabling/disabling lots of the forums key features.
44
	 *
45
	 * What it does:
46
	 *
47
	 * - Uses internally an array of all the features that can be enabled/disabled.
48
	 * - $core_features, each option can have the following:
49
	 *    - title - Text title of this item (If standard string does not exist).
50
	 *    - desc - Description of this feature (If standard string does not exist).
51
	 *    - settings - Array of settings to change (For each name => value) on enable
52
	 *      reverse is done for disable. If value > 1 will not change value if set.
53
	 *    - setting_callback - Function that returns an array of settings to save
54
	 *      takes one parameter which is value for this feature.
55
	 *    - save_callback - Function called on save, takes state as parameter.
56
	 */
57
	public function action_features()
58
	{
59
		global $txt, $context, $modSettings;
60
61
		require_once(SUBSDIR . '/Admin.subs.php');
62
63
		loadTemplate('CoreFeatures');
64
65
		$core_features = $this->settings();
66
67
		$this->loadGeneralSettingParameters();
68
69
		// Are we saving?
70
		if (isset($this->_req->post->save))
71
		{
72
			checkSession();
73
74
			if (isset($this->_req->query->xml))
75
			{
76
				$tokenValidation = validateToken('admin-core', 'post', false);
77
78
				if (empty($tokenValidation))
79
					return 'token_verify_fail';
80
			}
81
			else
82
				validateToken('admin-core');
83
84
			$this->_save_core_features($core_features);
85
86
			if (!isset($this->_req->query->xml))
87
				redirectexit('action=admin;area=corefeatures;' . $context['session_var'] . '=' . $context['session_id']);
88
		}
89
90
		// Put them in context.
91
		$context['features'] = $this->_prepare_corefeatures($core_features);
92
93
		// Are they a new user?
94
		$context['is_new_install'] = !isset($modSettings['admin_features']);
95
		$context['force_disable_tabs'] = $context['is_new_install'];
96
97
		// Don't show them this twice!
98
		if ($context['is_new_install'])
99
			updateSettings(array('admin_features' => ''));
100
101
		// sub_template is already generic_xml and the token is created somewhere else
102
		if (isset($this->_req->query->xml))
103
			return true;
104
105
		$context['sub_template'] = 'core_features';
106
		$context['page_title'] = $txt['core_settings_title'];
107
		$context[$context['admin_menu_name']]['tab_data'] = array(
108
			'title' => $txt['core_settings_title'],
109
			'help' => '',
110
			'description' => $txt['core_settings_desc'],
111
		);
112
		addJavascriptVar(array(
113
			'token_name' => '',
114
			'token_value' => '',
115
			'feature_on_text' => $txt['core_settings_switch_off'],
116
			'feature_off_text' => $txt['core_settings_switch_on']
117
		), true);
118
119
		// We love our tokens.
120
		createToken('admin-core');
121
122
		return true;
123
	}
124
125
	/**
126
	 * Return the configuration settings available for core features page.
127
	 *
128
	 * @event integrate_core_features passed $core_features array allowing for adding new
129
	 * ones to the feature page
130
	 */
131 1
	public function settings()
132
	{
133
		$core_features = array(
134
			// cp = custom profile fields.
135
			'cp' => array(
136 1
				'url' => 'action=admin;area=featuresettings;sa=profile',
137 1
				'save_callback' => 'custom_profiles_toggle_callback',
138
				'setting_callback' => function ($value) {
139
					if (!$value)
140
						return array(
141
							'disabled_profile_fields' => '',
142
							'registration_fields' => '',
143
							'displayFields' => '',
144
						);
145
					else
146
						return array();
147 1
				},
148 1
			),
149
			// k = karma.
150
			'k' => array(
151 1
				'url' => 'action=admin;area=featuresettings;sa=karma',
152
				'settings' => array(
153 1
					'karmaMode' => 2,
154 1
				),
155 1
			),
156
			// l = likes.
157
			'l' => array(
158 1
				'url' => 'action=admin;area=featuresettings;sa=likes',
159
				'settings' => array(
160 1
					'likes_enabled' => 1,
161 1
				),
162
				'setting_callback' => function ($value) {
163
					global $modSettings;
164
165
					require_once(SUBSDIR . '/Mentions.subs.php');
166
167
					// Makes all the like/rlike mentions invisible (or visible)
168
					toggleMentionsVisibility('likemsg', !empty($value));
169
					toggleMentionsVisibility('rlikemsg', !empty($value));
170
171
					$current = !empty($modSettings['enabled_mentions']) ? explode(',', $modSettings['enabled_mentions']) : array();
172
173
					if (!empty($value))
174
						return array('enabled_mentions' => implode(',', array_merge($current, array('likemsg', 'rlikemsg'))));
175
					else
176
						return array('enabled_mentions' => implode(',', array_diff($current, array('likemsg', 'rlikemsg'))));
177 1
				},
178 1
			),
179
			// ml = moderation log.
180
			'ml' => array(
181 1
				'url' => 'action=admin;area=logs;sa=modlog',
182
				'settings' => array(
183 1
					'modlog_enabled' => 1,
184 1
				),
185 1
			),
186
			// pe = post email
187
			'pe' => array(
188 1
				'url' => 'action=admin;area=maillist',
189 1
				'save_callback' => 'postbyemail_toggle_callback',
190
				'settings' => array(
191 1
					'maillist_enabled' => 1,
192 1
					'pbe_post_enabled' => 2,
193 1
					'pbe_pm_enabled' => 2,
194 1
				),
195 1
			),
196
			// pm = post moderation.
197
			'pm' => array(
198 1
				'url' => 'action=admin;area=permissions;sa=postmod',
199
				'setting_callback' => function ($value) {
200
					// Cannot use warning post moderation if disabled!
201
					if (!$value)
202
					{
203
						require_once(SUBSDIR . '/Moderation.subs.php');
204
						approveAllUnapproved();
205
206
						return array('warning_moderate' => 0);
207
					}
208
					else
209
						return array();
210 1
				},
211 1
			),
212
			// ps = Paid Subscriptions.
213
			'ps' => array(
214 1
				'url' => 'action=admin;area=paidsubscribe',
215
				'settings' => array(
216 1
					'paid_enabled' => 1,
217 1
				),
218 1
				'setting_callback' => 'subscriptions_toggle_callback',
219 1
			),
220
			// rg = report generator.
221
			'rg' => array(
222 1
				'url' => 'action=admin;area=reports',
223 1
			),
224
			// w = warning.
225
			'w' => array(
226 1
				'url' => 'action=admin;area=securitysettings;sa=moderation',
227
				'setting_callback' => function ($value) {
228
					global $modSettings;
229
230
					list ($modSettings['warning_enable'], $modSettings['user_limit'], $modSettings['warning_decrement']) = explode(',', $modSettings['warning_settings']);
231
					$warning_settings = ($value ? 1 : 0) . ',' . $modSettings['user_limit'] . ',' . $modSettings['warning_decrement'];
232
					if (!$value)
233
					{
234
						$returnSettings = array(
235
							'warning_watch' => 0,
236
							'warning_moderate' => 0,
237
							'warning_mute' => 0,
238
						);
239
					}
240
					elseif (empty($modSettings['warning_enable']) && $value)
241
					{
242
						$returnSettings = array(
243
							'warning_watch' => 10,
244
							'warning_moderate' => 35,
245
							'warning_mute' => 60,
246
						);
247
					}
248
					else
249
						$returnSettings = array();
250
251
					$returnSettings['warning_settings'] = $warning_settings;
252
					return $returnSettings;
253 1
				},
254 1
			),
255
			// Search engines
256
			'sp' => array(
257 1
				'url' => 'action=admin;area=sengines',
258
				'settings' => array(
259 1
					'spider_mode' => 1,
260 1
				),
261
				'setting_callback' => function ($value) {
262
					// Turn off the spider group if disabling.
263
					if (!$value)
264
						return array('spider_group' => 0, 'show_spider_online' => 0);
265 1
				},
266
				'on_save' => function () {
267
					require_once(SUBSDIR . '/SearchEngines.subs.php');
268 1
				},
269 1
			),
270 1
		);
271
272 1
		$this->_getModulesConfig($core_features);
273
274
		// Anyone who would like to add a core feature?
275 1
		call_integration_hook('integrate_core_features', array(&$core_features));
276
277 1
		return $core_features;
278
	}
279
280
	/**
281
	 * Searches the ADMINDIR looking for module managers and load the "Core Feature"
282
	 * if existing.
283
	 *
284
	 * @param mixed[] $core_features The core features array
285
	 */
286 1
	protected function _getModulesConfig(&$core_features)
287
	{
288
		// Find appropriately named core feature files in the admin directory
289 1
		$glob = new GlobIterator(ADMINDIR . '/Manage*Module.controller.php', FilesystemIterator::SKIP_DOTS);
290
291 1
		foreach ($glob as $file)
292
		{
293 1
			$class = $file->getBasename('.controller.php') . '_Controller';
294
295 1
			if (method_exists($class, 'addCoreFeature'))
296 1
				$class::addCoreFeature($core_features);
297 1
		}
298
299 1
		$integrations = Hooks::instance()->discoverIntegrations(ADDONSDIR);
300
301 1
		foreach ($integrations as $integration)
302
		{
303
			$core_features[$integration['id']] = array(
304
				'url' => empty($integration['details']->extra->setting_url) ? '?action=admin;area=addonsettings' : $integration['details']->extra->setting_url,
305
				'title' => $integration['title'],
306
				'desc' => $integration['description'],
307
			);
308
309 View Code Duplication
			if (method_exists($integration['class'], 'setting_callback'))
310
			{
311
				$core_features[$integration['id']]['setting_callback'] = function ($value) use ($integration) {
312
					$integration['class']::setting_callback($value);
313
				};
314
			}
315
316 View Code Duplication
			if (method_exists($integration['class'], 'on_save'))
317
			{
318
				$core_features[$integration['id']]['on_save'] = function () use ($integration) {
319
					$integration['class']::on_save();
320
				};
321
			}
322 1
		}
323 1
	}
324
325
	/**
326
	 * Return the array of core features in the format expected by search.
327
	 *
328
	 * - Callback for admin internal search.
329
	 *
330
	 * @return mixed[] array in a config_var format
331
	 */
332 1
	public function config_vars()
333
	{
334 1
		global $txt;
335
336 1
		$return_data = array();
337
338 1
		$core_features = $this->settings();
339
340
		// Convert this to a format that admin search will understand
341 1
		foreach ($core_features as $id => $data)
342 1
			$return_data[] = array('switch', isset($data['title']) ? $data['title'] : $txt['core_settings_item_' . $id]);
343
344 1
		return $return_data;
345
	}
346
347
	/**
348
	 * This function makes sure the requested subaction does exists, if it
349
	 * doesn't, it sets a default action.
350
	 *
351
	 * @param mixed[] $subActions = array() An array containing all possible subactions.
352
	 * @param string $defaultAction = '' the default action to be called if no valid subaction was found.
353
	 * @throws Elk_Exception
354
	 */
355
	public function loadGeneralSettingParameters($subActions = array(), $defaultAction = '')
356
	{
357
		global $context;
358
359
		// You need to be an admin to edit settings!
360
		isAllowedTo('admin_forum');
361
362
		loadLanguage('Help');
363
		loadLanguage('ManageSettings');
364
365
		$context['sub_template'] = 'show_settings';
366
367
		// By default do the basic settings.
368
		if (isset($this->_req->query->sa, $subActions[$this->_req->query->sa]))
369
			$context['sub_action'] = $this->_req->query->sa;
370
		elseif (!empty($defaultAction))
371
			$context['sub_action'] = $defaultAction;
372
		else
373
		{
374
			$temp = array_keys($subActions);
375
			$context['sub_action'] = array_pop($temp);
376
		}
377
	}
378
379
	/**
380
	 * Takes care os saving the core features status (enabled/disabled)
381
	 *
382
	 * @param mixed[] $core_features - The array of all the core features, as
383
	 *                returned by $this->settings()
384
	 */
385
	private function _save_core_features($core_features)
386
	{
387
		global $modSettings;
388
389
		$setting_changes = array('admin_features' => array());
390
391
		// Cycle each feature and change things as required!
392
		foreach ($core_features as $id => $feature)
393
		{
394
			$feature_id = $this->_req->getPost('feature_' . $id);
395
396
			// Enabled?
397
			if (!empty($feature_id))
398
				$setting_changes['admin_features'][] = $id;
399
400
			// Setting values to change?
401
			if (isset($feature['settings']))
402
			{
403
				foreach ($feature['settings'] as $key => $value)
404
				{
405
					if (empty($feature_id) || (!empty($feature_id) && ($value < 2 || empty($modSettings[$key]))))
406
						$setting_changes[$key] = !empty($feature_id) ? $value : !$value;
407
				}
408
			}
409
410
			// Is there a call back for settings?
411
			if (isset($feature['setting_callback']))
412
			{
413
				$returned_settings = $feature['setting_callback'](!empty($feature_id));
414
				if (!empty($returned_settings))
415
					$setting_changes = array_merge($setting_changes, $returned_settings);
416
			}
417
418
			// Standard save callback?
419
			if (isset($feature['on_save']))
420
				$feature['on_save']();
421
		}
422
423
		// Make sure this one setting is a string!
424
		$setting_changes['admin_features'] = implode(',', $setting_changes['admin_features']);
425
426
		// Make any setting changes!
427
		updateSettings($setting_changes);
428
429
		// This is needed to let menus appear if cache > 2
430
		if ($modSettings['cache_enable'] > 2)
431
			clean_cache('data');
432
433
		// Any post save things?
434
		foreach ($core_features as $id => $feature)
435
		{
436
			// Standard save callback?
437
			if (isset($feature['save_callback']))
438
			{
439
				$status = $this->_req->getPost('feature_' . $id);
440
				$feature['save_callback'](!empty($status));
441
			}
442
		}
443
	}
444
445
	/**
446
	 * Puts the core features data into a format usable by the template
447
	 *
448
	 * @param mixed[] $core_features - The array of all the core features, as
449
	 *                returned by $this->settings()
450
	 */
451
	protected function _prepare_corefeatures($core_features)
452
	{
453
		global $context, $txt, $settings, $scripturl;
454
455
		$features = array();
456
		foreach ($core_features as $id => $feature)
457
		{
458
			$features[$id] = array(
459
				'title' => isset($feature['title']) ? $feature['title'] : $txt['core_settings_item_' . $id],
460
				'desc' => isset($feature['desc']) ? $feature['desc'] : $txt['core_settings_item_' . $id . '_desc'],
461
				'enabled' => in_array($id, $context['admin_features']),
462
				'state' => in_array($id, $context['admin_features']) ? 'on' : 'off',
463
				'url' => !empty($feature['url']) ? $scripturl . '?' . $feature['url'] . ';' . $context['session_var'] . '=' . $context['session_id'] : '',
464
				'image' => (file_exists($settings['theme_dir'] . '/images/admin/feature_' . $id . '.png') ? $settings['images_url'] : $settings['default_images_url']) . '/admin/feature_' . $id . '.png',
465
			);
466
		}
467
468
		// Sort by title attribute
469
		uasort($features, function ($a, $b) {
470
			return strcmp(strtolower($a['title']), strtolower($b['title']));
471
		});
472
473
		return $features;
474
	}
475
}
476