Rev 1177 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2 | daniel-mar | 1 | <?php |
2 | |||
3 | /* |
||
4 | * OIDplus 2.0 |
||
1086 | daniel-mar | 5 | * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft |
2 | daniel-mar | 6 | * |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); |
||
8 | * you may not use this file except in compliance with the License. |
||
9 | * You may obtain a copy of the License at |
||
10 | * |
||
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
||
12 | * |
||
13 | * Unless required by applicable law or agreed to in writing, software |
||
14 | * distributed under the License is distributed on an "AS IS" BASIS, |
||
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
16 | * See the License for the specific language governing permissions and |
||
17 | * limitations under the License. |
||
18 | */ |
||
19 | |||
1050 | daniel-mar | 20 | namespace ViaThinkSoft\OIDplus; |
511 | daniel-mar | 21 | |
1086 | daniel-mar | 22 | // phpcs:disable PSR1.Files.SideEffects |
23 | \defined('INSIDE_OIDPLUS') or die; |
||
24 | // phpcs:enable PSR1.Files.SideEffects |
||
25 | |||
261 | daniel-mar | 26 | // OIDplusConfig contains settings that are stored in the database. |
294 | daniel-mar | 27 | // Not to be confused with OIDplusBaseConfig which is the basic ("static") |
28 | // configuration stored in userdata/baseconfig/config.inc.php, |
||
261 | daniel-mar | 29 | // e.g. database access credentials. |
730 | daniel-mar | 30 | class OIDplusConfig extends OIDplusBaseClass implements OIDplusGetterSetterInterface { |
2 | daniel-mar | 31 | |
1130 | daniel-mar | 32 | /** |
33 | * |
||
34 | */ |
||
269 | daniel-mar | 35 | /*public*/ const PROTECTION_EDITABLE = 0; |
1130 | daniel-mar | 36 | |
37 | /** |
||
38 | * |
||
39 | */ |
||
269 | daniel-mar | 40 | /*public*/ const PROTECTION_READONLY = 1; |
1130 | daniel-mar | 41 | |
42 | /** |
||
43 | * |
||
44 | */ |
||
269 | daniel-mar | 45 | /*public*/ const PROTECTION_HIDDEN = 2; |
263 | daniel-mar | 46 | |
1130 | daniel-mar | 47 | /** |
48 | * @var bool |
||
49 | */ |
||
326 | daniel-mar | 50 | protected $configTableReadOnce = false; // this ensures that all $values and $descriptions were read |
51 | |||
1130 | daniel-mar | 52 | /** |
53 | * @var array |
||
54 | */ |
||
75 | daniel-mar | 55 | protected $values = array(); |
1130 | daniel-mar | 56 | |
57 | /** |
||
58 | * @var array |
||
59 | */ |
||
295 | daniel-mar | 60 | protected $descriptions = array(); |
1130 | daniel-mar | 61 | |
62 | /** |
||
63 | * @var array |
||
64 | */ |
||
682 | daniel-mar | 65 | protected $protectSettings = array(); |
1130 | daniel-mar | 66 | |
67 | /** |
||
68 | * @var array |
||
69 | */ |
||
682 | daniel-mar | 70 | protected $visibleSettings = array(); |
1130 | daniel-mar | 71 | |
72 | /** |
||
73 | * @var array |
||
74 | */ |
||
263 | daniel-mar | 75 | protected $validateCallbacks = array(); |
13 | daniel-mar | 76 | |
1116 | daniel-mar | 77 | /** |
78 | * @param string $name |
||
79 | * @param string $description |
||
80 | * @param string $init_value |
||
81 | * @param int $protection |
||
1130 | daniel-mar | 82 | * @param callable|null $validateCallback |
1116 | daniel-mar | 83 | * @return void |
84 | * @throws OIDplusException |
||
85 | */ |
||
1130 | daniel-mar | 86 | public function prepareConfigKey(string $name, string $description, string $init_value, int $protection, callable $validateCallback=null) { |
326 | daniel-mar | 87 | // Check if the protection flag is valid |
263 | daniel-mar | 88 | switch ($protection) { |
89 | case OIDplusConfig::PROTECTION_EDITABLE: |
||
1172 | daniel-mar | 90 | $protected = false; |
91 | $visible = true; |
||
263 | daniel-mar | 92 | break; |
93 | case OIDplusConfig::PROTECTION_READONLY: |
||
1172 | daniel-mar | 94 | $protected = true; |
95 | $visible = true; |
||
263 | daniel-mar | 96 | break; |
97 | case OIDplusConfig::PROTECTION_HIDDEN: |
||
1172 | daniel-mar | 98 | $protected = true; |
99 | $visible = false; |
||
263 | daniel-mar | 100 | break; |
101 | default: |
||
360 | daniel-mar | 102 | throw new OIDplusException(_L('Invalid protection flag, use OIDplusConfig::PROTECTION_* constants')); |
263 | daniel-mar | 103 | } |
295 | daniel-mar | 104 | |
326 | daniel-mar | 105 | // Check length limitations given by the database tables |
166 | daniel-mar | 106 | if (strlen($name) > 50) { |
360 | daniel-mar | 107 | throw new OIDplusException(_L('Config key name "%1" is too long (max %2).',$name,50)); |
166 | daniel-mar | 108 | } |
109 | if (strlen($description) > 255) { |
||
360 | daniel-mar | 110 | throw new OIDplusException(_L('Description for config key "%1" is too long (max %2).',$name,255)); |
166 | daniel-mar | 111 | } |
326 | daniel-mar | 112 | |
113 | // Read all values and descriptions from the database once. |
||
150 | daniel-mar | 114 | $this->buildConfigArray(); |
326 | daniel-mar | 115 | |
116 | // Figure out if we need to create/update something at database level |
||
150 | daniel-mar | 117 | if (!isset($this->values[$name])) { |
326 | daniel-mar | 118 | // Case A: The config setting does not exist in the database. So we create it now. |
614 | daniel-mar | 119 | try { |
120 | OIDplus::db()->query("insert into ###config (name, description, value, protected, visible) values (?, ?, ?, ?, ?)", array($name, $description, $init_value, $protected, $visible)); |
||
1050 | daniel-mar | 121 | } catch (\Exception $e) { |
614 | daniel-mar | 122 | // After a software update that introduced a new config setting, |
123 | // there will be a race-condition at this place, because |
||
124 | // jsTree and content are loading simultaneously! |
||
125 | // So we ignore the error here. |
||
126 | } |
||
155 | daniel-mar | 127 | $this->values[$name] = $init_value; |
295 | daniel-mar | 128 | $this->descriptions[$name] = $description; |
682 | daniel-mar | 129 | $this->protectSettings[$name] = $protected; |
130 | $this->visibleSettings[$name] = $visible; |
||
326 | daniel-mar | 131 | } else { |
132 | // Case B: The config setting exists ... |
||
133 | if ($this->descriptions[$name] != $description) { |
||
134 | // ... but the human readable description is different. |
||
135 | // We want to give the plugin authors the possibility to automatically update the config descriptions for their plugins |
||
136 | // So we just edit the description |
||
137 | OIDplus::db()->query("update ###config set description = ? where name = ?", array($description, $name)); |
||
138 | $this->descriptions[$name] = $description; |
||
139 | } |
||
682 | daniel-mar | 140 | if ($this->protectSettings[$name] != $protected) { |
141 | OIDplus::db()->query("update ###config set protected = ? where name = ?", array($protected, $name)); |
||
142 | $this->protectSettings[$name] = $protected; |
||
143 | } |
||
144 | if ($this->visibleSettings[$name] != $visible) { |
||
145 | OIDplus::db()->query("update ###config set visible = ? where name = ?", array($visible, $name)); |
||
146 | $this->visibleSettings[$name] = $visible; |
||
147 | } |
||
150 | daniel-mar | 148 | } |
326 | daniel-mar | 149 | |
150 | // Register the validation callback |
||
263 | daniel-mar | 151 | if (!is_null($validateCallback)) { |
152 | $this->validateCallbacks[$name] = $validateCallback; |
||
153 | } |
||
13 | daniel-mar | 154 | } |
155 | |||
1116 | daniel-mar | 156 | /** |
157 | * @return void |
||
158 | * @throws OIDplusException |
||
159 | */ |
||
326 | daniel-mar | 160 | public function clearCache() { |
161 | $this->configTableReadOnce = false; |
||
162 | $this->buildConfigArray(); |
||
163 | } |
||
164 | |||
1116 | daniel-mar | 165 | /** |
166 | * @return void |
||
167 | * @throws OIDplusException |
||
168 | */ |
||
150 | daniel-mar | 169 | protected function buildConfigArray() { |
326 | daniel-mar | 170 | if ($this->configTableReadOnce) return; |
171 | |||
172 | $this->values = array(); |
||
173 | $this->descriptions = array(); |
||
682 | daniel-mar | 174 | $this->protectSettings = array(); |
175 | $this->visibleSettings = array(); |
||
176 | $res = OIDplus::db()->query("select name, description, protected, visible, value from ###config"); |
||
326 | daniel-mar | 177 | while ($row = $res->fetch_object()) { |
178 | $this->values[$row->name] = $row->value; |
||
179 | $this->descriptions[$row->name] = $row->description; |
||
682 | daniel-mar | 180 | $this->protectSettings[$row->name] = $row->protected; |
181 | $this->visibleSettings[$row->name] = $row->visible; |
||
75 | daniel-mar | 182 | } |
326 | daniel-mar | 183 | |
184 | $this->configTableReadOnce = true; |
||
150 | daniel-mar | 185 | } |
75 | daniel-mar | 186 | |
1116 | daniel-mar | 187 | /** |
1177 | daniel-mar | 188 | * @return string[] |
189 | * @throws OIDplusException |
||
190 | */ |
||
191 | public function getAllKeys(): array { |
||
192 | // TODO: put this method into the interface OIDplusGetterSetterInterface |
||
193 | |||
194 | // Read all config settings once and write them in array $this->values |
||
195 | $this->buildConfigArray(); |
||
196 | |||
197 | // Now we can see if our desired attribute is available |
||
198 | return array_keys($this->values); |
||
199 | } |
||
200 | |||
201 | /** |
||
1116 | daniel-mar | 202 | * @param string $name |
203 | * @param mixed|null $default |
||
204 | * @return mixed|null |
||
205 | * @throws OIDplusException |
||
206 | */ |
||
207 | public function getValue(string $name, $default=null) { |
||
326 | daniel-mar | 208 | // Read all config settings once and write them in array $this->values |
150 | daniel-mar | 209 | $this->buildConfigArray(); |
326 | daniel-mar | 210 | |
211 | // Now we can see if our desired attribute is available |
||
1130 | daniel-mar | 212 | return $this->values[$name] ?? $default; |
46 | daniel-mar | 213 | } |
214 | |||
1116 | daniel-mar | 215 | /** |
216 | * @param string $name |
||
217 | * @return bool |
||
218 | * @throws OIDplusException |
||
219 | */ |
||
220 | public function exists(string $name): bool { |
||
326 | daniel-mar | 221 | return !is_null($this->getValue($name, null)); |
74 | daniel-mar | 222 | } |
223 | |||
1116 | daniel-mar | 224 | /** |
225 | * @param string $name |
||
226 | * @param mixed $value |
||
227 | * @return void |
||
228 | * @throws OIDplusException |
||
229 | */ |
||
230 | public function setValue(string $name, $value) { |
||
574 | daniel-mar | 231 | // Read all config settings once and write them in array $this->values |
232 | $this->buildConfigArray(); |
||
233 | |||
326 | daniel-mar | 234 | if (isset($this->values[$name])) { |
574 | daniel-mar | 235 | // Avoid unnecessary database writes |
326 | daniel-mar | 236 | if ($this->values[$name] == $value) return; |
574 | daniel-mar | 237 | } else { |
238 | throw new OIDplusException(_L('Config value "%1" cannot be written because it was not prepared!', $name)); |
||
326 | daniel-mar | 239 | } |
240 | |||
263 | daniel-mar | 241 | // Give plugins the possibility to stop the process by throwing an Exception (e.g. if the value is invalid) |
326 | daniel-mar | 242 | // Required is that the plugin previously prepared the config setting using prepareConfigKey() |
263 | daniel-mar | 243 | if (isset($this->validateCallbacks[$name])) { |
244 | $this->validateCallbacks[$name]($value); |
||
14 | daniel-mar | 245 | } |
257 | daniel-mar | 246 | |
14 | daniel-mar | 247 | // Now change the value in the database |
1219 | daniel-mar | 248 | OIDplus::db()->query("update ###config set value = ? where name = ?", array("$value", "$name")); |
66 | daniel-mar | 249 | $this->values[$name] = $value; |
14 | daniel-mar | 250 | } |
251 | |||
1116 | daniel-mar | 252 | /** |
253 | * @param string $name |
||
254 | * @param string $value |
||
255 | * @return void |
||
256 | * @throws OIDplusException |
||
257 | */ |
||
258 | public function setValueNoCallback(string $name, string $value) { |
||
672 | daniel-mar | 259 | // Read all config settings once and write them in array $this->values |
260 | $this->buildConfigArray(); |
||
261 | |||
262 | if (isset($this->values[$name])) { |
||
263 | // Avoid unnecessary database writes |
||
264 | if ($this->values[$name] == $value) return; |
||
265 | } else { |
||
266 | throw new OIDplusException(_L('Config value "%1" cannot be written because it was not prepared!', $name)); |
||
267 | } |
||
268 | |||
269 | // Now change the value in the database |
||
270 | OIDplus::db()->query("update ###config set value = ? where name = ?", array($value, $name)); |
||
271 | $this->values[$name] = $value; |
||
272 | } |
||
273 | |||
1116 | daniel-mar | 274 | /** |
275 | * @param string $name |
||
276 | * @return void |
||
277 | * @throws OIDplusException |
||
278 | */ |
||
279 | public function delete(string $name) { |
||
326 | daniel-mar | 280 | if ($this->configTableReadOnce) { |
281 | if (isset($this->values[$name])) { |
||
282 | OIDplus::db()->query("delete from ###config where name = ?", array($name)); |
||
283 | } |
||
284 | } else { |
||
285 | // We do not know if the value exists. |
||
286 | // buildConfigArray() would do many reads which are unnecessary. |
||
287 | // So we just do a MySQL command to delete the stuff: |
||
294 | daniel-mar | 288 | OIDplus::db()->query("delete from ###config where name = ?", array($name)); |
289 | } |
||
326 | daniel-mar | 290 | |
291 | unset($this->values[$name]); |
||
292 | unset($this->descriptions[$name]); |
||
293 | unset($this->validateCallbacks[$name]); |
||
682 | daniel-mar | 294 | unset($this->protectSettings[$name]); |
295 | unset($this->visibleSettings[$name]); |
||
294 | daniel-mar | 296 | } |
297 | |||
569 | daniel-mar | 298 | } |