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 '
Editing files in . Each save creates a backup under _backups/.