Save config.php in Settings

Egg and hen problem solved :)
* Test database connection on save settings -> prevents from making website unusable if connection is wrong
* Test server_path -> same
There is no config.php anymore, just config.local.php, which can be edited manually and also from admin panel
This commit is contained in:
slawkens
2023-07-21 11:38:52 +02:00
parent 399f263b42
commit 1543dd864e
13 changed files with 336 additions and 74 deletions

View File

@@ -45,6 +45,7 @@ $deprecatedConfig = [
'signature_type',
'signature_cache_time',
'signature_browser_cache',
'gifts_system',
];
foreach ($deprecatedConfig as $key => $value) {

127
system/config.php Normal file
View File

@@ -0,0 +1,127 @@
<?php
/**
* This is MyAAC's Main Configuration file
*
* All the default values are kept here, you should not modify it but use
* a config.local.php file instead to override the settings from here.
*
* This is a piece of PHP code so PHP syntax applies!
* For boolean values please use true/false.
*
* Minimally 'server_path' directive have to be filled, other options are optional.
*
* @package MyAAC
* @author Slawkens <slawkens@gmail.com>
* @copyright 2019 MyAAC
* @link https://my-aac.org
*/
$config = array(
// directories & files
'server_path' => '', // path to the server directory (same directory where config file is located)
/**
* Environment Setting
*
* if you use this script on your live server - set to 'prod' (production)
* if you want to test and debug the script locally, or develop plugins, set to 'dev' (development)
* WARNING: on 'dev' cache is disabled, so site will be significantly slower !!!
* WARNING2: on 'dev' all PHP errors/warnings are displayed
* Recommended: 'prod' cause of speed (page load time is better)
*/
'env' => 'prod', // 'prod' for production and 'dev' for development
'gzip_output' => false, // gzip page content before sending it to the browser, uses less bandwidth but more cpu cycles
// cache system. by default file cache is used
'cache_engine' => 'auto', // apc, apcu, eaccelerator, xcache, file, auto, or blank to disable.
'cache_prefix' => 'myaac_', // have to be unique if running more MyAAC instances on the same server (except file system cache)
// database details (leave blank for auto detect from config.lua)
'database_host' => '',
'database_port' => '', // leave blank to default 3306
'database_user' => '',
'database_password' => '',
'database_name' => '',
'database_log' => false, // should database queries be logged and saved into system/logs/database.log?
'database_socket' => '', // set if you want to connect to database through socket (example: /var/run/mysqld/mysqld.sock)
'database_persistent' => false, // use database permanent connection (like server), may speed up your site
'account_mail_block_plus_sign' => true, // block email with '+' signs like test+box@gmail.com (help protect against spamming accounts)
'account_change_character_name' => false, // can user change their character name for premium points?
'account_change_character_name_points' => 30, // cost of name change
'account_change_character_sex' => false, // can user change their character sex for premium points?
'account_change_character_sex_points' => 30, // cost of sex change
'characters_per_account' => 10, // max. number of characters per account
//
'generate_new_reckey' => true, // let player generate new recovery key, he will receive e-mail with new rec key (not display on page, hacker can't generate rec key)
'generate_new_reckey_price' => 20, // price for new recovery key
'send_mail_when_change_password' => true, // send e-mail with new password when change password to account
'send_mail_when_generate_reckey' => true, // send e-mail with rec key (key is displayed on page anyway when generate)
// new character config
'character_samples' => array( // vocations, format: ID_of_vocation => 'Name of Character to copy'
//0 => 'Rook Sample',
1 => 'Sorcerer Sample',
2 => 'Druid Sample',
3 => 'Paladin Sample',
4 => 'Knight Sample'
),
'use_character_sample_skills' => false,
// it must show limited number of players after using search in character page
'characters_search_limit' => 15,
// town list used when creating character
// won't be displayed if there is only one item (rookgaard for example)
'character_towns' => array(1),
// characters length
// This is the minimum and the maximum length that a player can create a character. It is highly recommend the maximum length to be 21.
'character_name_min_length' => 4,
'character_name_max_length' => 21,
'character_name_npc_check' => true,
// list of towns
// if you use TFS 1.3 with support for 'towns' table in database, then you can ignore this - it will be configured automatically (from MySQL database - Table - towns)
// otherwise it will try to load from your .OTBM map file
// if you don't see towns on website, then you need to fill this out
'towns' => array(
0 => 'No town',
1 => 'Sample town'
),
// characters page
'characters' => array( // what things to display on character view page (true/false in each option)
'level' => true,
'experience' => false,
'magic_level' => false,
'balance' => false,
'marriage_info' => true, // only 0.3
'outfit' => true,
'creation_date' => true,
'quests' => true,
'skills' => true,
'equipment' => true,
'frags' => false,
'deleted' => false, // should deleted characters from same account be still listed on the list of characters? When enabled it will show that character is "[DELETED]"
),
'quests' => array(
//'Some Quest' => 123,
//'Some Quest Two' => 456,
), // quests list (displayed in character view), name => storage
// gifts/shop system
'gifts_system' => false,
// last kills
'last_kills_limit' => 50, // max. number of deaths shown on the last kills page
// other
'email_lai_sec_interval' => 60, // time in seconds between e-mails to one account from lost account interface, block spam
'google_analytics_id' => '', // e.g.: UA-XXXXXXX-X
'npc' => array()
);

View File

@@ -9,7 +9,11 @@
*/
defined('MYAAC') or die('Direct access not allowed!');
if(!isset($config['database_user'][0], $config['database_password'][0], $config['database_name'][0]))
if (!isset($config['database_overwrite'])) {
$config['database_overwrite'] = false;
}
if(!$config['database_overwrite'] && !isset($config['database_user'][0], $config['database_password'][0], $config['database_name'][0]))
{
if(isset($config['lua']['sqlType'])) {// tfs 0.3
if(isset($config['lua']['mysqlHost'])) {// tfs 0.2
@@ -116,4 +120,4 @@ catch(PDOException $error) {
'<li>MySQL is not configured propertly in <i>config.lua</i>.</li>' .
'<li>MySQL server is not running.</li>' .
'</ul>' . $error->getMessage());
}
}

View File

@@ -9,11 +9,6 @@
*/
defined('MYAAC') or die('Direct access not allowed!');
// load configuration
require_once BASE . 'config.php';
if(file_exists(BASE . 'config.local.php')) // user customizations
require BASE . 'config.local.php';
if(!isset($config['installed']) || !$config['installed']) {
throw new RuntimeException('MyAAC has not been installed yet or there was error during installation. Please install again.');
}
@@ -22,12 +17,16 @@ if(config('env') === 'dev') {
require SYSTEM . 'exception.php';
}
if(empty($config['server_path'])) {
throw new RuntimeException('Server Path has been not set. Go to config.php and set it.');
}
// take care of trailing slash at the end
if($config['server_path'][strlen($config['server_path']) - 1] !== '/')
$config['server_path'] .= '/';
// enable gzip compression if supported by the browser
if($config['gzip_output'] && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false && function_exists('ob_gzhandler'))
if(isset($config['gzip_output']) && $config['gzip_output'] && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false && function_exists('ob_gzhandler'))
ob_start('ob_gzhandler');
// cache

View File

@@ -53,11 +53,22 @@ class Settings implements ArrayAccess
}
}
public function save($pluginName, $settings) {
public function save($pluginName, $values) {
global $db;
if (!isset($this->settingsFile[$pluginName])) {
throw new RuntimeException('Error on save settings: plugin does not exist');
}
$settings = $this->settingsFile[$pluginName];
if (isset($settings['callbacks']['beforeSave'])) {
if (!$settings['callbacks']['beforeSave']($settings, $values)) {
return false;
}
}
$db->query('DELETE FROM `' . TABLE_PREFIX . 'settings` WHERE `name` = ' . $db->quote($pluginName) . ';');
foreach ($settings as $key => $value) {
foreach ($values as $key => $value) {
try {
$db->insert(TABLE_PREFIX . 'settings', ['name' => $pluginName, 'key' => $key, 'value' => $value]);
} catch (PDOException $error) {
@@ -69,6 +80,8 @@ class Settings implements ArrayAccess
if ($cache->enabled()) {
$cache->delete('settings');
}
return true;
}
public function updateInDatabase($pluginName, $key, $value)
@@ -103,6 +116,18 @@ class Settings implements ArrayAccess
}
}
$config = [];
require BASE . 'config.local.php';
foreach ($config as $key => $value) {
if (is_bool($value)) {
$settingsDb[$key] = $value ? 'true' : 'false';
}
else {
$settingsDb[$key] = (string)$value;
}
}
$javascript = '';
ob_start();
?>
@@ -261,7 +286,7 @@ class Settings implements ArrayAccess
echo '<select class="form-control" name="settings[' . $key . ']" id="' . $key . '">';
foreach ($setting['options'] as $value => $option) {
$compareTo = (isset($settingsDb[$key]) ? $settingsDb[$key] : (isset($setting['default']) ? $setting['default'] : ''));
$compareTo = ($settingsDb[$key] ?? ($setting['default'] ?? ''));
if($value === 'true') {
$selected = $compareTo === true;
}
@@ -283,9 +308,18 @@ class Settings implements ArrayAccess
<td>
<div class="well">
<?php
echo $setting['desc'];
echo ($setting['desc'] ?? '');
echo '<br/>';
echo '<strong>Default:</strong> ';
if (empty($setting['default'])) {
if ($setting['type'] === 'boolean') {
$setting['default'] = true;
}
else {
$setting['default'] = '';
}
}
if ($setting['type'] === 'boolean') {
echo ($setting['default'] ? 'Yes' : 'No');
}
@@ -293,7 +327,9 @@ class Settings implements ArrayAccess
echo $setting['default'];
}
else if ($setting['type'] === 'options') {
echo $setting['options'][$setting['default']];
if (!empty($setting['default'])) {
echo $setting['options'][$setting['default']];
}
}
?>
</div>
@@ -374,7 +410,7 @@ class Settings implements ArrayAccess
return;
}
unset($this->settingsFile[$pluginKeyName][$key]);
unset($this->settingsFile[$pluginKeyName]['settings'][$key]);
unset($this->settingsDatabase[$pluginKeyName][$key]);
$this->deleteFromDatabase($pluginKeyName, $key);
}
@@ -402,12 +438,12 @@ class Settings implements ArrayAccess
// return specified plugin settings (all)
if(!isset($key)) {
return $this->settingsFile[$pluginKeyName];
return $this->settingsFile[$pluginKeyName]['settings'];
}
$ret = [];
if(isset($this->settingsFile[$pluginKeyName][$key])) {
$ret = $this->settingsFile[$pluginKeyName][$key];
if(isset($this->settingsFile[$pluginKeyName]['settings'][$key])) {
$ret = $this->settingsFile[$pluginKeyName]['settings'][$key];
}
if(isset($this->settingsDatabase[$pluginKeyName][$key])) {
@@ -416,7 +452,7 @@ class Settings implements ArrayAccess
$ret['value'] = $value;
}
else {
$ret['value'] = $this->settingsFile[$pluginKeyName][$key]['default'];
$ret['value'] = $this->settingsFile[$pluginKeyName]['settings'][$key]['default'];
}
if(isset($ret['type'])) {
@@ -485,8 +521,67 @@ class Settings implements ArrayAccess
throw new \RuntimeException('Failed to load settings file for plugin: ' . $pluginKeyName);
}
$tmp = require $settingsFilePath;
$this->settingsFile[$pluginKeyName] = $tmp['settings'];
$this->settingsFile[$pluginKeyName] = require $settingsFilePath;
}
}
public static function saveConfig($config, $filename, &$content = '')
{
$content = "<?php" . PHP_EOL .
"\$config['installed'] = true;" . PHP_EOL;
foreach ($config as $key => $value) {
$content .= "\$config['$key'] = ";
$content .= var_export($value, true);
$content .= ';' . PHP_EOL;
}
$success = file_put_contents($filename, $content);
// we saved new config.php, need to revalidate cache (only if opcache is enabled)
if (function_exists('opcache_invalidate')) {
opcache_invalidate($filename);
}
return $success;
}
public static function testDatabaseConnection($config): bool
{
$user = null;
$password = null;
$dns = [];
if( isset($config['database_name']) ) {
$dns[] = 'dbname=' . $config['database_name'];
}
if( isset($config['database_user']) ) {
$user = $config['database_user'];
}
if( isset($config['database_password']) ) {
$password = $config['database_password'];
}
if( isset($config['database_host']) ) {
$dns[] = 'host=' . $config['database_host'];
}
if( isset($config['database_port']) ) {
$dns[] = 'port=' . $config['database_port'];
}
try {
$connectionTest = new PDO('mysql:' . implode(';', $dns), $user, $password);
$connectionTest->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $error) {
error('MySQL connection failed. Settings has been reverted.');
error($error->getMessage());
return false;
}
return true;
}
}

