Passed
Push — development ( e156b5...b889cb )
by Spuds
01:09 queued 28s
created

action_notificationsSettings_display()   D

Complexity

Conditions 18
Paths 17

Size

Total Lines 127
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 342

Importance

Changes 0
Metric Value
eloc 56
dl 0
loc 127
rs 4.8666
c 0
b 0
f 0
cc 18
nc 17
nop 0
ccs 0
cts 61
cp 0
crap 342

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
 * Manage features and options administration page.
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 dev
14
 *
15
 */
16
17
namespace ElkArte\AdminController;
18
19
use BBC\ParserWrapper;
20
use DateTimeZone;
21
use ElkArte\AbstractController;
22
use ElkArte\Action;
23
use ElkArte\Exceptions\Exception;
24
use ElkArte\Helper\DataValidator;
25
use ElkArte\Helper\Util;
26
use ElkArte\Hooks;
27
use ElkArte\Languages\Txt;
28
use ElkArte\Mentions\MentionType\AbstractNotificationMessage;
29
use ElkArte\MetadataIntegrate;
30
use ElkArte\Notifications\Notifications;
31
use ElkArte\SettingsForm\SettingsForm;
32
33
/**
34
 * Manage features and options administration page.
35
 *
36
 * This controller handles the pages which allow the admin
37
 * to see and change the basic feature settings of their site.
38 2
 */
39
class ManageFeatures extends AbstractController
40
{
41 2
	/**
42 2
	 * Pre Dispatch, called before other methods.
43
	 */
44
	public function pre_dispatch()
45
	{
46
		// We need this in few places so it's easier to have it loaded here
47
		require_once(SUBSDIR . '/ManageFeatures.subs.php');
48
	}
49
50
	/**
51
	 * This function passes control through to the relevant tab.
52
	 *
53
	 * @event integrate_sa_modify_features Use to add new Configuration tabs
54
	 * @see AbstractController::action_index()
55
	 * @uses Help, ManageSettings languages
56
	 * @uses sub_template show_settings
57
	 */
58
	public function action_index()
59
	{
60
		global $context, $txt, $settings;
61
62
		// Often Helpful
63
		Txt::load('Help+ManageSettings+Mentions');
64
65
		// All the actions we know about.  These must exist in loadMenu() of the admin controller.
66
		$subActions = array(
67
			'basic' => array(
68
				'controller' => $this,
69
				'function' => 'action_basicSettings_display',
70
				'permission' => 'admin_forum'
71
			),
72
			'layout' => array(
73
				'controller' => $this,
74
				'function' => 'action_layoutSettings_display',
75
				'permission' => 'admin_forum'
76
			),
77
			'pwa' => array(
78
				'controller' => $this,
79
				'function' => 'action_pwaSettings_display',
80
				'enabled' => true,
81
				'permission' => 'admin_forum'
82
			),
83
			'karma' => array(
84
				'controller' => $this,
85
				'function' => 'action_karmaSettings_display',
86
				'enabled' => featureEnabled('k'),
87
				'permission' => 'admin_forum'
88
			),
89
			'pmsettings' => array(
90
				'controller' => $this,
91
				'function' => 'action_pmsettings',
92
				'permission' => 'admin_forum'
93
			),
94
			'likes' => array(
95
				'controller' => $this,
96
				'function' => 'action_likesSettings_display',
97
				'enabled' => featureEnabled('l'),
98
				'permission' => 'admin_forum'
99
			),
100
			'mention' => array(
101
				'controller' => $this,
102
				'function' => 'action_notificationsSettings_display',
103
				'permission' => 'admin_forum'
104
			),
105
			'sig' => array(
106
				'controller' => $this,
107
				'function' => 'action_signatureSettings_display',
108
				'permission' => 'admin_forum'
109
			),
110
			'profile' => array(
111
				'controller' => $this,
112
				'function' => 'action_profile',
113
				'enabled' => featureEnabled('cp'),
114
				'permission' => 'admin_forum'
115
			),
116
			'profileedit' => array(
117
				'controller' => $this,
118
				'function' => 'action_profileedit',
119
				'permission' => 'admin_forum'
120
			),
121
		);
122
123
		// Set up the action control
124
		$action = new Action('modify_features');
125
126
		// By default, do the basic settings, call integrate_sa_modify_features
127
		$subAction = $action->initialize($subActions, 'basic');
128
129
		// Some final pieces for the template
130
		$context['sub_template'] = 'show_settings';
131
		$context['sub_action'] = $subAction;
132
		$context['page_title'] = $txt['modSettings_title'];
133
134
		// Load up all the tabs...
135
		$context[$context['admin_menu_name']]['object']->prepareTabData([
136
			'title' => 'modSettings_title',
137
			'help' => 'featuresettings',
138
			'description' => sprintf($txt['modSettings_desc'], getUrl('admin', ['action' => 'admin', 'area' => 'theme', 'sa' => 'list', 'th' => $settings['theme_id'], '{session_data}'])),
139
			// All valid $subActions will be added, here you just specify any special tab data
140
			'tabs' => [
141
				'mention' => [
142
					'description' => $txt['mentions_settings_desc'],
143
				],
144
				'sig' => [
145
					'description' => $txt['signature_settings_desc'],
146
				],
147
				'profile' => [
148
					'description' => $txt['custom_profile_desc'],
149
				],
150
				'pwa' => [
151
					'description' => $txt['pwa_settings_desc'],
152
				],
153
			],
154
		]);
155
156
		// Call the right function for this sub-action.
157
		$action->dispatch($subAction);
158
	}
159
160
	/**
161
	 * Config array for changing the basic forum settings
162
	 *
163
	 * - Accessed from ?action=admin;area=featuresettings;sa=basic;
164
	 *
165
	 * @event integrate_save_basic_settings
166
	 */
167
	public function action_basicSettings_display()
168
	{
169
		global $txt, $context, $modSettings;
170
171
		// Initialize the form
172
		$settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER);
173
174
		// Initialize it with our settings
175
		$settingsForm->setConfigVars($this->_basicSettings());
176
177
		theme()->addJavascriptVar(['txt_invalid_response' => $txt['ajax_bad_response']], true);
178
179
		// Saving?
180
		if (isset($this->_req->query->save))
181
		{
182
			checkSession();
183
184
			// Prevent absurd boundaries here - make it a day tops.
185
			if (isset($this->_req->post->lastActive))
186
			{
187
				$this->_req->post->lastActive = min((int) $this->_req->post->lastActive, 1440);
188
			}
189
190
			call_integration_hook('integrate_save_basic_settings');
191
192
			// Microdata needs to enable its integration
193
			if ($this->_req->isSet('metadata_enabled'))
194
			{
195
				Hooks::instance()->enableIntegration(MetadataIntegrate::class);
196
			}
197
			else
198
			{
199
				Hooks::instance()->disableIntegration(MetadataIntegrate::class);
200
			}
201
202
			// If they have changed Hive settings, lets clear them to avoid issues
203
			if (empty($modSettings['minify_css_js']) !== empty($this->_req->post->minify_css_js))
204
			{
205
				theme()->cleanHives();
206
			}
207
208
			$settingsForm->setConfigValues((array) $this->_req->post);
209
			$settingsForm->save();
210
211
			writeLog();
212
			redirectexit('action=admin;area=featuresettings;sa=basic');
213
		}
214
215
		if (isset($this->_req->post->cleanhives) && $this->getApi() === 'json')
216
		{
217
			$clean_hives_result = theme()->cleanHives();
218 2
219
			setJsonTemplate();
220 2
			$context['json_data'] = array(
221
				'success' => $clean_hives_result,
222
				'response' => $clean_hives_result ? $txt['clean_hives_sucess'] : $txt['clean_hives_failed']
223
			);
224 2
225
			return;
226
		}
227
228
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'basic', 'save']);
229 2
		$context['settings_title'] = $txt['mods_cat_features'];
230
231 2
		$settingsForm->prepare();
232
	}
233 2
234
	/**
235 2
	 * Return basic feature settings.
236 2
	 *
237 2
	 * @event integrate_modify_basic_settings Adds to General features and Options
238
	 */
239
	private function _basicSettings()
240 2
	{
241
		global $txt;
242 2
243
		$config_vars = array(
244
			// Basic stuff, titles, permissions...
245 2
			array('check', 'allow_guestAccess'),
246 2
			array('check', 'enable_buddylist'),
247
			array('check', 'allow_editDisplayName'),
248
			array('check', 'allow_hideOnline'),
249
			array('check', 'titlesEnable'),
250 2
			'',
251
			// Javascript and CSS options
252
			array('select', 'jquery_source', array('auto' => $txt['jquery_auto'], 'local' => $txt['jquery_local'], 'cdn' => $txt['jquery_cdn'])),
253
			array('check', 'minify_css_js', 'postinput' => '<a href="#" id="clean_hives" class="linkbutton">' . $txt['clean_hives'] . '</a>'),
254 2
			'',
255
			// Number formatting, timezones.
256
			array('text', 'time_format'),
257
			array('float', 'time_offset', 'subtext' => $txt['setting_time_offset_note'], 6, 'postinput' => $txt['hours']),
258 2
			'default_timezone' => array('select', 'default_timezone', array()),
259 2
			'',
260
			// Who's online?
261
			array('check', 'who_enabled'),
262
			array('int', 'lastActive', 6, 'postinput' => $txt['minutes']),
263
			'',
264
			// Statistics.
265
			array('check', 'trackStats'),
266 2
			array('check', 'hitStats'),
267
			'',
268 2
			// Option-ish things... miscellaneous sorta.
269
			array('check', 'metadata_enabled'),
270
			array('check', 'allow_disableAnnounce'),
271
			array('check', 'disallow_sendBody'),
272 2
			array('select', 'enable_contactform', array('disabled' => $txt['contact_form_disabled'], 'registration' => $txt['contact_form_registration'], 'menu' => $txt['contact_form_menu'])),
273
		);
274 2
275
		// Get all the time zones.
276
		$all_zones = DateTimeZone::listIdentifiers();
277
		if (empty($all_zones))
278
		{
279
			unset($config_vars['default_timezone']);
280
		}
281
		else
282
		{
283
			// Make sure we set the value to the same as the printed value.
284
			foreach ($all_zones as $zone)
285
			{
286
				$config_vars['default_timezone'][2][$zone] = $zone;
287
			}
288
		}
289
290
		theme()->addInlineJavascript('
291
			document.getElementById("clean_hives").addEventListener("click", function(event) {return cleanHives(event);});', ['defer' => true]);
0 ignored issues
show
Bug introduced by
array('defer' => true) of type array<string,true> is incompatible with the type boolean expected by parameter $defer of ElkArte\Themes\Theme::addInlineJavascript(). ( Ignorable by Annotation )

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

291
			document.getElementById("clean_hives").addEventListener("click", function(event) {return cleanHives(event);});', /** @scrutinizer ignore-type */ ['defer' => true]);
Loading history...
292
293
		call_integration_hook('integrate_modify_basic_settings', array(&$config_vars));
294
295
		return $config_vars;
296
	}
297
298
	/**
299
	 * Allows modifying the global layout settings in the forum
300
	 *
301
	 * - Accessed through ?action=admin;area=featuresettings;sa=layout;
302
	 *
303
	 * @event integrate_save_layout_settings
304
	 */
305
	public function action_layoutSettings_display()
306
	{
307
		global $txt, $context, $modSettings;
308
309
		// Initialize the form
310
		$settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER);
311
312
		// Initialize it with our settings
313
		$settingsForm->setConfigVars($this->_layoutSettings());
314
315
		// Saving?
316
		if (isset($this->_req->query->save))
317
		{
318
			// Setting a custom frontpage, set the hook to the FrontpageInterface of the controller
319
			if (!empty($this->_req->post->front_page))
320
			{
321
				// Addons may have left this blank
322
				$modSettings['front_page'] = empty($modSettings['front_page']) ? 'MessageIndex_Controller' : $modSettings['front_page'];
323
324
				$front_page = (string) $this->_req->post->front_page;
325
				if (
326
					class_exists($modSettings['front_page'])
327
					&& in_array('validateFrontPageOptions', get_class_methods($modSettings['front_page']))
328
					&& !$front_page::validateFrontPageOptions($this->_req->post)
329
				)
330 2
				{
331
					$this->_req->post->front_page = '';
332 2
				}
333
			}
334 2
335 2
			checkSession();
336
337
			call_integration_hook('integrate_save_layout_settings');
338 2
339
			$settingsForm->setConfigValues((array) $this->_req->post);
340
			$settingsForm->save();
341 2
			writeLog();
342
343 2
			redirectexit('action=admin;area=featuresettings;sa=layout');
344
		}
345
346 2
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'layout', 'save']);
347
		$context['settings_title'] = $txt['mods_cat_layout'];
348 2
349 2
		$settingsForm->prepare();
350 2
	}
351
352
	/**
353
	 * Return layout settings.
354
	 *
355 2
	 * @event integrate_modify_layout_settings Adds options to Configuration->Layout
356
	 */
357 2
	private function _layoutSettings()
358
	{
359
		global $txt;
360
361
		$config_vars = array_merge(getFrontPageControllers(), array(
362
			'',
363
			// Pagination stuff.
364
			array('check', 'compactTopicPagesEnable'),
365
			array('int', 'compactTopicPagesContiguous', 'subtext' => str_replace(' ', '&nbsp;', '"3" ' . $txt['to_display'] . ': <strong>1 ... 4 [5] 6 ... 9</strong>') . '<br />' . str_replace(' ', '&nbsp;', '"5" ' . $txt['to_display'] . ': <strong>1 ... 3 4 [5] 6 7 ... 9</strong>')),
366
			array('int', 'defaultMaxMembers'),
367
			array('check', 'displayMemberNames'),
368
			'',
369
			// Stuff that just is everywhere - today, search, online, etc.
370
			array('select', 'todayMod', array($txt['today_disabled'], $txt['today_only'], $txt['yesterday_today'], $txt['relative_time'])),
371
			array('check', 'onlineEnable'),
372
			array('check', 'enableVBStyleLogin'),
373
			'',
374
			// Automagic image resizing.
375
			array('int', 'max_image_width', 'subtext' => $txt['zero_for_no_limit']),
376
			array('int', 'max_image_height', 'subtext' => $txt['zero_for_no_limit']),
377
			'',
378
			// This is like debugging sorta.
379
			array('check', 'timeLoadPageEnable'),
380
		));
381
382
		call_integration_hook('integrate_modify_layout_settings', array(&$config_vars));
383
384
		return $config_vars;
385
	}
386
387
	/**
388
	 * Display configuration settings page for progressive web application settings.
389
	 *
390
	 * - Accessed from ?action=admin;area=featuresettings;sa=pwa;
391
	 *
392
	 * @event integrate_save_pwa_settings
393
	 */
394
	public function action_pwaSettings_display()
395
	{
396
		global $txt, $context;
397
398
		// Initialize the form
399
		$settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER);
400 2
401
		// Initialize it with our settings
402 2
		$settingsForm->setConfigVars($this->_pwaSettings());
403
404
		// Saving, lots of checks then
405
		if (isset($this->_req->query->save))
406 2
		{
407 2
			checkSession();
408
409 2
			call_integration_hook('integrate_save_pwa_settings');
410 2
411
			// Don't allow it to be enabled if we don't have SSL
412
			$canUse = detectServer()->supportsSSL();
413 2
			if (!$canUse)
414
			{
415
				$this->_req->post->pwa_enabled = 0;
416
			}
417
418
			// And you must enable this if PWA is enabled
419
			if ($this->_req->getPost('pwa_enabled', 'intval') === 1)
420 2
			{
421
				$this->_req->post->pwa_manifest_enabled = 1;
422 2
			}
423
424
			$validator = new DataValidator();
425
			$validation_rules = [
426
				'pwa_theme_color' => 'valid_color',
427
				'pwa_background_color' => 'valid_color',
428
				'pwa_short_name' => 'max_length[12]'
429
			];
430
431
			// Only check the rest if they entered something.
432
			$valid_urls = ['pwa_small_icon', 'pwa_large_icon', 'favicon_icon', 'apple_touch_icon'];
433
			foreach ($valid_urls as $url)
434
			{
435
				if ($this->_req->getPost($url, 'trim') !== '')
436
				{
437
					$validation_rules[$url] = 'valid_url';
438
				}
439
			}
440
			$validator->validation_rules($validation_rules);
441
442
			if (!$validator->validate($this->_req->post))
443
			{
444
				// Some input error, lets tell them what is wrong
445
				$context['error_type'] = 'minor';
446
				$context['settings_message'] = [];
447
				foreach ($validator->validation_errors() as $error)
448
				{
449
					$context['settings_message'][] = $error;
450
				}
451
			}
452
			else
453
			{
454
				$settingsForm->setConfigValues((array) $this->_req->post);
455
				$settingsForm->save();
456
				redirectexit('action=admin;area=featuresettings;sa=pwa');
457
			}
458
		}
