ManagePosts::action_censor()   F
last analyzed

Complexity

Conditions 20
Paths 660

Size

Total Lines 122
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 420

Importance

Changes 0
Metric Value
cc 20
eloc 65
c 0
b 0
f 0
nc 660
nop 0
dl 0
loc 122
rs 0.4722
ccs 0
cts 57
cp 0
crap 420

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Handles all the administration settings for topics and posts.
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 Beta 1
14
 *
15
 */
16
17
namespace ElkArte\AdminController;
18
19
use ElkArte\AbstractController;
20
use ElkArte\Action;
21
use ElkArte\Exceptions\Exception;
22
use ElkArte\SettingsForm\SettingsForm;
23
24
/**
25
 * ManagePosts controller handles all the administration settings for topics and posts.
26
 *
27
 * @package Posts
28
 */
29
class ManagePosts extends AbstractController
30
{
31
	/**
32
	 * The main entrance point for the 'Posts and topics' screen.
33
	 *
34
	 * What it does:
35
	 *
36
	 * - Like all others, it checks permissions, then forwards to the right function
37
	 * based on the given sub-action.
38
	 * - Defaults to sub-action 'posts'.
39
	 * - Accessed from ?action=admin;area=postsettings.
40
	 * - Requires (and checks for) the admin_forum permission.
41
	 *
42
	 * @event integrate_sa_manage_posts used to add new subactions
43
	 * @see AbstractController::action_index()
44
	 */
45
	public function action_index()
46
	{
47
		global $context, $txt;
48
49
		$subActions = [
50
			'posts' => [
51
				$this, 'action_postSettings_display', 'permission' => 'admin_forum'],
52
			'censor' => [
53
				$this, 'action_censor', 'permission' => 'admin_forum'],
54
			'topics' => [
55
				'function' => 'action_index',
56
				'controller' => ManageTopics::class,
57
				'permission' => 'admin_forum'],
58
		];
59
60
		// Good old action handle
61
		$action = new Action('manage_posts');
62
63
		// Default the sub-action to 'posts'. call integrate_sa_manage_posts
64
		$subAction = $action->initialize($subActions, 'posts');
65
66
		// Just for the template
67
		$context['page_title'] = $txt['manageposts_title'];
68
		$context['sub_action'] = $subAction;
69
70
		// Tabs for browsing the different post-functions.
71
		$context[$context['admin_menu_name']]['object']->prepareTabData([
72
				'title' => 'manageposts_title',
73
				'help' => 'posts_and_topics',
74
				'description' => 'manageposts_description',
75
				'tabs' => [
76
					'posts' => [
77
						'description' => $txt['manageposts_settings_description'],
78
					],
79
					'censor' => [
80
						'description' => $txt['admin_censored_desc'],
81
					],
82
					'topics' => [
83
						'description' => $txt['manageposts_topic_settings_description'],
84
					],
85
				]]
86
		);
87
88
		// Call the right function for this sub-action.
89
		$action->dispatch($subAction);
90
	}
91
92
	/**
93
	 * Shows an interface to set and test censored words.
94
	 *
95
	 * - It uses the censor_vulgar, censor_proper, censorWholeWord, and
96
	 * censorIgnoreCase settings.
97
	 * - Requires the admin_forum permission.
98
	 * - Accessed from ?action=admin;area=postsettings;sa=censor.
99
	 *
100
	 * @event integrate_save_censors
101
	 * @event integrate_censors
102
	 * @uses the Admin template and the edit_censored sub template.
103
	 */
104
	public function action_censor(): void
105
	{
106
		global $txt, $modSettings, $context;
107
108
		if ($this->_req->hasPost('save_censor'))
109
		{
110
			// Make sure censoring is something they can do.
111
			checkSession();
112
			validateToken('admin-censor');
113
114
			$censored_vulgar = [];
115
			$censored_proper = [];
116
117
			// Rip it apart, then split it into two arrays.
118
			if ($this->_req->hasPost('censortext'))
119
			{
120
				$censorText = $this->_req->getPost('censortext', 'trim', '');
121
				$censorText = explode("\n", strtr($censorText, ["\r" => '']));
122
				foreach ($censorText as $c)
123
				{
124
					[$censored_vulgar[], $censored_proper[]] = array_pad(explode('=', trim($c)), 2, '');
125
				}
126
			}
127
			elseif ($this->_req->hasPost('censor_vulgar') && $this->_req->hasPost('censor_proper'))
128
			{
129
				$posted_vulgar = $this->_req->getPost('censor_vulgar', null, []);
130
				if (is_array($posted_vulgar))
131
				{
132
					// Work on local copies to avoid mutating the request
133
					$local_vulgar = (array) $posted_vulgar;
134
					$local_proper = (array) $this->_req->getPost('censor_proper', null, []);
135
136
					foreach ($local_vulgar as $i => $value)
137
					{
138
						if (trim(str_replace('*', ' ', $value)) === '')
139
						{
140
							unset($local_vulgar[$i], $local_proper[$i]);
141
						}
142
					}
143
144
					$censored_vulgar = $local_vulgar;
145
					$censored_proper = $local_proper;
146
				}
147
				else
148
				{
149
					$vulgar_text = (string) $this->_req->getPost('censor_vulgar', null, '');
150
					$proper_text = (string) $this->_req->getPost('censor_proper', null, '');
151
					$censored_vulgar = explode("\n", strtr($vulgar_text, ["\r" => '']));
152
					$censored_proper = explode("\n", strtr($proper_text, ["\r" => '']));
153
				}
154
			}
155
156
			// Set the new arrays and settings in the database.
157
			$updates = [
158
				'censor_vulgar' => implode("\n", $censored_vulgar),
159
				'censor_proper' => implode("\n", $censored_proper),
160
				'censorWholeWord' => empty($this->_req->getPost('censorWholeWord', null, '')) ? '0' : '1',
161
				'censorIgnoreCase' => empty($this->_req->getPost('censorIgnoreCase', null, '')) ? '0' : '1',
162
				'allow_no_censored' => empty($this->_req->getPost('allow_no_censored', null, '')) ? '0' : '1',
163
			];
164
165
			call_integration_hook('integrate_save_censors', [&$updates]);
166
167
			updateSettings($updates);
168
		}
169
170
		// Testing a word to see how it will be censored?
171
		$pre_censor = '';
172
		if ($this->_req->hasPost('censortest'))
173
		{
174
			require_once(SUBSDIR . '/Post.subs.php');
175
			$raw = (string) $this->_req->getPost('censortest', null, '');
176
			$censorText = htmlspecialchars($raw, ENT_QUOTES, 'UTF-8');
177
			preparsecode($censorText);
178
			$pre_censor = $censorText;
179
			$context['censor_test'] = strtr(censor($censorText), ['"' => '&quot;']);
180
		}
181
182
		// Set everything up for the template to do its thang.
183
		$censor_vulgar = explode("\n", $modSettings['censor_vulgar']);
184
		$censor_proper = explode("\n", $modSettings['censor_proper']);
185
186
		$context['censored_words'] = [];
187
		foreach ($censor_vulgar as $i => $censor_vulgar_i)
188
		{
189
			if ($censor_vulgar_i === '' || $censor_vulgar_i === '0')
190
			{
191
				continue;
192
			}
193
194
			// Skip it, it's either spaces or stars only.
195
			if (trim(str_replace('*', ' ', $censor_vulgar_i)) === '')
196
			{
197
				continue;
198
			}
199
200
			$context['censored_words'][htmlspecialchars(trim($censor_vulgar_i))] = isset($censor_proper[$i])
201
				? htmlspecialchars($censor_proper[$i], ENT_COMPAT, 'UTF-8')
202
				: '';
203
		}
204
205
		call_integration_hook('integrate_censors');
206
		createToken('admin-censor');
207
208
		// Using ajax?
209
		if ($this->_req->hasPost('censortest') && $this->getApi() === 'json')
210
		{
211
			// Clear the templates
212
			setJsonTemplate();
213
214
			// Send back a response
215
			$context['json_data'] = [
216
				'result' => true,
217
				'censor' => $pre_censor . ' <i class="icon i-chevron-circle-right"></i> ' . $context['censor_test'],
218
				'token_val' => $context['admin-censor_token_var'],
219
				'token' => $context['admin-censor_token'],
220
			];
221
		}
222
		else
223
		{
224
			$context['sub_template'] = 'edit_censored';
225
			$context['page_title'] = $txt['admin_censored_words'];
226
		}
227
	}
228
229
	/**
230
	 * Modify any setting related to posts and posting.
231
	 *
232
	 * - Requires the admin_forum permission.
233
	 * - Accessed from ?action=admin;area=postsettings;sa=posts.
234
	 *
235
	 * @event integrate_save_post_settings
236
	 * @uses Admin template, edit_post_settings sub-template.
237
	 */
238
	public function action_postSettings_display(): void
239
	{
240
		global $context, $txt, $modSettings;
241
242
		// Initialize the form
243
		$settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER);
244
245
		if (!empty($modSettings['nofollow_allowlist']))
246
		{
247
			$modSettings['nofollow_allowlist'] = implode("\n", (array) json_decode($modSettings['nofollow_allowlist'], true));
248
		}
249
250
		// Initialize it with our settings
251
		$settingsForm->setConfigVars($this->_settings());
252
253
		// Set up the template.
254
		$context['page_title'] = $txt['manageposts_settings'];
255
		$context['sub_template'] = 'show_settings';
256
257
		// Are we saving them - are we??
258
		if ($this->_req->hasQuery('save'))
259
		{
260
			checkSession();
261
			$db = database();
262
263
			// If we're changing the message length (and we are using MySQL), let's check the column is big enough.
264
			$postedMaxLen = $this->_req->getPost('max_messageLength', 'intval');
265
			if ($postedMaxLen !== null && $postedMaxLen !== (int) $modSettings['max_messageLength'] && $db->supportMediumtext())
266
			{
267
				require_once(SUBSDIR . '/Maintenance.subs.php');
268
				$colData = getMessageTableColumns();
269
				foreach ($colData as $column)
270
				{
271
					if ($column['name'] === 'body')
272
					{
273
						$body_type = $column['type'];
274
					}
275
				}
276
277
				if (isset($body_type) && ($postedMaxLen > 65535 || $postedMaxLen === 0) && $body_type === 'text')
278
				{
279
					throw new Exception('convert_to_mediumtext', false, [getUrl('admin', ['action' => 'admin', 'area' => 'maintain', 'sa' => 'database'])]);
280
				}
281
			}
282
283
			// If we're changing the post-preview length, let's check its valid
284
			$preview_chars = $this->_req->getPost('preview_characters', 'intval');
285
			if (!empty($preview_chars))
286
			{
287
				$preview_chars = min(max(0, $preview_chars), 512);
288
			}
289
290
			// Set a min quote length of 3 lines of text (@ default font size)
291
			$heightBefore = $this->_req->getPost('heightBeforeShowMore', 'intval');
292
			if (!empty($heightBefore))
293
			{
294
				$heightBefore = max($heightBefore, 155);
295
			}
296
297
			$allowRaw = $this->_req->getPost('nofollow_allowlist', 'trim');
298
			if ($allowRaw !== null)
299
			{
300
				$allowList = array_unique(explode("\n", $allowRaw));
301
				$allowList = array_filter(array_map('\\ElkArte\\Helper\\Util::htmlspecialchars', array_map('trim', $allowList)));
302
				$allowRaw = json_encode($allowList);
303
			}
304
305
			call_integration_hook('integrate_save_post_settings');
306 4
307
			$config = (array) $this->_req->post;
308 4
			if ($postedMaxLen !== null)
309
			{
310
				$config['max_messageLength'] = $postedMaxLen;
311
			}
312
			if ($preview_chars !== null)
313 4
			{
314
				$config['preview_characters'] = $preview_chars;
315
			}
316
			if ($heightBefore !== null)
317 4
			{
318 4
				$config['heightBeforeShowMore'] = $heightBefore;
319
			}
320 4
			if ($allowRaw !== null)
321 4
			{
322 4
				$config['nofollow_allowlist'] = $allowRaw;
323
			}
324 4
325 4
			$settingsForm->setConfigValues($config);
326 4
			$settingsForm->save();
327 4
			redirectexit('action=admin;area=postsettings;sa=posts');
328
		}
329 4
330 4
		// Final settings...
331
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'postsettings', 'save', 'sa' => 'posts']);
332
		$context['settings_title'] = $txt['manageposts_settings'];