View File

@@ -18,6 +18,47 @@ return [
'type' => 'section',
'title' => 'General'
],
'env' => [
'name' => 'App Environment',
'type' => 'options',
'options' => ['prod' => 'Production', 'dev' => 'Development'],
'desc' => 'if you use this script on your live server - set production<br/>
* if you want to test and debug the script locally, or develop plugins, set to development<br/>
* WARNING: on "development" cache is disabled, so site will be significantly slower !!!<br/>
* WARNING2: on "development" all PHP errors/warnings are displayed<br/>
* Recommended: "production" cause of speed (page load time is better)',
'default' => 'prod',
'is_config' => true,
],
'server_path' => [
'name' => 'Server Path',
'type' => 'text',
'desc' => 'Path to the server directory (same directory where config file is located)',
'default' => '',
'is_config' => true,
],
'gzip_output' => [
'name' => 'gzip Output',
'type' => 'boolean',
'desc' => 'gzip page content before sending it to the browser, uses less bandwidth but more cpu cycles',
'default' => false,
'is_config' => true,
],
'cache_engine' => [
'name' => 'Cache Engine',
'type' => 'options',
'options' => ['auto' => 'Auto', 'file' => 'Files', 'apc' => 'APC', 'apcu' => 'APCu', 'eaccelerator' => 'eAccelerator', 'disable' => 'Disable'],
'desc' => 'Auto is most reasonable. It will detect the best cache engine',
'default' => 'auto',
'is_config' => true,
],
'cache_prefix' => [
'name' => 'Cache Prefix',
'type' => 'text',
'desc' => 'Have to be unique if running more MyAAC instances on the same server (except file system cache)',
'default' => 'myaac_' . generateRandomString(8, true, false, true),
'is_config' => true,
],
'date_timezone' => [
'name' => 'Date Timezone',
'type' => 'options',
@@ -47,6 +88,82 @@ return [
},
],
],
[
'type' => 'section',
'title' => 'Database',
],
'database_overwrite' => [
'name' => 'Database Manual',
'type' => 'boolean',
'desc' => 'Manual database configuration. Enable if you want to manually enter database details. If set to no - it will get from config.lua',
'default' => false,
'is_config' => true,
],
'database_host' => [
'name' => 'Database Host',
'type' => 'text',
'default' => '127.0.0.1',
'show_if' => [
'database_overwrite', '=', 'true'
],
'is_config' => true,
],
'database_port' => [
'name' => 'Database Port',
'type' => 'number',
'default' => 3306,
'show_if' => [
'database_overwrite', '=', 'true'
],
'is_config' => true,
],
'database_user' => [
'name' => 'Database User',
'type' => 'text',
'show_if' => [
'database_overwrite', '=', 'true'
],
'is_config' => true,
],
'database_password' => [
'name' => 'Database Password',
'type' => 'text',
'show_if' => [
'database_overwrite', '=', 'true'
],
'is_config' => true,
],
'database_name' => [
'name' => 'Database Name',
'type' => 'text',
'show_if' => [
'database_overwrite', '=', 'true'
],
'is_config' => true,
],
'database_socket' => [
'name' => 'Database Socket',
'desc' => 'Set if you want to connect to database through socket (example: /var/run/mysqld/mysqld.sock)',
'type' => 'text',
'show_if' => [
'database_overwrite', '=', 'true'
],
'is_config' => true,
],
'database_log' => [
'name' => 'Database Log',
'desc' => 'Should database queries be logged and saved into system/logs/database.log?',
'type' => 'boolean',
'default' => false,
'is_config' => true,
],
'database_persistent' => [
'name' => 'Database Persistent Connection',
'desc' => 'Use database permanent connection (like server), may speed up your site',
'type' => 'boolean',
'default' => false,
'is_config' => true,
],
[
'type' => 'section',
'title' => 'Template'
@@ -814,6 +931,16 @@ Sent by MyAAC,<br/>
'default' => 20,
'desc' => '',
],
[
'type' => 'section',
'title' => 'Gifts/shop system'
],
'gifts_system' => [
'name' => 'Enable gifts system',
'desc' => 'Plugin needs to be installed',
'type' => 'boolean',
'default' => false,
],
[
'type' => 'section',
'title' => 'Experience Table Page'
@@ -1038,4 +1165,62 @@ Sent by MyAAC,<br/>
],
],
],
'callbacks' => [
'beforeSave' => function(&$settings, &$values) {
global $config;
$configToSave = [];
$server_path = '';
$database = [];
foreach ($settings['settings'] as $key => $value) {
if (isset($value['is_config']) && getBoolean($value['is_config'])) {
if ($value['type'] === 'boolean') {
$values[$key] = ($values[$key] === 'true');
}
elseif ($value['type'] === 'number') {
$values[$key] = (int)$values[$key];
}
//elseif ($value['type'] === 'options') {
//
//}
$configToSave[$key] = $values[$key];
if ($key == 'server_path') {
$server_path = $values[$key];
}
elseif (strpos($key, 'database_') !== false) {
$database[$key] = $values[$key];
}
unset($settings[$key]);
unset($values[$key]);
}
}
if($server_path[strlen($server_path) - 1] != '/')
$server_path .= '/';
// test config.lua existence
// if fail - revert the setting and inform the user
if (!file_exists($server_path . 'config.lua')) {
error('Server Path is invalid - cannot find config.lua in the directory. Setting have been reverted.');
$configToSave['server_path'] = $config['server_path'];
}
// test database connection
// if fail - revert the setting and inform the user
if ($database['database_overwrite'] && !Settings::testDatabaseConnection($database)) {
foreach ($database as $key => $value) {
if (!in_array($key, ['database_log', 'database_persistent'])) { // ignore these two
$configToSave[$key] = $config[$key];
}
}
}
return Settings::saveConfig($configToSave, BASE . 'config.local.php');
},
],
];