459
460
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'pwa', 'save']);
461
		$context['settings_title'] = $txt['pwa_settings'];
462
		theme()->addInlineJavascript('
463
			pwaPreview("pwa_small_icon");
464
			pwaPreview("pwa_large_icon");
465 2
			pwaPreview("favicon_icon");
466
			pwaPreview("apple_touch_icon");', true);
467 2
468
		$settingsForm->prepare();
469
	}
470
471 2
	/**
472 2
	 * Return PWA settings.
473
	 *
474 2
	 * @event integrate_modify_karma_settings Adds to Configuration->Pwa
475 2
	 */
476
	private function _pwaSettings()
477
	{
478
		global $txt;
479 2
480
		// PWA requires SSL
481
		$canUse = detectServer()->supportsSSL();
482
483 2
		$config_vars = array(
484
			// PWA - On or off?
485 2
			array('check', 'pwa_enabled', 'disabled' => !$canUse, 'invalid' => !$canUse, 'postinput' => !$canUse ? $txt['pwa_disabled'] : ''),
486
			'',
487
			array('check', 'pwa_manifest_enabled', 'helptext' => $txt['pwa_manifest_enabled_desc']),
488
			array('text', 'pwa_short_name', 12, 'mask' => 'nohtml', 'helptext' => $txt['pwa_short_name_desc'], 'maxlength' => 12),
489
			array('color', 'pwa_theme_color', 'helptext' => $txt['pwa_theme_color_desc']),
490
			array('color', 'pwa_background_color', 'helptext' => $txt['pwa_background_color_desc']),
491
			'',
492
			array('url', 'pwa_small_icon', 'size' => 40, 'helptext' => $txt['pwa_small_icon_desc'], 'onchange' => "pwaPreview('pwa_small_icon');"),
493
			array('url', 'pwa_large_icon', 'size' => 40, 'helptext' => $txt['pwa_large_icon_desc'], 'onchange' => "pwaPreview('pwa_large_icon');"),
494
			array('title', 'other_icons_title'),
495
			array('url', 'favicon_icon', 'size' => 40, 'helptext' => $txt['favicon_icon_desc'], 'onchange' => "pwaPreview('favicon_icon');"),
496
			array('url', 'apple_touch_icon', 'size' => 40, 'helptext' => $txt['apple_touch_icon_desc'], 'onchange' => "pwaPreview('apple_touch_icon');"),
497
		);
498
499
		call_integration_hook('integrate_modify_pwa_settings', array(&$config_vars));
500
501
		return $config_vars;
502
	}
503
504
	/**
505
	 * Display configuration settings page for karma settings.
506
	 *
507
	 * - Accessed from ?action=admin;area=featuresettings;sa=karma;
508
	 *
509
	 * @event integrate_save_karma_settings
510
	 */
511
	public function action_karmaSettings_display()
512
	{
513
		global $txt, $context;
514
515
		// Initialize the form
516
		$settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER);
517
518
		// Initialize it with our settings
519
		$settingsForm->setConfigVars($this->_karmaSettings());
520
521
		// Saving?
522
		if (isset($this->_req->query->save))
523
		{
524
			checkSession();
525
526
			call_integration_hook('integrate_save_karma_settings');
527
528
			$settingsForm->setConfigValues((array) $this->_req->post);
529
			$settingsForm->save();
530
			redirectexit('action=admin;area=featuresettings;sa=karma');
531
		}
532
533
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'karma', 'save']);
534
		$context['settings_title'] = $txt['karma'];
535
536
		$settingsForm->prepare();
537
	}
538
539
	/**
540
	 * Return karma settings.
541
	 *
542
	 * @event integrate_modify_karma_settings Adds to Configuration->Karma
543
	 */
544
	private function _karmaSettings()
545
	{
546
		global $txt;
547
548
		$config_vars = array(
549
			// Karma - On or off?
550
			array('select', 'karmaMode', explode('|', $txt['karma_options'])),
551
			'',
552
			// Who can do it.... and who is restricted by time limits?
553
			array('int', 'karmaMinPosts', 6, 'postinput' => $txt['manageposts_posts']),
554
			array('float', 'karmaWaitTime', 6, 'postinput' => $txt['hours']),
555
			array('check', 'karmaTimeRestrictAdmins'),
556
			array('check', 'karmaDisableSmite'),
557
			'',
558
			// What does it look like?  [smite]?
559
			array('text', 'karmaLabel'),
560
			array('text', 'karmaApplaudLabel', 'mask' => 'nohtml'),
561
			array('text', 'karmaSmiteLabel', 'mask' => 'nohtml'),
562
		);
563
564
		call_integration_hook('integrate_modify_karma_settings', array(&$config_vars));
565
566
		return $config_vars;
567
	}
568
569
	/**
570
	 * Display configuration settings page for likes settings.
571
	 *
572
	 * - Accessed from ?action=admin;area=featuresettings;sa=likes;
573
	 *
574
	 * @event integrate_save_likes_settings
575
	 */
576
	public function action_likesSettings_display()
577
	{
578
		global $txt, $context;
579
580
		// Initialize the form
581
		$settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER);
582
583
		// Initialize it with our settings
584
		$settingsForm->setConfigVars($this->_likesSettings());
585
586
		// Saving?
587
		if (isset($this->_req->query->save))
588
		{
589
			checkSession();
590
591
			call_integration_hook('integrate_save_likes_settings');
592
593
			$settingsForm->setConfigValues((array) $this->_req->post);
594
			$settingsForm->save();
595
			redirectexit('action=admin;area=featuresettings;sa=likes');
596
		}
597
598
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'likes', 'save']);
599
		$context['settings_title'] = $txt['likes'];
600
601
		$settingsForm->prepare();
602
	}
603
604
	/**
605 2
	 * Return likes settings.
606
	 *
607 2
	 * @event integrate_modify_likes_settings Adds to Configuration->Likes
608
	 */
609 2
	private function _likesSettings()
610 2
	{
611
		global $txt;
612
613
		$config_vars = array(
614 2
			// Likes - On or off?
615
			array('check', 'likes_enabled'),
616
			'',
617
			// Who can do it.... and who is restricted by count limits?
618 2
			array('int', 'likeMinPosts', 6, 'postinput' => $txt['manageposts_posts']),
619 2
			array('int', 'likeWaitTime', 6, 'postinput' => $txt['minutes']),
620 2
			array('int', 'likeWaitCount', 6),
621
			array('check', 'likeRestrictAdmins'),
622 2
			array('check', 'likeAllowSelf'),
623
			'',
624
			array('int', 'likeDisplayLimit', 6)
625
		);
626
627
		call_integration_hook('integrate_modify_likes_settings', array(&$config_vars));
628
629
		return $config_vars;
630
	}
631
632
	/**
633
	 * Initializes the mentions settings admin page.
634
	 *
635 2
	 * - Accessed from ?action=admin;area=featuresettings;sa=mention;
636
	 *
637 2
	 * @event integrate_save_modify_mention_settings
638
	 */
639
	public function action_notificationsSettings_display()
640
	{
641
		global $txt, $context, $modSettings;
642
643
		Txt::load('Mentions');
644
645
		// Instantiate the form
646
		$settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER);
647
648
		// Initialize it with our settings
649
		$settingsForm->setConfigVars($this->_notificationsSettings());
650
651
		// Some context stuff
652
		$context['page_title'] = $txt['mentions_settings'];
653
		$context['sub_template'] = 'show_settings';
654
655
		// Saving the settings?
656
		if (isset($this->_req->query->save))
