Completed
Push — master ( 839a1f...d41ceb )
by Nazar
04:26
created

modules::admin_modules_install()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 26
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 2
Metric Value
c 4
b 0
f 2
dl 0
loc 26
rs 8.439
cc 6
eloc 20
nc 7
nop 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, 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\Page,
18
	cs\Permission,
19
	cs\Session,
20
	cs\modules\System\Packages_manipulation;
21
trait modules {
22
	/**
23
	 * @param int[]    $route_ids
24
	 * @param string[] $route_path
25
	 *
26
	 * @throws ExitException
27
	 */
28
	static function admin_modules_get ($route_ids, $route_path) {
29
		if (isset($route_path[3])) {
30
			switch ($route_path[3]) {
31
				/**
32
				 * Get dependent packages for module
33
				 */
34
				case 'dependent_packages':
35
					static::get_dependent_packages_for_module($route_path[2]);
36
					break;
37
				/**
38
				 * Get dependencies for module (packages, databases, storages)
39
				 */
40
				case 'dependencies':
41
					static::get_dependencies_for_module($route_path[2]);
42
					break;
43
				/**
44
				 * Get dependencies for module during update
45
				 */
46
				case 'update_dependencies':
47
					static::get_update_dependencies_for_module($route_path[2]);
48
					break;
49
				/**
50
				 * Get mapping of named module's databases to indexes of system databases
51
				 */
52
				case 'db':
53
					static::get_module_databases($route_path[2]);
54
					break;
55
				/**
56
				 * Get mapping of named module's storages to indexes of system storages
57
				 */
58
				case 'storage':
59
					static::get_module_storages($route_path[2]);
60
					break;
61
				default:
62
					throw new ExitException(400);
63
			}
64
		} elseif (isset($route_path[2]) && $route_path[2] == 'default') {
65
			/**
66
			 * Get current default module
67
			 */
68
			static::get_default_module();
69
		} else {
70
			/**
71
			 * Get array of modules in extended form
72
			 */
73
			static::get_modules_list();
74
		}
75
	}
76
	/**
77
	 * @param string $module
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
		Page::instance()->json(
87
			file_exists($meta_file) ? Packages_manipulation::get_dependent_packages(file_get_json($meta_file)) : []
88
		);
89
	}
90
	/**
91
	 * @param string $module
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
		Page::instance()->json(
101
			file_exists($meta_file) ? Packages_manipulation::get_dependencies(file_get_json($meta_file)) : []
102
		);
103
	}
104
	/**
105
	 * @param string $module
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::instance()->this_is_not_module_installer_file, 400);
124
		}
125
		Page::instance()->json(
126
			Packages_manipulation::get_dependencies($new_meta)
127
		);
128
	}
129
	/**
130
	 * @param array  $meta
131
	 * @param string $module
132
	 *
133
	 * @return bool
134
	 */
135
	protected static function is_same_module ($meta, $module) {
136
		return $meta['category'] == 'modules' && $meta['package'] == $module;
137
	}
138
	/**
139
	 * @param string $module
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
		Page::instance()->json(
149
			$Config->components['modules'][$module]['db']
150
		);
151
	}
152
	/**
153
	 * @param string $module
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
		Page::instance()->json(
163
			$Config->components['modules'][$module]['storage']
164
		);
165
	}
166
	protected static function get_modules_list () {
167
		$Config       = Config::instance();
168
		$modules_list = [];
169
		foreach ($Config->components['modules'] as $module_name => &$module_data) {
170
			$module = [
171
				'active'            => $module_data['active'],
172
				'name'              => $module_name,
173
				'has_user_section'  => file_exists_with_extension(MODULES."/$module_name/index", ['php', 'html', 'json']),
174
				'has_admin_section' => file_exists_with_extension(MODULES."/$module_name/admin/index", ['php', 'json'])
175
			];
176
			/**
177
			 * Check if API available
178
			 */
179
			static::check_module_feature_availability($module, 'readme', 'api');
180
			/**
181
			 * Check if readme available
182
			 */
183
			static::check_module_feature_availability($module, 'readme');
184
			/**
185
			 * Check if license available
186
			 */
187
			static::check_module_feature_availability($module, 'license');
188
			if (file_exists(MODULES."/$module_name/meta.json")) {
189
				$module['meta'] = file_get_json(MODULES."/$module_name/meta.json");
190
			}
191
			$modules_list[] = $module;
192
		}
