fix: config editor array-to-string warning and billing description INSERT failure
- config_servers.php: add gsp_normalize_config_value(), gsp_value_to_display_string(), gsp_value_to_editable_string() helpers; replace (string)$attrValue cast at line 124 with gsp_value_to_editable_string() so PHP arrays from SimpleXML attribute iteration never trigger "Array to string conversion" notices - adminserverlist.php sync_billing_services(): add description column (= service name) to INSERT so the query succeeds on databases where description is NOT NULL without a default; add pre-flight col_exists() schema guard that shows a friendly admin warning and aborts sync instead of crashing on completely missing columns Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/e8bfe531-e1ff-4257-b49c-f8376b84e772 Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
parent
04dc98380b
commit
3024e41121
2 changed files with 82 additions and 5 deletions
|
|
@ -57,6 +57,25 @@ function sync_billing_services(mysqli $db, string $prefix): array
|
|||
{
|
||||
$messages = [];
|
||||
|
||||
// Schema guard: verify billing_services has the expected columns before touching it.
|
||||
// col_exists() is provided by bootstrap.php.
|
||||
$requiredCols = ['home_cfg_id', 'mod_cfg_id', 'service_name', 'description',
|
||||
'remote_server_id', 'enabled', 'out_of_stock',
|
||||
'price_daily', 'price_monthly', 'price_year',
|
||||
'slot_min_qty', 'slot_max_qty', 'install_method'];
|
||||
$tableName = $prefix . 'billing_services';
|
||||
foreach ($requiredCols as $col) {
|
||||
if (!col_exists($db, $tableName, $col)) {
|
||||
$messages[] = "⚠ Schema issue: column '{$col}' missing from {$tableName}. Run the billing module migration.";
|
||||
}
|
||||
}
|
||||
// If critical columns are missing, skip the sync to avoid SQL errors.
|
||||
foreach (['service_name', 'mod_cfg_id', 'enabled'] as $critical) {
|
||||
if (!col_exists($db, $tableName, $critical)) {
|
||||
return $messages;
|
||||
}
|
||||
}
|
||||
|
||||
// Load all games/mods from panel config tables
|
||||
$gameMods = [];
|
||||
$res = $db->query(
|
||||
|
|
@ -97,12 +116,14 @@ function sync_billing_services(mysqli $db, string $prefix): array
|
|||
$homeCfgId = (int)$gm['home_cfg_id'];
|
||||
$db->query(
|
||||
"INSERT INTO `{$prefix}billing_services`
|
||||
(home_cfg_id, mod_cfg_id, service_name, remote_server_id,
|
||||
enabled, out_of_stock, price_daily, price_monthly, price_year,
|
||||
(home_cfg_id, mod_cfg_id, service_name, description,
|
||||
remote_server_id, enabled, out_of_stock,
|
||||
price_daily, price_monthly, price_year,
|
||||
slot_min_qty, slot_max_qty, install_method)
|
||||
VALUES
|
||||
({$homeCfgId}, {$modCfgId}, '{$svcName}', '',
|
||||
0, 0, 0, 0, 0,
|
||||
({$homeCfgId}, {$modCfgId}, '{$svcName}', '{$svcName}',
|
||||
'', 0, 0,
|
||||
0, 0, 0,
|
||||
1, 100, 'steamcmd')"
|
||||
);
|
||||
$messages[] = "Added new service: " . ($gm['mod_name'] ?: $gm['game_name']);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,62 @@
|
|||
|
||||
require_once("server_config_parser.php");
|
||||
|
||||
/**
|
||||
* Safely convert any config value (string, NULL, or array from SimpleXML) to a
|
||||
* plain PHP string without triggering "Array to string conversion" notices.
|
||||
*
|
||||
* - string / int / float → cast directly
|
||||
* - NULL → empty string
|
||||
* - SimpleXMLElement → (string) cast (its __toString returns node text)
|
||||
* - simple flat array → comma-separated list of leaf values
|
||||
* - nested/complex array → JSON (pretty-printed for readability in admin forms)
|
||||
*/
|
||||
function gsp_normalize_config_value($value): string
|
||||
{
|
||||
if ($value === null) {
|
||||
return '';
|
||||
}
|
||||
if ($value instanceof SimpleXMLElement) {
|
||||
return (string)$value;
|
||||
}
|
||||
if (!is_array($value)) {
|
||||
return (string)$value;
|
||||
}
|
||||
// Flat array → comma-separated list of scalar/castable items
|
||||
$isFlat = true;
|
||||
foreach ($value as $item) {
|
||||
if (is_array($item) || ($item instanceof SimpleXMLElement && count($item->children()) > 0)) {
|
||||
$isFlat = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($isFlat) {
|
||||
return implode(',', array_map(function ($item) {
|
||||
return $item instanceof SimpleXMLElement ? (string)$item : (string)$item;
|
||||
}, $value));
|
||||
}
|
||||
return json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an HTML-safe string suitable for display (echo) in the editor.
|
||||
* Always wraps the result in htmlspecialchars.
|
||||
*/
|
||||
function gsp_value_to_display_string($value): string
|
||||
{
|
||||
return htmlspecialchars(gsp_normalize_config_value($value), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an HTML-safe string suitable for use as an HTML form field value.
|
||||
* Identical to gsp_value_to_display_string — kept as a distinct function so
|
||||
* callers can signal intent and future formatting rules can differ.
|
||||
*/
|
||||
function gsp_value_to_editable_string($value): string
|
||||
{
|
||||
return htmlspecialchars(gsp_normalize_config_value($value), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
function config_games_normalize_path($path)
|
||||
{
|
||||
$clean = preg_replace('/[^A-Za-z0-9_\\[\\]\\/\\-]/', '', (string)$path);
|
||||
|
|
@ -121,7 +177,7 @@ function config_games_render_node(SimpleXMLElement $node, array $ancestors, arra
|
|||
$html .= "<div class='xml-node__attributes'><strong>Attributes</strong>";
|
||||
foreach ((array)$attributes as $attrName => $attrValue) {
|
||||
$attrSafe = htmlspecialchars($attrName, ENT_QUOTES, 'UTF-8');
|
||||
$valSafe = htmlspecialchars((string)$attrValue, ENT_QUOTES, 'UTF-8');
|
||||
$valSafe = gsp_value_to_editable_string($attrValue);
|
||||
$html .= "<div class='attr-row'><span>{$attrSafe}</span><input type='text' name=\"nodes[{$safeNodeKey}][attributes][{$attrSafe}]\" value=\"{$valSafe}\" placeholder='Leave blank to remove'></div>";
|
||||
}
|
||||
$html .= "<div class='attr-row'><input type='text' name=\"nodes[{$safeNodeKey}][new_attribute][name]\" placeholder='New attribute name'><input type='text' name=\"nodes[{$safeNodeKey}][new_attribute][value]\" placeholder='New attribute value'></div>";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue