small fixes

This commit is contained in:
Frank Harris 2026-06-17 09:15:42 -05:00
parent bd3875743e
commit 28533be24d
9 changed files with 310 additions and 524 deletions

View file

@ -222,14 +222,15 @@ function gsp_update_settings()
{
global $settings;
$repo_root = !empty($settings['gsp_update_repo_root']) ? (string)$settings['gsp_update_repo_root'] : GSP_DEFAULT_REPO_ROOT;
$panel_path = !empty($settings['gsp_update_panel_path']) ? (string)$settings['gsp_update_panel_path'] : GSP_DEFAULT_PANEL_PATH;
$repo_root = rtrim($repo_root, '/');
$panel_path = $repo_root . '/Panel';
return [
'repo_url' => !empty($settings['gsp_update_repo_url']) ? (string)$settings['gsp_update_repo_url'] : GSP_DEFAULT_REPO_URL,
'branch' => !empty($settings['gsp_update_branch']) ? (string)$settings['gsp_update_branch'] : GSP_DEFAULT_BRANCH,
'repo_root' => rtrim($repo_root, '/'),
'repo_root' => $repo_root,
'panel_path' => rtrim($panel_path, '/'),
'panel_source_path' => !empty($settings['gsp_update_panel_source_path']) ? trim((string)$settings['gsp_update_panel_source_path'], '/') : GSP_DEFAULT_PANEL_SOURCE,
'git_path' => !empty($settings['gsp_update_git_path']) ? trim((string)$settings['gsp_update_git_path']) : 'git',
'panel_source_path' => GSP_DEFAULT_PANEL_SOURCE,
'git_path' => 'git',
'backup_path' => !empty($settings['gsp_update_backup_path']) ? rtrim((string)$settings['gsp_update_backup_path'], '/') : GSP_BACKUP_BASE,
'backup_retention' => !empty($settings['gsp_update_backup_retention']) ? (string)$settings['gsp_update_backup_retention'] : (string)GSP_DEFAULT_BACKUP_RETENTION,
'panel_post_update_command' => !empty($settings['gsp_update_panel_post_update_command']) ? (string)$settings['gsp_update_panel_post_update_command'] : '',
@ -268,15 +269,6 @@ $errors[] = ucfirst(str_replace('_', ' ', $key)) . ' must be a safe absolute pat
if (isset($cfg['backup_retention']) && (!preg_match('/^\d+$/', (string)$cfg['backup_retention']) || (int)$cfg['backup_retention'] < 1 || (int)$cfg['backup_retention'] > 200)) {
$errors[] = 'Backup retention must be a whole number between 1 and 200.';
}
foreach (['panel_source_path'] as $key) {
$value = isset($cfg[$key]) ? trim((string)$cfg[$key], '/') : '';
if ($value === '' || strpos($value, "\0") !== false || strpos($value, '..') !== false || !preg_match('/^[A-Za-z0-9._\/-]+$/', $value)) {
$errors[] = ucfirst(str_replace('_', ' ', $key)) . ' is invalid.';
}
}
if (isset($cfg['git_path']) && trim((string)$cfg['git_path']) !== '' && (strpos((string)$cfg['git_path'], "\0") !== false || strpos((string)$cfg['git_path'], "\n") !== false || strpos((string)$cfg['git_path'], "\r") !== false)) {
$errors[] = 'Git executable path is invalid.';
}
foreach (['panel_post_update_command'] as $key) {
if (isset($cfg[$key]) && (strpos((string)$cfg[$key], "\0") !== false || strpos((string)$cfg[$key], "\n") !== false || strpos((string)$cfg[$key], "\r") !== false)) {
$errors[] = ucfirst(str_replace('_', ' ', $key)) . ' must be a single-line admin command.';
@ -1588,9 +1580,9 @@ $new_cfg = [
'repo_url' => isset($_POST['gsp_update_repo_url']) ? trim((string)$_POST['gsp_update_repo_url']) : '',
'branch' => isset($_POST['gsp_update_branch']) ? trim((string)$_POST['gsp_update_branch']) : '',
'repo_root' => isset($_POST['gsp_update_repo_root']) ? rtrim(trim((string)$_POST['gsp_update_repo_root']), '/') : '',
'panel_path' => isset($_POST['gsp_update_panel_path']) ? rtrim(trim((string)$_POST['gsp_update_panel_path']), '/') : '',
'panel_source_path' => isset($_POST['gsp_update_panel_source_path']) ? trim((string)$_POST['gsp_update_panel_source_path'], '/') : '',
'git_path' => isset($_POST['gsp_update_git_path']) ? trim((string)$_POST['gsp_update_git_path']) : '',
'panel_path' => isset($_POST['gsp_update_repo_root']) ? rtrim(trim((string)$_POST['gsp_update_repo_root']), '/') . '/Panel' : GSP_DEFAULT_PANEL_PATH,
'panel_source_path' => GSP_DEFAULT_PANEL_SOURCE,
'git_path' => 'git',
'backup_path' => isset($_POST['gsp_update_backup_path']) ? rtrim(trim((string)$_POST['gsp_update_backup_path']), '/') : '',
'backup_retention' => isset($_POST['gsp_update_backup_retention']) ? trim((string)$_POST['gsp_update_backup_retention']) : '',
'panel_post_update_command' => isset($_POST['gsp_update_panel_post_update_command']) ? trim((string)$_POST['gsp_update_panel_post_update_command']) : '',
@ -1606,7 +1598,7 @@ $db->setSettings([
'gsp_update_repo_root' => $new_cfg['repo_root'],
'gsp_update_panel_path' => $new_cfg['panel_path'],
'gsp_update_panel_source_path' => $new_cfg['panel_source_path'],
'gsp_update_git_path' => $new_cfg['git_path'],
'gsp_update_git_path' => 'git',
'gsp_update_backup_path' => $new_cfg['backup_path'],
'gsp_update_backup_retention' => $new_cfg['backup_retention'],
'gsp_update_panel_post_update_command' => $new_cfg['panel_post_update_command'],
@ -1617,7 +1609,7 @@ $settings['gsp_update_branch'] = $new_cfg['branch'];
$settings['gsp_update_repo_root'] = $new_cfg['repo_root'];
$settings['gsp_update_panel_path'] = $new_cfg['panel_path'];
$settings['gsp_update_panel_source_path'] = $new_cfg['panel_source_path'];
$settings['gsp_update_git_path'] = $new_cfg['git_path'];
$settings['gsp_update_git_path'] = 'git';
$settings['gsp_update_backup_path'] = $new_cfg['backup_path'];
$settings['gsp_update_backup_retention'] = $new_cfg['backup_retention'];
$settings['gsp_update_panel_post_update_command'] = $new_cfg['panel_post_update_command'];
@ -1630,9 +1622,9 @@ $update_cfg = [
'repo_url' => isset($_POST['gsp_update_repo_url']) ? trim((string)$_POST['gsp_update_repo_url']) : $update_cfg['repo_url'],
'branch' => isset($_POST['gsp_update_branch']) ? trim((string)$_POST['gsp_update_branch']) : $update_cfg['branch'],
'repo_root' => isset($_POST['gsp_update_repo_root']) ? rtrim(trim((string)$_POST['gsp_update_repo_root']), '/') : $update_cfg['repo_root'],
'panel_path' => isset($_POST['gsp_update_panel_path']) ? rtrim(trim((string)$_POST['gsp_update_panel_path']), '/') : $update_cfg['panel_path'],
'panel_source_path' => isset($_POST['gsp_update_panel_source_path']) ? trim((string)$_POST['gsp_update_panel_source_path'], '/') : $update_cfg['panel_source_path'],
'git_path' => isset($_POST['gsp_update_git_path']) ? trim((string)$_POST['gsp_update_git_path']) : $update_cfg['git_path'],
'panel_path' => (isset($_POST['gsp_update_repo_root']) ? rtrim(trim((string)$_POST['gsp_update_repo_root']), '/') : $update_cfg['repo_root']) . '/Panel',
'panel_source_path' => GSP_DEFAULT_PANEL_SOURCE,
'git_path' => 'git',
'backup_path' => isset($_POST['gsp_update_backup_path']) ? rtrim(trim((string)$_POST['gsp_update_backup_path']), '/') : $update_cfg['backup_path'],
'backup_retention' => isset($_POST['gsp_update_backup_retention']) ? trim((string)$_POST['gsp_update_backup_retention']) : $update_cfg['backup_retention'],
'panel_post_update_command' => isset($_POST['gsp_update_panel_post_update_command']) ? trim((string)$_POST['gsp_update_panel_post_update_command']) : $update_cfg['panel_post_update_command'],
@ -1644,7 +1636,7 @@ $db->setSettings([
'gsp_update_repo_root' => $update_cfg['repo_root'],
'gsp_update_panel_path' => $update_cfg['panel_path'],
'gsp_update_panel_source_path' => $update_cfg['panel_source_path'],
'gsp_update_git_path' => $update_cfg['git_path'],
'gsp_update_git_path' => 'git',
'gsp_update_backup_path' => $update_cfg['backup_path'],
'gsp_update_backup_retention' => $update_cfg['backup_retention'],
'gsp_update_panel_post_update_command' => $update_cfg['panel_post_update_command'],
@ -1711,19 +1703,40 @@ $preflight_result = gsp_preflight_check($update_cfg);
$ssl_issue_count = !empty($apache_scan_result['ssl_issues']) ? count($apache_scan_result['ssl_issues']) : 0;
echo "<h2>Panel Updates</h2>\n";
echo "<table class='administration-table'><tr><td>\n";
echo "<style>
.gsp-update-shell{display:grid;gap:18px;margin-top:18px}
.gsp-update-card{padding:18px 20px;background:rgba(11,20,36,.92);border:1px solid rgba(77,160,255,.18);border-radius:8px;box-shadow:0 14px 32px rgba(0,0,0,.22)}
.gsp-update-card h3{margin-top:0}
.gsp-update-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 18px}
.gsp-update-grid dt{font-weight:700;color:#e6f0ff}
.gsp-update-grid dd{margin:0;color:#d2def4;word-break:break-word}
.gsp-update-copy{margin:0 0 12px;color:#9fb5d8}
.gsp-update-form-grid{display:grid;grid-template-columns:minmax(180px,240px) minmax(0,1fr);gap:12px 16px;align-items:start}
.gsp-update-form-grid label{font-weight:700;color:#e6f0ff}
.gsp-update-form-grid input[type=text],.gsp-update-form-grid input[type=number]{width:100%;max-width:100%;box-sizing:border-box}
.gsp-update-actions,.gsp-update-inline-actions{display:flex;flex-wrap:wrap;gap:10px}
.gsp-update-note{display:block;margin-top:6px;color:#9fb5d8}
.gsp-update-path{word-break:break-all}
.gsp-update-details{margin-top:12px}
.gsp-update-details summary{cursor:pointer}
.gsp-update-select{max-width:100%;width:100%}
@media (max-width: 720px){.gsp-update-grid,.gsp-update-form-grid{grid-template-columns:1fr}.gsp-update-shell{gap:14px}}
</style>\n";
echo "<div class='gsp-update-shell'>\n";
echo "<section class='gsp-update-card'>\n";
echo "<h3>Current Installation</h3>\n";
echo "<table class='center'>\n";
echo "<tr><td><strong>Installed Version:</strong></td><td>" . htmlspecialchars($current_version) . "</td></tr>\n";
echo "<tr><td><strong>Current Branch:</strong></td><td>" . htmlspecialchars($current_branch) . "</td></tr>\n";
echo "<p class='gsp-update-copy'>Managed backups are stored update snapshots. Rollback backups are full snapshots currently available for restoration.</p>\n";
echo "<dl class='gsp-update-grid'>\n";
echo "<dt>Installed Version</dt><dd>" . htmlspecialchars($current_version) . "</dd>\n";
echo "<dt>Current Branch</dt><dd>" . htmlspecialchars($current_branch) . "</dd>\n";
if ($git_commit) {
echo "<tr><td><strong>Git Commit:</strong></td><td>" . htmlspecialchars(substr($git_commit, 0, 12)) . "</td></tr>\n";
echo "<dt>Git Commit</dt><dd>" . htmlspecialchars(substr($git_commit, 0, 12)) . "</dd>\n";
}
echo "<tr><td><strong>Update Trace Log:</strong></td><td><code>" . htmlspecialchars(GSP_UPDATE_LOG) . "</code></td></tr>\n";
echo "<tr><td><strong>Backup Path:</strong></td><td><code>" . htmlspecialchars($backup_base) . "</code></td></tr>\n";
echo "<tr><td><strong>Managed Backups Stored:</strong></td><td>" . intval(count($managed_backups)) . " (retention: " . intval($backup_retention) . ")</td></tr>\n";
echo "<tr><td><strong>Rollback Backups Available:</strong></td><td>" . intval(count($backups)) . "</td></tr>\n";
echo "</table>\n";
echo "<dt>Update Trace Log</dt><dd class='gsp-update-path'><code>" . htmlspecialchars(GSP_UPDATE_LOG) . "</code></dd>\n";
echo "<dt>Backup Path</dt><dd class='gsp-update-path'><code>" . htmlspecialchars($backup_base) . "</code></dd>\n";
echo "<dt>Managed Backups Stored</dt><dd>" . intval(count($managed_backups)) . " (retention: " . intval($backup_retention) . ")</dd>\n";
echo "<dt>Rollback Backups Available</dt><dd>" . intval(count($backups)) . "</dd>\n";
echo "</dl>\n";
if ($ssl_issue_count > 0) {
echo "<p style='color:#8a6d3b;'><strong>SSL warning:</strong> " . intval($ssl_issue_count) . " Apache SSL certificate issue(s) detected. Updates are not blocked by this. See Advanced Diagnostics for details.</p>\n";
}
@ -1733,41 +1746,51 @@ echo "<p style='color:#a94442;'><strong>Update preflight errors:</strong><br>" .
if (!empty($preflight_result['warnings'])) {
echo "<p style='color:#8a6d3b;'><strong>Update preflight warnings:</strong><br>" . implode('<br>', array_map('htmlspecialchars', $preflight_result['warnings'])) . "</p>\n";
}
echo "</section>\n";
echo "<h3>Repository Settings</h3>\n";
echo "<section class='gsp-update-card'>\n";
echo "<h3>Update Settings</h3>\n";
echo "<form method='POST'>\n";
echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($csrf_token) . "'>\n";
echo "<table class='center'>\n";
echo "<tr><td><strong>Repository URL / Path</strong></td><td><input type='text' name='gsp_update_repo_url' value='" . htmlspecialchars($update_cfg['repo_url'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n";
echo "<tr><td><strong>Branch / Channel</strong></td><td><input type='text' name='gsp_update_branch' value='" . htmlspecialchars($update_cfg['branch'], ENT_QUOTES, 'UTF-8') . "' size='40'></td></tr>\n";
echo "<tr><td><strong>Repository Root</strong></td><td><input type='text' name='gsp_update_repo_root' value='" . htmlspecialchars($update_cfg['repo_root'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n";
echo "<tr><td><strong>Panel Path</strong></td><td><input type='text' name='gsp_update_panel_path' value='" . htmlspecialchars($update_cfg['panel_path'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n";
echo "<tr><td><strong>Panel Source Folder</strong></td><td><input type='text' name='gsp_update_panel_source_path' value='" . htmlspecialchars($update_cfg['panel_source_path'], ENT_QUOTES, 'UTF-8') . "' size='45'></td></tr>\n";
echo "<tr><td><strong>Git Executable</strong></td><td><input type='text' name='gsp_update_git_path' value='" . htmlspecialchars($update_cfg['git_path'], ENT_QUOTES, 'UTF-8') . "' size='45'> <small>Usually <code>git</code>.</small></td></tr>\n";
echo "<tr><td><strong>Backup Path</strong></td><td><input type='text' name='gsp_update_backup_path' value='" . htmlspecialchars($update_cfg['backup_path'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n";
echo "<tr><td><strong>Backup Retention</strong></td><td><input type='number' min='1' max='200' name='gsp_update_backup_retention' value='" . htmlspecialchars($update_cfg['backup_retention'], ENT_QUOTES, 'UTF-8') . "' style='width:90px;'> <small>Keep newest backups only.</small></td></tr>\n";
echo "<tr><td><strong>Backup Before Update</strong></td><td><label><input type='checkbox' name='gsp_update_backup_before' value='1' " . (!empty($update_cfg['backup_before_update']) ? "checked" : "") . "> create backup before updating</label></td></tr>\n";
echo "<tr><td><strong>Panel Post-update Command</strong></td><td><input type='text' name='gsp_update_panel_post_update_command' value='" . htmlspecialchars($update_cfg['panel_post_update_command'], ENT_QUOTES, 'UTF-8') . "' size='85'> <small>Admin-only dangerous command.</small></td></tr>\n";
echo "</table>\n";
echo "<p>\n";
echo "<div class='gsp-update-form-grid'>\n";
echo "<label for='gsp_update_repo_url'>Repository URL / Path</label><div><input id='gsp_update_repo_url' type='text' name='gsp_update_repo_url' value='" . htmlspecialchars($update_cfg['repo_url'], ENT_QUOTES, 'UTF-8') . "'><small class='gsp-update-note'>Git URL or safe absolute local repository path.</small></div>\n";
echo "<label for='gsp_update_branch'>Branch / Channel</label><div><input id='gsp_update_branch' type='text' name='gsp_update_branch' value='" . htmlspecialchars($update_cfg['branch'], ENT_QUOTES, 'UTF-8') . "'><small class='gsp-update-note'>The updater checks out this branch before copying the Panel folder.</small></div>\n";
echo "<label for='gsp_update_repo_root'>Repository Root</label><div><input id='gsp_update_repo_root' type='text' name='gsp_update_repo_root' value='" . htmlspecialchars($update_cfg['repo_root'], ENT_QUOTES, 'UTF-8') . "'><small class='gsp-update-note'>Panel Path is derived automatically as <code>" . htmlspecialchars($update_cfg['panel_path'], ENT_QUOTES, 'UTF-8') . "</code>.</small></div>\n";
echo "<label for='gsp_update_backup_path'>Backup Path</label><div><input id='gsp_update_backup_path' type='text' name='gsp_update_backup_path' value='" . htmlspecialchars($update_cfg['backup_path'], ENT_QUOTES, 'UTF-8') . "'></div>\n";
echo "<label for='gsp_update_backup_retention'>Backup Retention</label><div><input id='gsp_update_backup_retention' type='number' min='1' max='200' name='gsp_update_backup_retention' value='" . htmlspecialchars($update_cfg['backup_retention'], ENT_QUOTES, 'UTF-8') . "'><small class='gsp-update-note'>Keep only the newest managed backups.</small></div>\n";
echo "<label>Backup Before Update</label><div><label><input type='checkbox' name='gsp_update_backup_before' value='1' " . (!empty($update_cfg['backup_before_update']) ? "checked" : "") . "> create a full backup before updating</label></div>\n";
echo "</div>\n";
echo "<details class='gsp-update-details'>\n";
echo "<summary><strong>Advanced Settings</strong></summary>\n";
echo "<div class='gsp-update-form-grid' style='margin-top:12px;'>\n";
echo "<label>Panel Source Folder</label><div><code>" . htmlspecialchars($update_cfg['panel_source_path'], ENT_QUOTES, 'UTF-8') . "</code><small class='gsp-update-note'>The updater currently copies the fixed <code>Panel</code> folder from the checked-out repository.</small></div>\n";
echo "<label>Panel Path</label><div><code class='gsp-update-path'>" . htmlspecialchars($update_cfg['panel_path'], ENT_QUOTES, 'UTF-8') . "</code><small class='gsp-update-note'>Derived from Repository Root plus <code>/Panel</code>.</small></div>\n";
echo "<label for='gsp_update_panel_post_update_command'>Post-update Command</label><div><input id='gsp_update_panel_post_update_command' type='text' name='gsp_update_panel_post_update_command' value='" . htmlspecialchars($update_cfg['panel_post_update_command'], ENT_QUOTES, 'UTF-8') . "'><small class='gsp-update-note'>Optional command to run after a successful update. Leave blank unless required.</small></div>\n";
echo "</div>\n";
echo "</details>\n";
echo "<p class='gsp-update-actions'>\n";
echo "<button type='submit' name='gsp_update_action' value='save_settings'>Save Settings</button> ";
echo "<button type='submit' name='gsp_update_action' value='update_configured' onclick='return confirm(\"Update Panel from the configured repository and branch?\");'>Update Panel</button>\n";
echo "</p>\n";
echo "</form>\n";
echo "</section>\n";
echo "<section class='gsp-update-card'>\n";
echo "<h3>Backup</h3>\n";
echo "<form method='POST'>\n";
echo "<input type='hidden' name='gsp_update_action' value='backup_only'>\n";
echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($csrf_token) . "'>\n";
echo "<button type='submit'>Create Backup</button>\n";
echo "</form>\n";
echo "</section>\n";
if (!empty($backups)) {
echo "<section class='gsp-update-card'>\n";
echo "<h3>Rollback</h3>\n";
echo "<form method='POST'>\n";
echo "<input type='hidden' name='gsp_update_action' value='revert'>\n";
echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($csrf_token) . "'>\n";
echo "<select name='gsp_revert_backup'>\n";
echo "<select class='gsp-update-select' name='gsp_revert_backup'>\n";
foreach ($backups as $bk) {
$label = htmlspecialchars($bk['ts']);
if (!empty($bk['meta']['update_target_type']) && !empty($bk['meta']['update_target_version'])) {
@ -1776,26 +1799,29 @@ $label .= ' (before ' . htmlspecialchars($bk['meta']['update_target_type']) . ':
echo "<option value='" . htmlspecialchars($bk['ts']) . "'>{$label}</option>\n";
}
echo "</select> ";
echo "<span class='gsp-update-note'>Rollback restores only full timestamped snapshots.</span>";
echo "<label><input type='checkbox' name='gsp_restore_apache' value='1'> restore Apache configs if backup contains them</label> ";
echo "<button type='submit' onclick='return confirm(\"Restore Panel, version.json, and database from selected backup?\");'>Rollback</button>\n";
echo "</form>\n";
echo "</section>\n";
}
echo "<details style='margin-top:18px;'>\n";
echo "<section class='gsp-update-card'>\n";
echo "<details>\n";
echo "<summary><strong>Advanced Diagnostics</strong></summary>\n";
echo "<h4>Preflight</h4>\n";
echo "<table class='center'>\n";
echo "<tr><td><strong>Status:</strong></td><td>" . ($preflight_result['success'] ? 'PASS' : 'FAIL') . "</td></tr>\n";
echo "<tr><td><strong>Pending Patches:</strong></td><td>" . intval(count($patch_overview['pending'])) . "</td></tr>\n";
echo "<tr><td><strong>Patch Directory:</strong></td><td><code>" . htmlspecialchars(GSP_PATCH_DIR) . "</code></td></tr>\n";
echo "</table>\n";
echo "<dl class='gsp-update-grid'>\n";
echo "<dt>Status</dt><dd>" . ($preflight_result['success'] ? 'PASS' : 'FAIL') . "</dd>\n";
echo "<dt>Pending Patches</dt><dd>" . intval(count($patch_overview['pending'])) . "</dd>\n";
echo "<dt>Patch Directory</dt><dd class='gsp-update-path'><code>" . htmlspecialchars(GSP_PATCH_DIR) . "</code></dd>\n";
echo "</dl>\n";
echo "<h4>Apache</h4>\n";
echo "<table class='center'>\n";
echo "<tr><td><strong>Config Directory:</strong></td><td><code>" . htmlspecialchars($apache_scan_result['base']) . "</code></td></tr>\n";
echo "<tr><td><strong>Configs Found:</strong></td><td>" . intval(count($apache_scan_result['files'])) . "</td></tr>\n";
echo "<tr><td><strong>Stale Path Hits:</strong></td><td>" . intval(count($apache_scan_result['stale_issues'])) . "</td></tr>\n";
echo "<tr><td><strong>SSL Certificate Issues:</strong></td><td>" . intval($ssl_issue_count) . "</td></tr>\n";
echo "</table>\n";
echo "<dl class='gsp-update-grid'>\n";
echo "<dt>Config Directory</dt><dd class='gsp-update-path'><code>" . htmlspecialchars($apache_scan_result['base']) . "</code></dd>\n";
echo "<dt>Configs Found</dt><dd>" . intval(count($apache_scan_result['files'])) . "</dd>\n";
echo "<dt>Stale Path Hits</dt><dd>" . intval(count($apache_scan_result['stale_issues'])) . "</dd>\n";
echo "<dt>SSL Certificate Issues</dt><dd>" . intval($ssl_issue_count) . "</dd>\n";
echo "</dl>\n";
if (!empty($apache_scan_result['stale_issues'])) {
echo "<p style='color:#a94442;'><strong>Apache stale path issues:</strong><br>" . implode('<br>', array_map('htmlspecialchars', array_unique($apache_scan_result['stale_issues']))) . "</p>\n";
}
@ -1822,7 +1848,8 @@ echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($c
echo "<button type='submit' onclick='return confirm(\"Backup Apache configs, run configtest, and apply path fixes?\");'>Fix Apache Paths</button>\n";
echo "</form>\n";
echo "</details>\n";
echo "</td></tr></table>\n";
echo "</section>\n";
echo "</div>\n";
if ($auto_restart_payload) {
gsp_render_restart_form($auto_restart_payload['action'], $csrf_token, $auto_restart_payload['nonce'], isset($auto_restart_payload['version']) ? $auto_restart_payload['version'] : '');

View file

@ -6,79 +6,53 @@
border-radius:0px;
-moz-border-radius:0px;
}
#column2 #ref.online_servers div{
float:left;
text-align:left;
width:35%;
height:15px;
overflow: hidden;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
-moz-binding: url('assets/xml/ellipsis.xml#ellipsis');
}
#column2 #ref.online_servers div#gamelink{
width:36%;
float:right;
text-align:right;
}
#column2 #ref.online_servers div.name{
width:29%;
height:18px;
white-space:nowrap;
}
#column2 #noref.online_servers div#gamelink{
float:right;
text-align:right;
width:40%;
height:18px;
overflow: hidden;
}
#column2 #noref.online_servers div.name{
float:left;
text-align:left;
width:57%;
height:18px;
overflow: hidden;
.dashboard-welcome {
margin: 0 0 18px;
}
.dragbox.bloc.rounded {
.dashboard-card-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 18px;
margin-top: 20px;
}
.dashboard-action-card {
padding: 18px 20px;
background: linear-gradient(180deg, rgba(10, 18, 32, 0.92) 0%, rgba(14, 25, 45, 0.94) 100%);
border: 1px solid rgba(77, 160, 255, 0.18);
border-radius: 8px;
box-shadow: 0 14px 32px rgba(0, 0, 0, 0.22);
color: #d2def4;
min-height: 100%;
}
.dragbox.bloc.rounded > h4,
.dashboard-promo-card > h4,
.dashboard-secondary-card > h4 {
margin: 0 0 12px;
.dashboard-card-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
margin-bottom: 12px;
}
.dashboard-card-header > h3 {
margin: 0;
padding-bottom: 12px;
border-bottom: 1px solid rgba(77, 160, 255, 0.18);
color: #e6f0ff;
letter-spacing: 0.04em;
flex: 1 1 auto;
}
.dragbox-content {
padding: 4px 2px 2px;
color: #d2def4;
}
.dashboard-widget-copy {
position: relative;
padding-right: 56px;
}
.dashboard-widget-copy p {
.dashboard-action-card p {
margin: 0 0 12px;
}
.dashboard-widget-icon {
position: absolute;
top: 4px;
right: 0;
width: 40px;
height: 40px;
opacity: 0.9;
flex: 0 0 auto;
}
.dashboard-link-group {
@ -132,34 +106,13 @@
box-shadow: 0 0 0 3px rgba(90, 199, 247, 0.12);
}
.dashboard-status-panel {
margin-top: 20px;
}
.dashboard-status-content {
padding: 18px 20px;
text-align: center;
color: #d2def4;
}
.dashboard-status-content p {
margin: 0 0 14px;
}
.dashboard-status-button {
margin-bottom: 10px;
}
.dashboard-status-note {
display: block;
color: #9fb5d8;
@media (max-width: 991px) {
.dashboard-card-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 430px) {
.dashboard-widget-copy {
padding-right: 0;
}
.dashboard-cta-button,
.dashboard-support-link {
width: 100%;
@ -167,8 +120,7 @@
}
.dashboard-widget-icon {
position: static;
display: block;
margin: 0 0 12px auto;
width: 36px;
height: 36px;
}
}

View file

@ -1,5 +1,3 @@
<script type="text/javascript" src="js/jquery/plugins/jquery.json-2.3.min.js"></script>
<script type="text/javascript" src="js/modules/dashboard.js"></script>
<?php
/*
*
@ -24,248 +22,92 @@
*
*/
require_once('includes/lib_remote.php');
function exec_ogp_module()
function exec_ogp_module()
{
global $db, $settings, $loggedInUserInfo;
global $settings;
$projectRequestUrl = htmlspecialchars(gsp_project_request_url(), ENT_QUOTES, 'UTF-8');
$discordInviteUrl = htmlspecialchars(gsp_discord_invite_url(), ENT_QUOTES, 'UTF-8');
$serverStatusUrl = htmlspecialchars(gsp_server_status_url(), ENT_QUOTES, 'UTF-8');
$isAdmin = $db->isAdmin($_SESSION['user_id']);
$user_id = $_SESSION['user_id'];
$theme = isset($settings['theme']) ? $settings['theme'] : 'SimpleBootstrap';
$themeBase = 'themes/' . htmlspecialchars($theme, ENT_QUOTES, 'UTF-8') . '/images/icons/';
$cards = array(
array(
'title' => 'Account Overview',
'copy' => 'Review your assigned game servers and open the Game Monitor.',
'button_label' => 'View My Servers',
'button_class' => 'dashboard-cta-button',
'url' => 'home.php?m=gamemanager&p=game_monitor',
'icon' => $themeBase . 'game_monitor.png',
),
array(
'title' => 'Custom Server Code',
'copy' => 'Need something beyond the standard server options? Our developers can customize, repair, automate, or extend your game server with mods, scripts, integrations, migrations, and server-specific tools.',
'secondary' => 'Tell us what you want to build or improve, and we will review the request with you.',
'button_label' => 'Request Custom Development',
'button_class' => 'dashboard-cta-button',
'url' => $projectRequestUrl,
'icon' => $themeBase . 'folder.png',
'external' => true,
),
array(
'title' => 'Support',
'copy' => 'Need help with an existing service? Create a support ticket or join our Discord support server.',
'secondary' => 'Support covers service problems, broken functionality, routine troubleshooting, and help using the features already included with your server.',
'button_label' => 'Create Support Ticket',
'button_class' => 'dashboard-support-link',
'url' => 'home.php?m=tickets&p=submitticket',
'icon' => $themeBase . 'support.png',
'secondary_button_label' => 'Join Discord',
'secondary_button_class' => 'dashboard-support-link dashboard-support-link-secondary',
'secondary_url' => $discordInviteUrl,
'secondary_external' => true,
),
array(
'title' => 'Server Status',
'copy' => 'View the current availability of all configured game-server locations.',
'secondary' => 'Status checks run only when you open the status page, so normal dashboard loading stays fast.',
'button_label' => 'View Server Status',
'button_class' => 'dashboard-cta-button',
'url' => $serverStatusUrl,
'icon' => $themeBase . 'game_monitor.png',
'external' => true,
),
);
$page_user = (isset($_GET['page']) && (int)$_GET['page'] > 0) ? (int)$_GET['page'] : 1; // thanks for Adjokip
$limit_user = isset($_GET['limit']) ? $_GET['limit'] : 10;
if(hasValue($loggedInUserInfo) && is_array($loggedInUserInfo) && $loggedInUserInfo["users_page_limit"] && !(isset($_GET['limit']) and !empty($_GET['limit']))){
$limit_user = $loggedInUserInfo["users_page_limit"];
}
if( isset($settings['welcome_title']) && $settings['welcome_title'] == "1" )
{
if( isset($settings['welcome_title_message']) && !empty($settings['welcome_title_message'] ))
{
echo "<div>" . $settings['welcome_title_message'] . "</div>";
}
if (isset($settings['welcome_title']) && $settings['welcome_title'] == "1" && isset($settings['welcome_title_message']) && !empty($settings['welcome_title_message'])) {
echo "<div class='dashboard-welcome'>" . $settings['welcome_title_message'] . "</div>";
}
require_once("includes/refreshed.php");
$refresh = new refreshed();
$OnlineServers = "<p>Recent updates and changes</p>";
?>
<div style="margin-top:20px;">
<?php
//$title[$id] = "The Title";
//$content[$id] = "Content of the Widget";
$title = array();
$content = array();
$href = array();
// Account Overview
$title[1] = "Account Overview";
$content[1] = 'Review your assigned servers and jump straight into the Game Monitor.';
$href[1] = 'home.php?m=gamemanager&p=game_monitor';
// Recent News
//$xml=simplexml_load_file("modules/news/data/listings.xml");
//$lastnews = count((array)$xml)-1;
//$title[2] = "Recent News";
//$content[2] = $xml->listing[$lastnews]->title;
//$href[2] = 'home.php?m=news&p=news';
// Notifications
$title[3] = "Notifications"; // get_lang('orders');
$content[3] = 'View all your notifications. ';
$href[3] = 'home.php?m=circular&p=show_circular&list=true';
// Custom Server Code
$title[4] = 'Custom Server Code';
$content[4] = '<div class="dashboard-widget-copy">'
. '<p>Need something beyond the standard server options? Our developers can customize, repair, automate, or extend your game server with mods, scripts, integrations, migrations, and server-specific tools.</p>'
. '<p class="dashboard-card-note">Tell us what you want to build or improve, and we will review the request with you.</p>'
. '<a class="dashboard-cta-button" href="' . $projectRequestUrl . '" target="_blank" rel="noopener noreferrer">Request Custom Development</a>'
. '</div>';
$href[4] = null;
// Support
$title[5] = 'Support';
$content[5] = '<div class="dashboard-widget-copy">'
. '<img src="themes/' . $settings['theme'] . '/images/icons/support.png" class="dashboard-widget-icon" alt="" />'
. '<p>Need help with an existing service? Submit a support ticket or join our Discord support server.</p>'
. '<p class="dashboard-card-note">Support is for service problems, broken functionality, routine troubleshooting, and help using the features already included with your server.</p>'
. '<div class="dashboard-link-group">'
. '<a class="dashboard-support-link" href="home.php?m=tickets">Open Support Tickets</a>'
. '<a class="dashboard-support-link dashboard-support-link-secondary" href="' . $discordInviteUrl . '" target="_blank" rel="noopener noreferrer">Join Discord Support</a>'
. '</div>'
. '</div>';
$href[5] = null;
$widgets = $db->resultQuery("SELECT * FROM OGP_DB_PREFIXwidgets_users WHERE user_id='".$_SESSION['user_id']."' ORDER BY sort_no");
if(!$widgets)
{
if($db->createUserWidgets($_SESSION['user_id']))
$widgets = $db->resultQuery("SELECT * FROM OGP_DB_PREFIXwidgets_users WHERE user_id='".$_SESSION['user_id']."' ORDER BY sort_no");
}
if($widgets)
{
$colhtml[1] = '<div class="column one_fourth" id="column1" >';
$colhtml[2] = '<div class="column one_two" id="column2" >';
$colhtml[3] = '<div class="column one_fourth" id="column3" >';
foreach ((array)$widgets as $widget)
{
if(array_key_exists($widget["widget_id"], (array)$title)){
if( (!isset($settings['old_dashboard_behavior']) or $settings['old_dashboard_behavior'] == 0) AND $widget['widget_id'] == "3" )
continue;
$colhtml[$widget['column_id']] .= '<div class="dragbox bloc rounded" id="item'.$widget['widget_id'].'">'.
'<h4><span class="configure"></span>';
if(!is_null($title[$widget['widget_id']]))
$colhtml[$widget['column_id']] .= $title[$widget['widget_id']];
$colhtml[$widget['column_id']] .= '</h4><div class="dragbox-content" ';
if(!is_null($href[$widget['widget_id']]))
{
$colhtml[$widget['column_id']] .= "onclick=\"location.href='". $href[$widget['widget_id']] . "'\" style=\"cursor:pointer;";
if($widget['collapsed']==1)
$colhtml[$widget['column_id']] .= 'display:none;';
$colhtml[$widget['column_id']] .= '"';
}
elseif($widget['collapsed']==1)
$colhtml[$widget['column_id']] .= 'style="display:none;"';
$colhtml[$widget['column_id']] .= '>';
if(!is_null($content[$widget['widget_id']]))
$colhtml[$widget['column_id']] .= $content[$widget['widget_id']];
$colhtml[$widget['column_id']] .= '</div></div>';
}
echo "<div class='dashboard-card-grid'>";
foreach ($cards as $card) {
echo "<section class='dashboard-action-card'>";
echo "<div class='dashboard-card-header'>";
echo "<h3>" . htmlspecialchars($card['title'], ENT_QUOTES, 'UTF-8') . "</h3>";
if (!empty($card['icon'])) {
echo "<img src='" . $card['icon'] . "' class='dashboard-widget-icon' alt=''>";
}
foreach ((array)$colhtml as $html)
echo $html.'</div>';
}
// Server Status Link - Available to all users
echo "<div class='dashboard-status-panel'>
<div class='bloc rounded'>
<h4>Server Status</h4>
<div class='dashboard-status-content'>
<p>Check the current state of configured remote servers without delaying the main dashboard.</p>
<a class='dashboard-cta-button dashboard-status-button' href='{$serverStatusUrl}' target='_blank' rel='noopener noreferrer'>View Server Status</a>
<small class='dashboard-status-note'>Opens in a new tab and runs remote checks only when requested.</small>
</div>
</div>
</div>";
if( $isAdmin AND $db->isModuleInstalled('status') )
{
echo "<h0>".get_lang('server_status')."</h0><br>";
$servers = $db->getRemoteServers();
echo "<div id='column4' style='float:left;width:40%;' >
<div class='bloc rounded' >
<h4>".get_lang('select_remote_server')."</h4>
<div>
<br>
<center>
<form action='' method='GET'>
<input type='hidden' name='m' value='".$_GET['m']."'/>
<input type='hidden' name='p' value='".$_GET['p']."'/>
<select name='remote_server_id' onchange=".'"this.form.submit()"'.">\n";
$agents_ips = array();
foreach ((array)$servers as $server_row)
{
$agents_ips[$server_row['remote_server_id']] = gethostbyname($server_row['agent_ip']);
if( !empty( $server_row['remote_server_id'] ) and !isset( $_GET['remote_server_id'] ) OR !empty( $server_row['remote_server_id'] ) and empty( $_GET['remote_server_id'] ) )
{
$_GET['remote_server_id'] = $server_row['remote_server_id'];
}
if( isset($_GET['remote_server_id']) AND $_GET['remote_server_id'] == $server_row['remote_server_id'] )
{
$remote = new OGPRemoteLibrary( $server_row['agent_ip'], $server_row['agent_port'],
$server_row['encryption_key'], $server_row['timeout'] );
$host_stat = $remote->status_chk();
if( $host_stat === 1 )
{
$checked = "selected='selected'";
}
else
{
$checked = '';
$_GET['remote_server_id'] = 'webhost';
}
}
else
{
$checked = '';
}
echo "<option value='".$server_row['remote_server_id']."' $checked >".$server_row['remote_server_name']."</option>\n";
echo "</div>";
echo "<p>" . htmlspecialchars($card['copy'], ENT_QUOTES, 'UTF-8') . "</p>";
if (!empty($card['secondary'])) {
echo "<p class='dashboard-card-note'>" . htmlspecialchars($card['secondary'], ENT_QUOTES, 'UTF-8') . "</p>";
}
if ( function_exists('exec') )
{
$host_ip = isset($_SERVER['LOCAL_ADDR']) ? $_SERVER['LOCAL_ADDR'] : $_SERVER['SERVER_ADDR'];
$remote_server_id = array_search($host_ip,$agents_ips);
$show_webhost = true;
if($remote_server_id)
{
$remote_server = $db->getRemoteServer($remote_server_id);
$remote = new OGPRemoteLibrary( $remote_server['agent_ip'], $remote_server['agent_port'],
$remote_server['encryption_key'], $remote_server['timeout'] );
$host_stat = $remote->status_chk();
if( $host_stat === 1 )
$show_webhost = false;
}
if($show_webhost)
{
$checked = ( isset($_GET['remote_server_id']) AND $_GET['remote_server_id'] == 'webhost' ) ? "selected='selected'" : "";
echo "<option value='webhost' $checked >Webhost Status</option>";
}
echo "<div class='dashboard-link-group'>";
echo "<a class='" . htmlspecialchars($card['button_class'], ENT_QUOTES, 'UTF-8') . "' href='" . htmlspecialchars($card['url'], ENT_QUOTES, 'UTF-8') . "'";
if (!empty($card['external'])) {
echo " target='_blank' rel='noopener noreferrer'";
}
echo " </select>
</form>
</center>
<br><br>
</div>
</div>
</div>\n";
if( isset($_GET['remote_server_id']) AND ( $_GET['remote_server_id'] == "webhost" or $_GET['remote_server_id'] == "" ) )
unset($_GET['remote_server_id']);
if( isset($_GET['remote_server_id']) )
$remote_server = "&remote_server_id=".$_GET['remote_server_id'];
else
$remote_server = "";
if( isset($_GET['remote_server_id']) OR function_exists('exec') )
echo $refresh->getdiv($refresh->add("home.php?m=status&type=cleared".$remote_server));
echo ">" . htmlspecialchars($card['button_label'], ENT_QUOTES, 'UTF-8') . "</a>";
if (!empty($card['secondary_button_label']) && !empty($card['secondary_url'])) {
echo "<a class='" . htmlspecialchars($card['secondary_button_class'], ENT_QUOTES, 'UTF-8') . "' href='" . htmlspecialchars($card['secondary_url'], ENT_QUOTES, 'UTF-8') . "'";
if (!empty($card['secondary_external'])) {
echo " target='_blank' rel='noopener noreferrer'";
}
echo ">" . htmlspecialchars($card['secondary_button_label'], ENT_QUOTES, 'UTF-8') . "</a>";
}
echo "</div>";
echo "</section>";
}
?>
</div>
<script type="text/javascript">
$(document).ready(function(){
<?php echo $refresh->build(isset($settings['query_cache_life']) ? $settings['query_cache_life'] * 2000 : 60000); ?>
});
</script>
<?php
echo "</div>";
}
?>