From 28533be24dd56e7a682c98647ec14e3299551537 Mon Sep 17 00:00:00 2001 From: Frank Harris Date: Wed, 17 Jun 2026 09:15:42 -0500 Subject: [PATCH] small fixes --- Panel/modules/administration/panel_update.php | 147 +++++---- Panel/modules/dashboard/dashboard.css | 112 ++----- Panel/modules/dashboard/dashboard.php | 308 +++++------------- Panel/server_status.php | 118 +------ docs/modules/PANEL_UPDATE.md | 22 ++ docs/modules/UPDATE.md | 29 +- docs/modules/dashboard.md | 57 ++-- docs/modules/status.md | 24 +- docs/modules/update.md | 17 +- 9 files changed, 310 insertions(+), 524 deletions(-) diff --git a/Panel/modules/administration/panel_update.php b/Panel/modules/administration/panel_update.php index 5773699a..c356d33e 100644 --- a/Panel/modules/administration/panel_update.php +++ b/Panel/modules/administration/panel_update.php @@ -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 "

Panel Updates

\n"; -echo "
\n"; +echo "\n"; +echo "
\n"; +echo "
\n"; echo "

Current Installation

\n"; -echo "\n"; -echo "\n"; -echo "\n"; +echo "

Managed backups are stored update snapshots. Rollback backups are full snapshots currently available for restoration.

\n"; +echo "
\n"; +echo "
Installed Version
" . htmlspecialchars($current_version) . "
\n"; +echo "
Current Branch
" . htmlspecialchars($current_branch) . "
\n"; if ($git_commit) { -echo "
\n"; +echo "
Git Commit
" . htmlspecialchars(substr($git_commit, 0, 12)) . "
\n"; } -echo "\n"; - echo "\n"; - echo "\n"; - echo "\n"; -echo "
Installed Version:" . htmlspecialchars($current_version) . "
Current Branch:" . htmlspecialchars($current_branch) . "
Git Commit:" . htmlspecialchars(substr($git_commit, 0, 12)) . "
Update Trace Log:" . htmlspecialchars(GSP_UPDATE_LOG) . "
Backup Path:" . htmlspecialchars($backup_base) . "
Managed Backups Stored:" . intval(count($managed_backups)) . " (retention: " . intval($backup_retention) . ")
Rollback Backups Available:" . intval(count($backups)) . "
\n"; +echo "
Update Trace Log
" . htmlspecialchars(GSP_UPDATE_LOG) . "
\n"; +echo "
Backup Path
" . htmlspecialchars($backup_base) . "
\n"; +echo "
Managed Backups Stored
" . intval(count($managed_backups)) . " (retention: " . intval($backup_retention) . ")
\n"; +echo "
Rollback Backups Available
" . intval(count($backups)) . "
\n"; +echo "\n"; if ($ssl_issue_count > 0) { echo "

SSL warning: " . intval($ssl_issue_count) . " Apache SSL certificate issue(s) detected. Updates are not blocked by this. See Advanced Diagnostics for details.

\n"; } @@ -1733,41 +1746,51 @@ echo "

Update preflight errors:
" . if (!empty($preflight_result['warnings'])) { echo "

Update preflight warnings:
" . implode('
', array_map('htmlspecialchars', $preflight_result['warnings'])) . "

\n"; } +echo "
\n"; -echo "

Repository Settings

\n"; +echo "
\n"; +echo "

Update Settings

\n"; echo "
\n"; echo "\n"; -echo "\n"; -echo "\n"; -echo "\n"; -echo "\n"; -echo "\n"; -echo "\n"; - echo "\n"; - echo "\n"; - echo "\n"; - echo "\n"; -echo "\n"; -echo "
Repository URL / Path
Branch / Channel
Repository Root
Panel Path
Panel Source Folder
Git Executable Usually git.
Backup Path
Backup Retention Keep newest backups only.
Backup Before Update
Panel Post-update Command Admin-only dangerous command.
\n"; -echo "

\n"; +echo "

\n"; +echo "
Git URL or safe absolute local repository path.
\n"; +echo "
The updater checks out this branch before copying the Panel folder.
\n"; +echo "
Panel Path is derived automatically as " . htmlspecialchars($update_cfg['panel_path'], ENT_QUOTES, 'UTF-8') . ".
\n"; +echo "
\n"; +echo "
Keep only the newest managed backups.
\n"; +echo "
\n"; +echo "
\n"; +echo "
\n"; +echo "Advanced Settings\n"; +echo "
\n"; +echo "
" . htmlspecialchars($update_cfg['panel_source_path'], ENT_QUOTES, 'UTF-8') . "The updater currently copies the fixed Panel folder from the checked-out repository.
\n"; +echo "
" . htmlspecialchars($update_cfg['panel_path'], ENT_QUOTES, 'UTF-8') . "Derived from Repository Root plus /Panel.
\n"; +echo "
Optional command to run after a successful update. Leave blank unless required.
\n"; +echo "
\n"; +echo "
\n"; +echo "

\n"; echo " "; echo "\n"; echo "

\n"; echo "
\n"; +echo "
\n"; +echo "
\n"; echo "

Backup