333
334 4
		// Prepare the settings...
335
		$settingsForm->prepare();
336 4
	}
337
338
	/**
339
	 * Return admin configuration settings for posts.
340
	 *
341
	 * @event integrate_modify_post_settings
342 4
	 */
343
	private function _settings()
344 4
	{
345
		global $txt;
346
347
		// Initialize it with our settings
348
		$config_vars = [
349
			// Quote options...
350
			['int', 'removeNestedQuotes', 'postinput' => $txt['zero_for_none']],
351
			['int', 'heightBeforeShowMore', 'postinput' => $txt['zero_to_disable']],
352
			'',
353
			// Video options
354
			['check', 'enableVideoEmbeding'],
355
			['int', 'video_embed_limit', 'postinput' => $txt['video_embed_limit_note']],
356
			'',
357
			// Posting limits...
358
			['int', 'max_messageLength', 'subtext' => $txt['max_messageLength_zero'], 'postinput' => $txt['manageposts_characters']],
359
			['int', 'topicSummaryPosts', 'postinput' => $txt['manageposts_posts']],
360
			'',
361
			// Posting time limits...
362
			['int', 'spamWaitTime', 'postinput' => $txt['manageposts_seconds']],
363
			['int', 'edit_wait_time', 'postinput' => $txt['manageposts_seconds']],
364
			['int', 'edit_disable_time', 'subtext' => $txt['edit_disable_time_zero'], 'postinput' => $txt['manageposts_minutes']],
365
			['check', 'show_modify'],
366
			'',
367
			['check', 'show_user_images'],
368
			['check', 'hide_post_group'],
369
			'',
370
			// First & Last message preview lengths
371
			['select', 'message_index_preview', [$txt['message_index_preview_off'], $txt['message_index_preview_first'], $txt['message_index_preview_last']]],
372
			['int', 'preview_characters', 'subtext' => $txt['preview_characters_zero'], 'postinput' => $txt['preview_characters_units']],
373
			// Misc
374
			['title', 'mods_cat_modifications_misc'],
375
			['check', 'enableCodePrettify'],
376
			['check', 'autoLinkUrls'],
377
			['large_text', 'nofollow_allowlist', 'subtext' => $txt['nofollow_allowlist_desc']],
378
			['check', 'enablePostHTML'],
379
			['check', 'enablePostMarkdown'],
380
		];
381
382
		// Add new settings with a nice hook, makes them available for admin settings search as well
383
		call_integration_hook('integrate_modify_post_settings', [&$config_vars]);
384
385
		return $config_vars;
386
	}
387
388
	/**
389
	 * Return the post-settings for use in admin search
390
	 */
391
	public function settings_search()
392
	{
393
		return $this->_settings();
394
	}
395
}
396