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
381
modules/steam_workshop/controllers/WorkshopModController.php
Normal file
381
modules/steam_workshop/controllers/WorkshopModController.php
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* OGP / GSP – Steam Workshop
|
||||
* WorkshopModController: user-facing mod management per game server.
|
||||
*
|
||||
* Actions (via ?action=...):
|
||||
* mods → show installed mods + available cached mods for a server
|
||||
* install → install a mod (POST: home_id, workshop_id)
|
||||
* remove → remove a mod (POST: home_id, workshop_id)
|
||||
* toggle → enable/disable (POST: home_id, workshop_id, enabled)
|
||||
* load_order → update load order (POST: home_id, workshop_id, load_order)
|
||||
* sync → sync now (POST: home_id, workshop_id)
|
||||
* search → JSON search (GET: home_id, q) – reuses SteamWorkshopService
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../lib/WorkshopRepository.php';
|
||||
require_once __DIR__ . '/../lib/WorkshopInstaller.php';
|
||||
require_once __DIR__ . '/../lib/SteamWorkshopService.php';
|
||||
|
||||
class WorkshopModController
|
||||
{
|
||||
private WorkshopRepository $repo;
|
||||
private WorkshopInstaller $installer;
|
||||
private SteamWorkshopService $searchService;
|
||||
private array $lang;
|
||||
|
||||
public function __construct(OGPDatabase $db)
|
||||
{
|
||||
$this->repo = new WorkshopRepository($db);
|
||||
$this->installer = new WorkshopInstaller($this->repo);
|
||||
$this->searchService = new SteamWorkshopService($db);
|
||||
$this->lang = $this->loadLang();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Dispatch
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
global $db;
|
||||
|
||||
$userId = (int)($_SESSION['user_id'] ?? 0);
|
||||
$isAdmin = $db->isAdmin($userId);
|
||||
$action = $_GET['action'] ?? 'index';
|
||||
|
||||
// JSON endpoint – no HTML output
|
||||
if ($action === 'search') {
|
||||
$this->handleSearch($userId, $isAdmin);
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<link rel="stylesheet" type="text/css" href="modules/steam_workshop/steam_workshop.css" />';
|
||||
echo '<script src="modules/steam_workshop/steam_workshop.js" defer></script>';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$postAction = $_POST['ws_action'] ?? $action;
|
||||
switch ($postAction) {
|
||||
case 'install':
|
||||
$this->handleInstall($userId, $isAdmin);
|
||||
return;
|
||||
case 'remove':
|
||||
$this->handleRemove($userId, $isAdmin);
|
||||
return;
|
||||
case 'toggle':
|
||||
$this->handleToggle($userId, $isAdmin);
|
||||
return;
|
||||
case 'load_order':
|
||||
$this->handleLoadOrder($userId, $isAdmin);
|
||||
return;
|
||||
case 'sync':
|
||||
$this->handleSync($userId, $isAdmin);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'mods':
|
||||
$this->handleModsPage($userId, $isAdmin);
|
||||
break;
|
||||
default:
|
||||
$this->handleIndex($userId, $isAdmin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Pages
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
private function handleIndex(int $userId, bool $isAdmin): void
|
||||
{
|
||||
$homes = $this->getHomesForUser($userId, $isAdmin);
|
||||
$records = [];
|
||||
|
||||
foreach ((array)$homes as $home) {
|
||||
$homeId = (int)($home['home_id'] ?? 0);
|
||||
$appId = $this->searchService->getSteamAppIdForGameKey((string)($home['game_key'] ?? ''));
|
||||
$profile = $appId !== null ? $this->repo->getProfileByAppId($appId) : null;
|
||||
$mods = $profile !== null ? $this->repo->listModsForHome($homeId) : [];
|
||||
|
||||
$records[] = [
|
||||
'home' => $home,
|
||||
'profile' => $profile,
|
||||
'mods' => $mods,
|
||||
];
|
||||
}
|
||||
|
||||
$this->render('user_workshop_index', [
|
||||
'lang' => $this->lang,
|
||||
'records' => $records,
|
||||
'isAdmin' => $isAdmin,
|
||||
]);
|
||||
}
|
||||
|
||||
private function handleModsPage(int $userId, bool $isAdmin): void
|
||||
{
|
||||
$homeId = (int)($_GET['home_id'] ?? 0);
|
||||
if ($homeId <= 0) {
|
||||
print_failure($this->lang['error_missing_home'] ?? 'Select a server first.');
|
||||
$this->handleIndex($userId, $isAdmin);
|
||||
return;
|
||||
}
|
||||
|
||||
$home = $this->getHome($homeId, $userId, $isAdmin);
|
||||
if ($home === null) {
|
||||
print_failure($this->lang['error_home_not_found'] ?? 'Server not found.');
|
||||
$this->handleIndex($userId, $isAdmin);
|
||||
return;
|
||||
}
|
||||
|
||||
$agentId = (int)($home['remote_server_id'] ?? 0);
|
||||
$appId = $this->searchService->getSteamAppIdForGameKey((string)($home['game_key'] ?? ''));
|
||||
$profile = $appId !== null ? $this->repo->getProfileByAppId($appId) : null;
|
||||
|
||||
$installedMods = $this->repo->listModsForHome($homeId);
|
||||
$availableMods = ($profile !== null && $appId !== null)
|
||||
? $this->repo->listCacheForAgent($agentId, $appId)
|
||||
: [];
|
||||
|
||||
$this->render('user_workshop_mods', [
|
||||
'lang' => $this->lang,
|
||||
'home' => $home,
|
||||
'homeId' => $homeId,
|
||||
'profile' => $profile,
|
||||
'appId' => $appId,
|
||||
'installedMods' => $installedMods,
|
||||
'availableMods' => $availableMods,
|
||||
'isAdmin' => $isAdmin,
|
||||
]);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// AJAX / POST actions
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
private function handleInstall(int $userId, bool $isAdmin): void
|
||||
{
|
||||
$homeId = (int)($_POST['home_id'] ?? 0);
|
||||
$workshopId = preg_replace('/[^0-9]/', '', (string)($_POST['workshop_id'] ?? '')) ?? '';
|
||||
|
||||
if ($homeId <= 0 || $workshopId === '') {
|
||||
print_failure($this->lang['error_missing_params'] ?? 'Missing home or workshop ID.');
|
||||
$this->handleIndex($userId, $isAdmin);
|
||||
return;
|
||||
}
|
||||
|
||||
$home = $this->getHome($homeId, $userId, $isAdmin);
|
||||
if ($home === null) {
|
||||
print_failure($this->lang['error_home_not_found'] ?? 'Server not found or access denied.');
|
||||
$this->handleIndex($userId, $isAdmin);
|
||||
return;
|
||||
}
|
||||
|
||||
$appId = $this->searchService->getSteamAppIdForGameKey((string)($home['game_key'] ?? ''));
|
||||
$profile = $appId !== null ? $this->repo->getProfileByAppId($appId) : null;
|
||||
|
||||
if ($profile === null) {
|
||||
print_failure($this->lang['error_no_profile'] ?? 'No Workshop profile configured for this game.');
|
||||
$this->handleModsPage($userId, $isAdmin);
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->installer->install($home, $profile, $workshopId);
|
||||
|
||||
if ($result['success']) {
|
||||
$msg = $this->lang['mod_installed'] ?? 'Mod installed successfully.';
|
||||
if (!empty($result['restart_required'])) {
|
||||
$msg .= ' ' . ($this->lang['restart_required'] ?? 'A server restart is required to activate this mod.');
|
||||
}
|
||||
print_success($msg);
|
||||
} else {
|
||||
print_failure(($this->lang['mod_install_error'] ?? 'Install failed: ') . $result['message']);
|
||||
}
|
||||
|
||||
$_GET['home_id'] = $homeId;
|
||||
$this->handleModsPage($userId, $isAdmin);
|
||||
}
|
||||
|
||||
private function handleRemove(int $userId, bool $isAdmin): void
|
||||
{
|
||||
$homeId = (int)($_POST['home_id'] ?? 0);
|
||||
$workshopId = preg_replace('/[^0-9]/', '', (string)($_POST['workshop_id'] ?? '')) ?? '';
|
||||
|
||||
if ($homeId <= 0 || $workshopId === '') {
|
||||
print_failure($this->lang['error_missing_params'] ?? 'Missing parameters.');
|
||||
$this->handleIndex($userId, $isAdmin);
|
||||
return;
|
||||
}
|
||||
|
||||
$home = $this->getHome($homeId, $userId, $isAdmin);
|
||||
if ($home === null) {
|
||||
print_failure($this->lang['error_home_not_found'] ?? 'Server not found.');
|
||||
$this->handleIndex($userId, $isAdmin);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->repo->removeMod($homeId, $workshopId)) {
|
||||
print_success($this->lang['mod_removed'] ?? 'Mod removed.');
|
||||
} else {
|
||||
print_failure($this->lang['mod_remove_error'] ?? 'Failed to remove mod.');
|
||||
}
|
||||
|
||||
$_GET['home_id'] = $homeId;
|
||||
$this->handleModsPage($userId, $isAdmin);
|
||||
}
|
||||
|
||||
private function handleToggle(int $userId, bool $isAdmin): void
|
||||
{
|
||||
$homeId = (int)($_POST['home_id'] ?? 0);
|
||||
$workshopId = preg_replace('/[^0-9]/', '', (string)($_POST['workshop_id'] ?? '')) ?? '';
|
||||
$enabled = !empty($_POST['enabled']);
|
||||
|
||||
if ($homeId <= 0 || $workshopId === '') {
|
||||
$this->jsonResponse(['ok' => false, 'error' => 'Missing parameters.']);
|
||||
return;
|
||||
}
|
||||
|
||||
$home = $this->getHome($homeId, $userId, $isAdmin);
|
||||
if ($home === null) {
|
||||
$this->jsonResponse(['ok' => false, 'error' => 'Access denied.']);
|
||||
return;
|
||||
}
|
||||
|
||||
$ok = $this->repo->toggleMod($homeId, $workshopId, $enabled);
|
||||
$this->jsonResponse(['ok' => $ok]);
|
||||
}
|
||||
|
||||
private function handleLoadOrder(int $userId, bool $isAdmin): void
|
||||
{
|
||||
$homeId = (int)($_POST['home_id'] ?? 0);
|
||||
$workshopId = preg_replace('/[^0-9]/', '', (string)($_POST['workshop_id'] ?? '')) ?? '';
|
||||
$order = (int)($_POST['load_order'] ?? 0);
|
||||
|
||||
if ($homeId <= 0 || $workshopId === '') {
|
||||
$this->jsonResponse(['ok' => false, 'error' => 'Missing parameters.']);
|
||||
return;
|
||||
}
|
||||
|
||||
$home = $this->getHome($homeId, $userId, $isAdmin);
|
||||
if ($home === null) {
|
||||
$this->jsonResponse(['ok' => false, 'error' => 'Access denied.']);
|
||||
return;
|
||||
}
|
||||
|
||||
$ok = $this->repo->updateLoadOrder($homeId, $workshopId, $order);
|
||||
$this->jsonResponse(['ok' => $ok]);
|
||||
}
|
||||
|
||||
private function handleSync(int $userId, bool $isAdmin): void
|
||||
{
|
||||
$homeId = (int)($_POST['home_id'] ?? 0);
|
||||
$workshopId = preg_replace('/[^0-9]/', '', (string)($_POST['workshop_id'] ?? '')) ?? '';
|
||||
|
||||
if ($homeId <= 0 || $workshopId === '') {
|
||||
print_failure($this->lang['error_missing_params'] ?? 'Missing parameters.');
|
||||
$this->handleIndex($userId, $isAdmin);
|
||||
return;
|
||||
}
|
||||
|
||||
$home = $this->getHome($homeId, $userId, $isAdmin);
|
||||
if ($home === null) {
|
||||
print_failure($this->lang['error_home_not_found'] ?? 'Server not found.');
|
||||
$this->handleIndex($userId, $isAdmin);
|
||||
return;
|
||||
}
|
||||
|
||||
$modRow = $this->repo->getServerMod($homeId, $workshopId);
|
||||
$profile = $modRow !== null ? $this->repo->getProfileById((int)$modRow['profile_id']) : null;
|
||||
|
||||
if ($modRow === null || $profile === null) {
|
||||
print_failure($this->lang['error_mod_not_found'] ?? 'Mod or profile not found.');
|
||||
} else {
|
||||
$result = $this->installer->syncMod($home, $modRow, $profile);
|
||||
if ($result['success']) {
|
||||
print_success($result['changed']
|
||||
? ($this->lang['sync_success'] ?? 'Mod synced successfully.')
|
||||
: ($this->lang['sync_no_change'] ?? 'Mod is already up to date.'));
|
||||
} else {
|
||||
print_failure(($this->lang['sync_error'] ?? 'Sync failed: ') . $result['message']);
|
||||
}
|
||||
}
|
||||
|
||||
$_GET['home_id'] = $homeId;
|
||||
$this->handleModsPage($userId, $isAdmin);
|
||||
}
|
||||
|
||||
private function handleSearch(int $userId, bool $isAdmin): void
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
$homeId = (int)($_GET['home_id'] ?? 0);
|
||||
$query = trim((string)($_GET['q'] ?? ''));
|
||||
|
||||
if ($homeId <= 0 || $query === '') {
|
||||
echo json_encode(['ok' => false, 'error' => 'Missing parameters.']);
|
||||
return;
|
||||
}
|
||||
|
||||
$home = $this->getHome($homeId, $userId, $isAdmin);
|
||||
if ($home === null) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Server not found.']);
|
||||
return;
|
||||
}
|
||||
|
||||
$gameKey = (string)($home['game_key'] ?? '');
|
||||
$payload = $this->searchService->searchWorkshopItems($gameKey, $query, 12, 1);
|
||||
|
||||
if ($payload['error'] !== null) {
|
||||
echo json_encode(['ok' => false, 'error' => $payload['error']]);
|
||||
return;
|
||||
}
|
||||
|
||||
echo json_encode(['ok' => true, 'results' => $payload['results'], 'pagination' => $payload['pagination']]);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/** @return array<int,array<string,mixed>> */
|
||||
private function getHomesForUser(int $userId, bool $isAdmin): array
|
||||
{
|
||||
global $db;
|
||||
$accessType = $isAdmin ? 'admin' : 'user_and_group';
|
||||
$homes = $db->getHomesFor($accessType, $userId);
|
||||
return is_array($homes) ? array_values($homes) : [];
|
||||
}
|
||||
|
||||
private function getHome(int $homeId, int $userId, bool $isAdmin): ?array
|
||||
{
|
||||
global $db;
|
||||
$row = $isAdmin ? $db->getGameHome($homeId) : $db->getUserGameHome($userId, $homeId);
|
||||
return is_array($row) ? $row : null;
|
||||
}
|
||||
|
||||
private function render(string $view, array $data = []): void
|
||||
{
|
||||
extract($data);
|
||||
require __DIR__ . '/../views/' . $view . '.php';
|
||||
}
|
||||
|
||||
/** @param array<string,mixed> $data */
|
||||
private function jsonResponse(array $data): void
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
private function loadLang(): array
|
||||
{
|
||||
$file = __DIR__ . '/../lang/en_US.php';
|
||||
if (is_file($file)) {
|
||||
$strings = require $file;
|
||||
if (is_array($strings)) {
|
||||
return $strings;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
221
modules/steam_workshop/controllers/WorkshopProfileController.php
Normal file
221
modules/steam_workshop/controllers/WorkshopProfileController.php
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* OGP / GSP – Steam Workshop
|
||||
* WorkshopProfileController: admin CRUD for Workshop game profiles
|
||||
* (gsp_workshop_game_profiles table).
|
||||
*
|
||||
* Routed via workshop_admin.php:
|
||||
* ?m=steam_workshop&p=workshop_admin&sw_action=profiles → list
|
||||
* ?m=steam_workshop&p=workshop_admin&sw_action=profile_form → create/edit
|
||||
* POST sw_action=profile_save → save
|
||||
* POST sw_action=profile_delete → delete
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../lib/WorkshopRepository.php';
|
||||
|
||||
class WorkshopProfileController
|
||||
{
|
||||
private WorkshopRepository $repo;
|
||||
private array $lang;
|
||||
|
||||
public function __construct(OGPDatabase $db)
|
||||
{
|
||||
$this->repo = new WorkshopRepository($db);
|
||||
$this->lang = $this->loadLang();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Dispatch
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
echo '<link rel="stylesheet" type="text/css" href="modules/steam_workshop/steam_workshop.css" />';
|
||||
|
||||
$action = $_GET['sw_action'] ?? 'profiles';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$postAction = $_POST['sw_action'] ?? '';
|
||||
switch ($postAction) {
|
||||
case 'profile_save':
|
||||
$this->handleSave();
|
||||
return;
|
||||
case 'profile_delete':
|
||||
$this->handleDelete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'profile_form':
|
||||
$this->handleForm((int)($_GET['profile_id'] ?? 0));
|
||||
break;
|
||||
default:
|
||||
$this->handleList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Actions
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
private function handleList(): void
|
||||
{
|
||||
$profiles = $this->repo->listProfiles();
|
||||
$this->render('admin/profiles', [
|
||||
'lang' => $this->lang,
|
||||
'profiles' => $profiles,
|
||||
]);
|
||||
}
|
||||
|
||||
private function handleForm(int $profileId): void
|
||||
{
|
||||
$profile = $profileId > 0 ? $this->repo->getProfileById($profileId) : null;
|
||||
$this->render('admin/profile_form', [
|
||||
'lang' => $this->lang,
|
||||
'profile' => $profile,
|
||||
'profileId' => $profileId,
|
||||
]);
|
||||
}
|
||||
|
||||
private function handleSave(): void
|
||||
{
|
||||
$id = (int)($_POST['profile_id'] ?? 0);
|
||||
$data = $this->extractProfileData($_POST);
|
||||
|
||||
$errors = $this->validateProfileData($data);
|
||||
if (!empty($errors)) {
|
||||
foreach ($errors as $err) {
|
||||
print_failure($err);
|
||||
}
|
||||
$profile = $id > 0 ? $this->repo->getProfileById($id) : null;
|
||||
$this->render('admin/profile_form', [
|
||||
'lang' => $this->lang,
|
||||
'profile' => array_merge($profile ?? [], $data, ['id' => $id]),
|
||||
'profileId' => $id,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$data['id'] = $id;
|
||||
$savedId = $this->repo->saveProfile($data);
|
||||
|
||||
if ($savedId > 0) {
|
||||
print_success($this->lang['profile_saved'] ?? 'Workshop profile saved.');
|
||||
} else {
|
||||
print_failure($this->lang['profile_save_error'] ?? 'Failed to save Workshop profile.');
|
||||
}
|
||||
|
||||
$this->handleList();
|
||||
}
|
||||
|
||||
private function handleDelete(): void
|
||||
{
|
||||
$id = (int)($_POST['profile_id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
print_failure($this->lang['profile_not_found'] ?? 'Profile not found.');
|
||||
$this->handleList();
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->repo->deleteProfile($id)) {
|
||||
print_success($this->lang['profile_deleted'] ?? 'Workshop profile deleted.');
|
||||
} else {
|
||||
print_failure($this->lang['profile_delete_error'] ?? 'Failed to delete Workshop profile.');
|
||||
}
|
||||
|
||||
$this->handleList();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Input helpers
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $post
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function extractProfileData(array $post): array
|
||||
{
|
||||
// supported_os can be multiple values (SET type)
|
||||
$osRaw = $post['supported_os'] ?? [];
|
||||
if (!is_array($osRaw)) {
|
||||
$osRaw = [$osRaw];
|
||||
}
|
||||
$allowedOs = ['linux', 'windows'];
|
||||
$osValues = array_values(array_intersect($osRaw, $allowedOs));
|
||||
$supportedOs = implode(',', $osValues !== [] ? $osValues : ['linux']);
|
||||
|
||||
$allowedMethods = ['rsync', 'robocopy', 'custom_script'];
|
||||
$copyMethod = in_array($post['copy_method'] ?? '', $allowedMethods, true)
|
||||
? (string)$post['copy_method']
|
||||
: 'rsync';
|
||||
|
||||
return [
|
||||
'game_key' => trim((string)($post['game_key'] ?? '')),
|
||||
'game_name' => trim((string)($post['game_name'] ?? '')),
|
||||
'workshop_app_id' => preg_replace('/[^0-9]/', '', (string)($post['workshop_app_id'] ?? '')) ?? '',
|
||||
'supported_os' => $supportedOs,
|
||||
'cache_path_template' => trim((string)($post['cache_path_template'] ?? '')),
|
||||
'install_path_template' => trim((string)($post['install_path_template'] ?? '')),
|
||||
'folder_name_template' => trim((string)($post['folder_name_template'] ?? '@{mod_id}')),
|
||||
'copy_method' => $copyMethod,
|
||||
'install_script' => trim((string)($post['install_script'] ?? '')),
|
||||
'config_file_template' => trim((string)($post['config_file_template'] ?? '')),
|
||||
'launch_param_template' => trim((string)($post['launch_param_template'] ?? '')),
|
||||
'requires_restart' => !empty($post['requires_restart']) ? 1 : 0,
|
||||
'enabled' => !empty($post['enabled']) ? 1 : 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $data
|
||||
* @return list<string>
|
||||
*/
|
||||
private function validateProfileData(array $data): array
|
||||
{
|
||||
$errors = [];
|
||||
if (($data['game_key'] ?? '') === '') {
|
||||
$errors[] = $this->lang['error_game_key_required'] ?? 'Game key is required.';
|
||||
} elseif (!preg_match('/^[a-z0-9_\-.]+$/i', (string)$data['game_key'])) {
|
||||
$errors[] = $this->lang['error_game_key_invalid'] ?? 'Game key may only contain letters, digits, underscores, dots, and hyphens.';
|
||||
}
|
||||
if (($data['game_name'] ?? '') === '') {
|
||||
$errors[] = $this->lang['error_game_name_required'] ?? 'Game name is required.';
|
||||
}
|
||||
if (($data['workshop_app_id'] ?? '') === '') {
|
||||
$errors[] = $this->lang['error_app_id_required'] ?? 'Workshop App ID is required.';
|
||||
}
|
||||
if (($data['cache_path_template'] ?? '') === '') {
|
||||
$errors[] = $this->lang['error_cache_path_required'] ?? 'Cache path template is required.';
|
||||
}
|
||||
if (($data['install_path_template'] ?? '') === '') {
|
||||
$errors[] = $this->lang['error_install_path_required'] ?? 'Install path template is required.';
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Rendering
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
private function render(string $view, array $data = []): void
|
||||
{
|
||||
extract($data);
|
||||
require __DIR__ . '/../views/' . $view . '.php';
|
||||
}
|
||||
|
||||
private function loadLang(): array
|
||||
{
|
||||
$file = __DIR__ . '/../lang/en_US.php';
|
||||
if (is_file($file)) {
|
||||
$strings = require $file;
|
||||
if (is_array($strings)) {
|
||||
return $strings;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue