feat: config_games XML tag descriptions + jump link; support optional fields; steam_workshop remove adapter terminology

Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/7c776773-fa8f-4f5d-afec-ff62cf7b2bba

Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-05-04 20:34:14 +00:00 committed by GitHub
parent e338eafb36
commit e30c6ac25d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 237 additions and 52 deletions

View file

@ -7,7 +7,7 @@ return [
'button_save' => 'Save settings',
'button_cancel' => 'Back to list',
'label_feature_flag' => 'Enable scheduled Workshop updates for this server',
'label_adapter' => 'Game adapter',
'label_adapter' => 'Game type',
'label_interval' => 'Update interval (minutes)',
'label_interval_hint' => 'Runs on the agent scheduler. Allowed range: 15360 minutes.',
'label_staging_dir' => 'Staging directory (optional)',
@ -17,24 +17,24 @@ return [
'label_mod_import' => 'Workshop IDs list (one "id,@ModName" per line)',
'hint_mod_import' => 'Paste from Modlist.txt or import from a collection. IDs are sanitized automatically.',
'hint_admin_only' => 'Managed by your administrator.',
'adapter_locked_note' => 'This adapter is enforced for the current game type by your administrator.',
'admin_heading_game_mapping' => 'Adapter mapping by game type',
'admin_subheading_game_mapping' => 'Pick which adapter becomes the default whenever a server of that game opens the Workshop UI.',
'adapter_locked_note' => 'The game type for this server is managed by your administrator.',
'admin_heading_game_mapping' => 'Game type mapping',
'admin_subheading_game_mapping' => 'Pick which game configuration becomes the default whenever a server of that game opens the Workshop UI.',
'admin_col_game_key' => 'Game key',
'admin_col_adapter' => 'Adapter',
'admin_col_adapter' => 'Game configuration',
'admin_no_game_keys' => 'No server configuration XML files were detected.',
'admin_heading_adapters' => 'Available adapters',
'admin_heading_adapters' => 'Available game configurations',
'admin_col_key' => 'Key',
'admin_col_mods_dir' => 'Mods directory',
'admin_col_notes' => 'Notes',
'admin_heading_per_game' => 'Per-game adapters',
'admin_subheading_per_game' => 'Each game should have its own adapter XML for Steam Workshop automation.',
'admin_heading_per_game' => 'Per-game Workshop configurations',
'admin_subheading_per_game' => 'Each game should have its own Workshop configuration to control how mods are installed.',
'admin_col_status' => 'Status',
'admin_col_updated' => 'Last updated',
'admin_col_actions' => 'Actions',
'admin_heading_edit_adapter' => 'Editing adapter for %s',
'admin_hint_select_game' => 'Select a game in the table above to edit or create its adapter.',
'status_no_adapter' => 'No adapter defined',
'admin_heading_edit_adapter' => 'Editing Workshop configuration for %s',
'admin_hint_select_game' => 'Select a game in the table above to edit or create its Workshop configuration.',
'status_no_adapter' => 'Not configured',
'status_enabled' => 'Enabled',
'status_disabled' => 'Disabled',
'status_hot_reload' => 'Hot reload ready',
@ -55,15 +55,15 @@ return [
'install_staging' => 'Download to staging only (manual apply)',
'action_queue_for_restart' => 'Queue for restart',
'action_hot_reload_if_supported' => 'Hot reload if the adapter allows it',
'summary_adapter' => 'Adapter',
'summary_adapter' => 'Game',
'summary_interval' => 'Interval',
'summary_mods' => 'Mods',
'summary_last_saved' => 'Last saved',
'summary_hot_reload' => 'Hot reload',
'raw_definition_label' => 'Raw Workshop list',
'message_mappings_saved' => 'Adapter mappings saved.',
'message_adapter_saved' => 'Adapter saved.',
'message_adapter_deleted' => 'Adapter deleted.',
'message_mappings_saved' => 'Game type mappings saved.',
'message_adapter_saved' => 'Game configuration saved.',
'message_adapter_deleted' => 'Game configuration deleted.',
'error_admin_only' => 'Administrator access required.',
'mod_picker_heading' => 'Workshop library',
'mod_picker_hint' => 'Search Steam Workshop and add mods to keep them synced automatically.',
@ -87,15 +87,15 @@ return [
'mod_picker_request_label' => 'Submitting request',
'mod_picker_request_hint' => 'Exact Steam URL preview. The input shows the text that will be submitted.',
'mod_picker_request_input_label' => 'Workshop query preview',
'error_game_key_required' => 'Select a valid game key before editing the adapter.',
'error_adapter_delete_failed' => 'Adapter could not be deleted.',
'error_game_key_required' => 'Select a valid game key before editing the Workshop configuration.',
'error_adapter_delete_failed' => 'Game configuration could not be deleted.',
'button_edit_adapter' => 'Edit',
'button_create_adapter' => 'Create',
'button_delete_adapter' => 'Delete',
'button_save_adapter' => 'Save adapter',
'confirm_delete_adapter' => 'Delete this adapter? Servers mapped to it will fall back to defaults.',
'button_save_adapter' => 'Save game configuration',
'confirm_delete_adapter' => 'Delete this game configuration? Servers mapped to it will fall back to defaults.',
'label_game_key' => 'Game key',
'label_adapter_name' => 'Adapter display name',
'label_adapter_name' => 'Game display name',
'label_adapter_app_id' => 'Steam App ID',
'label_adapter_mods_dir' => 'Mods directory',
'label_adapter_keys_dir' => 'Keys directory (optional)',

View file

@ -52,7 +52,7 @@ declare(strict_types=1);
<span class="sw-game-label__name"><?php echo htmlspecialchars($row['game_name']); ?></span>
<span class="sw-badge sw-badge--app">App ID <?php echo htmlspecialchars($row['app_id']); ?></span>
<?php if ($row['exists']): ?>
<span class="sw-badge sw-badge--custom"><?php echo htmlspecialchars($lang['badge_custom_xml'] ?? 'Custom XML'); ?></span>
<span class="sw-badge sw-badge--custom"><?php echo htmlspecialchars($lang['badge_custom_xml'] ?? 'Custom config'); ?></span>
<?php endif; ?>
</div>
<div class="sw-game-variants">
@ -61,7 +61,7 @@ declare(strict_types=1);
<?php endforeach; ?>
</div>
</div>
<small class="sw-game-label__hint"><?php echo htmlspecialchars($lang['admin_hint_inline_edit'] ?? 'Use the toggle to edit the XML inline.'); ?></small>
<small class="sw-game-label__hint"><?php echo htmlspecialchars($lang['admin_hint_inline_edit'] ?? 'Use the toggle to configure this game inline.'); ?></small>
</td>
<td>
<select form="sw-mapping-form" name="mapping[<?php echo htmlspecialchars($groupKey); ?>]">
@ -86,13 +86,13 @@ declare(strict_types=1);
</td>
<td class="sw-actions">
<button type="button" class="btn secondary js-toggle-adapter" data-target="<?php echo htmlspecialchars($formId); ?>" aria-expanded="<?php echo $isOpen ? 'true' : 'false'; ?>">
<?php echo htmlspecialchars($row['exists'] ? ($lang['button_edit_adapter'] ?? 'Edit adapter') : ($lang['button_create_adapter'] ?? 'Create adapter')); ?>
<?php echo htmlspecialchars($row['exists'] ? ($lang['button_edit_adapter'] ?? 'Edit') : ($lang['button_create_adapter'] ?? 'Create')); ?>
</button>
<?php if ($row['exists']): ?>
<form method="post" class="sw-inline-delete">
<input type="hidden" name="admin_action" value="delete_adapter">
<input type="hidden" name="game_key" value="<?php echo htmlspecialchars($primaryKey); ?>">
<button type="submit" class="btn danger" onclick="return confirm('<?php echo htmlspecialchars($lang['confirm_delete_adapter'] ?? 'Delete this adapter?'); ?>');">
<button type="submit" class="btn danger" onclick="return confirm('<?php echo htmlspecialchars($lang['confirm_delete_adapter'] ?? 'Delete this game configuration?'); ?>');">
<?php echo htmlspecialchars($lang['button_delete_adapter'] ?? 'Delete'); ?>
</button>
</form>
@ -111,7 +111,7 @@ declare(strict_types=1);
<input type="text" value="<?php echo htmlspecialchars($form['game_key']); ?>" readonly>
</label>
<label>
<?php echo htmlspecialchars($lang['label_adapter_name'] ?? 'Adapter display name'); ?>
<?php echo htmlspecialchars($lang['label_adapter_name'] ?? 'Game display name'); ?>
<input type="text" name="adapter[name]" value="<?php echo htmlspecialchars($form['name']); ?>" required>
</label>
<label>
@ -143,7 +143,7 @@ declare(strict_types=1);
</label>
<div class="sw-form__actions">
<button class="btn primary" type="submit"><?php echo htmlspecialchars($lang['button_save_adapter'] ?? 'Save adapter'); ?></button>
<button class="btn primary" type="submit"><?php echo htmlspecialchars($lang['button_save_adapter'] ?? 'Save game configuration'); ?></button>
<button type="button" class="btn js-toggle-adapter" data-target="<?php echo htmlspecialchars($formId); ?>"><?php echo htmlspecialchars($lang['button_cancel'] ?? 'Cancel'); ?></button>
</div>
</form>
@ -159,12 +159,12 @@ declare(strict_types=1);
<button class="btn primary" type="submit" form="sw-mapping-form"><?php echo htmlspecialchars($lang['button_save']); ?></button>
</div>
<h3><?php echo htmlspecialchars($lang['admin_heading_adapters'] ?? 'Available adapters'); ?></h3>
<h3><?php echo htmlspecialchars($lang['admin_heading_adapters'] ?? 'Available game configurations'); ?></h3>
<table class="table sw-mods__table">
<thead>
<tr>
<th><?php echo htmlspecialchars($lang['admin_col_key'] ?? 'Key'); ?></th>
<th><?php echo htmlspecialchars($lang['summary_adapter']); ?></th>
<th><?php echo htmlspecialchars($lang['summary_adapter'] ?? 'Game'); ?></th>
<th>Steam App ID</th>
<th><?php echo htmlspecialchars($lang['admin_col_mods_dir'] ?? 'Mods Dir'); ?></th>
<th><?php echo htmlspecialchars($lang['summary_hot_reload']); ?></th>

View file

@ -21,10 +21,10 @@ $currentAdapterName = $adapterOptions[$formConfig['adapter_key']] ?? strtoupper(
</label>
<label>
<span><?php echo htmlspecialchars($lang['label_adapter']); ?></span>
<span><?php echo htmlspecialchars($lang['label_adapter'] ?? 'Game type'); ?></span>
<?php if ($adapterLocked): ?>
<input type="text" value="<?php echo htmlspecialchars($currentAdapterName); ?>" disabled />
<small><?php echo htmlspecialchars($lang['adapter_locked_note'] ?? 'This adapter is managed by the administrator.'); ?></small>
<small><?php echo htmlspecialchars($lang['adapter_locked_note'] ?? 'The game type for this server is managed by the administrator.'); ?></small>
<?php else: ?>
<select name="workshop[adapter_key]">
<?php foreach ((array)$adapterOptions as $key => $label): ?>