Updated admin page
This commit is contained in:
parent
fcc1b18e4c
commit
cbd7995a31
6 changed files with 284 additions and 10 deletions
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/SteamWorkshopService.php';
|
||||
|
||||
class AdminWorkshopController
|
||||
{
|
||||
private SteamWorkshopService $service;
|
||||
private array $lang;
|
||||
|
||||
public function __construct(OGPDatabase $db)
|
||||
{
|
||||
$this->service = new SteamWorkshopService($db);
|
||||
$this->lang = $this->loadLang();
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
global $db;
|
||||
|
||||
$userId = (int)($_SESSION['user_id'] ?? 0);
|
||||
if (!$db->isAdmin($userId)) {
|
||||
print_failure($this->lang['error_admin_only'] ?? 'Admin access required.');
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<link rel="stylesheet" type="text/css" href="modules/steam_workshop/steam_workshop.css" />';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$this->processSave();
|
||||
}
|
||||
|
||||
$gameKeys = $this->service->listAvailableGameKeys();
|
||||
$mappings = $this->service->getAdapterMappings();
|
||||
$adapters = $this->service->loadAdapters();
|
||||
$adapterOptions = $this->service->getAdapterOptions();
|
||||
|
||||
$this->render('admin/index', [
|
||||
'lang' => $this->lang,
|
||||
'gameKeys' => $gameKeys,
|
||||
'mappings' => $mappings,
|
||||
'adapters' => $adapters,
|
||||
'adapterOptions' => $adapterOptions,
|
||||
]);
|
||||
}
|
||||
|
||||
private function processSave(): void
|
||||
{
|
||||
$payload = $_POST['mapping'] ?? [];
|
||||
if (!is_array($payload)) {
|
||||
$payload = [];
|
||||
}
|
||||
$this->service->saveAdapterMappings($payload);
|
||||
print_success($this->lang['message_mappings_saved'] ?? 'Adapter mappings saved.');
|
||||
}
|
||||
|
||||
private function render(string $view, array $data = []): void
|
||||
{
|
||||
extract($data);
|
||||
$lang = $this->lang;
|
||||
require __DIR__ . '/../views/' . $view . '.php';
|
||||
}
|
||||
|
||||
private function loadLang(): array
|
||||
{
|
||||
$langFile = __DIR__ . '/../lang/en_US.php';
|
||||
if (is_file($langFile)) {
|
||||
$strings = require $langFile;
|
||||
if (is_array($strings)) {
|
||||
return $strings;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -54,10 +54,11 @@ class SteamWorkshopController
|
|||
}
|
||||
|
||||
$config = $this->service->buildConfigFromRequest($_POST);
|
||||
$adapterLocked = $this->applyGameAdapterOverride($home, $config);
|
||||
$this->service->saveConfig($homeId, $config);
|
||||
print_success($this->lang['message_config_saved'] ?? 'Workshop configuration saved.');
|
||||
|
||||
$this->renderEdit($home, $config, $isAdmin);
|
||||
$this->renderEdit($home, $config, $isAdmin, $adapterLocked);
|
||||
}
|
||||
|
||||
private function handleEdit(int $userId, bool $isAdmin): void
|
||||
|
|
@ -77,7 +78,8 @@ class SteamWorkshopController
|
|||
}
|
||||
|
||||
$config = $this->service->loadConfig($homeId);
|
||||
$this->renderEdit($home, $config, $isAdmin);
|
||||
$adapterLocked = $this->applyGameAdapterOverride($home, $config);
|
||||
$this->renderEdit($home, $config, $isAdmin, $adapterLocked);
|
||||
}
|
||||
|
||||
private function renderIndex(int $userId, bool $isAdmin): void
|
||||
|
|
@ -86,6 +88,7 @@ class SteamWorkshopController
|
|||
$homes = $this->service->listHomesForUser($userId, $isAdmin);
|
||||
foreach ($homes as $home) {
|
||||
$config = $this->service->loadConfig((int)$home['home_id']);
|
||||
$this->applyGameAdapterOverride($home, $config);
|
||||
$adapter = $this->service->getAdapterByKey($config['adapter_key']);
|
||||
$records[] = [
|
||||
'home' => $home,
|
||||
|
|
@ -102,7 +105,7 @@ class SteamWorkshopController
|
|||
]);
|
||||
}
|
||||
|
||||
private function renderEdit(array $home, array $config, bool $isAdmin): void
|
||||
private function renderEdit(array $home, array $config, bool $isAdmin, bool $adapterLocked): void
|
||||
{
|
||||
$this->render('edit', [
|
||||
'lang' => $this->lang,
|
||||
|
|
@ -110,9 +113,22 @@ class SteamWorkshopController
|
|||
'config' => $config,
|
||||
'isAdmin' => $isAdmin,
|
||||
'adapterOptions' => $this->service->getAdapterOptions(),
|
||||
'adapterLocked' => $adapterLocked,
|
||||
]);
|
||||
}
|
||||
|
||||
private function applyGameAdapterOverride(array $home, array &$config): bool
|
||||
{
|
||||
$gameKey = isset($home['game_key']) ? (string)$home['game_key'] : '';
|
||||
$mapped = $this->service->getAdapterKeyForGame($gameKey);
|
||||
if ($mapped !== null && $mapped !== '') {
|
||||
$config['adapter_key'] = $mapped;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function render(string $view, array $data = []): void
|
||||
{
|
||||
extract($data);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ return [
|
|||
'label_post_install_script' => 'Post-install script (absolute path)',
|
||||
'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.',
|
||||
'adapter_locked_note' => 'This adapter is enforced for the current game type by your administrator.',
|
||||
'status_enabled' => 'Enabled',
|
||||
'status_disabled' => 'Disabled',
|
||||
'status_hot_reload' => 'Hot reload ready',
|
||||
|
|
|
|||
|
|
@ -9,16 +9,20 @@ class SteamWorkshopService
|
|||
private OGPDatabase $db;
|
||||
private string $configDir;
|
||||
private string $adapterDir;
|
||||
private string $adapterMapFile;
|
||||
|
||||
public function __construct(OGPDatabase $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->configDir = __DIR__ . '/../data/configs';
|
||||
$this->adapterDir = __DIR__ . '/GameAdapters';
|
||||
$this->adapterMapFile = __DIR__ . '/../data/game_adapter_map.json';
|
||||
|
||||
if (!is_dir($this->configDir)) {
|
||||
mkdir($this->configDir, 0775, true);
|
||||
}
|
||||
|
||||
$this->ensureDataFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -308,6 +312,89 @@ class SteamWorkshopService
|
|||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return adapter key chosen for the given game key, or null if unmapped.
|
||||
*/
|
||||
public function getAdapterKeyForGame(string $gameKey): ?string
|
||||
{
|
||||
$gameKey = trim($gameKey);
|
||||
if ($gameKey === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$map = $this->getAdapterMappings();
|
||||
return $map[$gameKey] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist adapter mappings (game_key => adapter_key).
|
||||
*/
|
||||
public function saveAdapterMappings(array $mappings): void
|
||||
{
|
||||
$sanitized = [];
|
||||
$options = $this->getAdapterOptions();
|
||||
foreach ($mappings as $gameKey => $adapterKey) {
|
||||
$gameKey = trim((string)$gameKey);
|
||||
$adapterKey = $this->sanitizeAdapterKey((string)$adapterKey);
|
||||
if ($gameKey === '' || !isset($options[$adapterKey])) {
|
||||
continue;
|
||||
}
|
||||
$sanitized[$gameKey] = $adapterKey;
|
||||
}
|
||||
|
||||
file_put_contents($this->adapterMapFile, json_encode($sanitized, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,string>
|
||||
*/
|
||||
public function getAdapterMappings(): array
|
||||
{
|
||||
if (!is_file($this->adapterMapFile)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$raw = file_get_contents($this->adapterMapFile);
|
||||
$decoded = json_decode((string)$raw, true);
|
||||
if (!is_array($decoded)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($decoded as $gameKey => $adapterKey) {
|
||||
if (!is_string($gameKey) || !is_string($adapterKey)) {
|
||||
continue;
|
||||
}
|
||||
$result[$gameKey] = $adapterKey;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover available game keys from server config XMLs.
|
||||
*
|
||||
* @return array<int,string>
|
||||
*/
|
||||
public function listAvailableGameKeys(): array
|
||||
{
|
||||
$keys = [];
|
||||
$configDir = defined('SERVER_CONFIG_LOCATION') ? SERVER_CONFIG_LOCATION : __DIR__ . '/../../config_games/server_configs';
|
||||
foreach (glob($configDir . '/*.xml') as $file) {
|
||||
$xml = @simplexml_load_file($file);
|
||||
if ($xml === false) {
|
||||
continue;
|
||||
}
|
||||
if (isset($xml->game_key)) {
|
||||
$keys[] = trim((string)$xml->game_key);
|
||||
}
|
||||
}
|
||||
|
||||
$keys = array_filter(array_unique($keys));
|
||||
sort($keys);
|
||||
return array_values($keys);
|
||||
}
|
||||
|
||||
private function sanitizeInterval(?int $minutes): int
|
||||
{
|
||||
if ($minutes === null || $minutes <= 0) {
|
||||
|
|
@ -387,4 +474,16 @@ class SteamWorkshopService
|
|||
'last_saved_at' => null,
|
||||
];
|
||||
}
|
||||
|
||||
private function ensureDataFiles(): void
|
||||
{
|
||||
$dir = dirname($this->adapterMapFile);
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0775, true);
|
||||
}
|
||||
|
||||
if (!is_file($this->adapterMapFile)) {
|
||||
file_put_contents($this->adapterMapFile, json_encode([]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
75
modules/steam_workshop/views/admin/index.php
Normal file
75
modules/steam_workshop/views/admin/index.php
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/** @var array $lang */
|
||||
/** @var array $gameKeys */
|
||||
/** @var array $mappings */
|
||||
/** @var array $adapterOptions */
|
||||
/** @var array $adapters */
|
||||
?>
|
||||
<div class="sw-admin">
|
||||
<h3><?php echo htmlspecialchars($lang['admin_heading_game_mapping'] ?? 'Game type adapter mapping'); ?></h3>
|
||||
<p><?php echo htmlspecialchars($lang['admin_subheading_game_mapping'] ?? 'Select which adapter will manage Steam Workshop installs for each supported game.'); ?></p>
|
||||
|
||||
<form method="post" class="sw-form">
|
||||
<table class="table sw-mods__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php echo htmlspecialchars($lang['admin_col_game_key'] ?? 'Game Key'); ?></th>
|
||||
<th><?php echo htmlspecialchars($lang['admin_col_adapter'] ?? 'Adapter'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($gameKeys)): ?>
|
||||
<tr>
|
||||
<td colspan="2"><?php echo htmlspecialchars($lang['admin_no_game_keys'] ?? 'No game definitions were found in modules/config_games/server_configs.'); ?></td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($gameKeys as $gameKey): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($gameKey); ?></td>
|
||||
<td>
|
||||
<select name="mapping[<?php echo htmlspecialchars($gameKey); ?>]">
|
||||
<option value="">--</option>
|
||||
<?php foreach ($adapterOptions as $key => $label): ?>
|
||||
<option value="<?php echo htmlspecialchars($key); ?>" <?php echo (isset($mappings[$gameKey]) && $mappings[$gameKey] === $key) ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($label); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="sw-form__actions">
|
||||
<button class="btn primary" type="submit"><?php echo htmlspecialchars($lang['button_save']); ?></button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h3><?php echo htmlspecialchars($lang['admin_heading_adapters'] ?? 'Available adapters'); ?></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>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>
|
||||
<th><?php echo htmlspecialchars($lang['admin_col_notes'] ?? 'Notes'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($adapters as $adapter): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($adapter['key']); ?></td>
|
||||
<td><?php echo htmlspecialchars($adapter['name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($adapter['steam_app_id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($adapter['mods_dir']); ?></td>
|
||||
<td><?php echo !empty($adapter['supports_hot_reload']) ? htmlspecialchars($lang['status_hot_reload']) : htmlspecialchars($lang['status_restart_required']); ?></td>
|
||||
<td><?php echo htmlspecialchars($adapter['notes']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -3,6 +3,7 @@ declare(strict_types=1);
|
|||
/** @var array $formConfig */
|
||||
/** @var array $adapterOptions */
|
||||
/** @var array $lang */
|
||||
/** @var bool $adapterLocked */
|
||||
$enabled = !empty($formConfig['workshop_enabled']);
|
||||
$interval = (int)$formConfig['update_interval_minutes'];
|
||||
$stagingDir = htmlspecialchars($formConfig['staging_dir']);
|
||||
|
|
@ -10,6 +11,7 @@ $postInstall = htmlspecialchars($formConfig['post_install_script']);
|
|||
$rawDefinition = htmlspecialchars($formConfig['raw_definition']);
|
||||
$installStrategy = $formConfig['install_strategy'];
|
||||
$onUpdateAction = $formConfig['on_update_action'];
|
||||
$currentAdapterName = $adapterOptions[$formConfig['adapter_key']] ?? strtoupper($formConfig['adapter_key']);
|
||||
?>
|
||||
<div class="sw-form__grid">
|
||||
<label class="sw-toggle">
|
||||
|
|
@ -19,13 +21,18 @@ $onUpdateAction = $formConfig['on_update_action'];
|
|||
|
||||
<label>
|
||||
<span><?php echo htmlspecialchars($lang['label_adapter']); ?></span>
|
||||
<select name="workshop[adapter_key]">
|
||||
<?php foreach ($adapterOptions as $key => $label): ?>
|
||||
<option value="<?php echo htmlspecialchars($key); ?>" <?php echo $formConfig['adapter_key'] === $key ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($label); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?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>
|
||||
<?php else: ?>
|
||||
<select name="workshop[adapter_key]">
|
||||
<?php foreach ($adapterOptions as $key => $label): ?>
|
||||
<option value="<?php echo htmlspecialchars($key); ?>" <?php echo $formConfig['adapter_key'] === $key ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($label); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php endif; ?>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue