Fixes config file editing
parent
9d3bb9ef04
commit
d253d87515
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API Response Handler
|
||||||
|
* Provides a consistent way to send JSON responses from controllers
|
||||||
|
*/
|
||||||
|
class ApiResponse {
|
||||||
|
/**
|
||||||
|
* Send a success response
|
||||||
|
* @param mixed $data Optional data to include in response
|
||||||
|
* @param string $message Optional success message
|
||||||
|
* @param int $status HTTP status code
|
||||||
|
*/
|
||||||
|
public static function success($data = null, $message = '', $status = 200) {
|
||||||
|
self::send([
|
||||||
|
'success' => true,
|
||||||
|
'data' => $data,
|
||||||
|
'message' => $message
|
||||||
|
], $status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an error response
|
||||||
|
* @param string $message Error message
|
||||||
|
* @param mixed $errors Optional error details
|
||||||
|
* @param int $status HTTP status code
|
||||||
|
*/
|
||||||
|
public static function error($message, $errors = null, $status = 400) {
|
||||||
|
self::send([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $message,
|
||||||
|
'errors' => $errors
|
||||||
|
], $status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the actual JSON response
|
||||||
|
* @param array $data Response data
|
||||||
|
* @param int $status HTTP status code
|
||||||
|
*/
|
||||||
|
private static function send($data, $status) {
|
||||||
|
http_response_code($status);
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode($data);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
/**
|
/**
|
||||||
* class Config
|
* class Config
|
||||||
*
|
*
|
||||||
* Handles editing and fetching ot the config files.
|
* Handles editing and fetching of the config files.
|
||||||
*/
|
*/
|
||||||
class Config {
|
class Config {
|
||||||
|
|
||||||
|
@ -13,41 +13,146 @@ class Config {
|
||||||
* @param array $updatedConfig Key-value pairs of config options to update.
|
* @param array $updatedConfig Key-value pairs of config options to update.
|
||||||
* @param string $config_file Path to the config file.
|
* @param string $config_file Path to the config file.
|
||||||
*
|
*
|
||||||
* @return mixed Returns true on success, or an error message on failure.
|
* @return array Returns an array with 'success' and 'updated' keys on success, or 'success' and 'error' keys on failure.
|
||||||
*/
|
*/
|
||||||
public function editConfigFile($updatedConfig, $config_file) {
|
public function editConfigFile($updatedConfig, $config_file) {
|
||||||
// first we get a fresh config file contents as text
|
global $logObject, $user_id;
|
||||||
$config_contents = file_get_contents($config_file);
|
$allLogs = [];
|
||||||
if (!$config_contents) {
|
$updated = [];
|
||||||
return "Failed to read the config file \"$config_file\".";
|
|
||||||
|
try {
|
||||||
|
if (!is_array($updatedConfig)) {
|
||||||
|
throw new Exception("Invalid config data: expected array");
|
||||||
}
|
}
|
||||||
|
|
||||||
// loop through the variables and updated them
|
if (!file_exists($config_file) || !is_writable($config_file)) {
|
||||||
foreach ($updatedConfig as $key => $newValue) {
|
throw new Exception("Config file does not exist or is not writable: $config_file");
|
||||||
// we look for 'option' => value
|
}
|
||||||
// option is always in single quotes
|
|
||||||
// value is without quotes, because it could be true/false
|
|
||||||
$pattern = "/(['\"]{$key}['\"]\s*=>\s*)([^,]+),/";
|
|
||||||
|
|
||||||
// prepare the value, make booleans w/out single quotes
|
// First we get a fresh config file contents as text
|
||||||
if ($newValue === 'true') {
|
$config_contents = file_get_contents($config_file);
|
||||||
|
if ($config_contents === false) {
|
||||||
|
throw new Exception("Failed to read the config file: $config_file");
|
||||||
|
}
|
||||||
|
|
||||||
|
$lines = explode("\n", $config_contents);
|
||||||
|
|
||||||
|
// We loop through the variables and update them
|
||||||
|
foreach ($updatedConfig as $key => $newValue) {
|
||||||
|
if (strpos($key, '[') !== false) {
|
||||||
|
preg_match_all('/([^\[\]]+)/', $key, $matches);
|
||||||
|
if (empty($matches[1])) continue;
|
||||||
|
|
||||||
|
$parts = $matches[1];
|
||||||
|
$currentPath = [];
|
||||||
|
$found = false;
|
||||||
|
$inTargetArray = false;
|
||||||
|
|
||||||
|
foreach ($lines as $i => $line) {
|
||||||
|
$line = rtrim($line);
|
||||||
|
|
||||||
|
if (preg_match("/^\\s*\\]/", $line)) {
|
||||||
|
if (!empty($currentPath)) {
|
||||||
|
if ($inTargetArray && end($currentPath) === $parts[0]) {
|
||||||
|
$inTargetArray = false;
|
||||||
|
}
|
||||||
|
array_pop($currentPath);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match("/^\\s*['\"]([^'\"]+)['\"]\\s*=>/", $line, $matches)) {
|
||||||
|
$key = $matches[1];
|
||||||
|
|
||||||
|
if (strpos($line, '[') !== false) {
|
||||||
|
$currentPath[] = $key;
|
||||||
|
if ($key === $parts[0]) {
|
||||||
|
$inTargetArray = true;
|
||||||
|
}
|
||||||
|
} else if ($key === end($parts) && $inTargetArray) {
|
||||||
|
$pathMatches = true;
|
||||||
|
$expectedPath = array_slice($parts, 0, -1);
|
||||||
|
|
||||||
|
if (count($currentPath) === count($expectedPath)) {
|
||||||
|
for ($j = 0; $j < count($expectedPath); $j++) {
|
||||||
|
if ($currentPath[$j] !== $expectedPath[$j]) {
|
||||||
|
$pathMatches = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pathMatches) {
|
||||||
|
if ($newValue === 'true' || $newValue === '1') {
|
||||||
$replacementValue = 'true';
|
$replacementValue = 'true';
|
||||||
} elseif ($newValue === 'false') {
|
} elseif ($newValue === 'false' || $newValue === '0') {
|
||||||
$replacementValue = 'false';
|
$replacementValue = 'false';
|
||||||
} else {
|
} else {
|
||||||
$replacementValue = var_export($newValue, true);
|
$replacementValue = var_export($newValue, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// value replacing
|
if (preg_match("/^(\\s*['\"]" . preg_quote($key, '/') . "['\"]\\s*=>\\s*).*?(,?)\\s*$/", $line, $matches)) {
|
||||||
$config_contents = preg_replace($pattern, "$1{$replacementValue},", $config_contents);
|
$lines[$i] = $matches[1] . $replacementValue . $matches[2];
|
||||||
|
$updated[] = implode('.', array_merge($currentPath, [$key]));
|
||||||
|
$found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the new config file
|
if (!$found) {
|
||||||
if (!file_put_contents($config_file, $config_contents)) {
|
$allLogs[] = "Failed to update: $key";
|
||||||
return "Failed to write the config file \"$config_file\".";
|
}
|
||||||
|
} else {
|
||||||
|
if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $key)) {
|
||||||
|
throw new Exception("Invalid config key format: $key");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
if ($newValue === 'true' || $newValue === '1') {
|
||||||
|
$replacementValue = 'true';
|
||||||
|
} elseif ($newValue === 'false' || $newValue === '0') {
|
||||||
|
$replacementValue = 'false';
|
||||||
|
} else {
|
||||||
|
$replacementValue = var_export($newValue, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$found = false;
|
||||||
|
foreach ($lines as $i => $line) {
|
||||||
|
if (preg_match("/^(\\s*['\"]" . preg_quote($key, '/') . "['\"]\\s*=>\\s*).*?(,?)\\s*$/", $line, $matches)) {
|
||||||
|
$lines[$i] = $matches[1] . $replacementValue . $matches[2];
|
||||||
|
$updated[] = $key;
|
||||||
|
$found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$found) {
|
||||||
|
$allLogs[] = "Failed to update: $key";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We write the new config file
|
||||||
|
$new_contents = implode("\n", $lines);
|
||||||
|
if (file_put_contents($config_file, $new_contents) === false) {
|
||||||
|
throw new Exception("Failed to write the config file: $config_file");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($allLogs)) {
|
||||||
|
$logObject->insertLog($user_id, implode("\n", $allLogs), 'system');
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'updated' => $updated
|
||||||
|
];
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$logObject->insertLog($user_id, "Config update error: " . $e->getMessage(), 'system');
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ return [
|
||||||
// site name used in emails and in the inteerface
|
// site name used in emails and in the inteerface
|
||||||
'site_name' => 'Jilo Web',
|
'site_name' => 'Jilo Web',
|
||||||
// set to false to disable new registrations
|
// set to false to disable new registrations
|
||||||
'registration_enabled' => '1',
|
'registration_enabled' => true,
|
||||||
// will be displayed on login screen
|
// will be displayed on login screen
|
||||||
'login_message' => '',
|
'login_message' => '',
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ require_once __DIR__ . '/../helpers/security.php';
|
||||||
require_once __DIR__ . '/../helpers/logs.php';
|
require_once __DIR__ . '/../helpers/logs.php';
|
||||||
|
|
||||||
function applyCsrfMiddleware() {
|
function applyCsrfMiddleware() {
|
||||||
|
global $logObject;
|
||||||
$security = SecurityHelper::getInstance();
|
$security = SecurityHelper::getInstance();
|
||||||
|
|
||||||
// Skip CSRF check for GET requests
|
// Skip CSRF check for GET requests
|
||||||
|
@ -21,7 +22,8 @@ function applyCsrfMiddleware() {
|
||||||
|
|
||||||
// Check CSRF token for all other POST requests
|
// Check CSRF token for all other POST requests
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$token = $_POST['csrf_token'] ?? '';
|
// Check for token in POST data or headers
|
||||||
|
$token = $_POST['csrf_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||||
if (!$security->verifyCsrfToken($token)) {
|
if (!$security->verifyCsrfToken($token)) {
|
||||||
// Log CSRF attempt
|
// Log CSRF attempt
|
||||||
$ipAddress = getUserIP();
|
$ipAddress = getUserIP();
|
||||||
|
@ -31,7 +33,7 @@ function applyCsrfMiddleware() {
|
||||||
$_GET['page'] ?? 'unknown',
|
$_GET['page'] ?? 'unknown',
|
||||||
$_SESSION['username'] ?? 'anonymous'
|
$_SESSION['username'] ?? 'anonymous'
|
||||||
);
|
);
|
||||||
$logObject->insertLog(0, $logMessage, 'system');
|
$logObject->insertLog(null, $logMessage, 'system');
|
||||||
|
|
||||||
// Return error message
|
// Return error message
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
|
|
|
@ -10,14 +10,28 @@
|
||||||
include '../app/helpers/feedback.php';
|
include '../app/helpers/feedback.php';
|
||||||
|
|
||||||
require '../app/classes/config.php';
|
require '../app/classes/config.php';
|
||||||
$configObject = new Config();
|
require '../app/classes/api_response.php';
|
||||||
|
|
||||||
require '../app/includes/rate_limit_middleware.php';
|
// Initialize required objects
|
||||||
|
$userObject = new User($dbWeb);
|
||||||
|
$logObject = new Log($dbWeb);
|
||||||
|
$configObject = new Config();
|
||||||
|
|
||||||
// For AJAX requests
|
// For AJAX requests
|
||||||
$isAjax = !empty($_SERVER['HTTP_X_REQUESTED_WITH']) &&
|
$isAjax = !empty($_SERVER['HTTP_X_REQUESTED_WITH']) &&
|
||||||
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
|
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
|
||||||
|
|
||||||
|
// Ensure config file path is set
|
||||||
|
if (!isset($config_file) || empty($config_file)) {
|
||||||
|
if ($isAjax) {
|
||||||
|
ApiResponse::error('Config file path not set');
|
||||||
|
} else {
|
||||||
|
Feedback::flash('ERROR', 'DEFAULT', 'Config file path not set');
|
||||||
|
header('Location: ' . htmlspecialchars($app_root));
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if file is writable
|
// Check if file is writable
|
||||||
$isWritable = is_writable($config_file);
|
$isWritable = is_writable($config_file);
|
||||||
$configMessage = '';
|
$configMessage = '';
|
||||||
|
@ -26,7 +40,19 @@ if (!$isWritable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
// Check if user has permission to edit config
|
||||||
|
if (!$userObject->hasRight($user_id, 'edit config file')) {
|
||||||
|
$logObject->insertLog($user_id, "Unauthorized: User \"$currentUser\" tried to edit config file. IP: $user_IP", 'system');
|
||||||
|
if ($isAjax) {
|
||||||
|
ApiResponse::error('Forbidden: You do not have permission to edit the config file', null, 403);
|
||||||
|
} else {
|
||||||
|
include '../app/templates/error-unauthorized.php';
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
// Apply rate limiting
|
// Apply rate limiting
|
||||||
|
require '../app/includes/rate_limit_middleware.php';
|
||||||
checkRateLimit($dbWeb, 'config', $user_id);
|
checkRateLimit($dbWeb, 'config', $user_id);
|
||||||
|
|
||||||
// Ensure no output before this point
|
// Ensure no output before this point
|
||||||
|
@ -34,50 +60,35 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
// For AJAX requests, get JSON data
|
// For AJAX requests, get JSON data
|
||||||
if ($isAjax) {
|
if ($isAjax) {
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
// Get raw input
|
// Get raw input
|
||||||
$jsonData = file_get_contents('php://input');
|
$jsonData = file_get_contents('php://input');
|
||||||
|
if ($jsonData === false) {
|
||||||
|
$logObject->insertLog($user_id, "Failed to read request data for config update", 'system');
|
||||||
|
ApiResponse::error('Failed to read request data');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to parse JSON
|
||||||
$postData = json_decode($jsonData, true);
|
$postData = json_decode($jsonData, true);
|
||||||
|
|
||||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
$error = json_last_error_msg();
|
$error = json_last_error_msg();
|
||||||
|
ApiResponse::error('Invalid JSON data received: ' . $error);
|
||||||
Feedback::flash('ERROR', 'DEFAULT', 'Invalid JSON data received: ' . $error, true);
|
|
||||||
echo json_encode([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'Invalid JSON data received: ' . $error
|
|
||||||
]);
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to update config file
|
// Try to update config file
|
||||||
$result = $configObject->editConfigFile($postData, $config_file);
|
$result = $configObject->editConfigFile($postData, $config_file);
|
||||||
if ($result === true) {
|
if ($result['success']) {
|
||||||
$messageData = Feedback::getMessageData('NOTICE', 'DEFAULT', 'Config file updated successfully', true);
|
ApiResponse::success($result['updated'], 'Config file updated successfully');
|
||||||
echo json_encode([
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'Config file updated successfully',
|
|
||||||
'messageData' => $messageData
|
|
||||||
]);
|
|
||||||
} else {
|
} else {
|
||||||
$messageData = Feedback::getMessageData('ERROR', 'DEFAULT', "Error updating config file: $result", true);
|
ApiResponse::error($result['error']);
|
||||||
echo json_encode([
|
|
||||||
'success' => false,
|
|
||||||
'message' => "Error updating config file: $result",
|
|
||||||
'messageData' => $messageData
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle non-AJAX POST
|
// Handle non-AJAX POST
|
||||||
$result = $configObject->editConfigFile($_POST, $config_file);
|
$result = $configObject->editConfigFile($_POST, $config_file);
|
||||||
if ($result === true) {
|
if ($result['success']) {
|
||||||
Feedback::flash('NOTICE', 'DEFAULT', 'Config file updated successfully', true);
|
Feedback::flash('NOTICE', 'DEFAULT', 'Config file updated successfully', true);
|
||||||
} else {
|
} else {
|
||||||
Feedback::flash('ERROR', 'DEFAULT', "Error updating config file: $result", true);
|
Feedback::flash('ERROR', 'DEFAULT', "Error updating config file: " . $result['error'], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
header('Location: ' . htmlspecialchars($app_root) . '?page=config');
|
header('Location: ' . htmlspecialchars($app_root) . '?page=config');
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
|
||||||
<!-- config file -->
|
<!-- config file -->
|
||||||
<div class="container-fluid mt-2">
|
<div class="container-fluid mt-2">
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-12 mb-4">
|
<div class="col-12 mb-4">
|
||||||
<h2 class="mb-0">Configuration</h2>
|
<h2>Configuration</h2>
|
||||||
<small>Jilo Web configuration file: <em><?= htmlspecialchars($localConfigPath) ?></em></small>
|
<small><?= htmlspecialchars($config['site_name']) ?> configuration file: <em><?= htmlspecialchars($localConfigPath) ?></em></small>
|
||||||
<?php if ($configMessage) { ?>
|
<?php if ($configMessage) { ?>
|
||||||
<?= $configMessage ?>
|
<?= $configMessage ?>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
<div class="card-header bg-light d-flex justify-content-between align-items-center py-3">
|
<div class="card-header bg-light d-flex justify-content-between align-items-center py-3">
|
||||||
<h5 class="card-title mb-0">
|
<h5 class="card-title mb-0">
|
||||||
<i class="fas fa-wrench me-2 text-secondary"></i>
|
<i class="fas fa-wrench me-2 text-secondary"></i>
|
||||||
Jilo Web app configuration
|
<?= htmlspecialchars($config['site_name']) ?> app configuration
|
||||||
</h5>
|
</h5>
|
||||||
<?php if ($userObject->hasRight($user_id, 'edit config file')) { ?>
|
<?php if ($userObject->hasRight($user_id, 'edit config file')) { ?>
|
||||||
<div>
|
<div>
|
||||||
|
@ -37,6 +37,8 @@
|
||||||
<div class="card-body p-4">
|
<div class="card-body p-4">
|
||||||
<form id="configForm">
|
<form id="configForm">
|
||||||
<?php
|
<?php
|
||||||
|
include 'csrf_token.php';
|
||||||
|
|
||||||
function renderConfigItem($key, $value, $path = '') {
|
function renderConfigItem($key, $value, $path = '') {
|
||||||
$fullPath = $path ? $path . '[' . $key . ']' : $key;
|
$fullPath = $path ? $path . '[' . $key . ']' : $key;
|
||||||
// Only capitalize first letter, not every word
|
// Only capitalize first letter, not every word
|
||||||
|
@ -78,25 +80,27 @@ function renderConfigItem($key, $value, $path = '') {
|
||||||
</div>
|
</div>
|
||||||
<?php } elseif ($key === 'environment') { ?>
|
<?php } elseif ($key === 'environment') { ?>
|
||||||
<select class="form-select form-select-sm" name="<?= htmlspecialchars($fullPath) ?>">
|
<select class="form-select form-select-sm" name="<?= htmlspecialchars($fullPath) ?>">
|
||||||
<option value="development" <?= $value === 'development' ? 'selected' : '' ?>>Development</option>
|
<option value="development" <?= $value === 'development' ? 'selected' : '' ?>>development</option>
|
||||||
<option value="production" <?= $value === 'production' ? 'selected' : '' ?>>Production</option>
|
<option value="production" <?= $value === 'production' ? 'selected' : '' ?>>production</option>
|
||||||
</select>
|
</select>
|
||||||
<?php } else { ?>
|
<?php } else { ?>
|
||||||
<input type="text" class="form-control form-control-sm" name="<?= htmlspecialchars($fullPath) ?>" value="<?= htmlspecialchars($value ?? '') ?>">
|
<input type="text" class="form-control form-control-sm" name="<?= htmlspecialchars($fullPath) ?>" value="<?= htmlspecialchars($value) ?>">
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php }
|
<?php
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($config as $key => $value) {
|
foreach ($config as $key => $value) {
|
||||||
renderConfigItem($key, $value);
|
renderConfigItem($key, $value);
|
||||||
} ?>
|
} ?>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(function() {
|
$(function() {
|
||||||
|
@ -125,20 +129,17 @@ $(function() {
|
||||||
|
|
||||||
// Handle text inputs
|
// Handle text inputs
|
||||||
$('#configForm input[type="text"]').each(function() {
|
$('#configForm input[type="text"]').each(function() {
|
||||||
const name = $(this).attr('name');
|
data[$(this).attr('name')] = $(this).val();
|
||||||
data[name] = $(this).val();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle checkboxes
|
// Handle checkboxes
|
||||||
$('#configForm input[type="checkbox"]').each(function() {
|
$('#configForm input[type="checkbox"]').each(function() {
|
||||||
const name = $(this).attr('name');
|
data[$(this).attr('name')] = $(this).prop('checked') ? '1' : '0';
|
||||||
data[name] = $(this).prop('checked') ? '1' : '0';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle selects
|
// Handle selects
|
||||||
$('#configForm select').each(function() {
|
$('#configForm select').each(function() {
|
||||||
const name = $(this).attr('name');
|
data[$(this).attr('name')] = $(this).val();
|
||||||
data[name] = $(this).val();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
@ -147,44 +148,30 @@ $(function() {
|
||||||
data: JSON.stringify(data),
|
data: JSON.stringify(data),
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
headers: {
|
headers: {
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
'X-CSRF-Token': $('input[name="csrf_token"]').val()
|
||||||
},
|
},
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
// Show message first
|
|
||||||
if (response.messageData) {
|
|
||||||
JsMessages.success(response.messageData['message']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only update UI if save was successful
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
// Update view mode values
|
JsMessages.success(response.message || 'Config file updated successfully');
|
||||||
Object.entries(data).forEach(([key, value]) => {
|
|
||||||
const $item = $(`[name="${key}"]`).closest('.config-item');
|
$('#configForm input[type="text"], #configForm input[type="checkbox"], #configForm select').each(function() {
|
||||||
|
const $input = $(this);
|
||||||
|
const $item = $input.closest('.config-item');
|
||||||
const $viewMode = $item.find('.view-mode');
|
const $viewMode = $item.find('.view-mode');
|
||||||
|
|
||||||
if ($item.length) {
|
if ($item.length) {
|
||||||
if ($item.find('input[type="checkbox"]').length) {
|
let value;
|
||||||
// Boolean value
|
if ($input.is('[type="checkbox"]')) {
|
||||||
|
value = $input.prop('checked') ? '1' : '0';
|
||||||
const isEnabled = value === '1';
|
const isEnabled = value === '1';
|
||||||
$viewMode.html(`
|
$viewMode.html(`<span class="badge ${isEnabled ? 'bg-success' : 'bg-secondary'}">${isEnabled ? 'Enabled' : 'Disabled'}</span>`);
|
||||||
<span class="badge ${isEnabled ? 'bg-success' : 'bg-secondary'}">
|
} else if ($input.is('select')) {
|
||||||
${isEnabled ? 'Enabled' : 'Disabled'}
|
value = $input.val();
|
||||||
</span>
|
$viewMode.html(`<span class="badge ${value === 'production' ? 'bg-danger' : 'bg-info'}">${value}</span>`);
|
||||||
`);
|
|
||||||
} else if ($item.find('select').length) {
|
|
||||||
// Environment value
|
|
||||||
$viewMode.html(`
|
|
||||||
<span class="badge ${value === 'production' ? 'bg-danger' : 'bg-info'}">
|
|
||||||
${value}
|
|
||||||
</span>
|
|
||||||
`);
|
|
||||||
} else {
|
} else {
|
||||||
// Text value
|
value = $input.val();
|
||||||
if (value === '') {
|
$viewMode.html(value === '' ? '<span class="text-muted fst-italic">blank</span>' : `<span class="text-body">${value}</span>`);
|
||||||
$viewMode.html('<span class="text-muted fst-italic">blank</span>');
|
|
||||||
} else {
|
|
||||||
$viewMode.html(`<span class="text-body">${value}</span>`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -193,6 +180,8 @@ $(function() {
|
||||||
$('.edit-controls').addClass('d-none');
|
$('.edit-controls').addClass('d-none');
|
||||||
$('.view-mode').show();
|
$('.view-mode').show();
|
||||||
$('.edit-mode').addClass('d-none');
|
$('.edit-mode').addClass('d-none');
|
||||||
|
} else {
|
||||||
|
JsMessages.error(response.error || 'Error saving config');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(xhr, status, error) {
|
error: function(xhr, status, error) {
|
||||||
|
|
Loading…
Reference in New Issue