3
* Xibo - Digital Signage - http://www.xibo.org.uk
4
* Copyright (C) 2006-2015 Daniel Garner
6
* This file (Config.php) is part of Xibo.
8
* Xibo is free software: you can redistribute it and/or modify
9
* it under the terms of the GNU Affero General Public License as published by
10
* the Free Software Foundation, either version 3 of the License, or
13
* Xibo is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU Affero General Public License for more details.
18
* You should have received a copy of the GNU Affero General Public License
19
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
21
namespace Xibo\Service;
23
use Stash\Interfaces\PoolInterface;
24
use Xibo\Exception\ConfigurationException;
25
use Xibo\Helper\Environment;
26
use Xibo\Storage\StorageServiceInterface;
30
* @package Xibo\Service
32
class ConfigService implements ConfigServiceInterface
35
* @var StorageServiceInterface
44
/** @var string Setting Cache Key */
45
private $settingCacheKey = 'settings';
47
/** @var bool Has the settings cache been dropped this request? */
48
private $settingsCacheDropped = false;
51
private $settings = null;
58
public $envTested = false;
59
public $envFault = false;
60
public $envWarning = false;
66
public static $dbConfig = [];
71
public $middleware = null;
72
public $logHandlers = null;
73
public $logProcessors = null;
74
public $authentication = null;
75
public $samlSettings = null;
76
public $cacheDrivers = null;
77
public $cacheNamespace = 'Xibo';
80
* Theme Specific Config
83
public $themeConfig = [];
84
/** @var bool Has a theme been loaded? */
85
private $themeLoaded = false;
90
public function setDependencies($store, $rootUri)
93
throw new \RuntimeException('ConfigService setDependencies called with null store');
96
throw new \RuntimeException('ConfigService setDependencies called with null rootUri');
98
$this->store = $store;
99
$this->rootUri = $rootUri;
105
public function setPool($pool)
112
* @return \Stash\Interfaces\PoolInterface
114
private function getPool()
121
* @return StorageServiceInterface
123
protected function getStore()
125
if ($this->store == null)
126
throw new \RuntimeException('Config Service called before setDependencies');
134
public function getDatabaseConfig()
136
return self::$dbConfig;
143
public function rootUri()
145
if ($this->rootUri == null)
146
throw new \RuntimeException('Config Service called before setDependencies');
148
return $this->rootUri;
154
public function getCacheDrivers()
156
return $this->cacheDrivers;
162
public function getCacheNamespace()
164
return $this->cacheNamespace;
168
* Loads the settings from file.
169
* DO NOT CALL ANY STORE() METHODS IN HERE
170
* @param string $settings
171
* @return ConfigServiceInterface
173
public static function Load($settings)
175
$config = new ConfigService();
177
// Include the provided settings file.
180
// Create a DB config
184
'password' => $dbpass,
188
// Pull in other settings
191
if (isset($logHandlers))
192
$config->logHandlers = $logHandlers;
195
if (isset($logProcessors))
196
$config->logProcessors = $logProcessors;
199
if (isset($middleware))
200
$config->middleware = $middleware;
203
if (isset($authentication))
204
$config->authentication = $authentication;
207
if (isset($samlSettings))
208
$config->samlSettings = $samlSettings;
211
if (isset($cacheDrivers))
212
$config->cacheDrivers = $cacheDrivers;
214
if (isset($cacheNamespace))
215
$config->cacheNamespace = $cacheNamespace;
217
// Set this as the global config
223
* @param string[Optional] $themeName
224
* @throws ConfigurationException
226
public function loadTheme($themeName = null)
228
// What is the currently selected theme?
229
$globalTheme = ($themeName == NULL) ? $this->GetSetting('GLOBAL_THEME_NAME', 'default') : $themeName;
231
// Is this theme valid?
232
$systemTheme = (is_dir(PROJECT_ROOT . '/web/theme/' . $globalTheme) && file_exists(PROJECT_ROOT . '/web/theme/' . $globalTheme . '/config.php'));
233
$customTheme = (is_dir(PROJECT_ROOT . '/web/theme/custom/' . $globalTheme) && file_exists(PROJECT_ROOT . '/web/theme/custom/' . $globalTheme . '/config.php'));
236
require(PROJECT_ROOT . '/web/theme/' . $globalTheme . '/config.php');
237
$themeFolder = 'theme/' . $globalTheme . '/';
238
} elseif ($customTheme) {
239
require(PROJECT_ROOT . '/web/theme/custom/' . $globalTheme . '/config.php');
240
$themeFolder = 'theme/custom/' . $globalTheme . '/';
242
throw new ConfigurationException(__('The theme "%s" does not exist', $globalTheme));
244
$this->themeLoaded = true;
245
$this->themeConfig = $config;
246
$this->themeConfig['themeCode'] = $globalTheme;
247
$this->themeConfig['themeFolder'] = $themeFolder;
251
* Get Theme Specific Settings
252
* @param null $settingName
253
* @param null $default
256
public function getThemeConfig($settingName = null, $default = null)
258
if ($settingName == null)
259
return $this->themeConfig;
261
if (isset($this->themeConfig[$settingName]))
262
return $this->themeConfig[$settingName];
273
public function uri($uri, $local = false)
275
$rootUri = ($local) ? '' : $this->rootUri();
277
if (!$this->themeLoaded)
278
return $rootUri . 'theme/default/' . $uri;
280
// Serve the appropriate theme file
281
if (is_dir(PROJECT_ROOT . '/web/' . $this->themeConfig['themeFolder'] . $uri)) {
282
return $rootUri . $this->themeConfig['themeFolder'] . $uri;
284
else if (file_exists(PROJECT_ROOT . '/web/' . $this->themeConfig['themeFolder'] . $uri)) {
285
return $rootUri . $this->themeConfig['themeFolder'] . $uri;
288
return $rootUri . 'theme/default/' . $uri;
293
public function getSettings()
297
if ($this->settings === null) {
298
// We need to load in our settings
299
if ($this->getPool() !== null) {
301
$item = $this->getPool()->getItem($this->settingCacheKey);
303
$data = $item->get();
306
$this->settings = $data;
309
// Are we still null?
310
if ($this->settings === null) {
311
// Load from the database
312
$results = $this->getStore()->select('SELECT `setting`, `value` FROM `setting`', []);
314
foreach ($results as $setting) {
315
$this->settings[$setting['setting']] = $setting['value'];
320
// We should have our settings by now, so cache them if we can/need to
321
if ($item !== null && $item->isMiss()) {
322
$item->set($this->settings);
324
// Do we have an elevated log level request? If so, then expire the cache sooner
325
if (isset($this->settings['ELEVATE_LOG_UNTIL']) && intval($this->settings['ELEVATE_LOG_UNTIL']) > time())
326
$item->expiresAfter(intval($this->settings['ELEVATE_LOG_UNTIL']));
328
$item->expiresAfter(60 * 5);
330
$this->getPool()->saveDeferred($item);
333
return $this->settings;
337
public function GetSetting($setting, $default = NULL)
339
$this->getSettings();
341
return (isset($this->settings[$setting])) ? $this->settings[$setting] : $default;
345
public function ChangeSetting($setting, $value)
347
$this->getSettings();
349
if (isset($this->settings[$setting])) {
350
// Update in memory cache
351
$this->settings[$setting] = $value;
353
// Update in database
354
$this->getStore()->update('UPDATE `setting` SET `value` = :value WHERE `setting` = :setting', [
355
'setting' => $setting, 'value' => $value
358
// Drop the cache if we've not already done so this time around
359
if (!$this->settingsCacheDropped && $this->getPool() !== null) {
360
$this->getPool()->deleteItem($this->settingCacheKey);
361
$this->settingsCacheDropped = true;
367
* Defines the Version and returns it
368
* @param $object string[optional]
369
* @return array|string
372
public function Version($object = '')
376
$sth = $this->getStore()->getConnection()->prepare('SELECT app_ver, XlfVersion, XmdsVersion, DBVersion FROM version');
379
if (!$row = $sth->fetch(\PDO::FETCH_ASSOC))
380
throw new \Exception('No results returned');
382
$appVer = $row['app_ver'];
383
$dbVer = intval($row['DBVersion']);
385
if (!defined('VERSION'))
386
define('VERSION', $appVer);
388
if (!defined('DBVERSION'))
389
define('DBVERSION', $dbVer);
392
return $row[$object];
395
} catch (\Exception $e) {
396
throw new \Exception(__('No Version information - please contact technical support'));
401
* Is an upgrade pending?
404
public function isUpgradePending()
406
return DBVERSION < Environment::$WEBSITE_VERSION;
410
* Should the host be considered a proxy exception
414
public function isProxyException($host)
416
$proxyExceptions = $this->GetSetting('PROXY_EXCEPTIONS');
418
// If empty, cannot be an exception
419
if (empty($proxyExceptions))
423
if (stripos($host, $proxyExceptions) !== false)
427
$parsedHost = parse_url($host, PHP_URL_HOST);
429
// Kick out extremely malformed hosts
430
if ($parsedHost === false)
433
// Go through each exception and test against the host
434
foreach (explode(',', $proxyExceptions) as $proxyException) {
435
if (stripos($parsedHost, $proxyException) !== false)
439
// If we've got here without returning, then we aren't an exception
444
* Get Proxy Configuration
445
* @param array $httpOptions
448
public function getGuzzleProxy($httpOptions = [])
451
if ($this->GetSetting('PROXY_HOST') != '') {
453
$proxy = $this->GetSetting('PROXY_HOST') . ':' . $this->GetSetting('PROXY_PORT');
455
if ($this->GetSetting('PROXY_AUTH') != '') {
456
$scheme = explode('://', $proxy);
458
$proxy = $scheme[0] . $this->GetSetting('PROXY_AUTH') . '@' . $scheme[1];
461
$httpOptions['proxy'] = [
466
if ($this->GetSetting('PROXY_EXCEPTIONS') != '') {
467
$httpOptions['proxy']['no'] = explode(',', $this->GetSetting('PROXY_EXCEPTIONS'));
474
private function testItem(&$results, $item, $result, $advice, $fault = true)
476
// 1=OK, 0=Failure, 2=Warning
477
$status = ($result) ? 1 : (($fault) ? 0 : 2);
480
if (!$result && $fault)
481
$this->envFault = true;
484
if (!$result && !$fault)
485
$this->envWarning = true;
495
* Checks the Environment and Determines if it is suitable
498
public function CheckEnvironment()
502
$this->testItem($rows, __('PHP Version'),
503
Environment::checkPHP(),
504
sprintf(__("PHP version %s or later required."), Environment::$VERSION_REQUIRED) . ' Detected ' . phpversion()
507
$this->testItem($rows, __('File System Permissions'),
508
Environment::checkFsPermissions(),
509
__('Write permissions are required for web/settings.php and cache/')
512
$this->testItem($rows, __('MySQL database (PDO MySql)'),
513
Environment::checkPDO(),
514
__('PDO support with MySQL drivers must be enabled in PHP.')
517
$this->testItem($rows, __('JSON Extension'),
518
Environment::checkJson(),
519
__('PHP JSON extension required to function.')
522
$this->testItem($rows, __('SOAP Extension'),
523
Environment::checkSoap(),
524
__('PHP SOAP extension required to function.')
527
$this->testItem($rows, __('GD Extension'),
528
Environment::checkGd(),
529
__('PHP GD extension required to function.')
532
$this->testItem($rows, __('Session'),
533
Environment::checkGd(),
534
__('PHP session support required to function.')
537
$this->testItem($rows, __('FileInfo'),
538
Environment::checkFileInfo(),
539
__('Requires PHP FileInfo support to function. If you are on Windows you need to enable the php_fileinfo.dll in your php.ini file.')
542
$this->testItem($rows, __('PCRE'),
543
Environment::checkPCRE(),
544
__('PHP PCRE support to function.')
547
$this->testItem($rows, __('Gettext'),
548
Environment::checkPCRE(),
549
__('PHP Gettext support to function.')
552
$this->testItem($rows, __('DOM Extension'),
553
Environment::checkDom(),
554
__('PHP DOM core functionality enabled.')
557
$this->testItem($rows, __('DOM XML Extension'),
558
Environment::checkDomXml(),
559
__('PHP DOM XML extension to function.')
562
$this->testItem($rows, __('Mcrypt Extension'),
563
Environment::checkMcrypt(),
564
__('PHP Mcrypt extension to function.')
567
$this->testItem($rows, __('Allow PHP to open external URLs'),
568
Environment::checkAllowUrlFopen(),
569
__('You must have allow_url_fopen = On in your PHP.ini file for RSS Feeds / Anonymous statistics gathering to function.'),
573
$this->testItem($rows, __('DateTimeZone'),
574
Environment::checkTimezoneIdentifiers(),
575
__('This enables us to get a list of time zones supported by the hosting server.'),
579
$this->testItem($rows, __('ZIP'),
580
Environment::checkZip(),
581
__('This enables import / export of layouts.')
584
$advice = __('Support for uploading large files is recommended.');
585
$advice .= __('We suggest setting your PHP post_max_size and upload_max_filesize to at least 128M, and also increasing your max_execution_time to at least 120 seconds.');
587
$this->testItem($rows, __('Large File Uploads'),
588
Environment::checkPHPUploads(),
593
$this->testItem($rows, __('cURL'),
594
Environment::checkCurlInstalled(),
595
__('cURL is used to fetch data from the Internet or Local Network')
598
$this->testItem($rows, __('ZeroMQ'),
599
Environment::checkZmq(),
600
__('ZeroMQ is used to send messages to XMR which allows push communications with player'),
604
$this->testItem($rows, __('OpenSSL'),
605
Environment::checkOpenSsl(),
606
__('OpenSSL is used to seal and verify messages sent to XMR'),
610
$this->testItem($rows, __('SimpleXML'),
611
Environment::checkSimpleXml(),
612
__('SimpleXML is used to parse RSS feeds and other XML data sources')
615
$this->envTested = true;
621
* Is there an environment fault
624
public function EnvironmentFault()
626
if (!$this->envTested) {
627
$this->checkEnvironment();
630
return $this->envFault;
634
* Is there an environment warning
637
public function EnvironmentWarning()
639
if (!$this->envTested) {
640
$this->checkEnvironment();
643
return $this->envWarning;
647
* Check binlog format
650
public function checkBinLogEnabled()
652
//TODO: move this into storage interface
653
$results = $this->getStore()->select('show variables like \'log_bin\'', []);
655
if (count($results) <= 0)
658
return ($results[0]['Value'] != 'OFF');
662
* Check binlog format
665
public function checkBinLogFormat()
667
//TODO: move this into storage interface
668
$results = $this->getStore()->select('show variables like \'binlog_format\'', []);
670
if (count($results) <= 0)
673
return ($results[0]['Value'] != 'STATEMENT');