From dc24d4392132b10a98c055ba9f5479b698dd1dc1 Mon Sep 17 00:00:00 2001 From: Frank Harris Date: Thu, 20 Nov 2025 08:10:31 -0500 Subject: [PATCH] site changes by codex --- .github/agent.md | 157 +++++ .github/copilot-instructions.md | 200 +++--- .github/module-map.md | 78 +++ modules/billing/FIXES_APPLIED.md | 3 +- modules/billing/README.md | 3 +- modules/billing/RECENT_FIXES_SUMMARY.md | 5 +- .../_archived/IMPLEMENTATION_SUMMARY.md | 5 +- modules/billing/_archived/README_LOGIN.md | 7 +- modules/billing/admin.php | 1 + modules/billing/adminserverlist.php | 41 +- modules/billing/api/capture_order.php | 116 +++- modules/billing/bootstrap.php | 3 + modules/billing/cart.php | 3 +- modules/billing/create_servers.php | 69 +- modules/billing/diag_remote.php | 3 +- modules/billing/docs.php | 3 +- .../docs/DOCUMENTATION_ENHANCEMENT_SUMMARY.md | 9 +- modules/billing/docs/XML-Notes.md | 622 ++++++++++++++++++ modules/billing/docs/xml_notes.php | 147 +++++ modules/billing/forgot_password.php | 3 +- modules/billing/includes/admin_auth.php | 7 +- modules/billing/includes/login_required.php | 6 +- modules/billing/includes/menu.php | 7 +- modules/billing/includes/panel_bridge.php | 97 +++ modules/billing/includes/session_bridge.php | 32 + modules/billing/login.php | 81 ++- modules/billing/logout.php | 3 +- modules/billing/module.php | 11 +- modules/billing/my_account.php | 3 +- modules/billing/register.php | 3 +- modules/billing/renew_server.php | 3 +- modules/billing/reset_password.php | 3 +- modules/billing/test_db_connection.php | 3 +- modules/config_games/config_servers.php | 246 ++++++- 34 files changed, 1736 insertions(+), 247 deletions(-) create mode 100644 .github/agent.md create mode 100644 .github/module-map.md create mode 100644 modules/billing/docs/XML-Notes.md create mode 100644 modules/billing/docs/xml_notes.php create mode 100644 modules/billing/includes/panel_bridge.php create mode 100644 modules/billing/includes/session_bridge.php diff --git a/.github/agent.md b/.github/agent.md new file mode 100644 index 00000000..558c2550 --- /dev/null +++ b/.github/agent.md @@ -0,0 +1,157 @@ +# GSP (GameServerPanel) – Copilot Instructions + +**Repo of truth:** `GameServerPanel/GSP`, branch `Panel-unstable`. +**Prime directive:** Read this document first. Keep `.github/agent.md` identical to this file—any edit here must be mirrored there in the same commit. + +## Deployment model & paths +- `modules/billing/` houses the public storefront. Those files are always present inside the panel repo and get deployed either (a) as the root of a dedicated virtual host or (b) through the panel module loader (`home.php?m=billing`). +- Because the storefront and the control panel live in the same tree, you may include panel helpers when needed. Use the dedicated bridge include (`modules/billing/includes/panel_bridge.php`) instead of sprinkling ad-hoc `../../includes/...` calls. +- We keep Apache/Nginx vhosts pointed at `modules/billing/`, so every storefront URL must look root-relative (see critical section below). Never expose `/modules/billing` in any URL sent to a browser or external service. +- Before touching billing logic or module wiring, skim `.github/module-map.md` to remember how the panel modules depend on each other. + +## CRITICAL: Website file paths and URLs (modules/billing) +- **The billing website files in `modules/billing/` are deployed at the WEBSITE ROOT when live.** +- **Never output `/modules/billing/` in any link, redirect, script tag, or webhook URL. All user-facing URLs must be root-relative**, e.g. `/payment_success.php`, `/cart.php`. +- Continue to use root-relative URLs inside HTML/JS and when building PayPal return/cancel links. The deployment tooling rewrites the document root; hardcoding `/modules/billing` breaks both standalone hosting and module embedding. + +### Examples of CORRECT usage +```php +$returnUrl = $siteBase . '/payment_success.php'; +header('Location: /order.php'); +
+My Account +``` + +### Examples of WRONG usage (NEVER DO THIS) +```php +$returnUrl = $siteBase . '/modules/billing/payment_success.php'; +header('Location: /modules/billing/cart.php'); +My Account +``` + +### Exception – backend includes only +- Server-side includes may use absolute filesystem paths, but route those through the bridge helpers when panel context is required: + - ✅ `require_once(__DIR__ . '/includes/config.inc.php');` + - ✅ `require_once(__DIR__ . '/includes/panel_bridge.php');` +- Avoid copy/pasting panel bootstrap code; lean on the helpers already shipped inside `modules/billing/includes/`. + +## 1) What to read first +- `.github/module-map.md` – living diagram of how the panel, billing site, daemons, and cron jobs talk to each other. +- `modules/billing/` – storefront runtime, payment handlers, provisioning bridge. +- `modules/config_games/server_configs/` – authoritative XML metadata for every supported game. +- `modules/` – control-panel modules (billing runs here too when embedded). +- `includes/` & `ogp_api.php` – database layer, shared helpers, remote agent operations. + +## 2) Planning mode (default) +While scoping multi-file work, do **not** emit PHP/SQL/XML or run shell commands unless a maintainer explicitly says “Generate code now.” Plans should cover: +- Impacted files and rationale. +- Data mappings (tables/fields) you will touch. +- Risks, rollback notes, validation/tests. + +## 3) Scope & principles +- **Single session across panel + storefront.** Every billing page must call `session_name('opengamepanel_web')` before `session_start()`. Always keep `$_SESSION['user_id']`, `$_SESSION['users_login']`, `$_SESSION['users_group']`, and `$_SESSION['website_user_id']` in sync so that logging into either surface signs the visitor into both. +- **Auth reuse.** Preferred order when verifying credentials: `users_pass_hash` (modern hash) → legacy `users_passwd` (MD5). Upgrading to a modern hash is allowed so long as panel logins keep working. +- **Bridge for panel helpers.** Use `modules/billing/includes/panel_bridge.php` to load panel classes (`OGPDatabase`, `OGPRemoteLibrary`, XML parsers) when the storefront needs to provision servers or read panel-only metadata. Do not reinvent ad-hoc copies of panel logic. +- **Storefront runtime.** Public pages continue to use mysqli with credentials from `modules/billing/includes/config.inc.php`. Provisioning steps may request an `OGPDatabase` handle from the bridge. +- **Provisioning pipeline.** Always funnel server creation or renewals through the shared provisioner (`modules/billing/includes/provisioner.php`). This helper wraps the old `create_servers.php` logic and ensures PayPal captures, cron jobs, and panel clicks all follow the same code path. +- **Catalog = XML.** Never hardcode game metadata. Parse `modules/config_games/server_configs/*.xml` at runtime; new XMLs must show up automatically. +- **Regions/Nodes = live DB.** Pull nodes/locations from the panel DB (`gsp_remote_servers`, etc.). Respect admin enable/disable flags and never mirror node lists into flat files. +- **Game XML wiki parity.** We ship a PHP-rendered version of https://github.com/OpenGamePanel/OGP-Website/wiki/XML-Notes inside `modules/billing/` (linked from the storefront admin area). Keep it updated so maintainers can edit XMLs without leaving the repo. + +## 4) Functional requirements +### 4.1 Catalog (from XML) +- Parse every XML under `modules/config_games/server_configs/`. +- Normalize: game key, display name, install/update commands, default ports, mod metadata. +- XML pages (`modules/billing/docs.php` and the new XML-notes mirror) must stay in sync so AI-powered edits can cross-reference expectations. + +### 4.2 Authentication & sessions +- Website registration must create/maintain panel users. Set both the legacy `users_passwd` and the modern hash column. +- Login flow must hydrate the shared session variables so `home.php` immediately recognizes the visitor. +- All storefront guards should treat `$_SESSION['user_id']` as the source of truth, falling back to `website_user_id` only for older sessions. + +### 4.3 Checkout → PayPal → Provisioning +- Flow: add to cart → invoices (`billing_invoices`) → PayPal order (`api/create_order.php`) → capture (`api/capture_order.php`) → immediately hand off to `BillingProvisioner`. +- Mark invoices paid **only** after verifying PayPal response/webhook. Support multiple servers per payment: loop through every paid invoice and either create a new order or extend an existing service. +- For renewals, extend `end_date` from its current value and keep status at `installed`. For new services set status `installing`, invoke the provisioner, then switch to `installed` on success. +- Provisioner is responsible for calling `modules/billing/create_servers.php` logic, adding homes, assigning ports, enabling FTP, and logging/notifications. Never bypass it. + +### 4.4 Regions/Nodes (multi-remote) +- `remote_servers` and `remote_server_ips` tables remain the source for available locations. Admin tooling (`adminserverlist.php`) must let staff toggle availability and restrict services per location. +- When a node is globally disabled it must disappear (or show as unavailable) in ordering and admin tools. + +### 4.5 Billing automation (website-side) +- Cron/workers under `modules/billing/cron-shop.php` still suspend/delete expired services. Renewals triggered via PayPal must update `billing_orders.status` and `end_date` consistently so cron jobs can pick up where they expect. +- Keep audit logs in `modules/billing/logs/` whenever automatic provisioning, renewals, refunds, or coupon adjustments happen. + +## 5) Data model alignment (no DDL during planning) +- Use panel tables as the source of truth (`gsp_billing_orders`, `gsp_billing_services`, `gsp_billing_invoices`, `gsp_game_mods`, etc.). +- Multi-remote fields (`remote_server_id`, IP IDs) already exist—never introduce duplicates in the storefront DB. +- When you truly need schema changes, follow the naming conventions, provide migrations under `modules/billing/*.sql`, and describe the plan first. + +## 6) Coding standards & security +- Parameterize SQL or escape inputs with mysqli real_escape-string helpers. +- Harden sessions (regenerate IDs on login, honor `modules/billing/timestamp.txt` for public timestamps). +- CSRF-protect every POST/DELETE-like operation in the storefront admin. +- Verify PayPal signatures, never trust client-side status. +- XML parsing: disable external entities, enforce file size limits. +- Observability: keep per-request IDs in `logs/` to trace provisioning attempts. +- Licensing: leave upstream license headers intact. + +## 7) Validation checklist +- Read `.github/module-map.md`, `modules/billing/`, panel helpers, and XMLs before proposing architecture changes. +- Confirm catalog pages only use XML metadata. +- Confirm node selectors reflect current DB state (respect enabled flags). +- Test that logging into either the panel (`index.php`) or storefront (`modules/billing/login.php`) logs you into both. +- PayPal capture should mark invoices paid, create/extend orders, and schedule provisioning instantly. Verify multi-item carts create all services. +- `BillingProvisioner` must be exercised via PayPal capture, panel module (`create_servers.php`), and any admin “retry” buttons. +- Documentation admin links must expose the XML-notes PHP mirror and the game docs browsers. +- Timestamp footer requirement (see below) satisfied whenever site content changes. + +## 8) Deliverables for Copilot +- Concise change plan with: + - Files to touch and why. + - Data tables/fields involved. + - UX notes (new buttons, admin affordances, etc.). + - Risks, rollback, and testing. +- Update `CHANGELOG.md` with a short, high-signal entry. +- Append one actionable line to `docs/COPILOT_TODO.md` if UI follow-ups remain. +- Keep `.github/module-map.md` current whenever inter-module behavior changes. + +## 9) Prohibited while in planning mode +- No PHP/SQL/XML snippets. +- No shell commands or tooling setup instructions. +- No auto-generated diff dumps. + +--- + +## Additional UI requirement: "Last updated" footer on key pages + +When making small content or page edits to the website, ensure the following pages display a human-friendly "Last updated" timestamp at the very bottom of the page (visible to site visitors): + +- `modules/billing/index.php` +- `index.php` (site root) +- `modules/dashboard/dashboard.php` + +Requirements: +- The text must read exactly: "Last updated at YYYY-MM-DD HH:MM:SS" (24-hour time) where the timestamp reflects the deliberate edit time of the page (see acceptance criteria below). +- Place the timestamp in the page footer area so it does not break layout on mobile or desktop. Keep styling minimal and consistent with the existing footer typography. +- Use the server/local timezone for the timestamp and include the date and time in the format above. Do not include timezone abbreviations in the UI; internal logs may record timezone if needed. + +Acceptance criteria: +- Visiting each page shows the "Last updated at" line at the very bottom of the rendered HTML. +- The timestamp matches the time the page's source was last edited (file modification time) or the annotated edit time used by the deployment process. The project maintainer must decide which of these sources is canonical; document the choice in the change plan. +- The line is visible and readable on small screens and does not overlap other UI elements. + +Testing checklist: +- Manually open each page and confirm the timestamp is present. +- After making a small edit and deploying, confirm the timestamp updates to the new edit time. +- If using automated deploys, ensure the deploy process preserves or updates the canonical timestamp source (e.g., touch file, update metadata) so the displayed value is accurate. + +Maintainer update requirement: +- The canonical human-friendly timestamp is stored in `modules/billing/timestamp.txt` and MUST be updated whenever site files or content are edited and deployed. +- Format and wording: use a single-line plain-text entry such as: "Last Updated at 7:25am on 2025-15-11". This exact text (including capitalization) is what appears in theme footers. +- Update process: include the `timestamp.txt` change in the same commit/PR as any content change that should alter the "Last Updated" time, or ensure your deployment process updates the file automatically (for example, a post-deploy hook that writes the current deploy time in the agreed format). +- Rationale: themes are non-PHP files and may not support SSI on all servers; keeping a single canonical plain-text file reduces duplication and avoids server-side includes. + + +**End of Copilot Instructions.** diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index c50cb667..558c2550 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,150 +1,126 @@ -# GSP (GameServerPanel) — Copilot Instructions (No-Code) +# GSP (GameServerPanel) – Copilot Instructions **Repo of truth:** `GameServerPanel/GSP`, branch `Panel-unstable`. -**Prime directive:** Read this document first. Propose changes that align with our repo and specs. Only search for external info if something contradicts this file. +**Prime directive:** Read this document first. Keep `.github/agent.md` identical to this file—any edit here must be mirrored there in the same commit. -## Standalone website mode -- When working on website features, treat the `_website/` folder as a standalone website root. All website-focused changes (pages, runtime, data persistence, webhooks, and admin UI for the storefront) should live inside `_website/` and be referenced relative to that folder. -- Do NOT modify files outside `_website/` (the panel codebase) unless a maintainer explicitly asks for cross-repo or panel-side changes. If a change necessarily touches panel files, call it out clearly in the plan and get maintainer approval first. -- All redirects, data directories, and public-facing endpoints implemented for the storefront must be scoped under `_website/` (absolute or root-relative to the `_website` site root), not the panel root or external panel dashboard pages. +## Deployment model & paths +- `modules/billing/` houses the public storefront. Those files are always present inside the panel repo and get deployed either (a) as the root of a dedicated virtual host or (b) through the panel module loader (`home.php?m=billing`). +- Because the storefront and the control panel live in the same tree, you may include panel helpers when needed. Use the dedicated bridge include (`modules/billing/includes/panel_bridge.php`) instead of sprinkling ad-hoc `../../includes/...` calls. +- We keep Apache/Nginx vhosts pointed at `modules/billing/`, so every storefront URL must look root-relative (see critical section below). Never expose `/modules/billing` in any URL sent to a browser or external service. +- Before touching billing logic or module wiring, skim `.github/module-map.md` to remember how the panel modules depend on each other. ## CRITICAL: Website file paths and URLs (modules/billing) -- **The billing website files in `modules/billing/` will be deployed at the WEBSITE ROOT when live.** -- **NEVER EVER use `/modules/billing/` in any URL, link, redirect, or file path within the billing website code.** -- **All URLs must be root-relative (starting with `/` but NOT including `/modules/billing/`):** - - ✅ CORRECT: `/payment_success.php`, `/cart.php`, `/order.php` - - ❌ WRONG: `/modules/billing/payment_success.php`, `modules/billing/cart.php` -- **This is a CRITICAL requirement that has been violated multiple times. Read this section carefully before making ANY changes to billing website files.** +- **The billing website files in `modules/billing/` are deployed at the WEBSITE ROOT when live.** +- **Never output `/modules/billing/` in any link, redirect, script tag, or webhook URL. All user-facing URLs must be root-relative**, e.g. `/payment_success.php`, `/cart.php`. +- Continue to use root-relative URLs inside HTML/JS and when building PayPal return/cancel links. The deployment tooling rewrites the document root; hardcoding `/modules/billing` breaks both standalone hosting and module embedding. -### Examples of CORRECT usage: +### Examples of CORRECT usage ```php -// PayPal return URLs $returnUrl = $siteBase . '/payment_success.php'; -$cancelUrl = $siteBase . '/payment_cancel.php'; - -// Header redirects -header('Location: /cart.php'); header('Location: /order.php'); - -// Links -My Account -Browse Servers - -// Form actions +My Account ``` -### Examples of WRONG usage (NEVER DO THIS): +### Examples of WRONG usage (NEVER DO THIS) ```php -// ❌ WRONG - includes modules/billing path $returnUrl = $siteBase . '/modules/billing/payment_success.php'; header('Location: /modules/billing/cart.php'); My Account ``` -### Exception - Backend includes only: -- Backend PHP includes CAN use `__DIR__` or relative paths for file inclusion: - - ✅ `require_once(__DIR__ . '/includes/config.inc.php')` - - ✅ `require_once(__DIR__ . '/../../includes/database_mysqli.php')` -- But these are for SERVER-SIDE file inclusion, NOT for user-facing URLs/redirects/links. +### Exception – backend includes only +- Server-side includes may use absolute filesystem paths, but route those through the bridge helpers when panel context is required: + - ✅ `require_once(__DIR__ . '/includes/config.inc.php');` + - ✅ `require_once(__DIR__ . '/includes/panel_bridge.php');` +- Avoid copy/pasting panel bootstrap code; lean on the helpers already shipped inside `modules/billing/includes/`. -## 1) What to read first (paths & context) -- `_website/` — canonical website storefront and Checkout/Webhooks flow. -- `modules/config_games/server_configs/` — authoritative game catalog XMLs (all supported games live here). -- `modules/` — panel modules (legacy `billing/` exists; its **schema** is authoritative for multi-remote, but the **pages** are deprecated). -- `modules/billing/` — frontend website for selling gameservers to customers. Can interface with panel from same machine or external web host via MySQL tables. Uses `gameservers_website` session namespace (separate from panel sessions). -- `includes/` — panel configuration and DB connectors. -- `ogp_api.php` — internal API entry point for panel-side actions. -- `api/` — Payment-related API code if present in this branch (previously under `paypal/` or `payments/`). +## 1) What to read first +- `.github/module-map.md` – living diagram of how the panel, billing site, daemons, and cron jobs talk to each other. +- `modules/billing/` – storefront runtime, payment handlers, provisioning bridge. +- `modules/config_games/server_configs/` – authoritative XML metadata for every supported game. +- `modules/` – control-panel modules (billing runs here too when embedded). +- `includes/` & `ogp_api.php` – database layer, shared helpers, remote agent operations. -## 2) No-Code Planning Mode (default) -- Do **not** emit PHP, SQL, XML, or shell commands unless a maintainer explicitly asks: **“Generate code now.”** -- While in planning mode, produce only: - - Impacted paths and files, - - Step-by-step plans with acceptance criteria, - - Risks, rollbacks, and test/validation checklists, - - Data mappings that reference existing tables/fields. +## 2) Planning mode (default) +While scoping multi-file work, do **not** emit PHP/SQL/XML or run shell commands unless a maintainer explicitly says “Generate code now.” Plans should cover: +- Impacted files and rationale. +- Data mappings (tables/fields) you will touch. +- Risks, rollback notes, validation/tests. ## 3) Scope & principles -- **Website ↔ Panel on the same host.** Website uses the **panel DB for authentication** and the **panel's internal APIs** for provisioning. **Sessions remain separate** (website session ≠ panel session). -- **Billing module is STANDALONE AND RELOCATABLE.** The `modules/billing/` directory is a **complete standalone website** that: - - Can be deployed on the **same machine as the panel** OR on a **completely separate external web host** - - Must **NEVER** use `require_once` to include panel files (like `includes/database_mysqli.php`, `includes/functions.php`, or any panel helper files) - - Must use **ONLY standard PHP libraries** (mysqli, json, curl, session, etc.) - - Connects directly to MySQL using `mysqli_connect()` with credentials from `modules/billing/includes/config.inc.php` - - All database operations use native mysqli functions: `mysqli_query()`, `mysqli_real_escape_string()`, `mysqli_fetch_assoc()`, etc. - - Must **NOT** use panel-specific functions like `$db->query()`, `createDatabaseConnection()`, `get_lang()`, etc. - - All file paths for includes use `__DIR__` relative paths (e.g., `require_once(__DIR__ . '/includes/config.inc.php')`) - - All URLs/redirects/links use root-relative paths WITHOUT `/modules/billing/` prefix (see CRITICAL section above) -- **Catalog = XML.** Enable **every game** present under `modules/config_games/server_configs/`. The website reads those XMLs for ports, params, install/update metadata. New XMLs should become available without code changes. -- **Regions/Nodes = panel DB.** Regions and nodes are configured in the panel and must be **queried live** from the panel DB. Never hardcode or mirror region lists on the website. -- **Slotless model.** Pricing/UX must not enforce slot caps. If an engine requires a player count parameter, set a safe high default and surface engine limits transparently if they exist. -- **Auth compatibility.** Panel users use legacy MD5 in `ogp_users`. The website should prefer a modern hashing shadow and upgrade transparently on successful login, **without breaking panel login**. -- **Checkout/Webhooks.** Follow the working **PayPal Checkout** flow in `_website/`. Use **REST Webhooks** only. Mark orders paid **only** after webhook verification. -- **Legacy billing module.** Treat `modules/billing/` **pages** as deprecated. Reuse the **existing tables/fields** introduced there for **multi-remote** support. Do not invent parallel schema. +- **Single session across panel + storefront.** Every billing page must call `session_name('opengamepanel_web')` before `session_start()`. Always keep `$_SESSION['user_id']`, `$_SESSION['users_login']`, `$_SESSION['users_group']`, and `$_SESSION['website_user_id']` in sync so that logging into either surface signs the visitor into both. +- **Auth reuse.** Preferred order when verifying credentials: `users_pass_hash` (modern hash) → legacy `users_passwd` (MD5). Upgrading to a modern hash is allowed so long as panel logins keep working. +- **Bridge for panel helpers.** Use `modules/billing/includes/panel_bridge.php` to load panel classes (`OGPDatabase`, `OGPRemoteLibrary`, XML parsers) when the storefront needs to provision servers or read panel-only metadata. Do not reinvent ad-hoc copies of panel logic. +- **Storefront runtime.** Public pages continue to use mysqli with credentials from `modules/billing/includes/config.inc.php`. Provisioning steps may request an `OGPDatabase` handle from the bridge. +- **Provisioning pipeline.** Always funnel server creation or renewals through the shared provisioner (`modules/billing/includes/provisioner.php`). This helper wraps the old `create_servers.php` logic and ensures PayPal captures, cron jobs, and panel clicks all follow the same code path. +- **Catalog = XML.** Never hardcode game metadata. Parse `modules/config_games/server_configs/*.xml` at runtime; new XMLs must show up automatically. +- **Regions/Nodes = live DB.** Pull nodes/locations from the panel DB (`gsp_remote_servers`, etc.). Respect admin enable/disable flags and never mirror node lists into flat files. +- **Game XML wiki parity.** We ship a PHP-rendered version of https://github.com/OpenGamePanel/OGP-Website/wiki/XML-Notes inside `modules/billing/` (linked from the storefront admin area). Keep it updated so maintainers can edit XMLs without leaving the repo. -## 4) Functional requirements (design-level only) +## 4) Functional requirements ### 4.1 Catalog (from XML) -- Parse all XMLs under `modules/config_games/server_configs/`. -- Normalize game key, display name, required ports, startup parameters, install/update routines, and any engine constraints. -- Support hot-add: new XMLs become available to the storefront after a repo update. +- Parse every XML under `modules/config_games/server_configs/`. +- Normalize: game key, display name, install/update commands, default ports, mod metadata. +- XML pages (`modules/billing/docs.php` and the new XML-notes mirror) must stay in sync so AI-powered edits can cross-reference expectations. ### 4.2 Authentication & sessions -- Website registration creates a panel user (legacy-compatible) and stores a **modern hash shadow** linked 1:1 to that user. -- Login prefers the modern hash; on MD5 success, upgrade silently to the modern hash. -- Maintain **separate sessions** for website and panel. +- Website registration must create/maintain panel users. Set both the legacy `users_passwd` and the modern hash column. +- Login flow must hydrate the shared session variables so `home.php` immediately recognizes the visitor. +- All storefront guards should treat `$_SESSION['user_id']` as the source of truth, falling back to `website_user_id` only for older sessions. -### 4.3 Checkout → Webhooks → Provisioning -- Mirror `_website/` structure and flows for Checkout. -- On verified webhook events: transition order state to paid, create service records, and **provision** a panel Home using internal panel APIs. -- Derive ports and startup parameters **from the XML metadata**. +### 4.3 Checkout → PayPal → Provisioning +- Flow: add to cart → invoices (`billing_invoices`) → PayPal order (`api/create_order.php`) → capture (`api/capture_order.php`) → immediately hand off to `BillingProvisioner`. +- Mark invoices paid **only** after verifying PayPal response/webhook. Support multiple servers per payment: loop through every paid invoice and either create a new order or extend an existing service. +- For renewals, extend `end_date` from its current value and keep status at `installed`. For new services set status `installing`, invoke the provisioner, then switch to `installed` on success. +- Provisioner is responsible for calling `modules/billing/create_servers.php` logic, adding homes, assigning ports, enabling FTP, and logging/notifications. Never bypass it. ### 4.4 Regions/Nodes (multi-remote) -- At checkout or during provisioning, present or auto-select regions/nodes by reading **the panel DB**. -- If a node is hidden/disabled in the panel, it must not appear in the website UI. +- `remote_servers` and `remote_server_ips` tables remain the source for available locations. Admin tooling (`adminserverlist.php`) must let staff toggle availability and restrict services per location. +- When a node is globally disabled it must disappear (or show as unavailable) in ordering and admin tools. ### 4.5 Billing automation (website-side) -- Reconcile renewals and invoke panel APIs to suspend/reactivate/terminate services. -- Operations must be idempotent and observable (logs/metrics defined at design time). +- Cron/workers under `modules/billing/cron-shop.php` still suspend/delete expired services. Renewals triggered via PayPal must update `billing_orders.status` and `end_date` consistently so cron jobs can pick up where they expect. +- Keep audit logs in `modules/billing/logs/` whenever automatic provisioning, renewals, refunds, or coupon adjustments happen. -## 5) Data model alignment (no DDL) -- Use the **panel DB as the source of truth**. -- **Multi-remote** tables and fields already exist (introduced by the legacy billing work). Reuse them. -- Only propose new fields/tables if strictly necessary; when doing so, reference existing naming conventions and provide a migration plan (still no SQL while in planning mode). +## 5) Data model alignment (no DDL during planning) +- Use panel tables as the source of truth (`gsp_billing_orders`, `gsp_billing_services`, `gsp_billing_invoices`, `gsp_game_mods`, etc.). +- Multi-remote fields (`remote_server_id`, IP IDs) already exist—never introduce duplicates in the storefront DB. +- When you truly need schema changes, follow the naming conventions, provide migrations under `modules/billing/*.sql`, and describe the plan first. -## 6) Coding standards & security (what to enforce when code is requested) -- **Repository-first:** Before proposing file names, endpoints, or structures, search `Panel-unstable` to reuse existing helpers, patterns, and locations. -- **Strictness:** Prefer strict comparisons; parameterized DB access; centralized input validation and output escaping. -- **Session & CSRF:** Harden website sessions and require CSRF tokens on state-changing requests. -- **Webhooks:** Verify signatures and event types server-side; never trust client redirects for payment state. -- **XML:** Harden parsing (no external entities; size/complexity limits). Treat XML as untrusted input even though it’s in-repo. -- **Observability:** Define success/failure metrics, audit logs for state changes, and trace IDs for provisioning flows. -- **Licensing:** Preserve upstream notices and ensure our additions stay license-compatible. +## 6) Coding standards & security +- Parameterize SQL or escape inputs with mysqli real_escape-string helpers. +- Harden sessions (regenerate IDs on login, honor `modules/billing/timestamp.txt` for public timestamps). +- CSRF-protect every POST/DELETE-like operation in the storefront admin. +- Verify PayPal signatures, never trust client-side status. +- XML parsing: disable external entities, enforce file size limits. +- Observability: keep per-request IDs in `logs/` to trace provisioning attempts. +- Licensing: leave upstream license headers intact. -## 7) Validation checklist (pre-PR / pre-merge) -- Read `_website/`, `modules/config_games/server_configs/`, `modules/`, `includes/`, `api/` (if present), and `ogp_api.php` to anchor proposals to actual code. -- Catalog uses only the XML metadata; no hardcoded ports/params. -- Regions/nodes are read live from the panel DB; no duplicates on the website. -- Auth plan preserves panel compatibility and modernizes website hashing; **sessions remain separate**. -- Checkout mirrors `_website/`; uses **REST Webhooks**; paid state changes occur only after verification. -- Provisioning calls panel internals (e.g., `ogp_api.php`), respects selected/auto node, and records mappings consistently. -- Legacy billing module pages are not extended; its schema is reused for multi-remote. -- Security items from §6 are addressed in the plan: CSRF, webhook verification, strict comparisons, hardened XML. +## 7) Validation checklist +- Read `.github/module-map.md`, `modules/billing/`, panel helpers, and XMLs before proposing architecture changes. +- Confirm catalog pages only use XML metadata. +- Confirm node selectors reflect current DB state (respect enabled flags). +- Test that logging into either the panel (`index.php`) or storefront (`modules/billing/login.php`) logs you into both. +- PayPal capture should mark invoices paid, create/extend orders, and schedule provisioning instantly. Verify multi-item carts create all services. +- `BillingProvisioner` must be exercised via PayPal capture, panel module (`create_servers.php`), and any admin “retry” buttons. +- Documentation admin links must expose the XML-notes PHP mirror and the game docs browsers. +- Timestamp footer requirement (see below) satisfied whenever site content changes. -## 8) Deliverables for Copilot (when planning) -- A concise change plan that lists: - - Files to create/modify/remove and their locations, - - Data sources and mappings to existing tables/fields, - - UX notes (e.g., region selector vs auto-placement), - - Risks, rollback approach, and test coverage, - - Acceptance criteria aligned to these instructions. -- Update `CHANGELOG.md` with a brief, high-signal entry (date, scope, rationale). -- Append a single line item to `docs/COPILOT_TODO.md` for any UI follow-ups or next steps. +## 8) Deliverables for Copilot +- Concise change plan with: + - Files to touch and why. + - Data tables/fields involved. + - UX notes (new buttons, admin affordances, etc.). + - Risks, rollback, and testing. +- Update `CHANGELOG.md` with a short, high-signal entry. +- Append one actionable line to `docs/COPILOT_TODO.md` if UI follow-ups remain. +- Keep `.github/module-map.md` current whenever inter-module behavior changes. -## 9) Prohibited while in planning -- No PHP/SQL/XML. -- No shell commands or system setup steps. -- No scaffolding diffs or auto-generated file dumps. +## 9) Prohibited while in planning mode +- No PHP/SQL/XML snippets. +- No shell commands or tooling setup instructions. +- No auto-generated diff dumps. --- @@ -178,4 +154,4 @@ Maintainer update requirement: - Rationale: themes are non-PHP files and may not support SSI on all servers; keeping a single canonical plain-text file reduces duplication and avoids server-side includes. -**End of Copilot Instructions (No-Code).** +**End of Copilot Instructions.** diff --git a/.github/module-map.md b/.github/module-map.md new file mode 100644 index 00000000..87a9e7b3 --- /dev/null +++ b/.github/module-map.md @@ -0,0 +1,78 @@ +# GSP Module & Interaction Map + +This file captures how the control panel, storefront, agents, and helper scripts talk to one another. Read it before diving into any subsystem—most regressions last time came from touching one module without realizing who consumed its data. + +## Core runtime (shared by every module) + +| Area | Key files | Responsibilities | Downstream callers | +| --- | --- | --- | --- | +| Database bootstrap | `includes/functions.php`, `includes/database_mysqli.php` | Creates the `OGPDatabase` instance and exposes helpers such as `resultQuery()`, `addGameHome()`, and logging. | Every panel page, `modules/billing/includes/panel_bridge.php`, cron jobs. | +| Session helpers | `includes/helpers.php` (`startSession()`) | Sets `session_name('opengamepanel_web')`, sanitizes request vars, loads locales. | `index.php`, `home.php`, provisioning pages, storefront session bridge. | +| Remote control | `includes/lib_remote.php` | Wraps agent RPC (install/update, FTP user management, rsync, SteamCMD). | `modules/gamemanager/*`, `modules/billing/create_servers.php`, cron jobs. | +| XML parser | `modules/config_games/server_config_parser.php` | Converts `modules/config_games/server_configs/*.xml` into PHP arrays used for provisioning and pricing metadata. | `modules/gamemanager`, `modules/billing` (catalog + provisioner), cron installers. | +| API surface | `ogp_api.php`, `includes/api_functions.php` | HTTP API for third-party tooling. Exposes operations such as starting/stopping homes, querying stats. | Mobile apps, automated provisioning, selected billing workflows. | +| Cron/automation | `scripts/` (`cron-shop.php`, `status/*`, etc.) | Suspends/unsuspends services, refreshes status caches, runs backups. | Triggered via system cron or panel scheduler. | + +## High-level flows + +1. **Auth/session** – Driven by `index.php` (panel) and `modules/billing/login.php` (storefront). Both set `$_SESSION['user_id']`, `users_login`, `users_group`, and `website_user_id`. The shared session cookie `opengamepanel_web` means logging into either surface immediately authenticates the other. +2. **Catalog** – `modules/config_games` hosts XML definitions. Panel modules (`gamemanager`, `config_games`) and storefront pages (`serverlist.php`, `order.php`, documentation pages, and the XML-notes mirror) parse these files for display and provisioning metadata. +3. **Provisioning** – Orders land in `gsp_billing_orders`. `modules/billing/includes/provisioner.php` reuses `modules/billing/create_servers.php` logic to allocate homes, assign nodes/IPs, configure mods, and kick off SteamCMD/rsync/manual installers. The same provisioner is invoked by: + - PayPal capture endpoint (`modules/billing/api/capture_order.php`). + - Panel module page `home.php?m=billing&p=provision_servers`. + - Cron/repair actions in `modules/billing/cron-shop.php`. +4. **Renewals** – `cron-shop.php` inspects `billing_orders.end_date` and toggles `status` between `installed`, `invoiced`, `suspended`, and `deleted`. PayPal renewals extend `end_date` in `capture_order.php` and immediately flip `status` back to `installed`. +5. **Documentation** – `modules/billing/docs.php`, per-game folders under `modules/billing/docs/`, and the XML wiki mirror (PHP port of `XML-Notes`) are used by both admins and AI helpers to craft game templates. + +## Panel modules (selected) + +| Module | Key files | Primary responsibilities | Upstream/Downstream dependencies | +| --- | --- | --- | --- | +| `dashboard` | `modules/dashboard/dashboard.php` | Landing page once authenticated. Pulls stats from homes, invoices, and support modules. Shows "Last updated" footer based on `modules/billing/timestamp.txt`. | Reads `billing_orders`, `game_homes`, `tickets`. | +| `gamemanager` | `modules/gamemanager/server_monitor.php`, `modules/gamemanager/game_monitor.php` | Shows owned homes, start/stop, update, reinstall, port usage. Uses XML to know command lines. | Relies on `lib_remote`, `config_games`, `user_games` assignments. | +| `config_games` | `modules/config_games/add_mod.php`, `server_config_parser.php`, XML files under `server_configs/` | Admin UI for XML definitions. Controls what appears in storefront/service catalog. | Feeds `gamemanager`, billing catalog, cron installers. | +| `user_games` | `modules/user_games/add_home.php`, `assign_home.php`, `edit_home.php` | Admin workflow to add homes manually or edit assignments. Shares DB tables with billing provisioner. | Uses `game_homes`, `remote_servers`, `billing_orders`. | +| `administration` / `user_admin` | CRUD around users, groups, permissions, expire dates. | Sets roles consumed by storefront admin guard and provisioning ACLs. | +| `server` | `modules/server/*` | Remote server management (agents, IPs, ports, reinstall keys). Billing uses these tables for available nodes/locations. | +| `modulemanager` | Manage module install/uninstall/menus. Billing module registers `navigation.xml` to surface `create_servers.php` & admin pages. | +| `tickets`, `support` | Support ticketing/email utilities. | Pulls user info and logger records. | +| `extras`, `addonsmanager` | Workshop/add-on management. | Hooks into game homes after provisioning. | +| `litefm`, `ftp`, `TS3Admin` | File managers and TeamSpeak controllers. | Depend on homes and remote server credentials set during provisioning. | +| `news`, `circular`, `faq` | Content modules for panel UI. | Use standard MVC wrappers, share session/auth. | +| `cron` | Scheduler UI feeding `scripts/` commands. | Maintains job metadata that OS cron reads. | + +## Storefront (modules/billing) + +| Area | Key files | Notes | +| --- | --- | --- | +| Public pages | `index.php`, `serverlist.php`, `order.php`, `cart.php`, `payment_success.php`, `docs.php` | All include `bootstrap.php`, header/footer, shared CSS. Links remain root-relative. | +| Auth | `login.php`, `register.php`, `reset_password.php`, `forgot_password.php`, `includes/login_required.php`, `includes/admin_auth.php` | Share `opengamepanel_web` session, call into panel DB to validate roles. | +| Admin | `admin.php`, `adminserverlist.php`, `admin_orders.php`, `admin_coupons.php`, `admin_config.php`, `my_orders_panel.php` | Manage services, coupons, prices, and provisioning. `adminserverlist.php` controls service availability per node. | +| PayPal API | `api/create_order.php`, `api/capture_order.php`, `webhook.php`, `logs/payment_capture.log` | Implements REST checkout. Once capture is confirmed, writes invoices/orders, updates coupons, and kicks `BillingProvisioner`. | +| Provisioning bridge | `create_servers.php`, `includes/provisioner.php`, `includes/panel_bridge.php` | Shared between panel module and storefront backend. Encapsulates whole server creation/renewal pipeline. | +| Cron helpers | `cron-shop.php`, `diag_remote.php` | Automations for renewals, diagnostics, health checks. | +| Documentation | `docs.php`, `docs/*`, `docs/admin_xml_notes.php` (PHP mirror of XML wiki) | Provide guidance for editing XML and game configs directly inside repo. | +| Logs/data | `logs/`, `data/`, `timestamp.txt` | Payment JSON archives, debug traces, and "Last updated" canonical string. | + +## External/agent side + +| Component | Location | Purpose | +| --- | --- | --- | +| Remote agent | `modules/gamemanager` talks to standalone agent binaries configured per `remote_servers`. | Executes installs, updates, start/stop commands. Provisioner relies on it for SteamCMD and rsync workflows. | +| Apache/Nginx vhosts | `/etc/apache2/sites-available` (not in repo) | Point either the storefront domain or panel subpath at `modules/billing/`. Required for shared session cookie scope. | + +## Data touchpoints + +- **Users** – `gsp_users` table is shared. Registration uses `modules/billing/register.php`, admin pages use `modules/user_admin`. Password upgrades must not break panel logins. +- **Billing tables** – `gsp_billing_services`, `gsp_billing_orders`, `gsp_billing_invoices`, `gsp_billing_coupons`. Admin edits (pricing, enable/disable, locations) are done via `adminserverlist.php`; automation uses `cron-shop.php`. +- **Homes/Mods/IPs** – Stored in `gsp_game_homes`, `gsp_game_mods`, `gsp_remote_server_ips`. Provisioner writes to these tables; `gamemanager`, `litefm`, `ftp`, and `user_games` read them. +- **Logging** – `$db->logger()` writes to `ogp_logs`. Storefront-specific logs live in `modules/billing/logs/` for quick inspection (payment capture, provisioning outcomes, coupon usage). + +## Usage tips + +1. **Need a DB object inside `modules/billing`?** Include `includes/panel_bridge.php` and call `billing_get_panel_db()`. It sets up constants, loads helpers, and caches the `OGPDatabase` instance so multi-call flows (e.g., capture → provision → email) reuse it. +2. **Want to change provisioning?** Update `modules/billing/includes/provisioner.php` once. `create_servers.php`, PayPal webhooks, cron jobs, and admin repair flows all use it. +3. **Working on XML or documentation?** Update the XML file under `modules/config_games/server_configs/`, regenerate docs if needed, and keep the PHP XML-notes mirror (`modules/billing/docs/xml_notes.php`) accurate so the admin link stays trustworthy. +4. **Need to know who uses a table?** Search `.github/module-map.md` first; the table above lists the canonical readers/writers for each major schema. + +_Last updated: 2025-11-20._ diff --git a/modules/billing/FIXES_APPLIED.md b/modules/billing/FIXES_APPLIED.md index 17e2a5a2..760ca498 100644 --- a/modules/billing/FIXES_APPLIED.md +++ b/modules/billing/FIXES_APPLIED.md @@ -205,7 +205,7 @@ The billing module is designed to be standalone and relocatable: - Does NOT include panel files (like includes/functions.php) - Connects directly to MySQL using mysqli_connect() - Can be deployed on same machine as panel OR external web host -- Sessions are separate: "gameservers_website" namespace +- Sessions are separate: "opengamepanel_web" namespace --- @@ -245,3 +245,4 @@ The billing module is now functional with: 4. All files validated for syntax correctness The changes are minimal, surgical, and follow the repository guidelines for standalone billing module architecture. + diff --git a/modules/billing/README.md b/modules/billing/README.md index 9e7aa64a..d72473a4 100644 --- a/modules/billing/README.md +++ b/modules/billing/README.md @@ -151,7 +151,7 @@ When game documentation is finished: ## Technical Notes ### Session Management -- **CRITICAL:** Always use `session_name("gameservers_website")` before `session_start()` +- **CRITICAL:** Always use `session_name("opengamepanel_web")` before `session_start()` - Sessions are separate from panel sessions - User authentication stored in `$_SESSION['website_user_id']` @@ -175,3 +175,4 @@ When game documentation is finished: --- **Last Updated:** December 19, 2024 **Version:** 2.0 (with Visual TODO System) + diff --git a/modules/billing/RECENT_FIXES_SUMMARY.md b/modules/billing/RECENT_FIXES_SUMMARY.md index 63b0336d..8735db3d 100644 --- a/modules/billing/RECENT_FIXES_SUMMARY.md +++ b/modules/billing/RECENT_FIXES_SUMMARY.md @@ -65,9 +65,9 @@ Implemented comprehensive system to visually identify incomplete game documentat ### 1. PayPal Payment Capture Session Issue (FIXED) **Problem:** Payment capture was failing with `NO_USER_SESSION` error even though user was logged in. -**Root Cause:** The `api/capture_order.php` file was calling `session_start()` without setting the session name first, so it couldn't access the `gameservers_website` session where the user_id is stored. +**Root Cause:** The `api/capture_order.php` file was calling `session_start()` without setting the session name first, so it couldn't access the `opengamepanel_web` session where the user_id is stored. -**Solution:** Added `session_name("gameservers_website")` before `session_start()` in `capture_order.php`. +**Solution:** Added `session_name("opengamepanel_web")` before `session_start()` in `capture_order.php`. **File Modified:** `modules/billing/api/capture_order.php` (line ~148) @@ -323,3 +323,4 @@ Each game's `index.php` should follow this structure: --- **End of Summary** + diff --git a/modules/billing/_archived/IMPLEMENTATION_SUMMARY.md b/modules/billing/_archived/IMPLEMENTATION_SUMMARY.md index d1cd43ad..d498e1f3 100644 --- a/modules/billing/_archived/IMPLEMENTATION_SUMMARY.md +++ b/modules/billing/_archived/IMPLEMENTATION_SUMMARY.md @@ -9,7 +9,7 @@ Successfully implemented login functionality for the website (_website/) that au Full-featured login page with: - Modern, responsive UI design - Authentication against panel DB using MD5 (panel-compatible) -- Separate website session: `gameservers_website` +- Separate website session: `opengamepanel_web` - Input validation and sanitization - Error and success message display - Automatic redirect after successful login @@ -71,7 +71,7 @@ Database testing utility that checks: ## Technical Details ### Session Management -- **Website Session Name:** `gameservers_website` +- **Website Session Name:** `opengamepanel_web` - **Panel Session Name:** `opengamepanel_web` (unchanged) - **Complete separation:** Users can be logged into one without the other @@ -178,3 +178,4 @@ All requirements from the problem statement have been met: ✅ Authenticate against panel DB ✅ Create separate login session ✅ Maintain panel compatibility + diff --git a/modules/billing/_archived/README_LOGIN.md b/modules/billing/_archived/README_LOGIN.md index 920f206c..1392d37c 100644 --- a/modules/billing/_archived/README_LOGIN.md +++ b/modules/billing/_archived/README_LOGIN.md @@ -8,7 +8,7 @@ This implementation adds login functionality to the website that authenticates u ### 1. `_website/login.php` (NEW) - Full-featured login page with modern UI - Authenticates against panel DB using MD5 password hashing (panel-compatible) -- Creates separate website session using `gameservers_website` session name +- Creates separate website session using `opengamepanel_web` session name - Logs all login attempts via logger() function - Session variables set: - `$_SESSION['website_user_id']` - User ID from ogp_users @@ -32,7 +32,7 @@ This implementation adds login functionality to the website that authenticates u ## Session Management ### Separate Sessions -- **Website Session**: `gameservers_website` (this implementation) +- **Website Session**: `opengamepanel_web` (this implementation) - **Panel Session**: `opengamepanel_web` (existing panel) These sessions are completely separate - users can be logged into one without being logged into the other. @@ -62,7 +62,7 @@ Requires connection to panel database with access to: ### For Developers: Check if user is logged in: ```php -session_name("gameservers_website"); +session_name("opengamepanel_web"); session_start(); if (isset($_SESSION['website_user_id']) && !empty($_SESSION['website_user_id'])) { @@ -107,3 +107,4 @@ This implementation follows the no-code planning guidelines from `.github/copilo - Login credentials are the same as panel login (same user table) - Website session does not grant access to panel - separate login required - Logger function from db.php creates logfile.txt for audit trail + diff --git a/modules/billing/admin.php b/modules/billing/admin.php index 61f9444a..7512ab87 100644 --- a/modules/billing/admin.php +++ b/modules/billing/admin.php @@ -26,6 +26,7 @@ function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); } Invoice History Manage Coupons Edit Site Config + XML Config Guide
diff --git a/modules/billing/adminserverlist.php b/modules/billing/adminserverlist.php index 33273593..7898d367 100644 --- a/modules/billing/adminserverlist.php +++ b/modules/billing/adminserverlist.php @@ -13,6 +13,7 @@ // Include billing bootstrap (loads config and DB helper) require_once(__DIR__ . '/bootstrap.php'); +$siteBaseUrl = isset($SITE_BASE_URL) ? trim((string)$SITE_BASE_URL) : ''; // Protect this page: require admin require_once(__DIR__ . '/includes/admin_auth.php'); @@ -27,6 +28,10 @@ if (!$db) { include(__DIR__ . '/includes/top.php'); include(__DIR__ . '/includes/menu.php'); +echo "
Need the XML field reference? "; +echo "Open XML Notes"; +echo "
"; + /* show errors during setup */ @ini_set('display_errors','1'); error_reporting(E_ALL); @@ -79,7 +84,12 @@ if (isset($_POST['update_remote_servers'])) { /* helper: update one service row from posted array */ function update_service_row(mysqli $db, string $locationCol, int $sid, array $svc){ $name = esc_mysqli($db, trim($svc['service_name'] ?? '')); - $price = esc_mysqli($db, trim($svc['price_monthly'] ?? '0.00')); + $priceMonthly = number_format((float)($svc['price_monthly'] ?? 0), 2, '.', ''); + $priceYearly = number_format((float)($svc['price_year'] ?? 0), 2, '.', ''); + $priceDaily = number_format((float)($svc['price_daily'] ?? 0), 2, '.', ''); + $priceMonthEsc = esc_mysqli($db, $priceMonthly); + $priceYearEsc = esc_mysqli($db, $priceYearly); + $priceDailyEsc = esc_mysqli($db, $priceDaily); $img = esc_mysqli($db, trim($svc['img_url'] ?? '')); $en = !empty($svc['enabled']) ? 1 : 0; @@ -104,7 +114,9 @@ function update_service_row(mysqli $db, string $locationCol, int $sid, array $sv `{$locationCol}`='{$locListEsc}', slot_min_qty={$minSlots}, slot_max_qty={$maxSlots}, - price_monthly='{$price}', + price_daily='{$priceDailyEsc}', + price_monthly='{$priceMonthEsc}', + price_year='{$priceYearEsc}', img_url='{$img}', enabled={$en} WHERE service_id={$sid}"; @@ -178,7 +190,9 @@ $services = fetch_all_assoc($db, "SELECT service_id, service_name, `{$locat Service Name (ID below) Min Slots Max Slots + Price (Daily) Price (Monthly) + Price (Year) Thumbnail URL Preview Update Row @@ -196,8 +210,8 @@ $services = fetch_all_assoc($db, "SELECT service_id, service_name, `{$locat if ($imgUrl !== '') { if (is_abs_url($imgUrl)) { $displayUrl = $imgUrl; - } elseif ($SITE_BASE_URL !== '') { - $displayUrl = join_base($SITE_BASE_URL, $imgUrl); + } elseif ($siteBaseUrl !== '') { + $displayUrl = join_base($siteBaseUrl, $imgUrl); } else { // Use relative path (local folder) $displayUrl = $imgUrl; @@ -227,10 +241,18 @@ $services = fetch_all_assoc($db, "SELECT service_id, service_name, `{$locat + + + + + + + + @@ -305,6 +327,17 @@ $services = fetch_all_assoc($db, "SELECT service_id, service_name, `{$locat
+
+

Environment

+ + + + + + +
Site Base URL
Data directory
PHP SAPI
Writable?
XML ReferenceOpen XML Notes
+
+