feat: add database-driven Steam Workshop system
- Create 3 new DB tables: workshop_game_profiles, workshop_cache, server_workshop_mods - Add WorkshopRepository (DB access layer for all 3 tables) - Add WorkshopInstaller (rsync/robocopy/custom_script copy logic, SteamCMD download via agent exec) - Add WorkshopUpdater (scheduled cache update functions grouped by agent) - Add WorkshopPreStart (pre-start mod sync helper) - Add WorkshopProfileController (admin CRUD for profiles) - Add WorkshopModController (user install/remove/toggle/load_order/sync) - Add admin views: profiles list + profile_form - Add user views: user_workshop_index + user_workshop_mods - Add cron_update.php CLI entry point (--all/--agent-id/--home-id/--profile-id/--workshop-id) - Add prestart_sync.php CLI helper for XML pre_start hook - Update workshop_admin.php to route to profile management - Update main.php to route to new mod management (legacy fallback preserved) - Update module.php with DB migration SQL and version bump to 2.1 - Update lang/en_US.php with all new strings Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/dbeebd0e-e7a5-469d-8a8c-e63193d1ebb0 Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
parent
4ad46c4332
commit
8eff063a93
17 changed files with 3007 additions and 8 deletions
152
modules/steam_workshop/views/admin/profile_form.php
Normal file
152
modules/steam_workshop/views/admin/profile_form.php
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/** @var array $lang */
|
||||
/** @var array|null $profile existing row when editing, null when creating */
|
||||
/** @var int $profileId */
|
||||
|
||||
$isEdit = $profileId > 0 && $profile !== null;
|
||||
$heading = $isEdit
|
||||
? sprintf($lang['profile_heading_edit'] ?? 'Edit Workshop Profile: %s', htmlspecialchars($profile['game_name'] ?? ''))
|
||||
: ($lang['profile_heading_create'] ?? 'Create Workshop Profile');
|
||||
|
||||
$v = static function (string $key, array $profile, string $default = ''): string {
|
||||
return htmlspecialchars((string)($profile[$key] ?? $default), ENT_QUOTES);
|
||||
};
|
||||
|
||||
$osList = ['linux' => 'Linux', 'windows' => 'Windows'];
|
||||
$currentOs = array_filter(explode(',', (string)($profile['supported_os'] ?? 'linux')));
|
||||
$methodList = ['rsync' => 'rsync (Linux)', 'robocopy' => 'robocopy (Windows)', 'custom_script' => 'custom_script'];
|
||||
$curMethod = (string)($profile['copy_method'] ?? 'rsync');
|
||||
|
||||
$tplVarNote = $lang['profile_template_vars'] ?? 'Available: {home_id} {agent_id} {workshop_app_id} {mod_id} {mod_title} {mod_folder} {steamcmd_path} {server_path} {install_path} {cache_path}';
|
||||
?>
|
||||
<div class="sw-admin sw-profile-form">
|
||||
<h3><?php echo $heading; ?></h3>
|
||||
<p><a href="?m=steam_workshop&p=workshop_admin&sw_action=profiles">← <?php echo htmlspecialchars($lang['profile_back_list'] ?? 'Back to profiles'); ?></a></p>
|
||||
|
||||
<form method="post" action="?m=steam_workshop&p=workshop_admin" class="sw-form">
|
||||
<input type="hidden" name="sw_action" value="profile_save">
|
||||
<input type="hidden" name="profile_id" value="<?php echo $profileId; ?>">
|
||||
|
||||
<!-- Basic info -->
|
||||
<fieldset>
|
||||
<legend><?php echo htmlspecialchars($lang['profile_section_basic'] ?? 'Basic info'); ?></legend>
|
||||
<div class="sw-form__grid">
|
||||
<label>
|
||||
<?php echo htmlspecialchars($lang['label_game_key'] ?? 'Game key'); ?> <em>*</em>
|
||||
<input type="text" name="game_key" value="<?php echo $v('game_key', $profile ?? []); ?>"
|
||||
pattern="[A-Za-z0-9_\-.]+" required maxlength="100"
|
||||
<?php echo $isEdit ? 'readonly' : ''; ?>>
|
||||
</label>
|
||||
<label>
|
||||
<?php echo htmlspecialchars($lang['profile_label_game_name'] ?? 'Game name'); ?> <em>*</em>
|
||||
<input type="text" name="game_name" value="<?php echo $v('game_name', $profile ?? []); ?>"
|
||||
required maxlength="255">
|
||||
</label>
|
||||
<label>
|
||||
<?php echo htmlspecialchars($lang['label_adapter_app_id'] ?? 'Workshop App ID'); ?> <em>*</em>
|
||||
<input type="text" name="workshop_app_id"
|
||||
value="<?php echo $v('workshop_app_id', $profile ?? []); ?>"
|
||||
pattern="[0-9]+" required maxlength="32">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<fieldset class="sw-form__os-group">
|
||||
<legend><?php echo htmlspecialchars($lang['profile_label_os'] ?? 'Supported OS'); ?></legend>
|
||||
<?php foreach ($osList as $osVal => $osLabel): ?>
|
||||
<label class="sw-checkbox">
|
||||
<input type="checkbox" name="supported_os[]" value="<?php echo $osVal; ?>"
|
||||
<?php echo in_array($osVal, $currentOs, true) ? 'checked' : ''; ?>>
|
||||
<span><?php echo htmlspecialchars($osLabel); ?></span>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
|
||||
<!-- Paths / templates -->
|
||||
<fieldset>
|
||||
<legend><?php echo htmlspecialchars($lang['profile_section_paths'] ?? 'Paths & templates'); ?></legend>
|
||||
<small class="sw-hint"><?php echo htmlspecialchars($tplVarNote); ?></small>
|
||||
|
||||
<label>
|
||||
<?php echo htmlspecialchars($lang['profile_label_cache_path'] ?? 'Cache path template'); ?> <em>*</em>
|
||||
<small><?php echo htmlspecialchars($lang['profile_hint_cache_path'] ?? 'Where SteamCMD downloads mods on the agent. E.g. {steamcmd_path}/steamapps/workshop/content/{workshop_app_id}/{mod_id}'); ?></small>
|
||||
<input type="text" name="cache_path_template"
|
||||
value="<?php echo $v('cache_path_template', $profile ?? []); ?>" required>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<?php echo htmlspecialchars($lang['profile_label_install_path'] ?? 'Install path template'); ?> <em>*</em>
|
||||
<small><?php echo htmlspecialchars($lang['profile_hint_install_path'] ?? 'Server-side mod directory. E.g. {server_path}/mods/{mod_folder}'); ?></small>
|
||||
<input type="text" name="install_path_template"
|
||||
value="<?php echo $v('install_path_template', $profile ?? []); ?>" required>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<?php echo htmlspecialchars($lang['profile_label_folder_name'] ?? 'Mod folder name template'); ?>
|
||||
<small><?php echo htmlspecialchars($lang['profile_hint_folder_name'] ?? 'Folder name for each mod. Default: @{mod_id}'); ?></small>
|
||||
<input type="text" name="folder_name_template"
|
||||
value="<?php echo $v('folder_name_template', $profile ?? [], '@{mod_id}'); ?>">
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<!-- Copy method -->
|
||||
<fieldset>
|
||||
<legend><?php echo htmlspecialchars($lang['profile_section_copy'] ?? 'Copy / sync method'); ?></legend>
|
||||
<label>
|
||||
<?php echo htmlspecialchars($lang['profile_label_copy_method'] ?? 'Copy method'); ?>
|
||||
<select name="copy_method">
|
||||
<?php foreach ($methodList as $mVal => $mLabel): ?>
|
||||
<option value="<?php echo $mVal; ?>" <?php echo $curMethod === $mVal ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($mLabel); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<?php echo htmlspecialchars($lang['profile_label_install_script'] ?? 'Custom install script (admin-defined only, optional)'); ?>
|
||||
<small><?php echo htmlspecialchars($lang['profile_hint_install_script'] ?? 'Only used when copy method is custom_script. Template variables are replaced before execution.'); ?></small>
|
||||
<textarea name="install_script" rows="4"><?php echo $v('install_script', $profile ?? []); ?></textarea>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<!-- Config / launch params -->
|
||||
<fieldset>
|
||||
<legend><?php echo htmlspecialchars($lang['profile_section_config'] ?? 'Config & launch parameters'); ?></legend>
|
||||
<label>
|
||||
<?php echo htmlspecialchars($lang['profile_label_config_tpl'] ?? 'Config file template (optional)'); ?>
|
||||
<textarea name="config_file_template" rows="4"><?php echo $v('config_file_template', $profile ?? []); ?></textarea>
|
||||
</label>
|
||||
<label>
|
||||
<?php echo htmlspecialchars($lang['profile_label_launch_tpl'] ?? 'Launch parameter template (optional)'); ?>
|
||||
<input type="text" name="launch_param_template"
|
||||
value="<?php echo $v('launch_param_template', $profile ?? []); ?>">
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<!-- Flags -->
|
||||
<fieldset>
|
||||
<legend><?php echo htmlspecialchars($lang['profile_section_flags'] ?? 'Flags'); ?></legend>
|
||||
<label class="sw-checkbox">
|
||||
<input type="checkbox" name="requires_restart" value="1"
|
||||
<?php echo !empty($profile['requires_restart']) ? 'checked' : ''; ?>>
|
||||
<span><?php echo htmlspecialchars($lang['profile_label_requires_restart'] ?? 'Restart required after mod install/update'); ?></span>
|
||||
</label>
|
||||
<label class="sw-checkbox">
|
||||
<input type="checkbox" name="enabled" value="1"
|
||||
<?php echo (!isset($profile['enabled']) || !empty($profile['enabled'])) ? 'checked' : ''; ?>>
|
||||
<span><?php echo htmlspecialchars($lang['profile_label_enabled'] ?? 'Profile enabled'); ?></span>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<div class="sw-form__actions">
|
||||
<button class="btn primary" type="submit">
|
||||
<?php echo htmlspecialchars($lang['button_save'] ?? 'Save'); ?>
|
||||
</button>
|
||||
<a class="btn" href="?m=steam_workshop&p=workshop_admin&sw_action=profiles">
|
||||
<?php echo htmlspecialchars($lang['button_cancel'] ?? 'Cancel'); ?>
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
73
modules/steam_workshop/views/admin/profiles.php
Normal file
73
modules/steam_workshop/views/admin/profiles.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/** @var array $lang */
|
||||
/** @var array[] $profiles */
|
||||
?>
|
||||
<div class="sw-admin sw-profiles">
|
||||
<div class="sw-admin__intro">
|
||||
<h3><?php echo htmlspecialchars($lang['profile_heading_list'] ?? 'Workshop Game Profiles'); ?></h3>
|
||||
<p><?php echo htmlspecialchars($lang['profile_intro'] ?? 'One profile per supported game. Each profile drives mod install and caching behaviour.'); ?></p>
|
||||
<a class="btn primary" href="?m=steam_workshop&p=workshop_admin&sw_action=profile_form">
|
||||
<?php echo htmlspecialchars($lang['profile_btn_create'] ?? 'Create Profile'); ?>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if (empty($profiles)): ?>
|
||||
<p class="sw-empty"><?php echo htmlspecialchars($lang['profile_list_empty'] ?? 'No Workshop profiles defined yet.'); ?></p>
|
||||
<?php else: ?>
|
||||
<table class="table sw-profiles__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php echo htmlspecialchars($lang['profile_col_game'] ?? 'Game'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['profile_col_key'] ?? 'Game Key'); ?></th>
|
||||
<th>App ID</th>
|
||||
<th>OS</th>
|
||||
<th><?php echo htmlspecialchars($lang['profile_col_method'] ?? 'Copy Method'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['profile_col_restart'] ?? 'Restart?'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['profile_col_status'] ?? 'Status'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['admin_col_actions'] ?? 'Actions'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ((array)$profiles as $profile): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($profile['game_name']); ?></td>
|
||||
<td><code><?php echo htmlspecialchars($profile['game_key']); ?></code></td>
|
||||
<td><?php echo htmlspecialchars($profile['workshop_app_id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($profile['supported_os']); ?></td>
|
||||
<td><?php echo htmlspecialchars($profile['copy_method']); ?></td>
|
||||
<td><?php echo $profile['requires_restart'] ? '✔' : '✘'; ?></td>
|
||||
<td>
|
||||
<?php if ($profile['enabled']): ?>
|
||||
<span class="sw-badge sw-badge--enabled"><?php echo htmlspecialchars($lang['status_enabled'] ?? 'Enabled'); ?></span>
|
||||
<?php else: ?>
|
||||
<span class="sw-badge sw-badge--disabled"><?php echo htmlspecialchars($lang['status_disabled'] ?? 'Disabled'); ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="sw-actions">
|
||||
<a class="btn secondary"
|
||||
href="?m=steam_workshop&p=workshop_admin&sw_action=profile_form&profile_id=<?php echo (int)$profile['id']; ?>">
|
||||
<?php echo htmlspecialchars($lang['button_edit'] ?? 'Edit'); ?>
|
||||
</a>
|
||||
<form method="post" action="?m=steam_workshop&p=workshop_admin" class="sw-inline-delete">
|
||||
<input type="hidden" name="sw_action" value="profile_delete">
|
||||
<input type="hidden" name="profile_id" value="<?php echo (int)$profile['id']; ?>">
|
||||
<button type="submit" class="btn danger"
|
||||
onclick="return confirm('<?php echo htmlspecialchars($lang['profile_confirm_delete'] ?? 'Delete this Workshop profile?'); ?>')">
|
||||
<?php echo htmlspecialchars($lang['button_delete_adapter'] ?? 'Delete'); ?>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<hr>
|
||||
<p>
|
||||
<a href="?m=steam_workshop&p=workshop_admin">←
|
||||
<?php echo htmlspecialchars($lang['profile_back_adapters'] ?? 'Back to adapter management'); ?>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
65
modules/steam_workshop/views/user_workshop_index.php
Normal file
65
modules/steam_workshop/views/user_workshop_index.php
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/** @var array $lang */
|
||||
/** @var array[] $records each: {home, profile, mods} */
|
||||
/** @var bool $isAdmin */
|
||||
?>
|
||||
<div class="sw-user sw-ws-index">
|
||||
<h3><?php echo htmlspecialchars($lang['user_workshop_heading'] ?? 'Steam Workshop'); ?></h3>
|
||||
|
||||
<?php if (empty($records)): ?>
|
||||
<p class="sw-empty">
|
||||
<?php echo htmlspecialchars($isAdmin ? ($lang['empty_state_admin'] ?? 'No game homes assigned.') : ($lang['empty_state_user'] ?? 'No servers available.')); ?>
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<table class="table sw-ws-index__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php echo htmlspecialchars($lang['col_server'] ?? 'Server'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['col_game'] ?? 'Game'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['col_mods_count'] ?? 'Installed mods'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['col_profile'] ?? 'Profile'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['admin_col_actions'] ?? 'Actions'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ((array)$records as $record): ?>
|
||||
<?php
|
||||
$home = $record['home'];
|
||||
$profile = $record['profile'];
|
||||
$mods = $record['mods'];
|
||||
$homeId = (int)($home['home_id'] ?? 0);
|
||||
?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($home['home_name'] ?? ('#' . $homeId)); ?></td>
|
||||
<td><?php echo htmlspecialchars($home['game_key'] ?? ''); ?></td>
|
||||
<td><?php echo count((array)$mods); ?></td>
|
||||
<td>
|
||||
<?php if ($profile !== null): ?>
|
||||
<span class="sw-badge sw-badge--enabled">
|
||||
<?php echo htmlspecialchars($profile['game_name']); ?>
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span class="sw-badge sw-badge--disabled">
|
||||
<?php echo htmlspecialchars($lang['no_profile'] ?? 'No profile'); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="sw-actions">
|
||||
<?php if ($profile !== null): ?>
|
||||
<a class="btn secondary"
|
||||
href="?m=steam_workshop&p=main&action=mods&home_id=<?php echo $homeId; ?>">
|
||||
<?php echo htmlspecialchars($lang['btn_manage_mods'] ?? 'Manage Mods'); ?>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<span class="sw-hint">
|
||||
<?php echo htmlspecialchars($lang['hint_no_profile'] ?? 'Ask an admin to create a Workshop profile for this game.'); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
220
modules/steam_workshop/views/user_workshop_mods.php
Normal file
220
modules/steam_workshop/views/user_workshop_mods.php
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/** @var array $lang */
|
||||
/** @var array $home */
|
||||
/** @var int $homeId */
|
||||
/** @var array|null $profile */
|
||||
/** @var string|null $appId */
|
||||
/** @var array[] $installedMods */
|
||||
/** @var array[] $availableMods */
|
||||
/** @var bool $isAdmin */
|
||||
|
||||
$homeName = htmlspecialchars($home['home_name'] ?? ('#' . $homeId));
|
||||
$baseAction = '?m=steam_workshop&p=main';
|
||||
?>
|
||||
<div class="sw-user sw-ws-mods">
|
||||
<p><a href="<?php echo $baseAction; ?>">← <?php echo htmlspecialchars($lang['button_cancel'] ?? 'Back'); ?></a></p>
|
||||
<h3><?php echo sprintf(htmlspecialchars($lang['user_workshop_server_heading'] ?? 'Workshop Mods – %s'), $homeName); ?></h3>
|
||||
|
||||
<?php if ($profile === null): ?>
|
||||
<div class="sw-notice">
|
||||
<p><?php echo htmlspecialchars($lang['no_profile_notice'] ?? 'No Workshop profile is configured for this game. An administrator needs to create one first.'); ?></p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
|
||||
<!-- Installed mods table -->
|
||||
<h4><?php echo htmlspecialchars($lang['heading_installed_mods'] ?? 'Installed Mods'); ?></h4>
|
||||
<?php if (empty($installedMods)): ?>
|
||||
<p class="sw-empty"><?php echo htmlspecialchars($lang['no_installed_mods'] ?? 'No mods installed yet.'); ?></p>
|
||||
<?php else: ?>
|
||||
<table class="table sw-ws-mods__table" id="sw-installed-<?php echo $homeId; ?>">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php echo htmlspecialchars($lang['col_mod_id'] ?? 'Workshop ID'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['col_mod_title'] ?? 'Title'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['mods_header_enabled'] ?? 'Enabled'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['col_load_order'] ?? 'Load order'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['admin_col_actions'] ?? 'Actions'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ((array)$installedMods as $mod): ?>
|
||||
<?php $wid = htmlspecialchars($mod['workshop_id']); ?>
|
||||
<tr data-workshop-id="<?php echo $wid; ?>">
|
||||
<td>
|
||||
<a href="https://steamcommunity.com/sharedfiles/filedetails/?id=<?php echo $wid; ?>"
|
||||
target="_blank" rel="noopener"><?php echo $wid; ?></a>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($mod['title'] ?? $mod['workshop_id']); ?></td>
|
||||
<td>
|
||||
<form method="post" action="<?php echo $baseAction; ?>" class="sw-toggle-form">
|
||||
<input type="hidden" name="ws_action" value="toggle">
|
||||
<input type="hidden" name="home_id" value="<?php echo $homeId; ?>">
|
||||
<input type="hidden" name="workshop_id" value="<?php echo $wid; ?>">
|
||||
<label class="sw-toggle">
|
||||
<input type="checkbox" name="enabled" value="1"
|
||||
class="js-ws-toggle"
|
||||
<?php echo !empty($mod['enabled']) ? 'checked' : ''; ?>>
|
||||
<span><?php echo !empty($mod['enabled']) ? htmlspecialchars($lang['status_enabled'] ?? 'Yes') : htmlspecialchars($lang['status_disabled'] ?? 'No'); ?></span>
|
||||
</label>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" action="<?php echo $baseAction; ?>" class="sw-order-form">
|
||||
<input type="hidden" name="ws_action" value="load_order">
|
||||
<input type="hidden" name="home_id" value="<?php echo $homeId; ?>">
|
||||
<input type="hidden" name="workshop_id" value="<?php echo $wid; ?>">
|
||||
<input type="number" name="load_order"
|
||||
value="<?php echo (int)$mod['load_order']; ?>"
|
||||
min="0" max="9999" class="sw-order-input js-ws-order"
|
||||
style="width:5em">
|
||||
</form>
|
||||
</td>
|
||||
<td class="sw-actions">
|
||||
<!-- Sync now -->
|
||||
<form method="post" action="<?php echo $baseAction; ?>" class="sw-inline">
|
||||
<input type="hidden" name="ws_action" value="sync">
|
||||
<input type="hidden" name="home_id" value="<?php echo $homeId; ?>">
|
||||
<input type="hidden" name="workshop_id" value="<?php echo $wid; ?>">
|
||||
<button type="submit" class="btn secondary">
|
||||
<?php echo htmlspecialchars($lang['btn_sync_now'] ?? 'Sync now'); ?>
|
||||
</button>
|
||||
</form>
|
||||
<!-- Remove -->
|
||||
<form method="post" action="<?php echo $baseAction; ?>" class="sw-inline">
|
||||
<input type="hidden" name="ws_action" value="remove">
|
||||
<input type="hidden" name="home_id" value="<?php echo $homeId; ?>">
|
||||
<input type="hidden" name="workshop_id" value="<?php echo $wid; ?>">
|
||||
<button type="submit" class="btn danger"
|
||||
onclick="return confirm('<?php echo htmlspecialchars($lang['confirm_remove_mod'] ?? 'Remove this mod?'); ?>')">
|
||||
<?php echo htmlspecialchars($lang['btn_remove_mod'] ?? 'Remove'); ?>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Install from cache -->
|
||||
<?php if (!empty($availableMods)): ?>
|
||||
<h4><?php echo htmlspecialchars($lang['heading_cached_mods'] ?? 'Available Cached Mods (this agent)'); ?></h4>
|
||||
<table class="table sw-ws-mods__cache-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php echo htmlspecialchars($lang['col_mod_id'] ?? 'Workshop ID'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['col_mod_title'] ?? 'Title'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['col_cache_status'] ?? 'Cache status'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['admin_col_actions'] ?? 'Actions'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ((array)$availableMods as $cached): ?>
|
||||
<?php $cid = htmlspecialchars($cached['workshop_id']); ?>
|
||||
<tr>
|
||||
<td><?php echo $cid; ?></td>
|
||||
<td><?php echo htmlspecialchars($cached['title'] ?? $cached['workshop_id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($cached['status']); ?></td>
|
||||
<td>
|
||||
<form method="post" action="<?php echo $baseAction; ?>">
|
||||
<input type="hidden" name="ws_action" value="install">
|
||||
<input type="hidden" name="home_id" value="<?php echo $homeId; ?>">
|
||||
<input type="hidden" name="workshop_id" value="<?php echo $cid; ?>">
|
||||
<button type="submit" class="btn secondary">
|
||||
<?php echo htmlspecialchars($lang['btn_install_mod'] ?? 'Install'); ?>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Search + install by Workshop ID -->
|
||||
<h4><?php echo htmlspecialchars($lang['heading_install_mod'] ?? 'Install Mod by Workshop ID'); ?></h4>
|
||||
<form method="post" action="<?php echo $baseAction; ?>" class="sw-form sw-install-form">
|
||||
<input type="hidden" name="ws_action" value="install">
|
||||
<input type="hidden" name="home_id" value="<?php echo $homeId; ?>">
|
||||
<div class="sw-form__row">
|
||||
<label>
|
||||
<?php echo htmlspecialchars($lang['label_workshop_id_input'] ?? 'Workshop ID'); ?>
|
||||
<input type="text" name="workshop_id" pattern="[0-9]+" required
|
||||
placeholder="<?php echo htmlspecialchars($lang['placeholder_workshop_id'] ?? 'e.g. 1234567890'); ?>">
|
||||
</label>
|
||||
<button type="submit" class="btn primary">
|
||||
<?php echo htmlspecialchars($lang['btn_install_mod'] ?? 'Install'); ?>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Steam Workshop search widget (reuse existing JS picker) -->
|
||||
<?php
|
||||
$scriptPath = (string)($_SERVER['PHP_SELF'] ?? '/index.php');
|
||||
$searchEndpoint = sprintf('%s?m=steam_workshop&p=main&action=search&home_id=%d', $scriptPath, $homeId);
|
||||
$langAttrs = [
|
||||
'add' => $lang['mod_picker_action_add'] ?? 'Add',
|
||||
'remove' => $lang['mod_picker_action_remove'] ?? 'Remove',
|
||||
'loading' => $lang['mod_picker_status_loading'] ?? 'Searching…',
|
||||
'error' => $lang['mod_picker_status_error'] ?? 'Search failed.',
|
||||
'empty' => $lang['mod_picker_results_empty'] ?? 'No results.',
|
||||
'query' => $lang['mod_picker_status_need_query'] ?? 'Enter a query.',
|
||||
'sync' => $lang['mod_picker_toggle_label'] ?? 'Sync',
|
||||
];
|
||||
?>
|
||||
<div class="sw-picker" id="sw-picker-ws-<?php echo $homeId; ?>"
|
||||
data-endpoint="<?php echo htmlspecialchars($searchEndpoint, ENT_QUOTES); ?>"
|
||||
data-detail-base="https://steamcommunity.com/sharedfiles/filedetails/?id="
|
||||
data-install-action="<?php echo $baseAction; ?>"
|
||||
data-home-id="<?php echo $homeId; ?>"
|
||||
<?php foreach ((array)$langAttrs as $lk => $lv): ?>data-lang-<?php echo $lk; ?>="<?php echo htmlspecialchars($lv, ENT_QUOTES); ?>" <?php endforeach; ?>>
|
||||
<div class="sw-picker__header">
|
||||
<h5><?php echo htmlspecialchars($lang['mod_picker_heading'] ?? 'Search Steam Workshop'); ?></h5>
|
||||
</div>
|
||||
<div class="sw-picker__search js-sw-search-form" role="search">
|
||||
<label>
|
||||
<span><?php echo htmlspecialchars($lang['mod_picker_search_label'] ?? 'Search'); ?></span>
|
||||
<input type="text" class="sw-picker__search-input js-sw-search-input"
|
||||
placeholder="<?php echo htmlspecialchars($lang['mod_picker_search_placeholder'] ?? 'ID or keyword'); ?>">
|
||||
</label>
|
||||
<button type="button" class="btn secondary js-sw-search-button">
|
||||
<?php echo htmlspecialchars($lang['mod_picker_search_button'] ?? 'Search'); ?>
|
||||
</button>
|
||||
</div>
|
||||
<div class="sw-picker__status js-sw-picker-status" role="status" aria-live="polite"></div>
|
||||
<div class="sw-picker__results">
|
||||
<table class="sw-picker__results-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php echo htmlspecialchars($lang['col_mod_id'] ?? 'ID'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['col_mod_title'] ?? 'Title'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['admin_col_actions'] ?? 'Action'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="js-sw-results"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php endif; // profile !== null ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* Simple toggle / order auto-submit for the mods table */
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Toggle enable/disable via form submit
|
||||
document.querySelectorAll('.js-ws-toggle').forEach(function (cb) {
|
||||
cb.addEventListener('change', function () {
|
||||
cb.closest('form').submit();
|
||||
});
|
||||
});
|
||||
|
||||
// Load order auto-submit on blur
|
||||
document.querySelectorAll('.js-ws-order').forEach(function (inp) {
|
||||
inp.addEventListener('change', function () {
|
||||
inp.closest('form').submit();
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Loading…
Add table
Add a link
Reference in a new issue