657
		{
658
			checkSession();
659
660
			call_integration_hook('integrate_save_modify_mention_settings');
661
662
			if (!empty($this->_req->post->mentions_enabled))
663
			{
664
				enableModules('mentions', array('post', 'display'));
665
			}
666
			else
667
			{
668
				disableModules('mentions', array('post', 'display'));
669
			}
670
671
			if (!empty($modSettings['hidden_notification_methods']))
672
			{
673
				foreach ($modSettings['hidden_notification_methods'] as $class)
674
				{
675
					$this->_req->post->notifications[$class::getType()] = $class::getSettings();
676
				}
677
			}
678
679
			if (empty($this->_req->post->notifications))
680
			{
681
				$notification_methods = serialize(array());
682
			}
683
			else
684
			{
685
				$notification_methods = [];
686
				foreach ($this->_req->post->notifications as $type => $notification)
687
				{
688
					if (!empty($notification['enable']))
689
					{
690
						$defaults = $notification['default'] ?? [];
691
						unset($notification['enable'], $notification['default']);
692
						foreach ($notification as $k => $v)
693
						{
694
							$notification[$k] = in_array($k, $defaults) ? Notifications::DEFAULT_LEVEL : $v;
695
						}
696
697
						$notification_methods[$type] = $notification;
698
					}
699
				}
700
701
				$notification_methods = serialize($notification_methods);
702
			}
703
704
			require_once(SUBSDIR . '/Mentions.subs.php');
705
			$enabled_mentions = array();
706
			$current_settings = Util::unserialize($modSettings['notification_methods']);
707
708
			// Fist hide what was visible
709
			$modules_toggle = array('enable' => array(), 'disable' => array());
710
			foreach ($current_settings as $type => $val)
711
			{
712
				if (!isset($this->_req->post->notifications[$type]))
713
				{
714
					toggleMentionsVisibility($type, false);
715
					$modules_toggle['disable'][] = $type;
716
				}
717
			}
718
719
			// Then make visible what was hidden, but only if there is anything
720
			if (!empty($this->_req->post->notifications))
721
			{
722
				foreach ($this->_req->post->notifications as $type => $val)
723
				{
724
					if (!isset($current_settings[$type]))
725
					{
726
						toggleMentionsVisibility($type, true);
727
						$modules_toggle['enable'][] = $type;
728
					}
729
				}
730
731
				$enabled_mentions = array_keys($this->_req->post->notifications);
732
			}
733
734
			// Let's just keep it active, there are too many reasons it should be.
735
			require_once(SUBSDIR . '/ScheduledTasks.subs.php');
736
			toggleTaskStatusByName('user_access_mentions', true);
737
738
			// Disable or enable modules as needed
739
			foreach ($modules_toggle as $action => $toggles)
740
			{
741
				if (!empty($toggles))
742
				{
743
					// The modules associated with the notification (mentionmem, likes, etc) area
744
					$modules = getMentionsModules($toggles);
745
746
					// The action will either be enable to disable
747
					$function = $action . 'Modules';
748
749
					// Something like enableModule('mentions', array('post', 'display');
750
					foreach ($modules as $key => $val)
751
					{
752
						$function($key, $val);
753
					}
754
				}
755
			}
756
757
			updateSettings(array('enabled_mentions' => implode(',', array_unique($enabled_mentions)), 'notification_methods' => $notification_methods));
758
			$settingsForm->setConfigValues((array) $this->_req->post);
759
			$settingsForm->save();
760
			redirectexit('action=admin;area=featuresettings;sa=mention');
761
		}
762
763
		// Prepare the settings for display
764
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'mention', 'save']);
765
		$settingsForm->prepare();
766
	}
767
768
	/**
769
	 * Return mentions settings.
770
	 *
771 2
	 * @event integrate_modify_mention_settings Adds to Configuration->Mentions
772
	 */
773 2
	private function _notificationsSettings()
774
	{
775
		global $txt, $modSettings;
776
777 2
		Txt::load('Profile+UserNotifications');
778 2
		loadJavascriptFile('ext/jquery.multiselect.min.js');
779
		theme()->addInlineJavascript('
780 2
			$(\'.select_multiple\').multiselect({\'language_strings\': {\'Select all\': ' . JavaScriptEscape($txt['notify_select_all']) . '}});
781 2
			document.addEventListener("DOMContentLoaded", function() {
782 2
                 prepareNotificationOptions();
783
			});', true);
784 2
		loadCSSFile('multiselect.css');
785 2
786
		// The mentions settings
787 2
		$config_vars = array(
788 2
			array('title', 'mentions_settings'),
789 2
			array('check', 'mentions_enabled'),
790
		);
791
792 2
		$notification_methods = Notifications::instance()->getNotifiers();
793
		$notification_classes = getAvailableNotifications();
794 2
		$current_settings = unserialize($modSettings['notification_methods'], ['allowed_classes' => false]);
795 2
796 2
		foreach ($notification_classes as $class)
797
		{
798
			// The canUse can be set by each notifier based on conditions, default is true;
799 2
			/* @var $class AbstractNotificationMessage */
800
			if ($class::canUse() === false)
801 2
			{
802 2
				continue;
803 2
			}
804 2
805
			if ($class::hasHiddenInterface() === true)
806
			{
807
				$modSettings['hidden_notification_methods'][] = $class;
808 2
				continue;
809
			}
810 2
811
			// Set up config enable/disable setting for all notifications.
812
			$title = strtolower($class::getType());
813
			$config_vars[] = array('title', 'setting_' . $title);
814
			$config_vars[] = array('check', 'notifications[' . $title . '][enable]', 'text_label' => $txt['setting_notify_enable_this']);
815
			$modSettings['notifications[' . $title . '][enable]'] = !empty($current_settings[$title]);
816
			$default_values = [];
817
			$is_default = [];
818
819
			// If its enabled, show all the available ways, like email, notify, weekly ...
820
			foreach (array_keys($notification_methods) as $method_name)
821
			{
822
				$method_name = strtolower($method_name);
823
824
				// Are they excluding any, like don't let mailfail be allowed to send email !
825
				if ($class::isNotAllowed($method_name))
826
				{
827
					continue;
828
				}
829
830
				$config_vars[] = array('check', 'notifications[' . $title . '][' . $method_name . ']', 'text_label' => $txt['notify_' . $method_name]);
831
				$modSettings['notifications[' . $title . '][' . $method_name . ']'] = !empty($current_settings[$title][$method_name]);
832
				$default_values[] = [$method_name, $txt['notify_' . $method_name]];
833
				if (empty($current_settings[$title][$method_name]))
834
				{
835
					continue;
836
				}
837
838
				if ((int) $current_settings[$title][$method_name] !== Notifications::DEFAULT_LEVEL)
839
				{
840
					continue;
841
				}
842
843
				$is_default[] = $method_name;
844
			}
845
846
			$config_vars[] = array('select', 'notifications[' . $title . '][default]', $default_values, 'text_label' => $txt['default_active'], 'multiple' => true, 'value' => $is_default);
847
			$modSettings['notifications[' . $title . '][default]'] = $is_default;
848
		}
849
850
		call_integration_hook('integrate_modify_mention_settings', array(&$config_vars));
851
852
		return $config_vars;
853
	}
854
855
	/**
856
	 * Display configuration settings for signatures on forum.
857
	 *
858
	 * - Accessed from ?action=admin;area=featuresettings;sa=sig;
859
	 *
860
	 * @event integrate_save_signature_settings
861
	 */
862
	public function action_signatureSettings_display()
863
	{
864
		global $context, $txt, $modSettings;
865
866
		// Initialize the form
867
		$settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER);
868
869
		// Initialize it with our settings
870
		$settingsForm->setConfigVars($this->_signatureSettings());
871
872
		// Setup the template.
873
		$context['page_title'] = $txt['signature_settings'];
874
		$context['sub_template'] = 'show_settings';
875
876
		// Disable the max smileys option if we don't allow smileys at all!
877
		theme()->addInlineJavascript('
878
			document.getElementById(\'signature_max_smileys\').disabled = !document.getElementById(\'signature_allow_smileys\').checked;', true);
879
880
		// Load all the signature settings.
881
		[$sig_limits, $sig_bbc] = explode(':', $modSettings['signature_settings']);
882
		$sig_limits = explode(',', $sig_limits);
883
		$disabledTags = empty($sig_bbc) ? array() : explode(',', $sig_bbc);
884
885
		// @todo temporary since it does not work, and seriously why would you do this?
886
		$disabledTags[] = 'footnote';
887
888
		// Applying to ALL signatures?!!
889
		if (isset($this->_req->query->apply))
890
		{
891
			// Security!
892
			checkSession('get');
893
894
			// This is horrid - but I suppose some people will want the option to do it.
895
			$applied_sigs = $this->_req->getQuery('step', 'intval', 0);
896
			updateAllSignatures($applied_sigs);
897
898
			$settings_applied = true;
899
		}
900
901
		$context['signature_settings'] = array(
902
			'enable' => $sig_limits[0] ?? 0,
903
			'max_length' => $sig_limits[1] ?? 0,
904
			'max_lines' => $sig_limits[2] ?? 0,
905
			'max_images' => $sig_limits[3] ?? 0,
906
			'allow_smileys' => isset($sig_limits[4]) && $sig_limits[4] == -1 ? 0 : 1,
907
			'max_smileys' => isset($sig_limits[4]) && $sig_limits[4] != -1 ? $sig_limits[4] : 0,
908
			'max_image_width' => $sig_limits[5] ?? 0,
909
			'max_image_height' => $sig_limits[6] ?? 0,
910
			'max_font_size' => $sig_limits[7] ?? 0,
911
			'repetition_guests' => $sig_limits[8] ?? 0,
912
			'repetition_members' => $sig_limits[9] ?? 0,
913
		);
914
915
		// Temporarily make each setting a modSetting!
916
		foreach ($context['signature_settings'] as $key => $value)
917
		{
918
			$modSettings['signature_' . $key] = $value;
919
		}
920
921
		// Make sure we check the right tags!
922
		$modSettings['bbc_disabled_signature_bbc'] = $disabledTags;
923
924
		// Saving?
925
		if (isset($this->_req->query->save))
926
		{
927
			checkSession();
928
929
			// Clean up the tag stuff!
930
			$codes = ParserWrapper::instance()->getCodes();
931
			$bbcTags = $codes->getTags();
932
933
			$signature_bbc_enabledTags = $this->_req->getPost('signature_bbc_enabledTags', null, []);
934
			if (!is_array($signature_bbc_enabledTags))
935
			{
936
				$signature_bbc_enabledTags = array($signature_bbc_enabledTags);
937
			}
938
939
			$this->_req->post->signature_bbc_enabledTags = $signature_bbc_enabledTags;
940
941
			$sig_limits = array();
942
			foreach (array_keys($context['signature_settings']) as $key)
943
			{
944
				if ($key === 'allow_smileys')
945
				{
946
					continue;
947
				}
948
				if ($key === 'max_smileys' && empty($this->_req->post->signature_allow_smileys))
949
				{
950
					$sig_limits[] = -1;
951
				}
952
				else
953
				{
954
					$current_key = $this->_req->getPost('signature_' . $key, 'intval');
955
					$sig_limits[] = empty($current_key) ? 0 : max(1, $current_key);
956
				}
957
			}
958
959
			call_integration_hook('integrate_save_signature_settings', array(&$sig_limits, &$bbcTags));
960
961
			$this->_req->post->signature_settings = implode(',', $sig_limits) . ':' . implode(',', array_diff($bbcTags, $this->_req->post->signature_bbc_enabledTags));
962
963
			// Even though we have practically no settings let's keep the convention going!
964
			$save_vars = array();
965
			$save_vars[] = array('text', 'signature_settings');
966
967
			$settingsForm->setConfigVars($save_vars);
968
			$settingsForm->setConfigValues((array) $this->_req->post);
969
			$settingsForm->save();
970
			redirectexit('action=admin;area=featuresettings;sa=sig');
971
		}
972
973
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'sig', 'save']);
974
		$context['settings_title'] = $txt['signature_settings'];
975
		$context['settings_message'] = empty($settings_applied) ? sprintf($txt['signature_settings_warning'], getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'sig', 'apply', '{session_data}'])) : $txt['signature_settings_applied'];
976
977
		$settingsForm->prepare();
978
	}
979
980
	/**
981
	 * Return signature settings.
982
	 *
983
	 * - Used in admin center search and settings form
984
	 *
985
	 * @event integrate_modify_signature_settings Adds options to Signature Settings
986
	 */
987
	private function _signatureSettings()
988
	{
989
		global $txt;
990
991
		$config_vars = array(
992
			// Are signatures even enabled?
993
			array('check', 'signature_enable'),
994
			'',
995
			// Tweaking settings!
996
			array('int', 'signature_max_length', 'subtext' => $txt['zero_for_no_limit']),
997
			array('int', 'signature_max_lines', 'subtext' => $txt['zero_for_no_limit']),
998
			array('int', 'signature_max_font_size', 'subtext' => $txt['zero_for_no_limit']),
999
			array('check', 'signature_allow_smileys', 'onclick' => "document.getElementById('signature_max_smileys').disabled = !this.checked;"),
1000
			array('int', 'signature_max_smileys', 'subtext' => $txt['zero_for_no_limit']),
1001
			array('select', 'signature_repetition_guests',
1002
				array(
1003
					$txt['signature_always'],
1004
					$txt['signature_onlyfirst'],
1005
					$txt['signature_never'],
1006
				),
1007
			),
1008
			array('select', 'signature_repetition_members',
1009
				array(
1010
					$txt['signature_always'],
1011
					$txt['signature_onlyfirst'],
1012
					$txt['signature_never'],
1013
				),
1014
			),
1015
			'',
1016
			// Image settings.
1017
			array('int', 'signature_max_images', 'subtext' => $txt['signature_max_images_note']),
1018
			array('int', 'signature_max_image_width', 'subtext' => $txt['zero_for_no_limit']),
1019
			array('int', 'signature_max_image_height', 'subtext' => $txt['zero_for_no_limit']),
1020
			'',
1021
			array('bbc', 'signature_bbc'),
1022
		);
1023
1024
		call_integration_hook('integrate_modify_signature_settings', array(&$config_vars));
1025
1026
		return $config_vars;
1027
	}
1028
1029
	/**
1030
	 * Show all the custom profile fields available to the user.
1031
	 *
1032
	 * - Allows for drag/drop sorting of custom profile fields
1033
	 * - Accessed with ?action=admin;area=featuresettings;sa=profile
1034
	 *
1035
	 * @uses sub template show_custom_profile
1036
	 */
1037
	public function action_profile()
1038
	{
1039
		global $txt, $context;
1040
1041
		theme()->getTemplates()->load('ManageFeatures');
1042
		$context['page_title'] = $txt['custom_profile_title'];
1043
		$context['sub_template'] = 'show_custom_profile';
1044
1045
		// What about standard fields they can tweak?
1046
		$standard_fields = array('website', 'posts', 'warning_status', 'date_registered', 'action');
1047
1048
		// What fields can't you put on the registration page?
1049
		$context['fields_no_registration'] = array('posts', 'warning_status', 'date_registered', 'action');
1050
1051
		// Are we saving any standard field changes?
1052
		if (isset($this->_req->post->save))
1053
		{
1054
			checkSession();
1055
			validateToken('admin-scp');
1056
1057
			$changes = array();
1058
1059
			// Do the active ones first.
1060
			$disable_fields = array_flip($standard_fields);
1061
			if (!empty($this->_req->post->active))
1062
			{
1063
				foreach ($this->_req->post->active as $value)
1064
				{
1065
					if (isset($disable_fields[$value]))
1066
					{
1067
						unset($disable_fields[$value]);
1068
					}
1069
				}
1070
			}
1071
1072
			// What we have left!
1073
			$changes['disabled_profile_fields'] = empty($disable_fields) ? '' : implode(',', array_keys($disable_fields));
1074
1075
			// Things we want to show on registration?
1076
			$reg_fields = array();
1077
			if (!empty($this->_req->post->reg))
1078
			{
1079
				foreach ($this->_req->post->reg as $value)
1080
				{
1081
					if (!in_array($value, $standard_fields))
1082
					{
1083
						continue;
1084
					}
1085
1086
					if (isset($disable_fields[$value]))
1087
					{
1088
						continue;
1089
					}
1090
1091
					$reg_fields[] = $value;
1092
				}
1093
			}
1094
1095
			// What we have left!
1096
			$changes['registration_fields'] = empty($reg_fields) ? '' : implode(',', $reg_fields);
1097
1098
			updateSettings($changes);
1099
		}
1100
1101
		createToken('admin-scp');
1102
1103
		// Create a listing for all our standard fields
1104
		$listOptions = array(
1105
			'id' => 'standard_profile_fields',
1106
			'title' => $txt['standard_profile_title'],
1107
			'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'profile']),
1108
			'get_items' => array(
1109
				'function' => 'list_getProfileFields',
1110
				'params' => array(
1111
					true,
1112
				),
1113
			),
1114
			'columns' => array(
1115
				'field' => array(
1116
					'header' => array(
1117
						'value' => $txt['standard_profile_field'],
1118
					),
1119
					'data' => array(
1120
						'db' => 'label',
1121
						'style' => 'width: 60%;',
1122
					),
1123
				),
1124
				'active' => array(
1125
					'header' => array(
1126
						'value' => $txt['custom_edit_active'],
1127
						'class' => 'centertext',
1128
					),
1129
					'data' => array(
1130
						'function' => static function ($rowData) {
1131
							$isChecked = $rowData['disabled'] ? '' : ' checked="checked"';
1132
							$onClickHandler = $rowData['can_show_register'] ? sprintf('onclick="document.getElementById(\'reg_%1$s\').disabled = !this.checked;"', $rowData['id']) : '';
1133
1134
							return sprintf('<input type="checkbox" name="active[]" id="active_%1$s" value="%1$s" class="input_check" %2$s %3$s />', $rowData['id'], $isChecked, $onClickHandler);
1135
						},
1136
						'style' => 'width: 20%;',
1137
						'class' => 'centertext',
1138
					),
1139
				),
1140
				'show_on_registration' => array(
1141
					'header' => array(
1142
						'value' => $txt['custom_edit_registration'],
1143
						'class' => 'centertext',
1144
					),
1145
					'data' => array(
1146
						'function' => static function ($rowData) {
1147
							$isChecked = $rowData['on_register'] && !$rowData['disabled'] ? ' checked="checked"' : '';
1148
							$isDisabled = $rowData['can_show_register'] ? '' : ' disabled="disabled"';
1149
1150
							return sprintf('<input type="checkbox" name="reg[]" id="reg_%1$s" value="%1$s" class="input_check" %2$s %3$s />', $rowData['id'], $isChecked, $isDisabled);
1151
						},
1152
						'style' => 'width: 20%;',
1153
						'class' => 'centertext',
1154
					),
1155
				),
1156
			),
1157
			'form' => array(
1158
				'href' => getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'profile']),
1159
				'name' => 'standardProfileFields',
1160
				'token' => 'admin-scp',
1161
			),
1162
			'additional_rows' => array(
1163
				array(
1164
					'position' => 'below_table_data',
1165
					'value' => '<input type="submit" name="save" value="' . $txt['save'] . '" class="right_submit" />',
1166
				),
1167
			),
1168
		);
1169
		createList($listOptions);
1170
1171
		// And now we do the same for all of our custom ones
1172
		$token = createToken('admin-sort');
1173
		$listOptions = array(
1174
			'id' => 'custom_profile_fields',
1175
			'title' => $txt['custom_profile_title'],
1176
			'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'profile']),
1177
			'default_sort_col' => 'vieworder',
1178
			'no_items_label' => $txt['custom_profile_none'],
1179
			'items_per_page' => 25,
1180
			'sortable' => true,
1181
			'get_items' => array(
1182
				'function' => 'list_getProfileFields',
1183
				'params' => array(
1184
					false,
1185
				),
1186
			),
1187
			'get_count' => array(
1188
				'function' => 'list_getProfileFieldSize',
1189
			),
1190
			'columns' => array(
1191
				'vieworder' => array(
1192
					'header' => array(
1193
						'value' => '',
1194
						'class' => 'hide',
1195
					),
1196
					'data' => array(
1197
						'db' => 'vieworder',
1198
						'class' => 'hide',
1199
					),
1200
					'sort' => array(
1201
						'default' => 'vieworder',
1202
					),
1203
				),
1204
				'field_name' => array(
1205
					'header' => array(
1206
						'value' => $txt['custom_profile_fieldname'],
1207
					),
1208
					'data' => array(
1209
						'function' => static fn($rowData) => sprintf('<a href="%1$s">%2$s</a><div class="smalltext">%3$s</div>', getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'profileedit', 'fid' => (int) $rowData['id_field']]), $rowData['field_name'], $rowData['field_desc']),
1210
						'style' => 'width: 65%;',
1211
					),
1212
					'sort' => array(
1213
						'default' => 'field_name',
1214
						'reverse' => 'field_name DESC',
1215
					),
1216
				),
1217
				'field_type' => array(
1218
					'header' => array(
1219
						'value' => $txt['custom_profile_fieldtype'],
1220
					),
1221
					'data' => array(
1222
						'function' => static function ($rowData) {
1223
							global $txt;
1224
1225
							$textKey = sprintf('custom_profile_type_%1$s', $rowData['field_type']);
1226
1227
							return $txt[$textKey] ?? $textKey;
1228
						},
1229
						'style' => 'width: 10%;',
1230
					),
1231
					'sort' => array(
1232
						'default' => 'field_type',
1233
						'reverse' => 'field_type DESC',
1234
					),
1235
				),
1236
				'cust' => array(
1237
					'header' => array(
1238
						'value' => $txt['custom_profile_active'],
1239
						'class' => 'centertext',
1240
					),
1241
					'data' => array(
1242
						'function' => static function ($rowData) {
1243
							$isChecked = $rowData['active'] === '1' ? ' checked="checked"' : '';
1244
1245
							return sprintf('<input type="checkbox" name="cust[]" id="cust_%1$s" value="%1$s" class="input_check"%2$s />', $rowData['id_field'], $isChecked);
1246
						},
1247
						'style' => 'width: 8%;',
1248
						'class' => 'centertext',
1249
					),
1250
					'sort' => array(
1251
						'default' => 'active DESC',
1252
						'reverse' => 'active',
1253
					),
1254
				),
1255
				'placement' => array(
1256
					'header' => array(
1257
						'value' => $txt['custom_profile_placement'],
1258
					),
1259
					'data' => array(
1260
						'function' => static function ($rowData) {
1261
							global $txt;
1262
1263
							$placement = 'custom_profile_placement_';
1264
							switch ((int) $rowData['placement'])
1265
							{
1266
								case 0:
1267
									$placement .= 'standard';
1268
									break;
1269
								case 1:
1270
									$placement .= 'withicons';
1271
									break;
1272
								case 2:
1273
									$placement .= 'abovesignature';
1274
									break;
1275
								case 3:
1276
									$placement .= 'aboveicons';
1277
									break;
1278
							}
1279
1280
							return $txt[$placement];
1281
						},
1282
						'style' => 'width: 5%;',
1283
					),
1284
					'sort' => array(
1285
						'default' => 'placement DESC',
1286
						'reverse' => 'placement',
1287
					),
1288
				),
1289
				'modify' => array(
1290
					'data' => array(
1291
						'sprintf' => array(
1292
							'format' => '<a href="' . getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'profileedit']) . ';fid=%1$s">' . $txt['modify'] . '</a>',
1293
							'params' => array(
1294
								'id_field' => false,
1295
							),
1296
						),
1297
						'style' => 'width: 5%;',
1298
					),
1299
				),
1300
			),
1301
			'form' => array(
1302
				'href' => getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'profileedit']),
1303
				'name' => 'customProfileFields',
1304
				'token' => 'admin-scp',
1305
			),
1306
			'additional_rows' => array(
1307
				array(
1308
					'class' => 'submitbutton flow_flex_additional_row',
1309
					'position' => 'below_table_data',
1310
					'value' => '
1311
						<input type="submit" name="onoff" value="' . $txt['save'] . '" />
1312
						<input type="submit" name="new" value="' . $txt['custom_profile_make_new'] . '" />',
1313
				),
1314
				array(
1315
					'position' => 'top_of_list',
1316
					'value' => '<p class="infobox">' . $txt['custom_profile_sort'] . '</p>',
1317
				),
1318
			),
1319
			'javascript' => '
1320
				$().elkSortable({
1321
					sa: "profileorder",
1322
					error: "' . $txt['admin_order_error'] . '",
1323
					title: "' . $txt['admin_order_title'] . '",
1324
					placeholder: "ui-state-highlight",
1325
					href: "?action=admin;area=featuresettings;sa=profile",
1326
					token: {token_var: "' . $token['admin-sort_token_var'] . '", token_id: "' . $token['admin-sort_token'] . '"}
1327
				});
1328
			',
1329
		);
1330
1331
		createList($listOptions);
1332
	}
1333
1334
	/**
1335
	 * Edit some profile fields?
1336
	 *
1337
	 * - Accessed with ?action=admin;area=featuresettings;sa=profileedit
1338
	 *
1339
	 * @uses sub template edit_profile_field
1340
	 */
1341
	public function action_profileedit()
1342
	{
1343
		global $txt, $context;
1344
1345
		theme()->getTemplates()->load('ManageFeatures');
1346
1347
		// Sort out the context!
1348
		$context['fid'] = $this->_req->getQuery('fid', 'intval', 0);
1349
		$context[$context['admin_menu_name']]['current_subsection'] = 'profile';
1350
		$context['page_title'] = $context['fid'] ? $txt['custom_edit_title'] : $txt['custom_add_title'];
1351
		$context['sub_template'] = 'edit_profile_field';
1352
1353
		// Any errors messages to show?
1354
		if (isset($this->_req->query->msg))
1355
		{
1356
			Txt::load('Errors');
1357
1358
			if (isset($txt['custom_option_' . $this->_req->query->msg]))
1359
			{
1360
				$context['custom_option__error'] = $txt['custom_option_' . $this->_req->query->msg];
1361
			}
1362
		}
1363
1364
		// Load the profile language for section names.
1365
		Txt::load('Profile');
1366
1367
		// Load up the profile field, if one was supplied
1368
		if ($context['fid'])
1369
		{
1370
			$context['field'] = getProfileField($context['fid']);
1371
		}
1372
1373
		// Setup the default values as needed.
1374
		if (empty($context['field']))
1375
		{
1376
			$context['field'] = array(
1377
				'name' => '',
1378
				'colname' => '???',
1379
				'desc' => '',
1380
				'profile_area' => 'forumprofile',
1381
				'reg' => false,
1382
				'display' => false,
1383
				'memberlist' => false,
1384
				'type' => 'text',
1385
				'max_length' => 255,
1386
				'rows' => 4,
1387
				'cols' => 30,
1388
				'bbc' => false,
1389
				'default_check' => false,
1390
				'default_select' => '',
1391
				'default_value' => '',
1392
				'options' => array('', '', ''),
1393
				'active' => true,
1394
				'private' => false,
1395
				'can_search' => false,
1396
				'mask' => 'nohtml',
1397
				'regex' => '',
1398
				'enclose' => '',
1399
				'placement' => 0,
1400
			);
1401
		}
1402
1403
		// All the javascript for this page... everything else is in admin.js
1404
		theme()->addJavascriptVar(array('startOptID' => count($context['field']['options'])));
1405
		theme()->addInlineJavascript('updateInputBoxes();', true);
1406
1407
		// Are we toggling which ones are active?
1408
		if (isset($this->_req->post->onoff))
1409
		{
1410
			checkSession();
1411
			validateToken('admin-scp');
1412
1413
			// Enable and disable custom fields as required.
1414
			$enabled = array(0);
1415
			if (isset($this->_req->post->cust) && is_array($this->_req->post->cust))
1416
			{
1417
				foreach ($this->_req->post->cust as $id)
1418
				{
1419
					$enabled[] = (int) $id;
1420
				}
1421
			}
1422
1423
			updateRenamedProfileStatus($enabled);
1424
		}
1425
		// Are we saving?
1426
		elseif (isset($this->_req->post->save))
1427
		{
1428
			checkSession();
1429
			validateToken('admin-ecp');
1430
1431
			// Everyone needs a name - even the (bracket) unknown...
1432
			if (trim($this->_req->post->field_name) === '')
1433
			{
1434
				redirectexit('action=admin;area=featuresettings;sa=profileedit;fid=' . $this->_req->query->fid . ';msg=need_name');
1435
			}
1436
1437
			// Regex you say?  Do a very basic test to see if the pattern is valid
1438
			if (!empty($this->_req->post->regex) && @preg_match($this->_req->post->regex, 'dummy') === false)
1439
			{
1440
				redirectexit('action=admin;area=featuresettings;sa=profileedit;fid=' . $this->_req->query->fid . ';msg=regex_error');
1441
			}
1442
1443
			$this->_req->post->field_name = $this->_req->getPost('field_name', '\\ElkArte\\Helper\\Util::htmlspecialchars');
1444
			$this->_req->post->field_desc = $this->_req->getPost('field_desc', '\\ElkArte\\Helper\\Util::htmlspecialchars');
1445
1446
			$rows = isset($this->_req->post->rows) ? (int) $this->_req->post->rows : 4;
1447
			$cols = isset($this->_req->post->cols) ? (int) $this->_req->post->cols : 30;
1448
1449
			// Checkboxes...
1450
			$show_reg = $this->_req->getPost('reg', 'intval', 0);
1451
			$show_display = isset($this->_req->post->display) ? 1 : 0;
1452
			$show_memberlist = isset($this->_req->post->memberlist) ? 1 : 0;
1453
			$bbc = isset($this->_req->post->bbc) ? 1 : 0;
1454
			$show_profile = $this->_req->post->profile_area;
1455
			$active = isset($this->_req->post->active) ? 1 : 0;
1456
			$private = $this->_req->getPost('private', 'intval', 0);
1457
			$can_search = isset($this->_req->post->can_search) ? 1 : 0;
1458
1459
			// Some masking stuff...
1460
			$mask = $this->_req->getPost('mask', 'strval', '');
1461
			if ($mask === 'regex' && isset($this->_req->post->regex))
1462
			{
1463
				$mask .= $this->_req->post->regex;
1464
			}
1465
1466
			$field_length = $this->_req->getPost('max_length', 'intval', 255);
1467
			$enclose = $this->_req->getPost('enclose', 'strval', '');
1468
			$placement = $this->_req->getPost('placement', 'intval', 0);
1469
1470
			// Select options?
1471
			$field_options = '';
1472
			$newOptions = array();
1473
1474
			// Set default
1475
			$default = '';
1476
1477
			switch ($this->_req->post->field_type)
1478
			{
1479
				case 'check':
1480
					$default = isset($this->_req->post->default_check) ? 1 : '';
1481
					break;
1482
				case 'select':
1483
				case 'radio':
1484
					if (!empty($this->_req->post->select_option))
1485
					{
1486
						foreach ($this->_req->post->select_option as $k => $v)
1487
						{
1488
							// Clean, clean, clean...
1489
							$v = Util::htmlspecialchars($v);
1490
							$v = strtr($v, array(',' => ''));
1491
1492
							// Nada, zip, etc...
1493
							if (trim($v) === '')
1494
							{
1495
								continue;
1496
							}
1497
1498
							// Otherwise, save it boy.
1499
							$field_options .= $v . ',';
1500
1501
							// This is just for working out what happened with old options...
1502
							$newOptions[$k] = $v;
1503
1504
							// Is it default?
1505
							if (!isset($this->_req->post->default_select))
1506
							{
1507
								continue;
1508
							}
1509
1510
							if ($this->_req->post->default_select != $k)
1511
							{
1512
								continue;
1513
							}
1514
1515
							$default = $v;
1516
						}
1517
1518
						if (isset($_POST['default_select']) && $_POST['default_select'] === 'no_default')
1519
						{
1520
							$default = 'no_default';
1521
						}
1522
1523
						$field_options = substr($field_options, 0, -1);
1524
					}
1525
1526
					break;
1527
				default:
1528
					$default = $this->_req->post->default_value ?? '';
1529
			}
1530
1531
			// Come up with the unique name?
1532
			if (empty($context['fid']))
1533
			{
1534
				$colname = Util::substr(strtr($this->_req->post->field_name, array(' ' => '')), 0, 6);
1535
				preg_match('~([\w_-]+)~', $colname, $matches);
1536
1537
				// If there is nothing to the name, then let's start our own - for foreign languages etc.
1538 2
				if (isset($matches[1]))
1539
				{
1540 2
					$colname = 'cust_' . strtolower($matches[1]);
1541
					$initial_colname = 'cust_' . strtolower($matches[1]);
1542
				}
1543
				else
1544
				{
1545
					$colname = 'cust_' . mt_rand(1, 999999);
1546 2
					$initial_colname = 'cust_' . mt_rand(1, 999999);
1547
				}
1548 2
1549
				$unique = ensureUniqueProfileField($colname, $initial_colname);
1550
1551
				// Still not a unique column name? Leave it up to the user, then.
1552
				if (!$unique)
1553
				{
1554 2
					throw new Exception('custom_option_not_unique');
1555
				}
1556 2
1557
				// And create a new field
1558
				$new_field = array(
1559
					'col_name' => $colname,
1560
					'field_name' => $this->_req->post->field_name,
1561
					'field_desc' => $this->_req->post->field_desc,
1562 2
					'field_type' => $this->_req->post->field_type,
1563
					'field_length' => $field_length,
1564 2
					'field_options' => $field_options,
1565
					'show_reg' => $show_reg,
1566
					'show_display' => $show_display,
1567
					'show_memberlist' => $show_memberlist,
1568
					'show_profile' => $show_profile,
1569
					'private' => $private,
1570 2
					'active' => $active,
1571
					'default_value' => $default,
1572 2
					'rows' => $rows,
1573
					'cols' => $cols,
1574
					'can_search' => $can_search,
1575
					'bbc' => $bbc,
1576
					'mask' => $mask,
1577
					'enclose' => $enclose,
1578 2
					'placement' => $placement,
1579
					'vieworder' => list_getProfileFieldSize() + 1,
1580 2
				);
1581
				addProfileField($new_field);
1582
			}
1583
			// Work out what to do with the user data otherwise...
1584
			else
1585
			{
1586
				// Anything going to check or select is pointless keeping - as is anything coming from check!
1587
				if (($this->_req->post->field_type === 'check' && $context['field']['type'] !== 'check')
1588
					|| (($this->_req->post->field_type === 'select' || $this->_req->post->field_type === 'radio') && $context['field']['type'] !== 'select' && $context['field']['type'] !== 'radio')
1589
					|| ($context['field']['type'] === 'check' && $this->_req->post->field_type !== 'check'))
1590
				{
1591
					deleteProfileFieldUserData($context['field']['colname']);
1592
				}
1593
				// Otherwise - if the select is edited may need to adjust!
1594
				elseif ($this->_req->post->field_type === 'select' || $this->_req->post->field_type === 'radio')
1595
				{
1596
					$optionChanges = $context['field']['options'];
1597
					$takenKeys = array();
1598
1599
					// Work out what's changed!
1600
					foreach ($optionChanges as $k => $option)
1601
					{
1602
						if (trim($option) === '')
1603
						{
1604
							continue;
1605
						}
1606
1607
						// Still exists?
1608
						if (in_array($option, $newOptions))
1609
						{
1610
							$takenKeys[] = $k;
1611
						}
1612
					}
1613
1614
					// Finally - have we renamed it - or is it really gone?
1615
					foreach ($optionChanges as $k => $option)
1616
					{
1617
						// Just been renamed?
1618
						if (in_array($k, $takenKeys))
1619
						{
1620
							continue;
1621
						}
1622
1623
						if (empty($newOptions[$k]))
1624
						{
1625
							continue;
1626
						}
1627
1628
						updateRenamedProfileField($k, $newOptions, $context['field']['colname'], $option);
1629
					}
1630
				}
1631
1632
				// @todo Maybe we should adjust based on new text length limits?
1633
1634
				// And finally update an existing field
1635
				$field_data = array(
1636
					'field_length' => $field_length,
1637
					'show_reg' => $show_reg,
1638
					'show_display' => $show_display,
1639
					'show_memberlist' => $show_memberlist,
1640
					'private' => $private,
1641
					'active' => $active,
1642
					'can_search' => $can_search,
1643
					'bbc' => $bbc,
1644
					'current_field' => $context['fid'],
1645
					'field_name' => $this->_req->post->field_name,
1646
					'field_desc' => $this->_req->post->field_desc,
1647
					'field_type' => $this->_req->post->field_type,
1648
					'field_options' => $field_options,
1649
					'show_profile' => $show_profile,
1650
					'default_value' => $default,
1651
					'mask' => $mask,
1652
					'enclose' => $enclose,
1653
					'placement' => $placement,
1654
					'rows' => $rows,
1655
					'cols' => $cols,
1656
				);
1657
1658
				updateProfileField($field_data);
1659
1660
				// Just clean up any old selects - these are a pain!
1661
				if (($this->_req->post->field_type == 'select' || $this->_req->post->field_type == 'radio') && !empty($newOptions))
1662
				{
1663
					deleteOldProfileFieldSelects($newOptions, $context['field']['colname']);
1664
				}
1665
			}
1666
		}
1667
		// Deleting?
1668
		elseif (isset($this->_req->post->delete) && $context['field']['colname'])
1669
		{
1670
			checkSession();
1671
			validateToken('admin-ecp');
1672
1673
			// Delete the old data first, then the field.
1674
			deleteProfileFieldUserData($context['field']['colname']);
1675
			deleteProfileField($context['fid']);
1676
		}
1677
1678
		// Rebuild display cache etc.
1679
		if (isset($this->_req->post->delete) || isset($this->_req->post->save) || isset($this->_req->post->onoff))
1680
		{
1681
			checkSession();
1682
1683
			// Update the display cache
1684
			updateDisplayCache();
1685
			redirectexit('action=admin;area=featuresettings;sa=profile');
1686
		}
1687
1688
		createToken('admin-ecp');
1689
	}