\n"; echo "
\n"; echo "\n"; echo "\n"; echo "\n"; echo "
\n"; +echo "
\n"; if (!empty($backups)) { +echo "
\n"; echo "

Rollback

\n"; echo "
\n"; echo "\n"; echo "\n"; -echo "\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 "\n"; } echo " "; +echo "Rollback restores only full timestamped snapshots."; echo " "; echo "\n"; echo "
\n"; +echo "
\n"; } -echo "
\n"; +echo "
\n"; +echo "
\n"; echo "Advanced Diagnostics\n"; echo "

Preflight

\n"; -echo "\n"; -echo "\n"; -echo "\n"; -echo "\n"; -echo "
Status:" . ($preflight_result['success'] ? 'PASS' : 'FAIL') . "
Pending Patches:" . intval(count($patch_overview['pending'])) . "
Patch Directory:" . htmlspecialchars(GSP_PATCH_DIR) . "
\n"; +echo "
\n"; +echo "
Status
" . ($preflight_result['success'] ? 'PASS' : 'FAIL') . "
\n"; +echo "
Pending Patches
" . intval(count($patch_overview['pending'])) . "
\n"; +echo "
Patch Directory
" . htmlspecialchars(GSP_PATCH_DIR) . "
\n"; +echo "
\n"; echo "

Apache

\n"; -echo "\n"; -echo "\n"; -echo "\n"; -echo "\n"; -echo "\n"; -echo "
Config Directory:" . htmlspecialchars($apache_scan_result['base']) . "
Configs Found:" . intval(count($apache_scan_result['files'])) . "
Stale Path Hits:" . intval(count($apache_scan_result['stale_issues'])) . "
SSL Certificate Issues:" . intval($ssl_issue_count) . "
\n"; +echo "
\n"; +echo "
Config Directory
" . htmlspecialchars($apache_scan_result['base']) . "
\n"; +echo "
Configs Found
" . intval(count($apache_scan_result['files'])) . "
\n"; +echo "
Stale Path Hits
" . intval(count($apache_scan_result['stale_issues'])) . "
\n"; +echo "
SSL Certificate Issues
" . intval($ssl_issue_count) . "
\n"; +echo "
\n"; if (!empty($apache_scan_result['stale_issues'])) { echo "

Apache stale path issues:
" . implode('
', array_map('htmlspecialchars', array_unique($apache_scan_result['stale_issues']))) . "

\n"; } @@ -1822,7 +1848,8 @@ echo "Fix Apache Paths\n"; echo "\n"; echo "
\n"; -echo "
\n"; +echo "\n"; +echo "\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'] : ''); diff --git a/Panel/modules/dashboard/dashboard.css b/Panel/modules/dashboard/dashboard.css index d354b9b0..7fc0099f 100644 --- a/Panel/modules/dashboard/dashboard.css +++ b/Panel/modules/dashboard/dashboard.css @@ -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; } } diff --git a/Panel/modules/dashboard/dashboard.php b/Panel/modules/dashboard/dashboard.php index 695ed32b..61d9132b 100644 --- a/Panel/modules/dashboard/dashboard.php +++ b/Panel/modules/dashboard/dashboard.php @@ -1,5 +1,3 @@ - - 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 "
" . $settings['welcome_title_message'] . "
"; - } + if (isset($settings['welcome_title']) && $settings['welcome_title'] == "1" && isset($settings['welcome_title_message']) && !empty($settings['welcome_title_message'])) { + echo "
" . $settings['welcome_title_message'] . "
"; } - - require_once("includes/refreshed.php"); - $refresh = new refreshed(); - $OnlineServers = "

Recent updates and changes

"; - ?> -
- 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] = '
' - . '

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.

' - . '

Tell us what you want to build or improve, and we will review the request with you.

' - . 'Request Custom Development' - . '
'; - $href[4] = null; - - // Support - $title[5] = 'Support'; - $content[5] = '
' - . '' - . '

Need help with an existing service? Submit a support ticket or join our Discord support server.

' - . '

Support is for service problems, broken functionality, routine troubleshooting, and help using the features already included with your server.

' - . '' - . '
'; - $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] = '
'; - $colhtml[2] = '
'; - $colhtml[3] = '
'; - 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']] .= '
'. - '

'; - if(!is_null($title[$widget['widget_id']])) - $colhtml[$widget['column_id']] .= $title[$widget['widget_id']]; - - $colhtml[$widget['column_id']] .= '

"; + foreach ($cards as $card) { + echo "
"; + echo "
"; + echo "

" . htmlspecialchars($card['title'], ENT_QUOTES, 'UTF-8') . "

"; + if (!empty($card['icon'])) { + echo ""; } - foreach ((array)$colhtml as $html) - echo $html.'
'; - } - - // Server Status Link - Available to all users - echo "
-
-

Server Status

-
-

Check the current state of configured remote servers without delaying the main dashboard.

- View Server Status - Opens in a new tab and runs remote checks only when requested. -
-
-
"; - - if( $isAdmin AND $db->isModuleInstalled('status') ) - { - echo "".get_lang('server_status')."
"; - $servers = $db->getRemoteServers(); - - echo "
-
-

".get_lang('select_remote_server')."

-
-
-
-
- - -