1<?php
2// Copyright (C) 2010-2018 Combodo SARL
3//
4//   This file is part of iTop.
5//
6//   iTop is free software; you can redistribute it and/or modify
7//   it under the terms of the GNU Affero General Public License as published by
8//   the Free Software Foundation, either version 3 of the License, or
9//   (at your option) any later version.
10//
11//   iTop is distributed in the hope that it will be useful,
12//   but WITHOUT ANY WARRANTY; without even the implied warranty of
13//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14//   GNU Affero General Public License for more details.
15//
16//   You should have received a copy of the GNU Affero General Public License
17//   along with iTop. If not, see <http://www.gnu.org/licenses/>
18
19
20define('ITOP_APPLICATION', 'iTop');
21define('ITOP_APPLICATION_SHORT', 'iTop');
22define('ITOP_VERSION', '2.6.1');
23define('ITOP_REVISION', '4463');
24define('ITOP_BUILD_DATE', '2019-03-25 16:49:30');
25
26define('ACCESS_USER_WRITE', 1);
27define('ACCESS_ADMIN_WRITE', 2);
28define('ACCESS_FULL', ACCESS_USER_WRITE | ACCESS_ADMIN_WRITE);
29define('ACCESS_READONLY', 0);
30
31/**
32 * Configuration read/write
33 *
34 * @copyright   Copyright (C) 2010-2018 Combodo SARL
35 * @license     http://opensource.org/licenses/AGPL-3.0
36 */
37
38require_once('coreexception.class.inc.php');
39require_once('attributedef.class.inc.php'); // For the defines
40require_once('simplecrypt.class.inc.php');
41
42class ConfigException extends CoreException
43{
44}
45
46// was utf8 but it only supports BMP chars (https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html)
47// so we switched to utf8mb4 in iTop 2.5, adding dependency to MySQL 5.5.3
48// The config params db_character_set and db_collation were introduced as a temporary workaround and removed in iTop 2.5
49// now everything uses those fixed value !
50define('DEFAULT_CHARACTER_SET', 'utf8mb4');
51define('DEFAULT_COLLATION', 'utf8mb4_unicode_ci');
52
53define('DEFAULT_LOG_GLOBAL', true);
54define('DEFAULT_LOG_NOTIFICATION', true);
55define('DEFAULT_LOG_ISSUE', true);
56define('DEFAULT_LOG_WEB_SERVICE', true);
57
58define('DEFAULT_QUERY_CACHE_ENABLED', true);
59
60
61define('DEFAULT_MIN_DISPLAY_LIMIT', 10);
62define('DEFAULT_MAX_DISPLAY_LIMIT', 15);
63define('DEFAULT_STANDARD_RELOAD_INTERVAL', 5 * 60);
64define('DEFAULT_FAST_RELOAD_INTERVAL', 1 * 60);
65define('DEFAULT_SECURE_CONNECTION_REQUIRED', false);
66define('DEFAULT_ALLOWED_LOGIN_TYPES', 'form|basic|external');
67define('DEFAULT_EXT_AUTH_VARIABLE', '$_SERVER[\'REMOTE_USER\']');
68define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random generated key later (if possible)
69define('DEFAULT_ENCRYPTION_LIB', 'Mcrypt'); // We'll define the best encryption available later
70/**
71 * Config
72 * configuration data (this class cannot not be localized, because it is responsible for loading the dictionaries)
73 *
74 * @package     iTopORM
75 *
76 * @see \MetaModel::GetConfig() to get the config, if the metamodel was already loaded
77 * @see utils::GetConfig() to load config from the current env, if metamodel is not loaded
78 */
79class Config
80{
81	//protected $m_bIsLoaded = false;
82	protected $m_sFile = '';
83
84	protected $m_aAppModules;
85	protected $m_aDataModels;
86	protected $m_aWebServiceCategories;
87	protected $m_aAddons;
88
89	protected $m_aModuleSettings;
90
91	/**
92	 * New way to store the settings !
93	 *
94	 * @var array
95	 * @since 2.5 db* variables
96	 */
97	protected $m_aSettings = array(
98		'app_env_label' => array(
99			'type' => 'string',
100			'description' => 'Label displayed to describe the current application environment, defaults to the environment name (e.g. "production")',
101			'default' => '',
102			'value' => '',
103			'source_of_value' => '',
104			'show_in_conf_sample' => false,
105		),
106		'app_root_url' => array(
107			'type' => 'string',
108			'description' => 'Root URL used for navigating within the application, or from an email to the application (you can put $SERVER_NAME$ as a placeholder for the server\'s name)',
109			'default' => '',
110			'value' => '',
111			'source_of_value' => '',
112			'show_in_conf_sample' => true,
113		),
114		'app_icon_url' => array(
115			'type' => 'string',
116			'description' => 'Hyperlink to redirect the user when clicking on the application icon (in the main window, or login/logoff pages)',
117			'default' => 'http://www.combodo.com/itop',
118			'value' => '',
119			'source_of_value' => '',
120			'show_in_conf_sample' => false,
121		),
122		'db_host' => array(
123			'type' => 'string',
124			'default' => null,
125			'value' => '',
126			'source_of_value' => '',
127			'show_in_conf_sample' => true,
128		),
129		'db_user' => array(
130			'type' => 'string',
131			'default' => null,
132			'value' => '',
133			'source_of_value' => '',
134			'show_in_conf_sample' => true,
135		),
136		'db_pwd' => array(
137			'type' => 'string',
138			'default' => null,
139			'value' => '',
140			'source_of_value' => '',
141			'show_in_conf_sample' => true,
142		),
143		'db_name' => array(
144			'type' => 'string',
145			'default' => null,
146			'value' => '',
147			'source_of_value' => '',
148			'show_in_conf_sample' => true,
149		),
150		'db_subname' => array(
151			'type' => 'string',
152			'default' => null,
153			'value' => '',
154			'source_of_value' => '',
155			'show_in_conf_sample' => true,
156		),
157		'db_tls.enabled' => array(
158			'type' => 'bool',
159			'description' => 'If true then the connection to the DB will be encrypted',
160			'default' => false,
161			'value' => false,
162			'source_of_value' => '',
163			'show_in_conf_sample' => false,
164		),
165		'db_tls.ca' => array(
166			'type' => 'string',
167			'description' => 'Path to certificate authority file for SSL',
168			'default' => null,
169			'value' => '',
170			'source_of_value' => '',
171			'show_in_conf_sample' => false,
172		),
173		'db_character_set' => array( // @deprecated to remove in 2.7 ? N°1001 utf8mb4 switch
174			'type' => 'string',
175			'description' => 'Deprecated since iTop 2.5 : now using utf8mb4',
176			'default' => 'DEPRECATED_2.5',
177			'value' => '',
178			'source_of_value' => '',
179			'show_in_conf_sample' => false,
180		),
181		'db_collation' => array( // @deprecated to remove in 2.7 ? N°1001 utf8mb4 switch
182			'type' => 'string',
183			'description' => 'Deprecated since iTop 2.5 : now using utf8mb4_unicode_ci',
184			'default' => 'DEPRECATED_2.5',
185			'value' => '',
186			'source_of_value' => '',
187			'show_in_conf_sample' => false,
188		),
189		'skip_check_to_write' => array(
190			'type' => 'bool',
191			'description' => 'Disable data format and integrity checks to boost up data load (insert or update)',
192			'default' => false,
193			'value' => false,
194			'source_of_value' => '',
195			'show_in_conf_sample' => false,
196		),
197		'skip_check_ext_keys' => array(
198			'type' => 'bool',
199			'description' => 'Disable external key check when checking the value of attributes',
200			'default' => false,
201			'value' => false,
202			'source_of_value' => '',
203			'show_in_conf_sample' => false,
204		),
205		'skip_strong_security' => array(
206			'type' => 'bool',
207			'description' => 'Disable strong security - TEMPORARY: this flag should be removed when we are more confident in the recent change in security',
208			'default' => true,
209			'value' => true,
210			'source_of_value' => '',
211			'show_in_conf_sample' => false,
212		),
213		'query_optimization_enabled' => array(
214			'type' => 'bool',
215			'description' => 'The queries are optimized based on the assumption that the DB integrity has been preserved. By disabling the optimization one can ensure that the fetched data is clean... but this can be really slower or not usable at all (some queries will exceed the allowed number of joins in MySQL: 61!)',
216			'default' => true,
217			'value' => true,
218			'source_of_value' => '',
219			'show_in_conf_sample' => false,
220		),
221		'query_indentation_enabled' => array(
222			'type' => 'bool',
223			'description' => 'For developers: format the SQL queries for human analysis',
224			'default' => false,
225			'value' => false,
226			'source_of_value' => '',
227			'show_in_conf_sample' => false,
228		),
229		'disable_mandatory_ext_keys' => array(
230			'type' => 'bool',
231			'description' => 'For developers: allow every external keys to be undefined',
232			'default' => false,
233			'value' => false,
234			'source_of_value' => '',
235			'show_in_conf_sample' => false,
236		),
237		'graphviz_path' => array(
238			'type' => 'string',
239			'description' => 'Path to the Graphviz "dot" executable for graphing objects lifecycle',
240			'default' => '/usr/bin/dot',
241			'value' => '',
242			'source_of_value' => '',
243			'show_in_conf_sample' => true,
244		),
245		'php_path' => array(
246			'type' => 'string',
247			'description' => 'Path to the php executable in CLI mode',
248			'default' => 'php',
249			'value' => 'php',
250			'source_of_value' => '',
251			'show_in_conf_sample' => true,
252		),
253		'session_name' => array(
254			'type' => 'string',
255			'description' => 'The name of the cookie used to store the PHP session id',
256			'default' => 'iTop',
257			'value' => '',
258			'source_of_value' => '',
259			'show_in_conf_sample' => true,
260		),
261		'max_combo_length' => array(
262			'type' => 'integer',
263			'description' => 'The maximum number of elements in a drop-down list. If more then an autocomplete will be used',
264			'default' => 50,
265			'value' => 50,
266			'source_of_value' => '',
267			'show_in_conf_sample' => false,
268		),
269		'min_autocomplete_chars' => array(
270			'type' => 'integer',
271			'description' => 'The minimum number of characters to type in order to trigger the "autocomplete" behavior',
272			'default' => 2,
273			'value' => 2,
274			'source_of_value' => '',
275			'show_in_conf_sample' => false,
276		),
277		'allow_menu_on_linkset' => array(
278			'type' => 'bool',
279			'description' => 'Display Action menus in view mode on any LinkedSet with edit_mode != none',
280			'default' => false,
281			'value' => false,
282			'source_of_value' => '',
283			'show_in_conf_sample' => false,
284		),
285		'allow_target_creation' => array(
286			'type' => 'bool',
287			'description' => 'Displays the + button on external keys to create target objects',
288			'default' => true,
289			'value' => true,
290			'source_of_value' => '',
291			'show_in_conf_sample' => false,
292		),
293		// Levels that trigger a confirmation in the CSV import/synchro wizard
294		'csv_import_min_object_confirmation' => array(
295			'type' => 'integer',
296			'description' => 'Minimum number of objects to check for the confirmation percentages',
297			'default' => 3,
298			'value' => 3,
299			'source_of_value' => '',
300			'show_in_conf_sample' => false,
301		),
302		'csv_import_errors_percentage' => array(
303			'type' => 'integer',
304			'description' => 'Percentage of errors that trigger a confirmation in the CSV import',
305			'default' => 50,
306			'value' => 50,
307			'source_of_value' => '',
308			'show_in_conf_sample' => false,
309		),
310		'csv_import_modifications_percentage' => array(
311			'type' => 'integer',
312			'description' => 'Percentage of modifications that trigger a confirmation in the CSV import',
313			'default' => 50,
314			'value' => 50,
315			'source_of_value' => '',
316			'show_in_conf_sample' => false,
317		),
318		'csv_import_creations_percentage' => array(
319			'type' => 'integer',
320			'description' => 'Percentage of creations that trigger a confirmation in the CSV import',
321			'default' => 50,
322			'value' => 50,
323			'source_of_value' => '',
324			'show_in_conf_sample' => false,
325		),
326		'csv_import_history_display' => array(
327			'type' => 'bool',
328			'description' => 'Display the history tab in the import wizard',
329			'default' => false,
330			'value' => false,
331			'source_of_value' => '',
332			'show_in_conf_sample' => true,
333		),
334		'access_mode' => array(
335			'type' => 'integer',
336			'description' => 'Access mode: ACCESS_READONLY = 0, ACCESS_ADMIN_WRITE = 2, ACCESS_FULL = 3',
337			'default' => ACCESS_FULL,
338			'value' => ACCESS_FULL,
339			'source_of_value' => '',
340			'show_in_conf_sample' => true,
341		),
342		'access_message' => array(
343			'type' => 'string',
344			'description' => 'Message displayed to the users when there is any access restriction',
345			'default' => 'iTop is temporarily frozen, please wait... (the admin team)',
346			'value' => '',
347			'source_of_value' => '',
348			'show_in_conf_sample' => true,
349		),
350		'online_help' => array(
351			'type' => 'string',
352			'description' => 'Hyperlink to the online-help web page',
353			'default' => 'http://www.combodo.com/itop-help',
354			'value' => '',
355			'source_of_value' => '',
356			'show_in_conf_sample' => true,
357		),
358		'log_usage' => array(
359			'type' => 'bool',
360			'description' => 'Log the usage of the application (i.e. the date/time and the user name of each login)',
361			'default' => false,
362			'value' => false,
363			'source_of_value' => '',
364			'show_in_conf_sample' => false,
365		),
366		'log_rest_service' => array(
367			'type' => 'bool',
368			'description' => 'Log the usage of the REST/JSON service',
369			'default' => false,
370			'value' => false,
371			'source_of_value' => '',
372			'show_in_conf_sample' => false,
373		),
374		'synchro_trace' => array(
375			'type' => 'string',
376			'description' => 'Synchronization details: none, display, save (includes \'display\')',
377			'default' => 'none',
378			'value' => 'none',
379			'source_of_value' => '',
380			'show_in_conf_sample' => true,
381		),
382		'link_set_item_separator' => array(
383			'type' => 'string',
384			'description' => 'Link set from string: line separator',
385			'default' => '|',
386			'value' => '|',
387			'source_of_value' => '',
388			'show_in_conf_sample' => true,
389		),
390		'link_set_attribute_separator' => array(
391			'type' => 'string',
392			'description' => 'Link set from string: attribute separator',
393			'default' => ';',
394			'value' => ';',
395			'source_of_value' => '',
396			'show_in_conf_sample' => true,
397		),
398		'link_set_value_separator' => array(
399			'type' => 'string',
400			'description' => 'Link set from string: value separator (between the attcode and the value itself',
401			'default' => ':',
402			'value' => ':',
403			'source_of_value' => '',
404			'show_in_conf_sample' => true,
405		),
406		'link_set_attribute_qualifier' => array(
407			'type' => 'string',
408			'description' => 'Link set from string: attribute qualifier (encloses both the attcode and the value)',
409			'default' => "'",
410			'value' => "'",
411			'source_of_value' => '',
412			'show_in_conf_sample' => true,
413		),
414		'tag_set_item_separator' => array(
415			'type' => 'string',
416			'description' => 'Tag set from string: tag label separator',
417			'default' => '|',
418			'value' => '|',
419			'source_of_value' => '',
420			'show_in_conf_sample' => true,
421		),
422		'cron_max_execution_time' => array(
423			'type' => 'integer',
424			'description' => 'Duration (seconds) of the page cron.php, must be shorter than php setting max_execution_time and shorter than the web server response timeout',
425			'default' => 600,
426			'value' => 600,
427			'source_of_value' => '',
428			'show_in_conf_sample' => true,
429		),
430		'cron_sleep' => array(
431			'type' => 'integer',
432			'description' => 'Duration (seconds) before cron.php checks again if something must be done',
433			'default' => 2,
434			'value' => 2,
435			'source_of_value' => '',
436			'show_in_conf_sample' => false,
437		),
438		'async_task_retries' => array(
439			'type' => 'array',
440			'description' => 'Automatic retries of asynchronous tasks in case of failure (per class)',
441			'default' => array('AsyncSendEmail' => array('max_retries' => 0, 'retry_delay' => 600)),
442			'value' => false,
443			'source_of_value' => '',
444			'show_in_conf_sample' => false,
445		),
446		'email_asynchronous' => array(
447			'type' => 'bool',
448			'description' => 'If set, the emails are sent off line, which requires cron.php to be activated. Exception: some features like the email test utility will force the serialized mode',
449			'default' => false,
450			'value' => false,
451			'source_of_value' => '',
452			'show_in_conf_sample' => true,
453		),
454		'email_transport' => array(
455			'type' => 'string',
456			'description' => 'Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocol)',
457			'default' => "PHPMail",
458			'value' => "PHPMail",
459			'source_of_value' => '',
460			'show_in_conf_sample' => true,
461		),
462		'email_transport_smtp.host' => array(
463			'type' => 'string',
464			'description' => 'host name or IP address (optional)',
465			'default' => "localhost",
466			'value' => "localhost",
467			'source_of_value' => '',
468			'show_in_conf_sample' => false,
469		),
470		'email_transport_smtp.port' => array(
471			'type' => 'integer',
472			'description' => 'port number (optional)',
473			'default' => 25,
474			'value' => 25,
475			'source_of_value' => '',
476			'show_in_conf_sample' => false,
477		),
478		'email_transport_smtp.encryption' => array(
479			'type' => 'string',
480			'description' => 'tls or ssl (optional)',
481			'default' => "",
482			'value' => "",
483			'source_of_value' => '',
484			'show_in_conf_sample' => false,
485		),
486		'email_transport_smtp.username' => array(
487			'type' => 'string',
488			'description' => 'Authentication user (optional)',
489			'default' => "",
490			'value' => "",
491			'source_of_value' => '',
492			'show_in_conf_sample' => false,
493		),
494		'email_transport_smtp.password' => array(
495			'type' => 'string',
496			'description' => 'Authentication password (optional)',
497			'default' => "",
498			'value' => "",
499			'source_of_value' => '',
500			'show_in_conf_sample' => false,
501		),
502		'email_css' => array(
503			'type' => 'string',
504			'description' => 'CSS that will override the standard stylesheet used for the notifications',
505			'default' => "",
506			'value' => "",
507			'source_of_value' => '',
508			'show_in_conf_sample' => false,
509		),
510		'email_default_sender_address' => array(
511			'type' => 'string',
512			'description' => 'Default address provided in the email from header field.',
513			'default' => "",
514			'value' => "",
515			'source_of_value' => '',
516			'show_in_conf_sample' => true,
517		),
518		'email_default_sender_label' => array(
519			'type' => 'string',
520			'description' => 'Default label provided in the email from header field.',
521			'default' => "",
522			'value' => "",
523			'source_of_value' => '',
524			'show_in_conf_sample' => true,
525		),
526		'apc_cache.enabled' => array(
527			'type' => 'bool',
528			'description' => 'If set, the APC cache is allowed (the PHP extension must also be active)',
529			'default' => true,
530			'value' => true,
531			'source_of_value' => '',
532			'show_in_conf_sample' => true,
533		),
534		'apc_cache.query_ttl' => array(
535			'type' => 'integer',
536			'description' => 'Time to live set in APC for the prepared queries (seconds - 0 means no timeout)',
537			'default' => 3600,
538			'value' => 3600,
539			'source_of_value' => '',
540			'show_in_conf_sample' => true,
541		),
542		'apc_cache_emulation.max_entries' => array(
543			'type' => 'integer',
544			'description' => 'Maximum number of cache entries (0 means no limit)',
545			'default' => 1000,
546			'value' => 1000,
547			'source_of_value' => '',
548			'show_in_conf_sample' => false,
549		),
550		'timezone' => array(
551			'type' => 'string',
552			'description' => 'Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitly configured in PHP',
553			// examples... not used (nor 'description')
554			'examples' => array(
555				'America/Sao_Paulo',
556				'America/New_York (standing for EDT)',
557				'America/Los_Angeles (standing for PDT)',
558				'Asia/Istanbul',
559				'Asia/Singapore',
560				'Africa/Casablanca',
561				'Australia/Sydney'
562			),
563			'default' => 'Europe/Paris',
564			'value' => 'Europe/Paris',
565			'source_of_value' => '',
566			'show_in_conf_sample' => true,
567		),
568		'cas_include_path' => array(
569			'type' => 'string',
570			'description' => 'The path where to find the phpCAS library',
571			// examples... not used (nor 'description')
572			'default' => '/usr/share/php',
573			'value' => '/usr/share/php',
574			'source_of_value' => '',
575			'show_in_conf_sample' => true,
576		),
577		'cas_version' => array(
578			'type' => 'string',
579			'description' => 'The CAS protocol version to use: "1.0" (CAS v1), "2.0" (CAS v2) or "S1" (SAML V1) )',
580			// examples... not used (nor 'description')
581			'default' => '2.0',
582			'value' => '',
583			'source_of_value' => '',
584			'show_in_conf_sample' => false,
585		),
586		'cas_host' => array(
587			'type' => 'string',
588			'description' => 'The name of the CAS host',
589			// examples... not used (nor 'description')
590			'default' => '',
591			'value' => '',
592			'source_of_value' => '',
593			'show_in_conf_sample' => false,
594		),
595		'cas_port' => array(
596			'type' => 'integer',
597			'description' => 'The port used by the CAS server',
598			// examples... not used (nor 'description')
599			'default' => 443,
600			'value' => 443,
601			'source_of_value' => '',
602			'show_in_conf_sample' => false,
603		),
604		'cas_context' => array(
605			'type' => 'string',
606			'description' => 'The CAS context',
607			// examples... not used (nor 'description')
608			'default' => '',
609			'value' => '',
610			'source_of_value' => '',
611			'show_in_conf_sample' => false,
612		),
613		'cas_server_ca_cert_path' => array(
614			'type' => 'string',
615			'description' => 'The path where to find the certificate of the CA for validating the certificate of the CAS server',
616			// examples... not used (nor 'description')
617			'default' => '',
618			'value' => '',
619			'source_of_value' => '',
620			'show_in_conf_sample' => false,
621		),
622		'cas_logout_redirect_service' => array(
623			'type' => 'string',
624			'description' => 'The redirect service (URL) to use when logging-out with CAS',
625			// examples... not used (nor 'description')
626			'default' => '',
627			'value' => '',
628			'source_of_value' => '',
629			'show_in_conf_sample' => false,
630		),
631		'cas_memberof' => array(
632			'type' => 'string',
633			'description' => 'A semicolon separated list of group names that the user must be member of (works only with SAML - e.g. cas_version=> "S1")',
634			// examples... not used (nor 'description')
635			'default' => '',
636			'value' => '',
637			'source_of_value' => '',
638			'show_in_conf_sample' => false,
639		),
640		'cas_user_synchro' => array(
641			'type' => 'bool',
642			'description' => 'Whether or not to synchronize users with CAS/LDAP',
643			// examples... not used (nor 'description')
644			'default' => 0,
645			'value' => 0,
646			'source_of_value' => '',
647			'show_in_conf_sample' => false,
648		),
649		'cas_update_profiles' => array(
650			'type' => 'bool',
651			'description' => 'Whether or not to update the profiles of an existing user from the CAS information',
652			// examples... not used (nor 'description')
653			'default' => 0,
654			'value' => 0,
655			'source_of_value' => '',
656			'show_in_conf_sample' => false,
657		),
658		'cas_profile_pattern' => array(
659			'type' => 'string',
660			'description' => 'A regular expression pattern to extract the name of the iTop profile from the name of an LDAP/CAS group',
661			// examples... not used (nor 'description')
662			'default' => '/^cn=([^,]+),/',
663			'value' => '/^cn=([^,]+),/',
664			'source_of_value' => '',
665			'show_in_conf_sample' => false,
666		),
667		'cas_default_profiles' => array(
668			'type' => 'string',
669			'description' => 'A semi-colon separated list of iTop Profiles to use when creating a new user if no profile is retrieved from CAS',
670			// examples... not used (nor 'description')
671			'default' => 'Portal user',
672			'value' => 'Portal user',
673			'source_of_value' => '',
674			'show_in_conf_sample' => false,
675		),
676		'cas_debug' => array(
677			'type' => 'bool',
678			'description' => 'Activate the CAS debug',
679			// examples... not used (nor 'description')
680			'default' => false,
681			'value' => false,
682			'source_of_value' => '',
683			'show_in_conf_sample' => false,
684		),
685		'forgot_password' => array(
686			'type' => 'bool',
687			'description' => 'Enable the "Forgot password" feature',
688			// examples... not used (nor 'description')
689			'default' => true,
690			'value' => true,
691			'source_of_value' => '',
692			'show_in_conf_sample' => false,
693		),
694		'forgot_password_from' => array(
695			'type' => 'string',
696			'description' => 'Sender email address for the "forgot password" feature. If empty, defaults to the recipient\'s  email address.',
697			// examples... not used (nor 'description')
698			'default' => '',
699			'value' => '',
700			'source_of_value' => '',
701			'show_in_conf_sample' => false,
702		),
703		'deadline_format' => array(
704			'type' => 'string',
705			'description' => 'The format used for displaying "deadline" attributes: any string with the following placeholders: $date$, $difference$',
706			// examples... $date$ ($deadline$)
707			'default' => '$difference$',
708			'value' => '$difference$',
709			'source_of_value' => '',
710			'show_in_conf_sample' => true,
711		),
712		'buttons_position' => array(
713			'type' => 'string',
714			'description' => 'Position of the forms buttons: bottom | top | both',
715			// examples... not used
716			'default' => 'both',
717			'value' => 'both',
718			'source_of_value' => '',
719			'show_in_conf_sample' => true,
720		),
721		'shortcut_actions' => array(
722			'type' => 'string',
723			'description' => 'Actions that are available as direct buttons next to the "Actions" menu',
724			// examples... not used
725			'default' => 'UI:Menu:Modify,UI:Menu:New',
726			'value' => 'UI:Menu:Modify',
727			'source_of_value' => '',
728			'show_in_conf_sample' => true,
729		),
730		'complex_actions_limit' => array(
731			'type' => 'integer',
732			'description' => 'Display the "actions" menu items that require long computation only if the list of objects is contains less objects than this number (0 means no limit)',
733			// examples... not used
734			'default' => 50,
735			'value' => 50,
736			'source_of_value' => '',
737			'show_in_conf_sample' => false,
738		),
739		'synchro_prevent_delete_all' => array(
740			'type' => 'bool',
741			'description' => 'Stop the synchro if all the replicas of a data source become obsolete at the same time.',
742			// examples... not used
743			'default' => true,
744			'value' => true,
745			'source_of_value' => '',
746			'show_in_conf_sample' => false,
747		),
748		'source_dir' => array(
749			'type' => 'string',
750			'description' => 'Source directory for the datamodel files. (which gets compiled to env-production).',
751			// examples... not used
752			'default' => '',
753			'value' => '',
754			'source_of_value' => '',
755			'show_in_conf_sample' => true,
756		),
757		'csv_file_default_charset' => array(
758			'type' => 'string',
759			'description' => 'Character set used by default for downloading and uploading data as a CSV file. Warning: it is case sensitive (uppercase is preferable).',
760			// examples... not used
761			'default' => 'ISO-8859-1',
762			'value' => '',
763			'source_of_value' => '',
764			'show_in_conf_sample' => true,
765		),
766		'debug_report_spurious_chars' => array(
767			'type' => 'bool',
768			'description' => 'Report, in the error log, the characters found in the output buffer, echoed by mistake in the loaded modules, and potentially corrupting the output',
769			// examples... not used
770			'default' => false,
771			'value' => '',
772			'source_of_value' => '',
773			'show_in_conf_sample' => false,
774		),
775		'impact_analysis_first_tab' => array(
776			'type' => 'string',
777			'description' => 'Which tab to display first in the impact analysis view: list or graphics. Graphics are nicer but slower to display when there are many objects',
778			// examples... not used
779			'default' => 'graphics',
780			'value' => '',
781			'source_of_value' => '',
782			'show_in_conf_sample' => false,
783		),
784		'url_validation_pattern' => array(
785			'type' => 'string',
786			'description' => 'Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)',
787			'default' => '(https?|ftp)\://([a-zA-Z0-9+!*(),;?&=\$_.-]+(\:[a-zA-Z0-9+!*(),;?&=\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\:[0-9]{2,5})?(/([a-zA-Z0-9%+\$_-]\.?)+)*/?(\?[a-zA-Z+&\$_.-][a-zA-Z0-9;:[\]@&%=+/\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\$_.-]*)?',
788			//            SHEME.......... USER....................... PASSWORD.......................... HOST/IP........... PORT.......... PATH........................ GET............................................ ANCHOR............................
789			// Example: http://User:passWord@127.0.0.1:8888/patH/Page.php?arrayArgument[2]=something:blah20#myAnchor
790			// Origin of this regexp: http://www.php.net/manual/fr/function.preg-match.php#93824
791			'value' => '',
792			'source_of_value' => '',
793			'show_in_conf_sample' => true,
794		),
795        'email_validation_pattern' => array(
796            'type' => 'string',
797            'description' => 'Regular expression to validate/detect the format of an eMail address',
798            'default' => "[a-zA-Z0-9._&'-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9-]{2,}",
799            'value' => '',
800            'source_of_value' => '',
801            'show_in_conf_sample' => true,
802        ),
803        'email_decoration_class' => array(
804            'type' => 'string',
805            'description' => 'CSS class(es) to use as decoration for the HTML rendering of the attribute. eg. "fa fa-envelope" will put a mail icon.',
806            'default' => 'fa fa-envelope',
807            'value' => '',
808            'source_of_value' => '',
809            'show_in_conf_sample' => false,
810        ),
811        'phone_number_validation_pattern' => array(
812            'type' => 'string',
813            'description' => 'Regular expression to validate/detect the format of a phone number',
814            'default' => "[0-9.\-\ \+\(\)]+",
815            'value' => '',
816            'source_of_value' => '',
817            'show_in_conf_sample' => false,
818        ),
819        'phone_number_url_pattern' => array(
820            'type' => 'string',
821            'description' => 'Format for phone number url, use %1$s as a placeholder for the value. eg. "tel:%1$s" for regular phone applications or "callto:%1$s" for Skype. Default is "tel:%1$s".',
822            'default' => 'tel:%1$s',
823            'value' => '',
824            'source_of_value' => '',
825            'show_in_conf_sample' => false,
826        ),
827        'phone_number_decoration_class' => array(
828            'type' => 'string',
829            'description' => 'CSS class(es) to use as decoration for the HTML rendering of the attribute. eg. "fa fa-phone" will put a phone icon.',
830            'default' => 'fa fa-phone',
831            'value' => '',
832            'source_of_value' => '',
833            'show_in_conf_sample' => false,
834        ),
835		'log_kpi_duration' => array(
836			'type' => 'integer',
837			'description' => 'Level of logging for troubleshooting performance issues (1 to enable, 2 +blame callers)',
838			// examples... not used
839			'default' => 0,
840			'value' => 0,
841			'source_of_value' => '',
842			'show_in_conf_sample' => false,
843		),
844		'log_kpi_memory' => array(
845			'type' => 'integer',
846			'description' => 'Level of logging for troubleshooting memory limit issues',
847			// examples... not used
848			'default' => 0,
849			'value' => 0,
850			'source_of_value' => '',
851			'show_in_conf_sample' => false,
852		),
853		'log_kpi_user_id' => array(
854			'type' => 'string',
855			'description' => 'Limit the scope of users to the given user id (* means no limit)',
856			// examples... not used
857			'default' => '*',
858			'value' => '*',
859			'source_of_value' => '',
860			'show_in_conf_sample' => false,
861		),
862		'max_linkset_output' => array(
863			'type' => 'integer',
864			'description' => 'Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.',
865			'default' => 100,
866			'value' => 100,
867			'source_of_value' => '',
868			'show_in_conf_sample' => true,
869		),
870		'demo_mode' => array(
871			'type' => 'bool',
872			'description' => 'Set to true to prevent users from changing passwords/languages',
873			'default' => false,
874			'value' => '',
875			'source_of_value' => '',
876			'show_in_conf_sample' => false,
877		),
878		'portal_tickets' => array(
879			'type' => 'string',
880			'description' => 'CSV list of classes supported in the portal',
881			// examples... not used
882			'default' => 'UserRequest',
883			'value' => 'UserRequest',
884			'source_of_value' => '',
885			'show_in_conf_sample' => true,
886		),
887		'portal_dispatch_urls' => array(
888			'type' => 'array',
889			'description' => 'Associative array of sPortalId => Home page URL (relatively to the application root)',
890			// examples... not used
891			'default' => array(),
892			'value' => false,
893			'source_of_value' => '',
894			'show_in_conf_sample' => false,
895		),
896		'max_execution_time_per_loop' => array(
897			'type' => 'integer',
898			'description' => 'Maximum execution time requested, per loop, during bulk operations. Zero means no limit.',
899			// examples... not used
900			'default' => 30,
901			'value' => 30,
902			'source_of_value' => '',
903			'show_in_conf_sample' => false,
904		),
905		'max_history_length' => array(
906			'type' => 'integer',
907			'description' => 'Maximum length of the history table (in the "History" tab on each object) before it gets truncated. Latest modifications are displayed first.',
908			// examples... not used
909			'default' => 50,
910			'value' => 50,
911			'source_of_value' => '',
912			'show_in_conf_sample' => false,
913		),
914		'max_history_case_log_entry_length' => array(
915			'type' => 'integer',
916			'description' => 'The length (in number of characters) at which to truncate the (expandable) display (in the history) of a case log entry. If zero, the display in the history is not truncated.',
917			// examples... not used
918			'default' => 60,
919			'value' => 60,
920			'source_of_value' => '',
921			'show_in_conf_sample' => false,
922		),
923		'full_text_chunk_duration' => array(
924			'type' => 'integer',
925			'description' => 'Delay after which the results are displayed.',
926			// examples... not used
927			'default' => 2,
928			'value' => 2,
929			'source_of_value' => '',
930			'show_in_conf_sample' => false,
931		),
932		'full_text_accelerators' => array(
933			'type' => 'array',
934			'description' => 'Specifies classes to be searched at first (and the subset of data) when running the full text search.',
935			'default' => array(),
936			'value' => false,
937			'source_of_value' => '',
938			'show_in_conf_sample' => false,
939		),
940		'full_text_needle_min' => array(
941			'type' => 'integer',
942			'description' => 'Minimum size of the full text needle.',
943			'default' => 3,
944			'value' => 3,
945			'source_of_value' => '',
946			'show_in_conf_sample' => false,
947		),
948		'tracking_level_linked_set_default' => array(
949			'type' => 'integer',
950			'description' => 'Default tracking level if not explicitly set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)',
951			'default' => LINKSET_TRACKING_LIST,
952			'value' => LINKSET_TRACKING_LIST,
953			'source_of_value' => '',
954			'show_in_conf_sample' => false,
955		),
956		'tracking_level_linked_set_indirect_default' => array(
957			'type' => 'integer',
958			'description' => 'Default tracking level if not explicitly set at the attribute level, for AttributeLinkedSetIndirect',
959			'default' => LINKSET_TRACKING_ALL,
960			'value' => LINKSET_TRACKING_ALL,
961			'source_of_value' => '',
962			'show_in_conf_sample' => false,
963		),
964		'user_rights_legacy' => array(
965			'type' => 'bool',
966			'description' => 'Set to true to restore the buggy algorithm for the computation of user rights (within the same profile, ALLOW on the class itself has precedence on DENY of a parent class)',
967			'default' => false,
968			'value' => '',
969			'source_of_value' => '',
970			'show_in_conf_sample' => false,
971		),
972		'xlsx_exporter_memory_limit' => array(
973			'type' => 'string',
974			'description' => 'Memory limit to use when (interactively) exporting data to Excel',
975			'default' => '2048M', // Huuuuuuge 2GB!
976			'value' => '',
977			'source_of_value' => '',
978			'show_in_conf_sample' => false,
979		),
980		'min_reload_interval' => array(
981			'type' => 'integer',
982			'description' => 'Minimum refresh interval (seconds) for dashboards, shortcuts, etc. Even if the interval is set programmatically, it is forced to that minimum',
983			'default' => 5, // In iTop 2.0.3, this was the hardcoded value
984			'value' => '',
985			'source_of_value' => '',
986			'show_in_conf_sample' => false,
987		),
988		'relations_max_depth' => array(
989			'type' => 'integer',
990			'description' => 'Maximum number of successive levels (depth) to explore when displaying the impact/depends on relations.',
991			'default' => 20, // In iTop 2.0.3, this was the hardcoded value
992			'value' => '',
993			'source_of_value' => '',
994			'show_in_conf_sample' => false,
995		),
996		'transaction_storage' => array(
997			'type' => 'string',
998			'description' => 'The type of mechanism to use for storing the unique identifiers for transactions (Session|File).',
999			'default' => 'File',
1000			'value' => '',
1001			'source_of_value' => '',
1002			'show_in_conf_sample' => false,
1003		),
1004		'transactions_enabled' => array(
1005			'type' => 'bool',
1006			'description' => 'Whether or not the whole mechanism to prevent multiple submissions of a page is enabled.',
1007			'default' => true,
1008			'value' => '',
1009			'source_of_value' => '',
1010			'show_in_conf_sample' => false,
1011		),
1012		'log_transactions' => array(
1013			'type' => 'bool',
1014			'description' => 'Whether or not to enable the debug log for the transactions.',
1015			'default' => false,
1016			'value' => '',
1017			'source_of_value' => '',
1018			'show_in_conf_sample' => false,
1019		),
1020		'concurrent_lock_enabled' => array(
1021			'type' => 'bool',
1022			'description' => 'Whether or not to activate the locking mechanism in order to prevent concurrent edition of the same object.',
1023			'default' => false,
1024			'value' => '',
1025			'source_of_value' => '',
1026			'show_in_conf_sample' => false,
1027		),
1028		'concurrent_lock_expiration_delay' => array(
1029			'type' => 'integer',
1030			'description' => 'Delay (in seconds) for a concurrent lock to expire',
1031			'default' => 120,
1032			'value' => '',
1033			'source_of_value' => '',
1034			'show_in_conf_sample' => false,
1035		),
1036		'concurrent_lock_override_profiles' => array(
1037			'type' => 'array',
1038			'description' => 'The list of profiles allowed to "kill" a lock',
1039			'default' => array('Administrator'),
1040			'value' => '',
1041			'source_of_value' => '',
1042			'show_in_conf_sample' => false,
1043		),
1044		'html_sanitizer' => array(
1045			'type' => 'string',
1046			'description' => 'The class to use for HTML sanitization: HTMLDOMSanitizer, HTMLPurifierSanitizer or HTMLNullSanitizer',
1047			'default' => 'HTMLDOMSanitizer',
1048			'value' => '',
1049			'source_of_value' => '',
1050			'show_in_conf_sample' => false,
1051		),
1052		'inline_image_max_display_width' => array(
1053			'type' => 'integer',
1054			'description' => 'The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.',
1055			'default' => '250',
1056			'value' => '',
1057			'source_of_value' => '',
1058			'show_in_conf_sample' => true,
1059		),
1060		'inline_image_max_storage_width' => array(
1061			'type' => 'integer',
1062			'description' => 'The maximum width (in pixels) when uploading images to be used inside an HTML formatted attribute. Images larger than the given size will be downsampled before storing them in the database.',
1063			'default' => '1600',
1064			'value' => '',
1065			'source_of_value' => '',
1066			'show_in_conf_sample' => true,
1067		),
1068		'draft_attachments_lifetime' => array(
1069			'type' => 'integer',
1070			'description' => 'Lifetime (in seconds) of drafts\' attachments and inline images: after this duration, the garbage collector will delete them.',
1071			'default' => 86400,
1072			'value' => '',
1073			'source_of_value' => '',
1074			'show_in_conf_sample' => false,
1075		),
1076		'date_and_time_format' => array(
1077			'type' => 'array',
1078			'description' => 'Format for date and time display (per language)',
1079			'default' => array('default' => array('date' => 'Y-m-d', 'time' => 'H:i:s', 'date_time' => '$date $time')),
1080			'value' => false,
1081			'source_of_value' => '',
1082			'show_in_conf_sample' => true,
1083		),
1084		'breadcrumb.max_count' => array(
1085			'type' => 'integer',
1086			'description' => 'Maximum number of items kept in the history breadcrumb. Set it to 0 to entirely disable the breadcrumb.',
1087			'default' => 8,
1088			'value' => 8,
1089			'source_of_value' => '',
1090			'show_in_conf_sample' => false,
1091		),
1092		'obsolescence.show_obsolete_data' => array(
1093			'type' => 'bool',
1094			'description' => 'Default value for the user preference "show obsolete data"',
1095			'default' => false,
1096			'value' => '',
1097			'source_of_value' => '',
1098			'show_in_conf_sample' => false,
1099		),
1100		'obsolescence.date_update_interval' => array(
1101			'type' => 'integer',
1102			'description' => 'Delay in seconds between two refreshes of the obsolescence dates.',
1103			'default' => 600,
1104			'value' => 600,
1105			'source_of_value' => '',
1106			'show_in_conf_sample' => false,
1107		),
1108		'disable_attachments_download_legacy_portal' => array(
1109			'type' => 'bool',
1110			'description' => 'Disable attachments download from legacy portal',
1111			'default' => true,
1112			'value' => true,
1113			'source_of_value' => '',
1114			'show_in_conf_sample' => true,
1115		),
1116		'secure_rest_services' => array(
1117			'type' => 'bool',
1118			'description' => 'When set to true, only the users with the profile "REST Services User" are allowed to use the REST web services.',
1119			'default' => true,
1120			'value' => true,
1121			'source_of_value' => '',
1122			'show_in_conf_sample' => false,
1123		),
1124		'search_manual_submit' => array(
1125			'type' => 'array',
1126			'description' => 'Force manual submit of search all requests',
1127			'default' => false,
1128			'value' => true,
1129			'source_of_value' => '',
1130			'show_in_conf_sample' => true,
1131		),
1132		'optimize_requests_for_join_count' => array(
1133			'type' => 'bool',
1134			'description' => 'Optimize request joins to minimize the count (default is true, try to set it to false in case of performance issues)',
1135			'default' => true,
1136			'value' => true,
1137			'source_of_value' => '',
1138			'show_in_conf_sample' => true,
1139		),
1140		'high_cardinality_classes' => array(
1141			'type' => 'array',
1142			'description' => 'List of classes with high cardinality (Force manual submit of search)',
1143			'default' => array(),
1144			'value' => array(),
1145			'source_of_value' => '',
1146			'show_in_conf_sample' => true,
1147		),
1148		'newsroom_enabled' => array(
1149			'type' => 'bool',
1150			'description' => 'Whether or not the whole newsroom is enabled',
1151			'default' => true,
1152			'value' => true,
1153			'source_of_value' => '',
1154			'show_in_conf_sample' => false,
1155		),
1156		'regenerate_session_id_enabled' => array(
1157			'type' => 'bool',
1158			'description' => 'If true then session id will be regenerated on each login, to prevent session fixation.',
1159			'default' => true,
1160			'value' => true,
1161			'source_of_value' => '',
1162			'show_in_conf_sample' => false,
1163		),
1164	);
1165
1166	public function IsProperty($sPropCode)
1167	{
1168		return (array_key_exists($sPropCode, $this->m_aSettings));
1169	}
1170
1171	/**
1172	 * @return string identifier that can be used for example to name WebStorage/SessionStorage keys (they
1173	 *     are related to a whole domain, and a domain can host multiple itop)
1174	 *     Beware: do not expose server side information to the client !
1175	 */
1176	public function GetItopInstanceid()
1177	{
1178		return md5(utils::GetAbsoluteUrlAppRoot()
1179			.'==='.$this->Get('db_host')
1180			.'/'.$this->Get('db_name')
1181			.'/'.$this->Get('db_subname'));
1182	}
1183
1184	public function GetDescription($sPropCode)
1185	{
1186		return $this->m_aSettings[$sPropCode];
1187	}
1188
1189	/**
1190	 * @param string $sPropCode
1191	 * @param mixed $value
1192	 * @param string $sSourceDesc mandatory for variables with show_in_conf_sample=false
1193	 *
1194	 * @throws \CoreException
1195	 */
1196	public function Set($sPropCode, $value, $sSourceDesc = 'unknown')
1197	{
1198		$sType = $this->m_aSettings[$sPropCode]['type'];
1199		switch ($sType)
1200		{
1201			case 'bool':
1202				$value = (bool)$value;
1203				break;
1204			case 'string':
1205				$value = (string)$value;
1206				break;
1207			case 'integer':
1208				$value = (integer)$value;
1209				break;
1210			case 'float':
1211				$value = (float)$value;
1212				break;
1213			case 'array':
1214				break;
1215			default:
1216				throw new CoreException('Unknown type for setting', array('property' => $sPropCode, 'type' => $sType));
1217		}
1218		$this->m_aSettings[$sPropCode]['value'] = $value;
1219		$this->m_aSettings[$sPropCode]['source_of_value'] = $sSourceDesc;
1220
1221	}
1222
1223    /**
1224     * @param string $sPropCode
1225     *
1226     * @return mixed
1227     */
1228    public function Get($sPropCode)
1229    {
1230        return $this->m_aSettings[$sPropCode]['value'];
1231    }
1232
1233	/**
1234	 * Event log options (see LOG_... definition)
1235	 */
1236	// Those variables will be deprecated later, when the transition to ...Get('my_setting') will be done
1237	protected $m_bLogGlobal;
1238	protected $m_bLogNotification;
1239	protected $m_bLogIssue;
1240	protected $m_bLogWebService;
1241	protected $m_bQueryCacheEnabled; // private setting
1242
1243	/**
1244	 * @var integer Number of elements to be displayed when there are more than m_iMaxDisplayLimit elements
1245	 */
1246	protected $m_iMinDisplayLimit;
1247	/**
1248	 * @var integer Max number of elements before truncating the display
1249	 */
1250	protected $m_iMaxDisplayLimit;
1251
1252	/**
1253	 * @var integer Number of seconds between two reloads of the display (standard)
1254	 */
1255	protected $m_iStandardReloadInterval;
1256	/**
1257	 * @var integer Number of seconds between two reloads of the display (fast)
1258	 */
1259	protected $m_iFastReloadInterval;
1260
1261	/**
1262	 * @var boolean Whether or not a secure connection is required for using the application.
1263	 *              If set, any attempt to connect to an iTop page with http:// will be redirected
1264	 *              to https://
1265	 */
1266	protected $m_bSecureConnectionRequired;
1267
1268	/**
1269	 * @var string Langage code, default if the user language is undefined
1270	 */
1271	protected $m_sDefaultLanguage;
1272
1273	/**
1274	 * @var string Type of login process allowed: form|basic|url|external
1275	 */
1276	protected $m_sAllowedLoginTypes;
1277
1278	/**
1279	 * @var string Name of the PHP variable in which external authentication information is passed by the web server
1280	 */
1281	protected $m_sExtAuthVariable;
1282
1283	/**
1284	 * @var string Encryption key used for all attributes of type "encrypted string". Can be set to a random value
1285	 *             unless you want to import a database from another iTop instance, in which case you must use
1286	 *             the same encryption key in order to properly decode the encrypted fields
1287	 */
1288	protected $m_sEncryptionKey;
1289
1290	/**
1291	 * @var string Encryption key used for all attributes of type "encrypted string". Can be set to a random value
1292	 *             unless you want to import a database from another iTop instance, in which case you must use
1293	 *             the same encryption key in order to properly decode the encrypted fields
1294	 */
1295	protected $m_sEncryptionLibrary;
1296
1297	/**
1298	 * @var array Additional character sets to be supported by the interactive CSV import
1299	 *            'iconv_code' => 'display name'
1300	 */
1301	protected $m_aCharsets;
1302
1303    /**
1304     * Config constructor.
1305     *
1306     * @param string|null $sConfigFile
1307     * @param bool $bLoadConfig
1308     *
1309     * @throws \ConfigException
1310     * @throws \CoreException
1311     */
1312    public function __construct($sConfigFile = null, $bLoadConfig = true)
1313	{
1314		$this->m_sFile = $sConfigFile;
1315		if (is_null($sConfigFile))
1316		{
1317			$bLoadConfig = false;
1318		}
1319
1320		$this->m_aAddons = array(
1321			// Default AddOn, always present can be moved to an official iTop Module later if needed
1322			'user rights' => 'addons/userrights/userrightsprofile.class.inc.php',
1323		);
1324
1325		foreach ($this->m_aSettings as $sPropCode => $aSettingInfo)
1326		{
1327			$this->m_aSettings[$sPropCode]['value'] = $aSettingInfo['default'];
1328		}
1329
1330		$this->m_bLogGlobal = DEFAULT_LOG_GLOBAL;
1331		$this->m_bLogNotification = DEFAULT_LOG_NOTIFICATION;
1332		$this->m_bLogIssue = DEFAULT_LOG_ISSUE;
1333		$this->m_bLogWebService = DEFAULT_LOG_WEB_SERVICE;
1334		$this->m_iMinDisplayLimit = DEFAULT_MIN_DISPLAY_LIMIT;
1335		$this->m_iMaxDisplayLimit = DEFAULT_MAX_DISPLAY_LIMIT;
1336		$this->m_iStandardReloadInterval = DEFAULT_STANDARD_RELOAD_INTERVAL;
1337		$this->m_iFastReloadInterval = DEFAULT_FAST_RELOAD_INTERVAL;
1338		$this->m_bSecureConnectionRequired = DEFAULT_SECURE_CONNECTION_REQUIRED;
1339		$this->m_sDefaultLanguage = 'EN US';
1340		$this->m_sAllowedLoginTypes = DEFAULT_ALLOWED_LOGIN_TYPES;
1341		$this->m_sExtAuthVariable = DEFAULT_EXT_AUTH_VARIABLE;
1342		$this->m_aCharsets = array();
1343		$this->m_bQueryCacheEnabled = DEFAULT_QUERY_CACHE_ENABLED;
1344
1345		//define default encryption params according to php install
1346		$aEncryptParams = SimpleCrypt::GetNewDefaultParams();
1347		$this->m_sEncryptionLibrary = isset($aEncryptParams['lib']) ? $aEncryptParams['lib'] : DEFAULT_ENCRYPTION_LIB;
1348		$this->m_sEncryptionKey= isset($aEncryptParams['key']) ? $aEncryptParams['key'] : DEFAULT_ENCRYPTION_KEY;
1349
1350		$this->m_aModuleSettings = array();
1351
1352		if ($bLoadConfig)
1353		{
1354			$this->Load($sConfigFile);
1355			$this->Verify();
1356		}
1357
1358		// Application root url: set a default value, then normalize it
1359		/*
1360		 * Does not work in CLI/unattended mode
1361				$sAppRootUrl = trim($this->Get('app_root_url'));
1362				if (strlen($sAppRootUrl) == 0)
1363				{
1364					$sAppRootUrl = utils::GetDefaultUrlAppRoot();
1365				}
1366				if (substr($sAppRootUrl, -1, 1) != '/')
1367				{
1368					$sAppRootUrl .= '/';
1369				}
1370				$this->Set('app_root_url', $sAppRootUrl);
1371		 */
1372	}
1373
1374    /**
1375     * @param string $sPurpose
1376     * @param string $sFileName
1377     *
1378     * @throws \ConfigException
1379     */
1380    protected function CheckFile($sPurpose, $sFileName)
1381	{
1382		if (!file_exists($sFileName))
1383		{
1384			throw new ConfigException("Could not find $sPurpose file", array('file' => $sFileName));
1385		}
1386		if (!is_readable($sFileName))
1387		{
1388			throw new ConfigException("Could not read $sPurpose file (the file exists but cannot be read). Do you have the rights to access this file?",
1389				array('file' => $sFileName));
1390		}
1391	}
1392
1393	/**
1394	 * @param string $sConfigFile
1395	 *
1396	 * @throws \ConfigException
1397	 * @throws \CoreException
1398	 */
1399	protected function Load($sConfigFile)
1400	{
1401		$this->CheckFile('configuration', $sConfigFile);
1402
1403		$sConfigCode = trim(file_get_contents($sConfigFile));
1404
1405		// Variables created when doing an eval() on the config file
1406		/** @var array $MySettings */
1407		$MySettings = null;
1408		/** @var array $MyModuleSettings */
1409		$MyModuleSettings = null;
1410		/** @var array $MyModules */
1411		$MyModules = null;
1412
1413		// This does not work on several lines
1414		// preg_match('/^<\\?php(.*)\\?'.'>$/', $sConfigCode, $aMatches)...
1415		// So, I've implemented a solution suggested in the PHP doc (search for phpWrapper)
1416		try
1417		{
1418			ob_start();
1419			eval('?'.'>'.trim($sConfigCode));
1420			$sNoise = trim(ob_get_contents());
1421			ob_end_clean();
1422		}
1423		catch (Exception $e)
1424		{
1425			// well, never reach in case of parsing error :-(
1426			// will be improved in PHP 6 ?
1427			throw new ConfigException('Error in configuration file',
1428				array('file' => $sConfigFile, 'error' => $e->getMessage()));
1429		}
1430		catch(Error $e)
1431		{
1432		    // PHP 7
1433		    throw new ConfigException('Error in configuration file',
1434		        array('file' => $sConfigFile, 'error' => $e->getMessage().' at line '.$e->getLine()));
1435		}
1436		if (strlen($sNoise) > 0)
1437		{
1438			// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
1439			throw new ConfigException('Syntax error in configuration file',
1440				array('file' => $sConfigFile, 'error' => '<tt>'.htmlentities($sNoise, ENT_QUOTES, 'UTF-8').'</tt>'));
1441		}
1442
1443		if (!isset($MySettings) || !is_array($MySettings))
1444		{
1445			throw new ConfigException('Missing array in configuration file',
1446				array('file' => $sConfigFile, 'expected' => '$MySettings'));
1447		}
1448
1449		if (!array_key_exists('addons', $MyModules))
1450		{
1451			throw new ConfigException('Missing item in configuration file',
1452				array('file' => $sConfigFile, 'expected' => '$MyModules[\'addons\']'));
1453		}
1454		if (!array_key_exists('user rights', $MyModules['addons']))
1455		{
1456			// Add one, by default
1457			$MyModules['addons']['user rights'] = '/addons/userrights/userrightsnull.class.inc.php';
1458		}
1459
1460		$this->m_aAddons = $MyModules['addons'];
1461
1462		foreach ($MySettings as $sPropCode => $rawvalue)
1463		{
1464			if ($this->IsProperty($sPropCode))
1465			{
1466				if (is_string($rawvalue))
1467				{
1468					$value = trim($rawvalue);
1469				}
1470				else
1471				{
1472					$value = $rawvalue;
1473				}
1474				$this->Set($sPropCode, $value, $sConfigFile);
1475			}
1476		}
1477
1478		$this->m_bLogGlobal = isset($MySettings['log_global']) ? (bool)trim($MySettings['log_global']) : DEFAULT_LOG_GLOBAL;
1479		$this->m_bLogNotification = isset($MySettings['log_notification']) ? (bool)trim($MySettings['log_notification']) : DEFAULT_LOG_NOTIFICATION;
1480		$this->m_bLogIssue = isset($MySettings['log_issue']) ? (bool)trim($MySettings['log_issue']) : DEFAULT_LOG_ISSUE;
1481		$this->m_bLogWebService = isset($MySettings['log_web_service']) ? (bool)trim($MySettings['log_web_service']) : DEFAULT_LOG_WEB_SERVICE;
1482		$this->m_bQueryCacheEnabled = isset($MySettings['query_cache_enabled']) ? (bool)trim($MySettings['query_cache_enabled']) : DEFAULT_QUERY_CACHE_ENABLED;
1483
1484		$this->m_iMinDisplayLimit = isset($MySettings['min_display_limit']) ? trim($MySettings['min_display_limit']) : DEFAULT_MIN_DISPLAY_LIMIT;
1485		$this->m_iMaxDisplayLimit = isset($MySettings['max_display_limit']) ? trim($MySettings['max_display_limit']) : DEFAULT_MAX_DISPLAY_LIMIT;
1486		$this->m_iStandardReloadInterval = isset($MySettings['standard_reload_interval']) ? trim($MySettings['standard_reload_interval']) : DEFAULT_STANDARD_RELOAD_INTERVAL;
1487		$this->m_iFastReloadInterval = isset($MySettings['fast_reload_interval']) ? trim($MySettings['fast_reload_interval']) : DEFAULT_FAST_RELOAD_INTERVAL;
1488		$this->m_bSecureConnectionRequired = isset($MySettings['secure_connection_required']) ? (bool)trim($MySettings['secure_connection_required']) : DEFAULT_SECURE_CONNECTION_REQUIRED;
1489
1490		$this->m_aModuleSettings = isset($MyModuleSettings) ? $MyModuleSettings : array();
1491
1492		$this->m_sDefaultLanguage = isset($MySettings['default_language']) ? trim($MySettings['default_language']) : 'EN US';
1493		$this->m_sAllowedLoginTypes = isset($MySettings['allowed_login_types']) ? trim($MySettings['allowed_login_types']) : DEFAULT_ALLOWED_LOGIN_TYPES;
1494		$this->m_sExtAuthVariable = isset($MySettings['ext_auth_variable']) ? trim($MySettings['ext_auth_variable']) : DEFAULT_EXT_AUTH_VARIABLE;
1495		$this->m_sEncryptionKey = isset($MySettings['encryption_key']) ? trim($MySettings['encryption_key']) : $this->m_sEncryptionKey;
1496		$this->m_sEncryptionLibrary = isset($MySettings['encryption_library']) ? trim($MySettings['encryption_library']) : $this->m_sEncryptionLibrary;
1497		$this->m_aCharsets = isset($MySettings['csv_import_charsets']) ? $MySettings['csv_import_charsets'] : array();
1498	}
1499
1500	protected function Verify()
1501	{
1502		// Files are verified later on, just before using them -see MetaModel::Plugin()
1503		// (we have their final path at that point)
1504	}
1505
1506	public function GetModuleSetting($sModule, $sProperty, $defaultvalue = null)
1507	{
1508		if (isset($this->m_aModuleSettings[$sModule][$sProperty]))
1509		{
1510			return $this->m_aModuleSettings[$sModule][$sProperty];
1511		}
1512
1513		// Fall back to the predefined XML parameter, if any
1514		return $this->GetModuleParameter($sModule, $sProperty, $defaultvalue);
1515	}
1516
1517    /**
1518     * @param string $sModule
1519     * @param string $sProperty
1520     * @param mixed|null $defaultvalue
1521     *
1522     * @return mixed|null
1523     */
1524    public function GetModuleParameter($sModule, $sProperty, $defaultvalue = null)
1525	{
1526		$ret = $defaultvalue;
1527		if (class_exists('ModulesXMLParameters'))
1528		{
1529			$aAllParams = ModulesXMLParameters::GetData($sModule);
1530			if (array_key_exists($sProperty, $aAllParams))
1531			{
1532				$ret = $aAllParams[$sProperty];
1533			}
1534		}
1535
1536		return $ret;
1537	}
1538
1539	public function SetModuleSetting($sModule, $sProperty, $value)
1540	{
1541		$this->m_aModuleSettings[$sModule][$sProperty] = $value;
1542	}
1543
1544	public function GetAddons()
1545	{
1546		return $this->m_aAddons;
1547	}
1548
1549	public function SetAddons($aAddons)
1550	{
1551		$this->m_aAddons = $aAddons;
1552	}
1553
1554	/**
1555	 * @return string
1556	 *
1557	 * @deprecated 2.5 will be removed in 2.6
1558	 * @see Config::Get() as a replacement
1559	 */
1560	public function GetDBHost()
1561	{
1562		return $this->Get('db_host');
1563	}
1564
1565	/**
1566	 * @return string
1567	 *
1568	 * @deprecated 2.5 will be removed in 2.6
1569	 * @see Config::Get() as a replacement
1570	 */
1571	public function GetDBName()
1572	{
1573		return $this->Get('db_name');
1574	}
1575
1576	/**
1577	 * @return string
1578	 *
1579	 * @deprecated 2.5 will be removed in 2.6
1580	 * @see Config::Get() as a replacement
1581	 */
1582	public function GetDBSubname()
1583	{
1584		return $this->Get('db_subname');
1585	}
1586
1587	/**
1588	 * @return string
1589	 *
1590	 * @deprecated 2.5 will be removed in 2.6 N°1001 utf8mb4 switch
1591	 * @see Config::DEFAULT_CHARACTER_SET
1592	 */
1593	public function GetDBCharacterSet()
1594	{
1595		return DEFAULT_CHARACTER_SET;
1596	}
1597
1598	/**
1599	 * @return string
1600	 *
1601	 * @deprecated 2.5 will be removed in 2.6 N°1001 utf8mb4 switch
1602	 * @see Config::DEFAULT_COLLATION
1603	 */
1604	public function GetDBCollation()
1605	{
1606		return DEFAULT_COLLATION;
1607	}
1608
1609	/**
1610	 * @return string
1611	 *
1612	 * @deprecated 2.5 will be removed in 2.6
1613	 * @see Config::Get() as a replacement
1614	 */
1615	public function GetDBUser()
1616	{
1617		return $this->Get('db_user');
1618	}
1619
1620	/**
1621	 * @return string
1622	 *
1623	 * @deprecated 2.5 will be removed in 2.6
1624	 * @see Config::Get() as a replacement
1625	 */
1626	public function GetDBPwd()
1627	{
1628		return $this->Get('db_pwd');
1629	}
1630
1631	public function GetLogGlobal()
1632	{
1633		return $this->m_bLogGlobal;
1634	}
1635
1636	public function GetLogNotification()
1637	{
1638		return $this->m_bLogNotification;
1639	}
1640
1641	public function GetLogIssue()
1642	{
1643		return $this->m_bLogIssue;
1644	}
1645
1646	public function GetLogWebService()
1647	{
1648		return $this->m_bLogWebService;
1649	}
1650
1651	public function GetLogQueries()
1652	{
1653		return false;
1654	}
1655
1656	public function GetQueryCacheEnabled()
1657	{
1658		return $this->m_bQueryCacheEnabled;
1659	}
1660
1661	public function GetMinDisplayLimit()
1662	{
1663		return $this->m_iMinDisplayLimit;
1664	}
1665
1666	public function GetMaxDisplayLimit()
1667	{
1668		return $this->m_iMaxDisplayLimit;
1669	}
1670
1671	public function GetStandardReloadInterval()
1672	{
1673		return $this->m_iStandardReloadInterval;
1674	}
1675
1676	public function GetFastReloadInterval()
1677	{
1678		return $this->m_iFastReloadInterval;
1679	}
1680
1681	public function GetSecureConnectionRequired()
1682	{
1683		return $this->m_bSecureConnectionRequired;
1684	}
1685
1686	public function GetDefaultLanguage()
1687	{
1688		return $this->m_sDefaultLanguage;
1689	}
1690
1691	public function GetEncryptionKey()
1692	{
1693		return $this->m_sEncryptionKey;
1694	}
1695
1696	public function GetEncryptionLibrary()
1697	{
1698		return $this->m_sEncryptionLibrary;
1699	}
1700
1701	public function GetAllowedLoginTypes()
1702	{
1703		return explode('|', $this->m_sAllowedLoginTypes);
1704	}
1705
1706	public function GetExternalAuthenticationVariable()
1707	{
1708		return $this->m_sExtAuthVariable;
1709	}
1710
1711	public function GetCSVImportCharsets()
1712	{
1713		return $this->m_aCharsets;
1714	}
1715
1716	public function SetLogGlobal($iLogGlobal)
1717	{
1718		$this->m_iLogGlobal = $iLogGlobal;
1719	}
1720
1721	public function SetLogNotification($iLogNotification)
1722	{
1723		$this->m_iLogNotification = $iLogNotification;
1724	}
1725
1726	public function SetLogIssue($iLogIssue)
1727	{
1728		$this->m_iLogIssue = $iLogIssue;
1729	}
1730
1731	public function SetLogWebService($iLogWebService)
1732	{
1733		$this->m_iLogWebService = $iLogWebService;
1734	}
1735
1736	public function SetMinDisplayLimit($iMinDisplayLimit)
1737	{
1738		$this->m_iMinDisplayLimit = $iMinDisplayLimit;
1739	}
1740
1741	public function SetMaxDisplayLimit($iMaxDisplayLimit)
1742	{
1743		$this->m_iMaxDisplayLimit = $iMaxDisplayLimit;
1744	}
1745
1746	public function SetStandardReloadInterval($iStandardReloadInterval)
1747	{
1748		$this->m_iStandardReloadInterval = $iStandardReloadInterval;
1749	}
1750
1751	public function SetFastReloadInterval($iFastReloadInterval)
1752	{
1753		$this->m_iFastReloadInterval = $iFastReloadInterval;
1754	}
1755
1756	public function SetSecureConnectionRequired($bSecureConnectionRequired)
1757	{
1758		$this->m_bSecureConnectionRequired = $bSecureConnectionRequired;
1759	}
1760
1761	public function SetDefaultLanguage($sLanguageCode)
1762	{
1763		$this->m_sDefaultLanguage = $sLanguageCode;
1764	}
1765
1766	public function SetAllowedLoginTypes($aAllowedLoginTypes)
1767	{
1768		$this->m_sAllowedLoginTypes = implode('|', $aAllowedLoginTypes);
1769	}
1770
1771	public function SetExternalAuthenticationVariable($sExtAuthVariable)
1772	{
1773		$this->m_sExtAuthVariable = $sExtAuthVariable;
1774	}
1775
1776	public function SetEncryptionKey($sKey)
1777	{
1778		$this->m_sEncryptionKey = $sKey;
1779	}
1780
1781	public function SetCSVImportCharsets($aCharsets)
1782	{
1783		$this->m_aCharsets = $aCharsets;
1784	}
1785
1786	public function AddCSVImportCharset($sIconvCode, $sDisplayName)
1787	{
1788		$this->m_aCharsets[$sIconvCode] = $sDisplayName;
1789	}
1790
1791	public function GetLoadedFile()
1792	{
1793		if (is_null($this->m_sFile))
1794		{
1795			return '';
1796		}
1797		else
1798		{
1799			return $this->m_sFile;
1800		}
1801	}
1802
1803	/**
1804	 * Render the configuration as an associative array
1805	 *
1806	 * @return array
1807	 */
1808	public function ToArray()
1809	{
1810		$aSettings = array();
1811		foreach ($this->m_aSettings as $sPropCode => $aSettingInfo)
1812		{
1813			$aSettings[$sPropCode] = $aSettingInfo['value'];
1814		}
1815		$aSettings['log_global'] = $this->m_bLogGlobal;
1816		$aSettings['log_notification'] = $this->m_bLogNotification;
1817		$aSettings['log_issue'] = $this->m_bLogIssue;
1818		$aSettings['log_web_service'] = $this->m_bLogWebService;
1819		$aSettings['query_cache_enabled'] = $this->m_bQueryCacheEnabled;
1820		$aSettings['min_display_limit'] = $this->m_iMinDisplayLimit;
1821		$aSettings['max_display_limit'] = $this->m_iMaxDisplayLimit;
1822		$aSettings['standard_reload_interval'] = $this->m_iStandardReloadInterval;
1823		$aSettings['fast_reload_interval'] = $this->m_iFastReloadInterval;
1824		$aSettings['secure_connection_required'] = $this->m_bSecureConnectionRequired;
1825		$aSettings['default_language'] = $this->m_sDefaultLanguage;
1826		$aSettings['allowed_login_types'] = $this->m_sAllowedLoginTypes;
1827		$aSettings['ext_auth_variable'] = $this->m_sExtAuthVariable;
1828		$aSettings['encryption_key'] = $this->m_sEncryptionKey;
1829		$aSettings['encryption_library'] = $this->m_sEncryptionLibrary;
1830		$aSettings['csv_import_charsets'] = $this->m_aCharsets;
1831
1832		foreach ($this->m_aModuleSettings as $sModule => $aProperties)
1833		{
1834			foreach ($aProperties as $sProperty => $value)
1835			{
1836				$aSettings['module_settings'][$sModule][$sProperty] = $value;
1837			}
1838		}
1839		foreach ($this->m_aAddons as $sKey => $sFile)
1840		{
1841			$aSettings['addon_list'][] = $sFile;
1842		}
1843
1844		return $aSettings;
1845	}
1846
1847    /**
1848     * Write the configuration to a file (php format) that can be reloaded later
1849     * By default write to the same file that was specified when constructing the object
1850     *
1851     * @param string $sFileName string Name of the file to write to (emtpy to write to the same file)
1852     *
1853     * @return boolean True otherwise throws an Exception
1854	 *
1855     * @throws \ConfigException
1856     */
1857	public function WriteToFile($sFileName = '')
1858	{
1859		if (empty($sFileName))
1860		{
1861			$sFileName = $this->m_sFile;
1862		}
1863		$hFile = @fopen($sFileName, 'w');
1864		if ($hFile !== false)
1865		{
1866			fwrite($hFile, "<?php\n");
1867			fwrite($hFile, "\n/**\n");
1868			fwrite($hFile, " *\n");
1869			fwrite($hFile, " * Configuration file, generated by the ".ITOP_APPLICATION." configuration wizard\n");
1870			fwrite($hFile, " *\n");
1871			fwrite($hFile,
1872				" * The file is used in MetaModel::LoadConfig() which does all the necessary initialization job\n");
1873			fwrite($hFile, " *\n");
1874			fwrite($hFile, " */\n");
1875
1876			$aConfigSettings = $this->m_aSettings;
1877
1878			// Old fashioned boolean settings
1879			$aBoolValues = array(
1880				'log_global' => $this->m_bLogGlobal,
1881				'log_notification' => $this->m_bLogNotification,
1882				'log_issue' => $this->m_bLogIssue,
1883				'log_web_service' => $this->m_bLogWebService,
1884				'query_cache_enabled' => $this->m_bQueryCacheEnabled,
1885				'secure_connection_required' => $this->m_bSecureConnectionRequired,
1886			);
1887			foreach ($aBoolValues as $sKey => $bValue)
1888			{
1889				$aConfigSettings[$sKey] = array(
1890					'show_in_conf_sample' => true,
1891					'type' => 'bool',
1892					'value' => $bValue,
1893				);
1894			}
1895
1896			// Old fashioned integer settings
1897			$aIntValues = array(
1898				'fast_reload_interval' => $this->m_iFastReloadInterval,
1899				'max_display_limit' => $this->m_iMaxDisplayLimit,
1900				'min_display_limit' => $this->m_iMinDisplayLimit,
1901				'standard_reload_interval' => $this->m_iStandardReloadInterval,
1902			);
1903			foreach ($aIntValues as $sKey => $iValue)
1904			{
1905				$aConfigSettings[$sKey] = array(
1906					'show_in_conf_sample' => true,
1907					'type' => 'integer',
1908					'value' => $iValue,
1909				);
1910			}
1911
1912			// Old fashioned remaining values
1913			$aOtherValues = array(
1914				'default_language' => $this->m_sDefaultLanguage,
1915				'allowed_login_types' => $this->m_sAllowedLoginTypes,
1916				'ext_auth_variable' => $this->m_sExtAuthVariable,
1917				'encryption_key' => $this->m_sEncryptionKey,
1918				'encryption_library' => $this->m_sEncryptionLibrary,
1919				'csv_import_charsets' => $this->m_aCharsets,
1920			);
1921			foreach ($aOtherValues as $sKey => $value)
1922			{
1923				$aConfigSettings[$sKey] = array(
1924					'show_in_conf_sample' => true,
1925					'type' => is_string($value) ? 'string' : 'mixed',
1926					'value' => $value,
1927				);
1928			}
1929
1930			ksort($aConfigSettings);
1931			fwrite($hFile, "\$MySettings = array(\n");
1932			foreach ($aConfigSettings as $sPropCode => $aSettingInfo)
1933			{
1934				// Write all values that are either always visible or present in the cloned config file
1935				if ($aSettingInfo['show_in_conf_sample'] || (!empty($aSettingInfo['source_of_value']) && ($aSettingInfo['source_of_value'] != 'unknown')))
1936				{
1937					$sType = $aSettingInfo['type'];
1938					switch ($sType)
1939					{
1940						case 'bool':
1941							$sSeenAs = $aSettingInfo['value'] ? 'true' : 'false';
1942							break;
1943						default:
1944							$sSeenAs = self::PrettyVarExport($aSettingInfo['value'], "\t");
1945					}
1946					fwrite($hFile, "\n");
1947					if (isset($aSettingInfo['description']))
1948					{
1949						fwrite($hFile, "\t// $sPropCode: {$aSettingInfo['description']}\n");
1950					}
1951					if (isset($aSettingInfo['default']))
1952					{
1953						$default = $aSettingInfo['default'];
1954						if ($aSettingInfo['type'] == 'bool')
1955						{
1956							$default = $default ? 'true' : 'false';
1957						}
1958						fwrite($hFile,
1959							"\t//\tdefault: ".self::PrettyVarExport($aSettingInfo['default'], "\t//\t\t", true)."\n");
1960					}
1961					fwrite($hFile, "\t'$sPropCode' => $sSeenAs,\n");
1962				}
1963			}
1964			fwrite($hFile, ");\n");
1965
1966			fwrite($hFile, "\n");
1967			fwrite($hFile, "/**\n *\n * Modules specific settings\n *\n */\n");
1968			fwrite($hFile, "\$MyModuleSettings = array(\n");
1969			foreach ($this->m_aModuleSettings as $sModule => $aProperties)
1970			{
1971				fwrite($hFile, "\t'$sModule' => array (\n");
1972				foreach ($aProperties as $sProperty => $value)
1973				{
1974					$sNiceExport = self::PrettyVarExport($value, "\t\t");
1975					fwrite($hFile, "\t\t'$sProperty' => $sNiceExport,\n");
1976				}
1977				fwrite($hFile, "\t),\n");
1978			}
1979			fwrite($hFile, ");\n");
1980
1981			fwrite($hFile, "\n/**\n");
1982			fwrite($hFile, " *\n");
1983			fwrite($hFile, " * Data model modules to be loaded. Names are specified as relative paths\n");
1984			fwrite($hFile, " *\n");
1985			fwrite($hFile, " */\n");
1986			fwrite($hFile, "\$MyModules = array(\n");
1987			fwrite($hFile, "\t'addons' => array (\n");
1988			foreach ($this->m_aAddons as $sKey => $sFile)
1989			{
1990				fwrite($hFile, "\t\t'$sKey' => '$sFile',\n");
1991			}
1992			fwrite($hFile, "\t),\n");
1993			fwrite($hFile, ");\n");
1994			fwrite($hFile, '?'.'>'); // Avoid perturbing the syntax highlighting !
1995
1996			return fclose($hFile);
1997		}
1998		else
1999		{
2000			throw new ConfigException("Could not write to configuration file", array('file' => $sFileName));
2001		}
2002	}
2003
2004    /**
2005     * Helper function to initialize a configuration from the page arguments
2006     *
2007     * @param array $aParamValues
2008     * @param string|null $sModulesDir
2009     * @param bool $bPreserveModuleSettings
2010     *
2011     * @throws \Exception
2012     * @throws \CoreException
2013     */
2014	public function UpdateFromParams($aParamValues, $sModulesDir = null, $bPreserveModuleSettings = false)
2015	{
2016		if (isset($aParamValues['application_path']))
2017		{
2018			$this->Set('app_root_url', $aParamValues['application_path']);
2019		}
2020		if (isset($aParamValues['graphviz_path']))
2021		{
2022			$this->Set('graphviz_path', $aParamValues['graphviz_path']);
2023		}
2024		if (isset($aParamValues['mode']) && isset($aParamValues['language']))
2025		{
2026			if (($aParamValues['mode'] == 'install') || $this->GetDefaultLanguage() == '')
2027			{
2028				$this->SetDefaultLanguage($aParamValues['language']);
2029			}
2030		}
2031		if (isset($aParamValues['db_server']))
2032		{
2033			$this->Set('db_host', $aParamValues['db_server']);
2034			$this->Set('db_user', $aParamValues['db_user']);
2035			$this->Set('db_pwd', $aParamValues['db_pwd']);
2036			$sDBName = $aParamValues['db_name'];
2037			if ($sDBName == '')
2038			{
2039				// Todo - obsolete after the transition to the new setup (2.0) is complete (WARNING: used by the designer)
2040				if (isset($aParamValues['new_db_name']))
2041				{
2042					$sDBName = $aParamValues['new_db_name'];
2043				}
2044			}
2045			$this->Set('db_name', $sDBName);
2046			$this->Set('db_subname', $aParamValues['db_prefix']);
2047
2048			$bDbTlsEnabled = (bool) $aParamValues['db_tls_enabled'];
2049			if ($bDbTlsEnabled)
2050			{
2051				$this->Set('db_tls.enabled', $bDbTlsEnabled, 'UpdateFromParams');
2052			}
2053			else
2054			{
2055				// disabled : we don't want parameter in the file
2056				$this->Set('db_tls.enabled', $bDbTlsEnabled, null);
2057			}
2058			$sDbTlsCa = $bDbTlsEnabled ? $aParamValues['db_tls_ca'] : null;
2059			if (isset($sDbTlsCa) && !empty($sDbTlsCa)) {
2060				$this->Set('db_tls.ca', $sDbTlsCa, 'UpdateFromParams');
2061			} else {
2062				// empty parameter : we don't want it in the file
2063				$this->Set('db_tls.ca', null, null);
2064			}
2065		}
2066
2067		if (isset($aParamValues['selected_modules']))
2068		{
2069			$aSelectedModules = explode(',', $aParamValues['selected_modules']);
2070		}
2071		else
2072		{
2073			$aSelectedModules = null;
2074		}
2075		$this->UpdateIncludes($sModulesDir, $aSelectedModules);
2076
2077		if (isset($aParamValues['source_dir']))
2078		{
2079			$this->Set('source_dir', $aParamValues['source_dir']);
2080		}
2081	}
2082
2083	/**
2084	 * Helper function to rebuild the default configuration and the list of includes from a directory and a list of
2085	 * selected modules
2086	 *
2087	 * @param string $sModulesDir The relative path to the directory to scan for modules (typically the 'env-xxx'
2088	 *     directory resulting from the compilation)
2089	 * @param array $aSelectedModules An array of selected modules' identifiers. If null all modules found will be
2090	 *     considered as installed
2091	 *
2092	 * @throws Exception
2093	 */
2094	public function UpdateIncludes($sModulesDir, $aSelectedModules = null)
2095	{
2096		if (!is_null($sModulesDir))
2097		{
2098			// Initialize the arrays below with default values for the application...
2099			$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
2100			$aAddOns = $oEmptyConfig->GetAddOns();
2101
2102			$aModules = ModuleDiscovery::GetAvailableModules(array(APPROOT.$sModulesDir));
2103			foreach ($aModules as $sModuleId => $aModuleInfo)
2104			{
2105				list ($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
2106				if (is_null($aSelectedModules) || in_array($sModuleName, $aSelectedModules))
2107				{
2108					if (isset($aModuleInfo['settings']))
2109					{
2110						list ($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
2111						foreach ($aModuleInfo['settings'] as $sProperty => $value)
2112						{
2113							if (isset($this->m_aModuleSettings[$sName][$sProperty]))
2114							{
2115								// Do nothing keep the original value
2116							}
2117							else
2118							{
2119								$this->SetModuleSetting($sName, $sProperty, $value);
2120							}
2121						}
2122					}
2123					if (isset($aModuleInfo['installer']))
2124					{
2125						$sModuleInstallerClass = $aModuleInfo['installer'];
2126						if (!class_exists($sModuleInstallerClass))
2127						{
2128							throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not a PHP class - Module: ".$aModuleInfo['label']);
2129						}
2130						if (!is_subclass_of($sModuleInstallerClass, 'ModuleInstallerAPI'))
2131						{
2132							throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aModuleInfo['label']);
2133						}
2134						$aCallSpec = array($sModuleInstallerClass, 'BeforeWritingConfig');
2135						call_user_func_array($aCallSpec, array($this));
2136					}
2137				}
2138			}
2139			$this->SetAddOns($aAddOns);
2140		}
2141	}
2142
2143    /**
2144     * Helper: for an array of string, change the prefix when found
2145     *
2146     * @param array $aStrings
2147     * @param string $sSearchPrefix
2148     * @param string $sNewPrefix
2149     */
2150	protected static function ChangePrefix(&$aStrings, $sSearchPrefix, $sNewPrefix)
2151	{
2152		foreach ($aStrings as &$sFile)
2153		{
2154			if (substr($sFile, 0, strlen($sSearchPrefix)) == $sSearchPrefix)
2155			{
2156				$sFile = $sNewPrefix.substr($sFile, strlen($sSearchPrefix));
2157			}
2158		}
2159	}
2160
2161    /**
2162     * Obsolete: kept only for backward compatibility of the Toolkit
2163     * Quick and dirty way to clone a config file into another environment
2164     *
2165     * @param string $sSourceEnv
2166     * @param string $sTargetEnv
2167     */
2168	public function ChangeModulesPath($sSourceEnv, $sTargetEnv)
2169	{
2170		// Now does nothing since the includes are built into the environment itself
2171	}
2172
2173	/**
2174	 * Pretty format a var_export'ed value so that (if possible) the identation is preserved on every line
2175	 *
2176	 * @param mixed $value The value to export
2177	 * @param string $sIndentation The string to use to indent the text
2178	 * @param bool $bForceIndentation Forces the identation (enven if it breaks/changes an eval, for example to ouput a
2179	 *     value inside a comment)
2180	 *
2181	 * @return string The indented export string
2182	 */
2183	protected static function PrettyVarExport($value, $sIndentation, $bForceIndentation = false)
2184	{
2185		$sExport = var_export($value, true);
2186		$sNiceExport = str_replace(array("\r\n", "\n", "\r"), "\n".$sIndentation, trim($sExport));
2187		if (!$bForceIndentation)
2188		{
2189			/** @var array $aImported */
2190			$aImported = null;
2191			eval('$aImported='.$sNiceExport.';');
2192			// Check if adding the identations at the beginning of each line
2193			// did not modify the values (in case of a string containing a line break)
2194			if ($aImported != $value)
2195			{
2196				$sNiceExport = $sExport;
2197			}
2198		}
2199
2200		return $sNiceExport;
2201	}
2202
2203}
2204