1690
1691
	/**
1692
	 * Editing personal messages settings
1693
	 *
1694
	 * - Accessed with ?action=admin;area=featuresettings;sa=pmsettings
1695
	 *
1696
	 * @event integrate_save_pmsettings_settings
1697
	 */
1698
	public function action_pmsettings()
1699
	{
1700
		global $txt, $context;
1701
1702
		// Initialize the form
1703
		$settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER);
1704
1705
		// Initialize it with our settings
1706
		$settingsForm->setConfigVars($this->_pmSettings());
1707
1708
		require_once(SUBSDIR . '/PersonalMessage.subs.php');
1709
		Txt::load('ManageMembers');
1710
1711
		$context['pm_limits'] = loadPMLimits();
1712
1713
		// Saving?
1714
		if (isset($this->_req->query->save))
1715
		{
1716
			checkSession();
1717
1718
			require_once(SUBSDIR . '/Membergroups.subs.php');
1719
			foreach ($context['pm_limits'] as $group_id => $group)
1720
			{
1721
				if (!isset($this->_req->post->group[$group_id]))
1722
				{
1723
					continue;
1724
				}
1725
1726
				if ($this->_req->post->group[$group_id] == $group['max_messages'])
1727
				{
1728
					continue;
1729
				}
1730
1731
				updateMembergroupProperties(array('current_group' => $group_id, 'max_messages' => $this->_req->post->group[$group_id]));
1732
			}
1733
1734
			call_integration_hook('integrate_save_pmsettings_settings');
1735
1736
			$settingsForm->setConfigValues((array) $this->_req->post);
1737
			$settingsForm->save();
1738
			redirectexit('action=admin;area=featuresettings;sa=pmsettings');
1739
		}
1740
1741
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'featuresettings', 'sa' => 'pmsettings', 'save']);
1742
		$context['settings_title'] = $txt['personal_messages'];
1743
1744
		$settingsForm->prepare();
1745
	}
1746
1747
	/**
1748
	 * Return pm settings.
1749
	 *
1750
	 * - Used in admin center search and settings form
1751
	 *
1752
	 * @event integrate_modify_pmsettings_settings Adds / Modifies PM Settings
1753
	 */
1754
	private function _pmSettings()
1755
	{
1756
		global $txt;
1757
1758
		$config_vars = array(
1759
			// Reporting of personal messages?
1760
			array('check', 'enableReportPM'),
1761
			// Inline permissions.
1762
			array('permissions', 'pm_send'),
1763
			// PM Settings
1764
			array('title', 'antispam_PM'),
1765
			'pm1' => array('int', 'max_pm_recipients', 'postinput' => $txt['max_pm_recipients_note']),
1766
			'pm2' => array('int', 'pm_posts_verification', 'postinput' => $txt['pm_posts_verification_note']),
1767
			'pm3' => array('int', 'pm_posts_per_hour', 'postinput' => $txt['pm_posts_per_hour_note']),
1768
			array('title', 'membergroups_max_messages'),
1769
			array('desc', 'membergroups_max_messages_desc'),
1770
			array('callback', 'pm_limits'),
1771
		);
1772
1773
		call_integration_hook('integrate_modify_pmsettings_settings', array(&$config_vars));
1774
1775
		return $config_vars;
1776
	}
1777
1778
	/**
1779
	 * Public method to return the basic settings, used for admin search
1780
	 */
1781
	public function basicSettings_search()
1782
	{
1783
		return $this->_basicSettings();
1784
	}
1785
1786
	/**
1787
	 * Public method to return the layout settings, used for admin search
1788
	 */
1789
	public function layoutSettings_search()
1790
	{
1791
		return $this->_layoutSettings();
1792
	}
1793
1794
	/**
1795
	 * Public method to return the karma settings, used for admin search
1796
	 */
1797
	public function karmaSettings_search()
1798
	{
1799
		return $this->_karmaSettings();
1800
	}
1801
1802
	/**
1803
	 * Public method to return the likes settings, used for admin search
1804
	 */
1805
	public function likesSettings_search()
1806
	{
1807
		return $this->_likesSettings();
1808
	}
1809
1810
	/**
1811
	 * Public method to return the mention settings, used for admin search
1812
	 */
1813
	public function mentionSettings_search()
1814
	{
1815
		return $this->_notificationsSettings();
1816
	}
1817
1818
	/**
1819
	 * Public method to return the signature settings, used for admin search
1820
	 */
1821
	public function signatureSettings_search()
1822
	{
1823
		return $this->_signatureSettings();
1824
	}
1825
1826
	/**
1827
	 * Public method to return the PM settings, used for admin search
1828
	 */
1829
	public function pmSettings_search()
1830
	{
1831
		return $this->_pmSettings();
1832
	}
1833
}
1834