summaryrefslogtreecommitdiffstats
path: root/modules-available/baseconfig/inc/baseconfig.inc.php
blob: 36622dcee167834902ccdd9a1590018d812f5934 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
<?php

class BaseConfig
{

	private static $modulesDone = [];

	/**
	 * @var array key-value-pairs of override vars, can be accessed by hooks
	 */
	private static $overrides = [];

	/**
	 * Fill the ConfigHolder with values from various hooks, while taking
	 * into account UUID and IP-address of the client making the current
	 * HTTP request.
	 */
	public static function prepareFromRequest()
	{
		$ip = $_SERVER['REMOTE_ADDR'] ?? null;
		if ($ip === null)
			ErrorHandler::traceError('No REMOTE_ADDR given in $_SERVER');
		if (substr($ip, 0, 7) === '::ffff:') {
			$ip = substr($ip, 7);
		}
		$uuid = Request::any('uuid', null, 'string');
		if ($uuid !== null && strlen($uuid) !== 36) {
			$uuid = null;
		}
		// Handle any hooks by other modules first
		// other modules should generally only populate $configVars
		foreach (glob('modules/*/baseconfig/getconfig.inc.php') as $file) {
			preg_match('#^modules/([^/]+)/#', $file, $out);
			ConfigHolder::setContext($out[1]);
			self::handleModule($out[1], $ip, $uuid, false);
		}
		self::commonBase();
	}

	/**
	 * Fill the ConfigHolder with data from various hooks that supply
	 * static overrides for config variables. The overrides can be used
	 * to make the hooks behave in certain ways, by setting specific values like
	 * 'locationid'
	 * @param array $overrides key value pairs of overrides
	 */
	public static function prepareWithOverrides(array $overrides): void
	{
		self::$overrides = $overrides;
		$ip = $uuid = null;
		if (self::hasOverride('ip')) {
			$ip = self::getOverride('ip');
		}
		if (self::hasOverride('uuid')) {
			$uuid = self::getOverride('uuid');
		}
		// Handle only static hooks that don't dynamically generate output
		foreach (glob('modules/*/baseconfig/hook.json') as $file) {
			preg_match('#^modules/([^/]+)/#', $file, $out);
			ConfigHolder::setContext($out[1]);
			self::handleModule($out[1], $ip, $uuid, true);
		}
		self::commonBase();
	}

	/**
	 * Just fill the ConfigHolder with the defaults from all the json files
	 * that define config variables.
	 */
	public static function prepareDefaults()
	{
		$defaults = BaseConfigUtil::getVariables();
		self::addDefaults($defaults);
	}

	private static function commonBase()
	{
		$defaults = BaseConfigUtil::getVariables();

		// Dump global config from DB
		ConfigHolder::setContext('<global>', function($id) {
			return [
				'name' => Dictionary::translate('source-global'),
				'locationid' => 0,
			];
		});
		$res = Database::simpleQuery('SELECT setting, value, displayvalue FROM setting_global');
		foreach ($res as $row) {
			if (!isset($defaults[$row['setting']]))
				continue; // Setting is not defined in any <module>/baseconfig/settings.json
			ConfigHolder::add($row['setting'], $row, -1);
		}

		// Fallback to default values from json files
		self::addDefaults($defaults);

	}

	private static function addDefaults($defaults)
	{
		ConfigHolder::setContext('<default>', function($id) {
			return [
				'name' => Dictionary::translate('source-default'),
				'locationid' => 0,
			];
		});
		foreach ($defaults as $setting => $value) {
			ConfigHolder::add($setting, $value['defaultvalue'], -1000);
		}
	}

	private static function handleModule(string $name, ?string $ip, ?string $uuid, bool $needJsonHook): void
	{
		// Pass ip and uuid instead of global to make them read only
		if (isset(self::$modulesDone[$name]))
			return;
		self::$modulesDone[$name] = true;
		// Module has getconfig hook
		$file = 'modules/' . $name . '/baseconfig/getconfig.inc.php';
		if (!is_file($file))
			return;
		// We want only static hooks that have a json config (currently used for displaying inheritance tree)
		if ($needJsonHook && !is_file('modules/' . $name . '/baseconfig/hook.json'))
			return;
		// Properly registered and can be activated
		$mod = Module::get($name);
		if ($mod === false)
			return;
		if (!$mod->activate(1, false))
			return;
		// Process dependencies first
		foreach ($mod->getDependencies() as $dep) {
			self::handleModule($dep, $ip, $uuid, $needJsonHook);
		}
		ConfigHolder::setContext($name);
		(function (string $file, ?string $ip, ?string $uuid) {
			include_once($file);
		})($file, $ip, $uuid);
	}

	public static function hasOverride($key): bool
	{
		return array_key_exists($key, self::$overrides);
	}

	public static function getOverride($key)
	{
		return self::$overrides[$key];
	}

}