diff --git a/modules/billing/admin.php b/modules/billing/admin.php index 8139bcfe..95f77b76 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 Editor XML Config Guide diff --git a/modules/billing/admin_xml_editor.php b/modules/billing/admin_xml_editor.php new file mode 100644 index 00000000..06dec30c --- /dev/null +++ b/modules/billing/admin_xml_editor.php @@ -0,0 +1,161 @@ +isFile() && strtolower($fileInfo->getExtension()) === 'xml') { + $availableFiles[] = $fileInfo->getFilename(); + } +} +sort($availableFiles, SORT_NATURAL | SORT_FLAG_CASE); + +$selectedFile = ''; +$fileContents = ''; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $postedFile = $_POST['file'] ?? ''; + $postedFile = basename(trim((string)$postedFile)); + if ($postedFile === '' || !in_array($postedFile, $availableFiles, true)) { + $errors[] = 'Invalid file selected.'; + } else { + $fullPath = $serverConfigDir . DIRECTORY_SEPARATOR . $postedFile; + if (!is_file($fullPath) || !is_readable($fullPath)) { + $errors[] = 'Selected file is missing or unreadable.'; + } elseif (!is_writable($fullPath)) { + $errors[] = 'Selected file is not writable.'; + } else { + $newContents = $_POST['xml_contents'] ?? ''; + $backupDir = $serverConfigDir . DIRECTORY_SEPARATOR . '_backups'; + if (!is_dir($backupDir)) { + @mkdir($backupDir, 0775, true); + } + $timestamp = date('Ymd-His'); + $backupPath = $backupDir . DIRECTORY_SEPARATOR . $postedFile . '.' . $timestamp . '.bak'; + $original = file_get_contents($fullPath); + if ($original === false) { + $errors[] = 'Unable to read original file for backup.'; + } elseif (@file_put_contents($backupPath, $original) === false) { + $errors[] = 'Failed to create backup copy before saving.'; + } elseif (@file_put_contents($fullPath, $newContents) === false) { + $errors[] = 'Failed to write new XML contents.'; + } else { + $messages[] = 'Saved changes to ' . htmlspecialchars($postedFile, ENT_QUOTES, 'UTF-8') . ' (backup: ' . basename($backupPath) . ').'; + $selectedFile = $postedFile; + $fileContents = $newContents; + } + } + } +} + +if ($selectedFile === '') { + $queryFile = $_GET['file'] ?? ''; + $queryFile = basename(trim((string)$queryFile)); + if ($queryFile !== '' && in_array($queryFile, $availableFiles, true)) { + $selectedFile = $queryFile; + } +} + +if ($selectedFile !== '' && $fileContents === '') { + $fullPath = $serverConfigDir . DIRECTORY_SEPARATOR . $selectedFile; + if (is_file($fullPath) && is_readable($fullPath)) { + $fileContents = file_get_contents($fullPath); + if ($fileContents === false) { + $errors[] = 'Unable to read the selected file.'; + $fileContents = ''; + } + } else { + $errors[] = 'Selected file is missing or unreadable.'; + $selectedFile = ''; + } +} + +function billing_render_flash(array $items, string $cssClass): void { + if (!$items) { + return; + } + echo '
'; + foreach ($items as $item) { + echo '
' . $item . '
'; + } + echo '
'; +} + +?> + + + + + Admin — XML Config Editor + + + + + +
+

XML Config Editor

+

Editing files in . Each save creates a backup under _backups/.

+ + + + +
+
+

Server Config XML Files

+ +

No XML files found.

+ + + + + + +
+
+ +

Select an XML file from the list to begin editing.

+ +
+ + +
+ + Backup created before each save. +
+
+ +
+
+
+ + + diff --git a/modules/billing/includes/menu.php b/modules/billing/includes/menu.php index 8e278dc4..74c4b873 100644 --- a/modules/billing/includes/menu.php +++ b/modules/billing/includes/menu.php @@ -6,6 +6,27 @@ require_once(__DIR__ . '/session_bridge.php'); +if (!function_exists('billing_nav_escape')) { + function billing_nav_escape($value) { + return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); + } +} + +$nav_prefix = ''; +$scriptName = $_SERVER['SCRIPT_NAME'] ?? ''; +if (is_string($scriptName) && $scriptName !== '') { + if (preg_match('#/modules/billing/(.*)$#', $scriptName, $match)) { + $subPath = $match[1]; + if ($subPath !== '') { + $depth = substr_count($subPath, '/'); + if ($depth > 0) { + $nav_prefix = str_repeat('../', $depth); + } + } + } +} +$nav_prefix = $nav_prefix ?: ''; + // Check login status // Primary check uses website_user_id, but some remote deployments may only set website_username. // Treat presence of website_username as a fallback to consider the user logged in for UI purposes. @@ -63,7 +84,7 @@ if ($is_logged_in) { } } ?> - +
@@ -71,8 +92,8 @@ if ($is_logged_in) {
@@ -84,23 +105,23 @@ if ($is_logged_in) { $return_to_param = $current; ?> - - Logout + + Logout - Login + Login
diff --git a/modules/billing/timestamp.txt b/modules/billing/timestamp.txt index 3f827ca4..c1ed03b0 100644 --- a/modules/billing/timestamp.txt +++ b/modules/billing/timestamp.txt @@ -1 +1 @@ -Last Updated at 3:15pm on 2025-12-05 +Last Updated at 11:40am on 2025-06-12