193
		unset($module_name, $module_data, $module);
194
		Page::instance()->json($modules_list);
195
	}
196
	/**
197
	 * @param array  $module
198
	 * @param string $feature
199
	 * @param string $dir
200
	 */
201
	protected static function check_module_feature_availability (&$module, $feature, $dir = '') {
202
		/**
203
		 * Check if feature available
204
		 */
205
		$file = file_exists_with_extension(MODULES."/$module[name]/$dir/$feature", ['txt', 'html']);
206
		if ($file) {
207
			$module[$dir ?: $feature] = [
208
				'type'    => substr($file, -3) == 'txt' ? 'txt' : 'html',
209
				'content' => file_get_contents($file)
210
			];
211
		} elseif ($dir && is_dir(MODULES."/$module[name]/$dir")) {
212
			$module[$dir ?: $feature] = [];
213
		}
214
	}
215
	protected static function get_default_module () {
216
		Page::instance()->json(
217
			Config::instance()->core['default_module']
218
		);
219
	}
220
	/**
221
	 * @param int[]    $route_ids
222
	 * @param string[] $route_path
223
	 *
224
	 * @throws ExitException
225
	 */
226
	static function admin_modules_put ($route_ids, $route_path) {
227
		if (isset($route_path[3])) {
228
			switch ($route_path[3]) {
229
				/**
230
				 * Set mapping of named module's databases to indexes of system databases
231
				 */
232
				case 'db':
233
					static::set_module_databases($route_path[2]);
234
					break;
235
				/**
236
				 * Set mapping of named module's storages to indexes of system storages
237
				 */
238
				case 'storage':
239
					static::set_module_storages($route_path[2]);
240
					break;
241
				default:
242
					throw new ExitException(400);
243
			}
244
		} elseif (isset($route_path[2]) && $route_path[2] == 'default') {
245
			if (!isset($_POST['module'])) {
246
				throw new ExitException(400);
247
			}
248
			/**
249
			 * Set current default module
250
			 */
251
			static::set_default_module($_POST['module']);
252
		} else {
253
			throw new ExitException(400);
254
		}
255
	}
256
	/**
257
	 * @param string $module
258
	 *
259
	 * @throws ExitException
260
	 */
261
	protected static function set_module_databases ($module) {
262
		$Config = Config::instance();
263
		if (!isset($Config->components['modules'][$module]['db'], $_POST['db'])) {
264
			throw new ExitException(404);
265
		}
266
		$Config->components['modules'][$module]['db'] = _int($_POST['db']);
267
		static::admin_modules_save();
268
	}
269
	/**
270
	 * @param string $module
271
	 *
272
	 * @throws ExitException
273
	 */
274
	protected static function set_module_storages ($module) {
275
		$Config = Config::instance();
276
		if (!isset($Config->components['modules'][$module]['storage'], $_POST['storage'])) {
277
			throw new ExitException(404);
278
		}
279
		$Config->components['modules'][$module]['storage'] = _int($_POST['storage']);
280
		static::admin_modules_save();
281
	}
282
	/**
283
	 * @param string $module
284
	 *
285
	 * @throws ExitException
286
	 */
287
	protected static function set_default_module ($module) {
288
		$Config      = Config::instance();
289
		$module_data = $Config->module($module);
290
		if (!$module_data) {
291
			throw new ExitException(404);
292
		}
293
		if (
294
			$module == $Config->core['default_module'] ||
295
			!$module_data->enabled() ||
296
			!file_exists_with_extension(MODULES."/$module/index", ['php', 'html', 'json'])
297
		) {
298
			throw new ExitException(400);
299
		}
300
		if (!Event::instance()->fire('admin/System/components/modules/default', ['name' => $module])) {
301
			throw new ExitException(500);
302
		}
303
		$Config->core['default_module'] = $module;
304
		static::admin_modules_save();
305
	}
306
	/**
307
	 * Enable module
308
	 *
309
	 * Provides next events:
310
	 *  admin/System/components/modules/enable/before
311
	 *  ['name' => module_name]
312
	 *
313
	 *  admin/System/components/modules/enable/after
314
	 *  ['name' => module_name]
315
	 *
316
	 * @param int[]    $route_ids
317
	 * @param string[] $route_path
318
	 *
319
	 * @throws ExitException
320
	 */
321
	static function admin_modules_enable ($route_ids, $route_path) {
322
		if (!isset($route_path[2])) {
323
			throw new ExitException(400);
324
		}
325
		$module = $route_path[2];
326
		$Config = Config::instance();
327
		if (!$Config->module($module)->disabled()) {
328
			throw new ExitException(400);
329
		}
330
		if (!Event::instance()->fire('admin/System/components/modules/enable/before', ['name' => $module])) {
331
			throw new ExitException(500);
332
		}
333
		$Config->components['modules'][$module]['active'] = Config\Module_Properties::ENABLED;
334
		static::admin_modules_save();
335
		Event::instance()->fire('admin/System/components/modules/enable/after', ['name' => $module]);
336
		static::admin_modules_cleanup();
337
	}
338
	protected static function admin_modules_cleanup () {
339
		clean_pcache();
340
		$Cache = System_cache::instance();
341
		unset(
342
			$Cache->functionality,
343
			$Cache->languages
344
		);
345
		clean_classes_cache();
346
	}
347
	/**
348
	 * Disable module
349
	 *
350
	 * Provides next events:
351
	 *  admin/System/components/modules/disable/before
352
	 *  ['name' => module_name]
353
	 *
354
	 *  admin/System/components/modules/disable/after
355
	 *  ['name' => module_name]
356
	 *
357
	 * @param int[]    $route_ids
358
	 * @param string[] $route_path
359
	 *
360
	 * @throws ExitException
361
	 */
362
	static function admin_modules_disable ($route_ids, $route_path) {
363
		if (!isset($route_path[2])) {
364
			throw new ExitException(400);
365
		}
366
		$module = $route_path[2];
367
		$Config = Config::instance();
368
		if (
369
			$module == Config::SYSTEM_MODULE ||
370
			$Config->core['default_module'] == $module ||
371
			!$Config->module($module)->enabled()
372
		) {
373
			throw new ExitException(400);
374
		}
375
		if (!Event::instance()->fire('admin/System/components/modules/disable/before', ['name' => $module])) {
376
			throw new ExitException(500);
377
		}
378
		$Config->components['modules'][$module]['active'] = Config\Module_Properties::DISABLED;
379
		static::admin_modules_save();
380
		Event::instance()->fire('admin/System/components/modules/disable/after', ['name' => $module]);
381
		static::admin_modules_cleanup();
382
	}
383
	/**
384
	 * Install module
385
	 *
386
	 * Provides next events:
387
	 *  admin/System/components/modules/install/before
388
	 *  ['name' => module_name]
389
	 *
390
	 *  admin/System/components/modules/install/after
391
	 *  ['name' => module_name]
392
	 *
393
	 * @param int[]    $route_ids
394
	 * @param string[] $route_path
395
	 *
396
	 * @throws ExitException
397
	 */
398
	static function admin_modules_install ($route_ids, $route_path) {
399
		if (!isset($route_path[2])) {
400
			throw new ExitException(400);
401
		}
402
		$module  = $route_path[2];
403
		$Config  = Config::instance();
404
		$modules = &$Config->components['modules'];
405
		if (!$Config->module($module)->uninstalled()) {
406
			throw new ExitException(400);
407
		}
408
		if (!Event::instance()->fire('admin/System/components/modules/install/before', ['name' => $module])) {
409
			throw new ExitException(500);
410
		}
411
		$module_data = &$modules[$module];
412
		if (isset($_POST['db'])) {
413
			$module_data['db'] = $_POST['db'];
414
			Packages_manipulation::execute_sql_from_directory(MODULES."/$module/meta/install_db", $module_data['db']);
415
		}
416
		if (isset($_POST['storage'])) {
417
			$module_data['storage'] = $_POST['storage'];
418
		}
419
		$module_data['active'] = Config\Module_Properties::DISABLED;
420
		static::admin_modules_save();
421
		Event::instance()->fire('admin/System/components/modules/install/after', ['name' => $module]);
422
		static::admin_modules_cleanup();
423
	}
