Completed
Push — master ( 85c2b8...2c0be7 )
by Nazar
03:57
created

modules::admin_modules_disable()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 9
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 12
rs 9.2
1
<?php
2
/**
3
 * @package    CleverStyle CMS
4
 * @subpackage System module
5
 * @category   modules
6
 * @author     Nazar Mokrynskyi <[email protected]>
7
 * @copyright  Copyright (c) 2015-2016, Nazar Mokrynskyi
8
 * @license    MIT License, see license.txt
9
 */
10
namespace cs\modules\System\api\Controller\admin;
11
use
12
	cs\Cache as System_cache,
13
	cs\Config,
14
	cs\Event,
15
	cs\ExitException,
16
	cs\Language,
17
	cs\Permission,
18
	cs\Session,
19
	cs\modules\System\Packages_dependencies,
20
	cs\modules\System\Packages_manipulation;
21
22
trait modules {
23
	/**
24
	 * @param \cs\Request $Request
25
	 *
26
	 * @return mixed
27
	 *
28
	 * @throws ExitException
29
	 */
30
	static function admin_modules_get ($Request) {
31
		if ($Request->route_path(3)) {
32
			$route_path = $Request->route_path;
33
			switch ($route_path[3]) {
34
				/**
35
				 * Get dependent packages for module
36
				 */
37
				case 'dependent_packages':
38
					return static::get_dependent_packages_for_module($route_path[2]);
39
				/**
40
				 * Get dependencies for module (packages, databases, storages)
41
				 */
42
				case 'dependencies':
43
					return static::get_dependencies_for_module($route_path[2]);
44
				/**
45
				 * Get dependencies for module during update
46
				 */
47
				case 'update_dependencies':
48
					return static::get_update_dependencies_for_module($route_path[2]);
49
				/**
50
				 * Get mapping of named module's databases to indexes of system databases
51
				 */
52
				case 'db':
53
					return static::get_module_databases($route_path[2]);
54
				/**
55
				 * Get mapping of named module's storages to indexes of system storages
56
				 */
57
				case 'storage':
58
					return static::get_module_storages($route_path[2]);
59
				default:
60
					throw new ExitException(400);
61
			}
62
		} elseif ($Request->route_path(2) == 'default') {
63
			/**
64
			 * Get current default module
65
			 */
66
			return static::get_default_module();
67
		} else {
68
			/**
69
			 * Get array of modules in extended form
70
			 */
71
			return static::get_modules_list();
72
		}
73
	}
74
	/**
75
	 * @param string $module
76
	 *
77
	 * @return string[][]
78
	 *
79
	 * @throws ExitException
80
	 */
81
	protected static function get_dependent_packages_for_module ($module) {
82
		if (!Config::instance()->module($module)) {
83
			throw new ExitException(404);
84
		}
85
		$meta_file = MODULES."/$module/meta.json";
86
		return file_exists($meta_file) ? Packages_dependencies::get_dependent_packages(file_get_json($meta_file)) : [];
87
	}
88
	/**
89
	 * @param string $module
90
	 *
91
	 * @return array
92
	 *
93
	 * @throws ExitException
94
	 */
95
	protected static function get_dependencies_for_module ($module) {
96
		if (!Config::instance()->module($module)) {
97
			throw new ExitException(404);
98
		}
99
		$meta_file = MODULES."/$module/meta.json";
100
		return file_exists($meta_file) ? Packages_dependencies::get_dependencies(file_get_json($meta_file)) : [];
101
	}
102
	/**
103
	 * @param string $module
104
	 *
105
	 * @return array
106
	 *
107
	 * @throws ExitException
108
	 */
109
	protected static function get_update_dependencies_for_module ($module) {
110
		if (!Config::instance()->module($module)) {
111
			throw new ExitException(404);
112
		}
113
		$tmp_location = TEMP.'/System/admin/'.Session::instance()->get_id().'.phar';
114
		$tmp_dir      = "phar://$tmp_location";
115
		if (
116
			!file_exists(MODULES."/$module/meta.json") ||
117
			!file_exists("$tmp_dir/meta.json")
118
		) {
119
			throw new ExitException(400);
120
		}
121
		$new_meta = file_get_json("$tmp_dir/meta.json");
122
		if (!static::is_same_module($new_meta, $module)) {
123
			throw new ExitException(Language::prefix('system_admin_modules_')->this_is_not_module_installer_file, 400);
124
		}
125
		return Packages_dependencies::get_dependencies($new_meta);
126
	}
127
	/**
128
	 * @param array  $meta
129
	 * @param string $module
130
	 *
131
	 * @return bool
132
	 */
133
	protected static function is_same_module ($meta, $module) {
134
		return $meta['category'] == 'modules' && $meta['package'] == $module;
135
	}
136
	/**
137
	 * @param string $module
138
	 *
139
	 * @return array
140
	 *
141
	 * @throws ExitException
142
	 */
143
	protected static function get_module_databases ($module) {
144
		$Config = Config::instance();
145
		if (!isset($Config->components['modules'][$module]['db'])) {
146
			throw new ExitException(404);
147
		}
148
		return $Config->components['modules'][$module]['db'];
149
	}
150
	/**
151
	 * @param string $module
152
	 *
153
	 * @return array
154
	 *
155
	 * @throws ExitException
156
	 */
157
	protected static function get_module_storages ($module) {
158
		$Config = Config::instance();
159
		if (!isset($Config->components['modules'][$module]['storage'])) {
160
			throw new ExitException(404);
161
		}
162
		return $Config->components['modules'][$module]['storage'];
163
	}
164
	protected static function get_modules_list () {
165
		$Config       = Config::instance();
166
		$modules_list = [];
167
		foreach ($Config->components['modules'] as $module_name => &$module_data) {
168
			$module = [
169
				'active'            => $module_data['active'],
170
				'name'              => $module_name,
171
				'has_user_section'  => file_exists_with_extension(MODULES."/$module_name/index", ['php', 'html', 'json']),
172
				'has_admin_section' => file_exists_with_extension(MODULES."/$module_name/admin/index", ['php', 'json'])
173
			];
174
			/**
175
			 * Check if API available
176
			 */
177
			static::check_module_feature_availability($module, 'readme', 'api');
178
			/**
179
			 * Check if readme available
180
			 */
181
			static::check_module_feature_availability($module, 'readme');
182
			/**
183
			 * Check if license available
184
			 */
185
			static::check_module_feature_availability($module, 'license');
186
			if (file_exists(MODULES."/$module_name/meta.json")) {
187
				$module['meta'] = file_get_json(MODULES."/$module_name/meta.json");
188
			}
189
			$modules_list[] = $module;
190
		}
191
		return $modules_list;
192
	}
193
	/**
194
	 * @param array  $module
195
	 * @param string $feature
196
	 * @param string $dir
197
	 */
198
	protected static function check_module_feature_availability (&$module, $feature, $dir = '') {
199
		/**
200
		 * Check if feature available
201
		 */
202
		$file = file_exists_with_extension(MODULES."/$module[name]/$dir/$feature", ['txt', 'html']);
203
		if ($file) {
204
			$module[$dir ?: $feature] = [
205
				'type'    => substr($file, -3) == 'txt' ? 'txt' : 'html',
206
				'content' => file_get_contents($file)
207
			];
208
		} elseif ($dir && is_dir(MODULES."/$module[name]/$dir")) {
209
			$module[$dir ?: $feature] = [];
210
		}
211
	}
212
	/**
213
	 * @return string
214
	 */
215
	protected static function get_default_module () {
216
		return Config::instance()->core['default_module'];
217
	}
218
	/**
219
	 * @param \cs\Request $Request
220
	 *
221
	 * @throws ExitException
222
	 */
223
	static function admin_modules_put ($Request) {
224
		if ($Request->route_path(3)) {
225
			$module = $Request->route_path[2];
226
			switch ($Request->route_path[3]) {
227
				/**
228
				 * Set mapping of named module's databases to indexes of system databases
229
				 */
230
				case 'db':
231
					static::set_module_databases($Request, $module);
232
					break;
233
				/**
234
				 * Set mapping of named module's storages to indexes of system storages
235
				 */
236
				case 'storage':
237
					static::set_module_storages($Request, $module);
238
					break;
239
				default:
240
					throw new ExitException(400);
241
			}
242
		} elseif ($Request->route_path(2) == 'default') {
243
			/**
244
			 * Set current default module
245
			 */
246
			static::set_default_module($Request->data('module'));
247
		} else {
248
			throw new ExitException(400);
249
		}
250
	}
251
	/**
252
	 * @param \cs\Request $Request
253
	 * @param string      $module
254
	 *
255
	 * @throws ExitException
256
	 */
257
	protected static function set_module_databases ($Request, $module) {
258
		$Config          = Config::instance();
259
		$database_config = $Request->data('db');
260
		if (!$database_config || !isset($Config->components['modules'][$module]['db'])) {
261
			throw new ExitException(404);
262
		}
263
		$Config->components['modules'][$module]['db'] = _int($database_config);
264
		static::admin_modules_save();
265
	}
266
	/**
267
	 * @param \cs\Request $Request
268
	 * @param string      $module
269
	 *
270
	 * @throws ExitException
271
	 */
272
	protected static function set_module_storages ($Request, $module) {
273
		$Config         = Config::instance();
274
		$storage_config = $Request->data('storage');
275
		if (!$storage_config || !isset($Config->components['modules'][$module]['storage'])) {
276
			throw new ExitException(404);
277
		}
278
		$Config->components['modules'][$module]['storage'] = _int($storage_config);
279
		static::admin_modules_save();
280
	}
281
	/**
282
	 * @param string $module
283
	 *
284
	 * @throws ExitException
285
	 */
286
	protected static function set_default_module ($module) {
287
		$Config      = Config::instance();
288
		$module_data = $Config->module($module);
289
		if (!$module_data) {
290
			throw new ExitException(404);
291
		}
292
		if (
293
			$module == $Config->core['default_module'] ||
294
			!$module_data->enabled() ||
295
			!file_exists_with_extension(MODULES."/$module/index", ['php', 'html', 'json'])
296
		) {
297
			throw new ExitException(400);
298
		}
299
		if (!Event::instance()->fire('admin/System/components/modules/default', ['name' => $module])) {
300
			throw new ExitException(500);
301
		}
302
		$Config->core['default_module'] = $module;
303
		static::admin_modules_save();
304
	}
305
	/**
306
	 * Enable module
307
	 *
308
	 * Provides next events:
309
	 *  admin/System/components/modules/enable/before
310
	 *  ['name' => module_name]
311
	 *
312
	 *  admin/System/components/modules/enable/after
313
	 *  ['name' => module_name]
314
	 *
315
	 * @param \cs\Request $Request
316
	 *
317
	 * @throws ExitException
318
	 */
319
	static function admin_modules_enable ($Request) {
320
		$Config = Config::instance();
321
		$module = $Request->route_path(2);
322
		if (!$Config->module($module)->disabled()) {
323
			throw new ExitException(400);
324
		}
325
		if (!Event::instance()->fire('admin/System/components/modules/enable/before', ['name' => $module])) {
326
			throw new ExitException(500);
327
		}
328
		$Config->components['modules'][$module]['active'] = Config\Module_Properties::ENABLED;
329
		static::admin_modules_save();
330
		Event::instance()->fire('admin/System/components/modules/enable/after', ['name' => $module]);
331
		static::admin_modules_cleanup();
332
	}
333
	protected static function admin_modules_cleanup () {
334
		clean_pcache();
335
		$Cache = System_cache::instance();
336
		unset(
337
			$Cache->functionality,
338
			$Cache->languages,
339
			$Cache->events_files_paths
340
		);
341
		clean_classes_cache();
342
	}
343
	/**
344
	 * Disable module
345
	 *
346
	 * Provides next events:
347
	 *  admin/System/components/modules/disable/before
348
	 *  ['name' => module_name]
349
	 *
350
	 *  admin/System/components/modules/disable/after
351
	 *  ['name' => module_name]
352
	 *
353
	 * @param \cs\Request $Request
354
	 *
355
	 * @throws ExitException
356
	 */
357
	static function admin_modules_disable ($Request) {
358
		$Config = Config::instance();
359
		$module = $Request->route_path(2);
360
		if (
361
			$module == Config::SYSTEM_MODULE ||
362
			$Config->core['default_module'] == $module ||
363
			!$Config->module($module)->enabled()
364
		) {
365
			throw new ExitException(400);
366
		}
367
		static::admin_modules_disable_internal($Config, $module);
368
	}
369
	/**
370
	 * @param Config $Config
371
	 * @param string $module
372
	 *
373
	 * @throws ExitException
374
	 */
375
	protected static function admin_modules_disable_internal ($Config, $module) {
376
		if (!Event::instance()->fire('admin/System/components/modules/disable/before', ['name' => $module])) {
377
			throw new ExitException(500);
378
		}
379
		$Config->components['modules'][$module]['active'] = Config\Module_Properties::DISABLED;
380
		static::admin_modules_save();
381
		Event::instance()->fire('admin/System/components/modules/disable/after', ['name' => $module]);
382
		static::admin_modules_cleanup();
383
	}
384
	/**
385
	 * Install module
386
	 *
387
	 * Provides next events:
388
	 *  admin/System/components/modules/install/before
389
	 *  ['name' => module_name]
390
	 *
391
	 *  admin/System/components/modules/install/after
392
	 *  ['name' => module_name]
393
	 *
394
	 * @param \cs\Request $Request
395
	 *
396
	 * @throws ExitException
397
	 */
398
	static function admin_modules_install ($Request) {
399
		$Config = Config::instance();
400
		$module = $Request->route_path(2);
401
		if (!$Config->module($module)->uninstalled()) {
402
			throw new ExitException(400);
403
		}
404
		if (!Event::instance()->fire('admin/System/components/modules/install/before', ['name' => $module])) {
405
			throw new ExitException(500);
406
		}
407
		$module_data     = &$Config->components['modules'][$module];
408
		$database_config = $Request->data('db');
409
		if ($database_config) {
410
			$module_data['db'] = _int($database_config);
411
			Packages_manipulation::execute_sql_from_directory(MODULES."/$module/meta/install_db", $module_data['db']);
412
		}
413
		$storage_config = $Request->data('storage');
414
		if ($storage_config) {
415
			$module_data['storage'] = _int($storage_config);
416
		}
417
		$module_data['active'] = Config\Module_Properties::DISABLED;
418
		static::admin_modules_save();
419
		Event::instance()->fire('admin/System/components/modules/install/after', ['name' => $module]);
420
		static::admin_modules_cleanup();
421
	}
422
	/**
423
	 * Uninstall module
424
	 *
425
	 * Provides next events:
426
	 *  admin/System/components/modules/uninstall/before
427
	 *  ['name' => module_name]
428
	 *
429
	 *  admin/System/components/modules/uninstall/after
430
	 *  ['name' => module_name]
431
	 *
432
	 * @param \cs\Request $Request
433
	 *
434
	 * @throws ExitException
435
	 */
436
	static function admin_modules_uninstall ($Request) {
437
		$Config  = Config::instance();
438
		$module  = $Request->route_path(2);
439
		$modules = &$Config->components['modules'];
440
		/**
441
		 * Do not allow to uninstall enabled module, it should be explicitly disabled first
442
		 */
443
		if (!$Config->module($module)->disabled()) {
444
			throw new ExitException(400);
445
		}
446
		if (!Event::instance()->fire('admin/System/components/modules/uninstall/before', ['name' => $module])) {
447
			throw new ExitException(500);
448
		}
449
		$module_data = &$modules[$module];
450
		if (isset($module_data['db'])) {
451
			Packages_manipulation::execute_sql_from_directory(MODULES."/$module/meta/uninstall_db", $module_data['db']);
452
		}
453
		static::delete_permissions_for_module($module);
454
		$modules[$module] = ['active' => Config\Module_Properties::UNINSTALLED];
455
		static::admin_modules_save();
456
		Event::instance()->fire('admin/System/components/modules/uninstall/after', ['name' => $module]);
457
		static::admin_modules_cleanup();
458
	}
459
	/**
460
	 * @param string $module
461
	 */
462
	protected static function delete_permissions_for_module ($module) {
463
		$Permission      = Permission::instance();
464
		$permissions_ids = array_merge(
465
			$Permission->get(null, $module),
466
			$Permission->get(null, "admin/$module"),
467
			$Permission->get(null, "api/$module")
468
		);
469
		if (!empty($permissions_ids)) {
470
			$Permission->del(
471
				array_column($permissions_ids, 'id')
472
			);
473
		}
474
	}
475
	/**
476
	 * Extract uploaded module
477
	 *
478
	 * @throws ExitException
479
	 */
480
	static function admin_modules_extract () {
481
		$Config       = Config::instance();
482
		$L            = Language::prefix('system_admin_modules_');
483
		$tmp_location = TEMP.'/System/admin/'.Session::instance()->get_id().'.phar';
484
		$tmp_dir      = "phar://$tmp_location";
485
		if (
486
			!file_exists($tmp_location) ||
487
			!file_exists("$tmp_dir/meta.json")
488
		) {
489
			throw new ExitException(400);
490
		}
491
		$new_meta = file_get_json("$tmp_dir/meta.json");
492
		if ($new_meta['category'] !== 'modules') {
493
			throw new ExitException($L->this_is_not_module_installer_file, 400);
494
		}
495
		if (!Packages_manipulation::install_extract(MODULES."/$new_meta[package]", $tmp_location)) {
496
			throw new ExitException($L->module_files_unpacking_error, 500);
497
		}
498
		$Config->components['modules'][$new_meta['package']] = ['active' => Config\Module_Properties::UNINSTALLED];
499
		ksort($Config->components['modules'], SORT_STRING | SORT_FLAG_CASE);
500
		static::admin_modules_save();
501
		static::admin_modules_cleanup();
502
	}
503
	/**
504
	 * Update module (or system if module name is System)
505
	 *
506
	 * @param \cs\Request $Request
507
	 *
508
	 * @throws ExitException
509
	 */
510
	static function admin_modules_update ($Request) {
511
		$module = $Request->route_path(2);
512
		if (!Config::instance()->module($module)) {
513
			throw new ExitException(404);
514
		}
515
		$tmp_location = TEMP.'/System/admin/'.Session::instance()->get_id().'.phar';
516
		$tmp_dir      = "phar://$tmp_location";
517
		$module_dir   = MODULES."/$module";
518
		if (
519
			!file_exists($tmp_location) ||
520
			!file_exists("$module_dir/meta.json") ||
521
			!file_exists("$tmp_dir/meta.json")
522
		) {
523
			throw new ExitException(400);
524
		}
525
		$existing_meta = file_get_json("$module_dir/meta.json");
526
		$new_meta      = file_get_json("$tmp_dir/meta.json");
527
		if ($module == Config::SYSTEM_MODULE) {
528
			static::update_system($module, $existing_meta, $new_meta, $tmp_location);
529
		} else {
530
			static::update_module($module, $existing_meta, $new_meta, $tmp_location, $Request);
531
		}
532
		static::admin_modules_cleanup();
533
	}
534
	/**
535
	 * Provides next events:
536
	 *  admin/System/components/modules/update/before
537
	 *  ['name' => module_name]
538
	 *
539
	 *  admin/System/components/modules/update/after
540
	 *  ['name' => module_name]
541
	 *
542
	 * @param string      $module
543
	 * @param array       $existing_meta
544
	 * @param array       $new_meta
545
	 * @param string      $tmp_location
546
	 * @param \cs\Request $Request
547
	 *
548
	 * @throws ExitException
549
	 */
550
	protected static function update_module ($module, $existing_meta, $new_meta, $tmp_location, $Request) {
551
		$Config     = Config::instance();
552
		$L          = Language::prefix('system_admin_modules_');
553
		$module_dir = MODULES."/$module";
554
		$enabled    = $Config->module($module)->enabled();
555
		// If module is currently enabled - disable it temporary
556
		if ($enabled) {
557
			static::admin_modules_disable_internal($Config, $module);
558
		}
559
		if (!static::is_same_module($new_meta, $module)) {
560
			throw new ExitException($L->this_is_not_module_installer_file, 400);
561
		}
562
		if (!Event::instance()->fire('admin/System/components/modules/update/before', ['name' => $module])) {
563
			throw new ExitException(500);
564
		}
565
		if (!is_writable($module_dir)) {
566
			throw new ExitException($L->cant_unpack_module_no_write_permissions, 500);
567
		}
568
		if (!Packages_manipulation::update_extract($module_dir, $tmp_location)) {
569
			throw new ExitException($L->module_files_unpacking_error, 500);
570
		}
571
		$module_data = $Config->components['modules'][$module];
572
		// Run PHP update scripts and SQL queries if any
573
		Packages_manipulation::update_php_sql($module_dir, $existing_meta['version'], isset($module_data['db']) ? $module_data['db'] : null);
574
		Event::instance()->fire('admin/System/components/modules/update/after', ['name' => $module]);
575
		// If module was enabled before update - enable it back
576
		if ($enabled) {
577
			static::admin_modules_enable($Request);
578
		}
579
	}
580
	/**
581
	 * Provides next events:
582
	 *  admin/System/components/modules/update_system/before
583
	 *
584
	 *  admin/System/components/modules/update_system/after
585
	 *
586
	 * @param string $module
587
	 * @param array  $existing_meta
588
	 * @param array  $new_meta
589
	 * @param string $tmp_location
590
	 *
591
	 * @throws ExitException
592
	 */
593
	protected static function update_system ($module, $existing_meta, $new_meta, $tmp_location) {
594
		$Config     = Config::instance();
595
		$L          = Language::prefix('system_admin_modules_');
596
		$module_dir = MODULES."/$module";
597
		/**
598
		 * Temporary close site
599
		 */
600
		$site_mode = $Config->core['site_mode'];
601
		if ($site_mode) {
602
			$Config->core['site_mode'] = 0;
603
			static::admin_modules_save();
604
		}
605
		if (!static::is_same_module($new_meta, Config::SYSTEM_MODULE)) {
606
			throw new ExitException($L->this_is_not_system_installer_file, 400);
607
		}
608
		if (!Event::instance()->fire('admin/System/components/modules/update_system/before')) {
609
			throw new ExitException(500);
610
		}
611
		if (!Packages_manipulation::update_extract(DIR, $tmp_location, DIR.'/core', $module_dir)) {
612
			throw new ExitException($L->system_files_unpacking_error, 500);
613
		}
614
		$module_data = $Config->components['modules'][$module];
615
		// Run PHP update scripts and SQL queries if any
616
		Packages_manipulation::update_php_sql($module_dir, $existing_meta['version'], isset($module_data['db']) ? $module_data['db'] : null);
617
		Event::instance()->fire('admin/System/components/modules/update_system/after');
618
		/**
619
		 * Restore previous site mode
620
		 */
621
		if ($site_mode) {
622
			$Config->core['site_mode'] = 1;
623
			static::admin_modules_save();
624
		}
625
		static::admin_modules_cleanup();
626
	}
627
	/**
628
	 * Delete module completely
629
	 *
630
	 * @param \cs\Request $Request
631
	 *
632
	 * @throws ExitException
633
	 */
634
	static function admin_modules_delete ($Request) {
635
		$Config = Config::instance();
636
		$module = $Request->route_path(2);
637
		if (!$Config->module($module)->uninstalled()) {
638
			throw new ExitException(400);
639
		}
640
		if (!rmdir_recursive(MODULES."/$module")) {
641
			throw new ExitException(500);
642
		}
643
		unset($Config->components['modules'][$module]);
644
		static::admin_modules_save();
645
	}
646
	/**
647
	 * Update information about present modules
648
	 *
649
	 * @throws ExitException
650
	 */
651
	static function admin_modules_update_list () {
652
		$Config = Config::instance();
653
		/**
654
		 * List of currently presented modules in file system
655
		 */
656
		$modules_in_fs = get_files_list(MODULES, false, 'd');
657
		$modules_list  = array_fill_keys(
658
			$modules_in_fs,
659
			[
660
				'active'  => -1,
661
				'db'      => [],
662
				'storage' => []
663
			]
664
		);
665
		/**
666
		 * Already known modules
667
		 */
668
		$modules       = &$Config->components['modules'];
669
		$known_modules = array_keys($modules);
670
		if ($modules_in_fs != $known_modules) {
671
			/**
672
			 * Delete permissions of modules that are mot present anymore
673
			 */
674
			foreach ($known_modules as $module) {
675
				if (!isset($modules_list[$module])) {
676
					static::delete_permissions_for_module($module);
677
				}
678
			}
679
			unset($module);
680
		}
681
		unset($modules_in_fs, $known_modules);
682
		$modules = array_merge($modules_list, array_intersect_key($modules, $modules_list));
683
		ksort($modules, SORT_STRING | SORT_FLAG_CASE);
684
		static::admin_modules_save();
685
		static::admin_modules_cleanup();
686
	}
687
	/**
688
	 * Save changes
689
	 *
690
	 * @throws ExitException
691
	 */
692
	protected static function admin_modules_save () {
693
		if (!Config::instance()->save()) {
694
			throw new ExitException(500);
695
		}
696
	}
697
}
698