424
	/**
425
	 * Uninstall module
426
	 *
427
	 * Provides next events:
428
	 *  admin/System/components/modules/uninstall/before
429
	 *  ['name' => module_name]
430
	 *
431
	 *  admin/System/components/modules/uninstall/after
432
	 *  ['name' => module_name]
433
	 *
434
	 * @param int[]    $route_ids
435
	 * @param string[] $route_path
436
	 *
437
	 * @throws ExitException
438
	 */
439
	static function admin_modules_uninstall ($route_ids, $route_path) {
440
		if (!isset($route_path[2])) {
441
			throw new ExitException(400);
442
		}
443
		$module  = $route_path[2];
444
		$Config  = Config::instance();
445
		$modules = &$Config->components['modules'];
446
		/**
447
		 * Do not allow to uninstall enabled module, it should be explicitly disabled first
448
		 */
449
		if (!$Config->module($module)->disabled()) {
450
			throw new ExitException(400);
451
		}
452
		if (!Event::instance()->fire('admin/System/components/modules/uninstall/before', ['name' => $module])) {
453
			throw new ExitException(500);
454
		}
455
		$module_data = &$modules[$module];
456
		if (isset($module_data['db'])) {
457
			Packages_manipulation::execute_sql_from_directory(MODULES."/$module/meta/uninstall_db", $module_data['db']);
458
		}
459
		static::delete_permissions_for_module($module);
460
		$modules[$module] = ['active' => Config\Module_Properties::UNINSTALLED];
461
		static::admin_modules_save();
462
		Event::instance()->fire('admin/System/components/modules/uninstall/after', ['name' => $module]);
463
		static::admin_modules_cleanup();
464
	}
465
	/**
466
	 * @param string $module
467
	 */
468
	protected static function delete_permissions_for_module ($module) {
469
		$Permission      = Permission::instance();
470
		$permissions_ids = array_merge(
471
			$Permission->get(null, $module),
472
			$Permission->get(null, "admin/$module"),
473
			$Permission->get(null, "api/$module")
474
		);
475
		if (!empty($permissions_ids)) {
476
			$Permission->del(
477
				array_column($permissions_ids, 'id')
478
			);
479
		}
480
	}
481
	/**
482
	 * Extract uploaded module
483
	 *
484
	 * @throws ExitException
485
	 */
486
	static function admin_modules_extract () {
487
		$Config       = Config::instance();
488
		$L            = Language::instance();
489
		$tmp_location = TEMP.'/System/admin/'.Session::instance()->get_id().'.phar';
490
		$tmp_dir      = "phar://$tmp_location";
491
		if (
492
			!file_exists($tmp_location) ||
493
			!file_exists("$tmp_dir/meta.json")
494
		) {
495
			throw new ExitException(400);
496
		}
497
		$new_meta = file_get_json("$tmp_dir/meta.json");
498
		if ($new_meta['category'] !== 'modules') {
499
			throw new ExitException($L->this_is_not_module_installer_file, 400);
500
		}
501
		if (!Packages_manipulation::install_extract(MODULES."/$new_meta[package]", $tmp_location)) {
502
			throw new ExitException($L->module_files_unpacking_error, 500);
503
		}
504
		$Config->components['modules'][$new_meta['package']] = ['active' => Config\Module_Properties::UNINSTALLED];
505
		ksort($Config->components['modules'], SORT_STRING | SORT_FLAG_CASE);
506
		static::admin_modules_save();
507
	}
508
	/**
509
	 * Update module (or system if module name is System)
510
	 *
511
	 * @param int[]    $route_ids
512
	 * @param string[] $route_path
513
	 *
514
	 * @throws ExitException
515
	 */
516
	static function admin_modules_update ($route_ids, $route_path) {
517
		if (!isset($route_path[2])) {
518
			throw new ExitException(400);
519
		}
520
		$module = $route_path[2];
521
		if (!Config::instance()->module($module)) {
522
			throw new ExitException(404);
523
		}
524
		$tmp_location = TEMP.'/System/admin/'.Session::instance()->get_id().'.phar';
525
		$tmp_dir      = "phar://$tmp_location";
526
		$module_dir   = MODULES."/$module";
527
		if (
528
			!file_exists($tmp_location) ||
529
			!file_exists("$module_dir/meta.json") ||
530
			!file_exists("$tmp_dir/meta.json")
531
		) {
532
			throw new ExitException(400);
533
		}
534
		$existing_meta = file_get_json("$module_dir/meta.json");
535
		$new_meta      = file_get_json("$tmp_dir/meta.json");
536
		if ($module == Config::SYSTEM_MODULE) {
537
			static::update_system($module, $existing_meta, $new_meta, $tmp_location, $tmp_dir);
538
		} else {
539
			static::update_module($module, $existing_meta, $new_meta, $tmp_location, $route_ids, $route_path);
540
		}
541
		static::admin_modules_cleanup();
542
	}
543
	/**
544
	 * Provides next events:
545
	 *  admin/System/components/modules/update/before
546
	 *  ['name' => module_name]
547
	 *
548
	 *  admin/System/components/modules/update/after
549
	 *  ['name' => module_name]
550
	 *
551
	 * @param string   $module
552
	 * @param array    $existing_meta
553
	 * @param array    $new_meta
554
	 * @param string   $tmp_location
555
	 * @param int[]    $route_ids
556
	 * @param string[] $route_path
557
	 *
558
	 * @throws ExitException
559
	 */
560
	protected static function update_module ($module, $existing_meta, $new_meta, $tmp_location, $route_ids, $route_path) {
561
		$Config     = Config::instance();
562
		$L          = Language::instance();
563
		$module_dir = MODULES."/$module";
564
		$enabled    = $Config->module($module)->enabled();
565
		// If module is currently enabled - disable it temporary
566
		if ($enabled) {
567
			static::admin_modules_disable($route_ids, $route_path);
568
		}
569
		if (!static::is_same_module($new_meta, $module)) {
570
			throw new ExitException($L->this_is_not_module_installer_file, 400);
571
		}
572
		if (!Event::instance()->fire('admin/System/components/modules/update/before', ['name' => $module])) {
573
			throw new ExitException(500);
574
		}
575
		if (!is_writable($module_dir)) {
576
			throw new ExitException($L->cant_unpack_module_no_write_permissions, 500);
577
		}
578
		if (!Packages_manipulation::update_extract($module_dir, $tmp_location)) {
579
			throw new ExitException($L->module_files_unpacking_error, 500);
580
		}
581
		$module_data = $Config->components['modules'][$module];
582
		// Run PHP update scripts and SQL queries if any
583
		Packages_manipulation::update_php_sql($module_dir, $existing_meta['version'], isset($module_data['db']) ? $module_data['db'] : null);
584
		Event::instance()->fire('admin/System/components/modules/update/after', ['name' => $module]);
585
		// If module was enabled before update - enable it back
586
		if ($enabled) {
587
			static::admin_modules_enable($route_ids, $route_path);
588
		}
589
	}
590
	/**
591
	 * Provides next events:
592
	 *  admin/System/components/modules/update_system/before
593
	 *
594
	 *  admin/System/components/modules/update_system/after
595
	 *
596
	 * @param string $module
597
	 * @param array  $existing_meta
598
	 * @param array  $new_meta
599
	 * @param string $tmp_location
600
	 * @param string $tmp_dir
601
	 *
602
	 * @throws ExitException
603
	 */
604
	protected static function update_system ($module, $existing_meta, $new_meta, $tmp_location, $tmp_dir) {
0 ignored issues
show
Unused Code introduced by
The parameter $tmp_dir is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
605
		$Config     = Config::instance();
606
		$L          = Language::instance();
607
		$module_dir = MODULES."/$module";
608
		/**
609
		 * Temporary close site
610
		 */
611
		$site_mode = $Config->core['site_mode'];
612
		if ($site_mode) {
613
			$Config->core['site_mode'] = 0;
614
			static::admin_modules_save();
615
		}
616
		if (!static::is_same_module($new_meta, Config::SYSTEM_MODULE)) {
617
			throw new ExitException($L->this_is_not_system_installer_file, 400);
618
		}
619
		if (!Event::instance()->fire('admin/System/components/modules/update_system/before')) {
620
			throw new ExitException(500);
621
		}
622
		if (!Packages_manipulation::update_extract(DIR, $tmp_location, DIR.'/core', $module_dir)) {
623
			throw new ExitException($L->system_files_unpacking_error, 500);
624
		}
625
		$module_data = $Config->components['modules'][$module];
626
		// Run PHP update scripts and SQL queries if any
627
		Packages_manipulation::update_php_sql($module_dir, $existing_meta['version'], isset($module_data['db']) ? $module_data['db'] : null);
628
		Event::instance()->fire('admin/System/components/modules/update_system/after');
629
		/**
630
		 * Restore previous site mode
631
		 */
632
		if ($site_mode) {
633
			$Config->core['site_mode'] = 1;
634
			static::admin_modules_save();
635
		}
636
	}
637
	/**
638
	 * Delete module completely
639
	 *
640
	 * @param int[]    $route_ids
641
	 * @param string[] $route_path
642
	 *
643
	 * @throws ExitException
644
	 */
645
	static function admin_modules_delete ($route_ids, $route_path) {
646
		if (!isset($route_path[2])) {
647
			throw new ExitException(400);
648
		}
649
		$module = $route_path[2];
650
		$Config = Config::instance();
651
		if (
652
			$module == Config::SYSTEM_MODULE ||
653
			!$Config->module($module)->uninstalled()
654
		) {
655
			throw new ExitException(400);
656
		}
657
		if (!rmdir_recursive(MODULES."/$module")) {
658
			throw new ExitException(500);
659
		}
660
		unset($Config->components['modules'][$module]);
661
		static::admin_modules_save();
662
	}
663
	/**
664
	 * Update information about present modules
665
	 *
666
	 * @throws ExitException
667
	 */
668
	static function admin_modules_update_list () {
669
		$Config = Config::instance();
670
		/**
671
		 * List of currently presented modules in file system
672
		 */
673
		$modules_in_fs = get_files_list(MODULES, false, 'd');
674
		$modules_list  = array_fill_keys(
675
			$modules_in_fs,
676
			[
677
				'active'  => -1,
678
				'db'      => [],
679
				'storage' => []
680
			]
681
		);
682
		/**
683
		 * Already known modules
684
		 */
685
		$modules       = &$Config->components['modules'];
686
		$known_modules = array_keys($modules);
687
		if ($modules_in_fs != $known_modules) {
688
			/**
689
			 * Delete permissions of modules that are mot present anymore
690
			 */
691
			foreach ($known_modules as $module) {
692
				if (!isset($modules_list[$module])) {
693
					static::delete_permissions_for_module($module);
694
				}
695
			}
696
			unset($module);
697
		}
698
		unset($modules_in_fs, $known_modules);
699
		$modules = array_merge($modules_list, array_intersect_key($modules, $modules_list));
700
		ksort($modules, SORT_STRING | SORT_FLAG_CASE);
701
		static::admin_modules_save();
702
	}
703
	/**
704
	 * Save changes
705
	 *
706
	 * @throws ExitException
707
	 */
708
	protected static function admin_modules_save () {
709
		if (!Config::instance()->save()) {
710
			throw new ExitException(500);
711
		}
712
	}
713
}
714