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
|
||||
*
|
||||
* Handles editing and fetching ot the config files.
|
||||
* Handles editing and fetching of the config files.
|
||||
*/
|
||||
class Config {
|
||||
|
||||
|
@ -13,41 +13,146 @@ class Config {
|
|||
* @param array $updatedConfig Key-value pairs of config options to update.
|
||||
* @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) {
|
||||
// first we get a fresh config file contents as text
|
||||
$config_contents = file_get_contents($config_file);
|
||||
if (!$config_contents) {
|
||||
return "Failed to read the config file \"$config_file\".";
|
||||
}
|
||||
global $logObject, $user_id;
|
||||
$allLogs = [];
|
||||
$updated = [];
|
||||
|
||||
// loop through the variables and updated them
|
||||
foreach ($updatedConfig as $key => $newValue) {
|
||||
// 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
|
||||
if ($newValue === 'true') {
|
||||
$replacementValue = 'true';
|
||||
} elseif ($newValue === 'false') {
|
||||
$replacementValue = 'false';
|
||||
} else {
|
||||
$replacementValue = var_export($newValue, true);
|
||||
try {
|
||||
if (!is_array($updatedConfig)) {
|
||||
throw new Exception("Invalid config data: expected array");
|
||||
}
|
||||
|
||||
// value replacing
|
||||
$config_contents = preg_replace($pattern, "$1{$replacementValue},", $config_contents);
|
||||
}
|
||||
if (!file_exists($config_file) || !is_writable($config_file)) {
|
||||
throw new Exception("Config file does not exist or is not writable: $config_file");
|
||||
}
|
||||
|
||||
// write the new config file
|
||||
if (!file_put_contents($config_file, $config_contents)) {
|
||||
return "Failed to write the config file \"$config_file\".";
|
||||
}
|
||||
// First we get a fresh config file contents as text
|
||||
$config_contents = file_get_contents($config_file);
|
||||
if ($config_contents === false) {
|
||||
throw new Exception("Failed to read the config file: $config_file");
|
||||
}
|
||||
|
||||
return true;
|
||||
$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';
|
||||
} elseif ($newValue === 'false' || $newValue === '0') {
|
||||
$replacementValue = 'false';
|
||||
} else {
|
||||
$replacementValue = var_export($newValue, true);
|
||||
}
|
||||
|
||||
if (preg_match("/^(\\s*['\"]" . preg_quote($key, '/') . "['\"]\\s*=>\\s*).*?(,?)\\s*$/", $line, $matches)) {
|
||||
$lines[$i] = $matches[1] . $replacementValue . $matches[2];
|
||||
$updated[] = implode('.', array_merge($currentPath, [$key]));
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
$allLogs[] = "Failed to update: $key";
|
||||
}
|
||||
} else {
|
||||
if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $key)) {
|
||||
throw new Exception("Invalid config key format: $key");
|
||||
}
|
||||
|
||||
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' => 'Jilo Web',
|
||||
// set to false to disable new registrations
|
||||
'registration_enabled' => '1',
|
||||
'registration_enabled' => true,
|
||||
// will be displayed on login screen
|
||||
'login_message' => '',
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ require_once __DIR__ . '/../helpers/security.php';
|
|||
require_once __DIR__ . '/../helpers/logs.php';
|
||||
|
||||
function applyCsrfMiddleware() {
|
||||
global $logObject;
|
||||
$security = SecurityHelper::getInstance();
|
||||
|
||||
// Skip CSRF check for GET requests
|
||||
|
@ -21,7 +22,8 @@ function applyCsrfMiddleware() {
|
|||
|
||||
// Check CSRF token for all other POST requests
|
||||
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)) {
|
||||
// Log CSRF attempt
|
||||
$ipAddress = getUserIP();
|
||||
|
@ -31,7 +33,7 @@ function applyCsrfMiddleware() {
|
|||
$_GET['page'] ?? 'unknown',
|
||||
$_SESSION['username'] ?? 'anonymous'
|
||||
);
|
||||
$logObject->insertLog(0, $logMessage, 'system');
|
||||
$logObject->insertLog(null, $logMessage, 'system');
|
||||
|
||||
// Return error message
|
||||
http_response_code(403);
|
||||
|
|
|
@ -10,14 +10,28 @@
|
|||
include '../app/helpers/feedback.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
|
||||
$isAjax = !empty($_SERVER['HTTP_X_REQUESTED_WITH']) &&
|
||||
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
|
||||
$isWritable = is_writable($config_file);
|
||||
$configMessage = '';
|
||||
|
@ -26,7 +40,19 @@ if (!$isWritable) {
|
|||
}
|
||||
|
||||
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
|
||||
require '../app/includes/rate_limit_middleware.php';
|
||||
checkRateLimit($dbWeb, 'config', $user_id);
|
||||
|
||||
// Ensure no output before this point
|
||||
|
@ -34,50 +60,35 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
|
||||
// For AJAX requests, get JSON data
|
||||
if ($isAjax) {
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Get raw 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);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
$error = json_last_error_msg();
|
||||
|
||||
Feedback::flash('ERROR', 'DEFAULT', 'Invalid JSON data received: ' . $error, true);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Invalid JSON data received: ' . $error
|
||||
]);
|
||||
exit;
|
||||
ApiResponse::error('Invalid JSON data received: ' . $error);
|
||||
}
|
||||
|
||||
// Try to update config file
|
||||
$result = $configObject->editConfigFile($postData, $config_file);
|
||||
if ($result === true) {
|
||||
$messageData = Feedback::getMessageData('NOTICE', 'DEFAULT', 'Config file updated successfully', true);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Config file updated successfully',
|
||||
'messageData' => $messageData
|
||||
]);
|
||||
if ($result['success']) {
|
||||
ApiResponse::success($result['updated'], 'Config file updated successfully');
|
||||
} else {
|
||||
$messageData = Feedback::getMessageData('ERROR', 'DEFAULT', "Error updating config file: $result", true);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => "Error updating config file: $result",
|
||||
'messageData' => $messageData
|
||||
]);
|
||||
ApiResponse::error($result['error']);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// Handle non-AJAX POST
|
||||
$result = $configObject->editConfigFile($_POST, $config_file);
|
||||
if ($result === true) {
|
||||
if ($result['success']) {
|
||||
Feedback::flash('NOTICE', 'DEFAULT', 'Config file updated successfully', true);
|
||||
} 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');
|
||||
|
|
|
@ -1,42 +1,44 @@
|
|||
|
||||
<!-- config file -->
|
||||
<div class="container-fluid mt-2">
|
||||
<div class="row mb-4">
|
||||
<div class="col-12 mb-4">
|
||||
<h2 class="mb-0">Configuration</h2>
|
||||
<small>Jilo Web configuration file: <em><?= htmlspecialchars($localConfigPath) ?></em></small>
|
||||
<!-- config file -->
|
||||
<div class="container-fluid mt-2">
|
||||
<div class="row mb-4">
|
||||
<div class="col-12 mb-4">
|
||||
<h2>Configuration</h2>
|
||||
<small><?= htmlspecialchars($config['site_name']) ?> configuration file: <em><?= htmlspecialchars($localConfigPath) ?></em></small>
|
||||
<?php if ($configMessage) { ?>
|
||||
<?= $configMessage ?>
|
||||
<?= $configMessage ?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-light d-flex justify-content-between align-items-center py-3">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fas fa-wrench me-2 text-secondary"></i>
|
||||
Jilo Web app configuration
|
||||
</h5>
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-light d-flex justify-content-between align-items-center py-3">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fas fa-wrench me-2 text-secondary"></i>
|
||||
<?= htmlspecialchars($config['site_name']) ?> app configuration
|
||||
</h5>
|
||||
<?php if ($userObject->hasRight($user_id, 'edit config file')) { ?>
|
||||
<div>
|
||||
<button type="button" class="btn btn-outline-primary btn-sm toggle-edit" <?= !$isWritable ? 'disabled' : '' ?>>
|
||||
<i class="fas fa-edit me-2"></i>Edit
|
||||
</button>
|
||||
<div class="edit-controls d-none">
|
||||
<button type="button" class="btn btn-danger btn-sm save-config">
|
||||
<i class="fas fa-save me-2"></i>Save
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm ms-2 cancel-edit">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="btn btn-outline-primary btn-sm toggle-edit" <?= !$isWritable ? 'disabled' : '' ?>>
|
||||
<i class="fas fa-edit me-2"></i>Edit
|
||||
</button>
|
||||
<div class="edit-controls d-none">
|
||||
<button type="button" class="btn btn-danger btn-sm save-config">
|
||||
<i class="fas fa-save me-2"></i>Save
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm ms-2 cancel-edit">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body p-4">
|
||||
<form id="configForm">
|
||||
<div class="card-body p-4">
|
||||
<form id="configForm">
|
||||
<?php
|
||||
include 'csrf_token.php';
|
||||
|
||||
function renderConfigItem($key, $value, $path = '') {
|
||||
$fullPath = $path ? $path . '[' . $key . ']' : $key;
|
||||
// Only capitalize first letter, not every word
|
||||
|
@ -53,50 +55,52 @@ function renderConfigItem($key, $value, $path = '') {
|
|||
echo "\t\t\t\t\t\t\t\t</div>\n";
|
||||
} else {
|
||||
?>
|
||||
<div class="config-item row mb-3 align-items-center">
|
||||
<div class="col-md-4 text-end">
|
||||
<label class="form-label mb-0"><?= htmlspecialchars($displayName) ?></label>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="view-mode">
|
||||
<div class="config-item row mb-3 align-items-center">
|
||||
<div class="col-md-4 text-end">
|
||||
<label class="form-label mb-0"><?= htmlspecialchars($displayName) ?></label>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="view-mode">
|
||||
<?php if (is_bool($value) || $key === 'registration_enabled') { ?>
|
||||
<span class="badge <?= $value ? 'bg-success' : 'bg-secondary' ?>"><?= $value ? 'Enabled' : 'Disabled' ?></span>
|
||||
<span class="badge <?= $value ? 'bg-success' : 'bg-secondary' ?>"><?= $value ? 'Enabled' : 'Disabled' ?></span>
|
||||
<?php } elseif ($key === 'environment') { ?>
|
||||
<span class="badge <?= $value === 'production' ? 'bg-danger' : 'bg-info' ?>"><?= htmlspecialchars($value) ?></span>
|
||||
<span class="badge <?= $value === 'production' ? 'bg-danger' : 'bg-info' ?>"><?= htmlspecialchars($value) ?></span>
|
||||
<?php } else {
|
||||
if (empty($value) && $value !== '0') { ?>
|
||||
<span class="text-muted fst-italic">blank</span>
|
||||
<span class="text-muted fst-italic">blank</span>
|
||||
<?php } else { ?>
|
||||
<span class="text-body"><?= htmlspecialchars($value) ?></span>
|
||||
<span class="text-body"><?= htmlspecialchars($value) ?></span>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<div class="edit-mode d-none">
|
||||
</div>
|
||||
<div class="edit-mode d-none">
|
||||
<?php if (is_bool($value) || $key === 'registration_enabled') { ?>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" name="<?= htmlspecialchars($fullPath) ?>" <?= $value ? 'checked' : '' ?>>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" name="<?= htmlspecialchars($fullPath) ?>" <?= $value ? 'checked' : '' ?>>
|
||||
</div>
|
||||
<?php } elseif ($key === 'environment') { ?>
|
||||
<select class="form-select form-select-sm" name="<?= htmlspecialchars($fullPath) ?>">
|
||||
<option value="development" <?= $value === 'development' ? 'selected' : '' ?>>Development</option>
|
||||
<option value="production" <?= $value === 'production' ? 'selected' : '' ?>>Production</option>
|
||||
</select>
|
||||
<select class="form-select form-select-sm" name="<?= htmlspecialchars($fullPath) ?>">
|
||||
<option value="development" <?= $value === 'development' ? 'selected' : '' ?>>development</option>
|
||||
<option value="production" <?= $value === 'production' ? 'selected' : '' ?>>production</option>
|
||||
</select>
|
||||
<?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 } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php }
|
||||
}
|
||||
foreach ($config as $key => $value) {
|
||||
renderConfigItem($key, $value);
|
||||
} ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($config as $key => $value) {
|
||||
renderConfigItem($key, $value);
|
||||
} ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
|
@ -125,20 +129,17 @@ $(function() {
|
|||
|
||||
// Handle text inputs
|
||||
$('#configForm input[type="text"]').each(function() {
|
||||
const name = $(this).attr('name');
|
||||
data[name] = $(this).val();
|
||||
data[$(this).attr('name')] = $(this).val();
|
||||
});
|
||||
|
||||
// Handle checkboxes
|
||||
$('#configForm input[type="checkbox"]').each(function() {
|
||||
const name = $(this).attr('name');
|
||||
data[name] = $(this).prop('checked') ? '1' : '0';
|
||||
data[$(this).attr('name')] = $(this).prop('checked') ? '1' : '0';
|
||||
});
|
||||
|
||||
// Handle selects
|
||||
$('#configForm select').each(function() {
|
||||
const name = $(this).attr('name');
|
||||
data[name] = $(this).val();
|
||||
data[$(this).attr('name')] = $(this).val();
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
|
@ -147,44 +148,30 @@ $(function() {
|
|||
data: JSON.stringify(data),
|
||||
contentType: 'application/json',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'X-CSRF-Token': $('input[name="csrf_token"]').val()
|
||||
},
|
||||
success: function(response) {
|
||||
// Show message first
|
||||
if (response.messageData) {
|
||||
JsMessages.success(response.messageData['message']);
|
||||
}
|
||||
|
||||
// Only update UI if save was successful
|
||||
if (response.success) {
|
||||
// Update view mode values
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
const $item = $(`[name="${key}"]`).closest('.config-item');
|
||||
JsMessages.success(response.message || 'Config file updated successfully');
|
||||
|
||||
$('#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');
|
||||
|
||||
if ($item.length) {
|
||||
if ($item.find('input[type="checkbox"]').length) {
|
||||
// Boolean value
|
||||
let value;
|
||||
if ($input.is('[type="checkbox"]')) {
|
||||
value = $input.prop('checked') ? '1' : '0';
|
||||
const isEnabled = value === '1';
|
||||
$viewMode.html(`
|
||||
<span class="badge ${isEnabled ? 'bg-success' : 'bg-secondary'}">
|
||||
${isEnabled ? 'Enabled' : 'Disabled'}
|
||||
</span>
|
||||
`);
|
||||
} else if ($item.find('select').length) {
|
||||
// Environment value
|
||||
$viewMode.html(`
|
||||
<span class="badge ${value === 'production' ? 'bg-danger' : 'bg-info'}">
|
||||
${value}
|
||||
</span>
|
||||
`);
|
||||
$viewMode.html(`<span class="badge ${isEnabled ? 'bg-success' : 'bg-secondary'}">${isEnabled ? 'Enabled' : 'Disabled'}</span>`);
|
||||
} else if ($input.is('select')) {
|
||||
value = $input.val();
|
||||
$viewMode.html(`<span class="badge ${value === 'production' ? 'bg-danger' : 'bg-info'}">${value}</span>`);
|
||||
} else {
|
||||
// Text value
|
||||
if (value === '') {
|
||||
$viewMode.html('<span class="text-muted fst-italic">blank</span>');
|
||||
} else {
|
||||
$viewMode.html(`<span class="text-body">${value}</span>`);
|
||||
}
|
||||
value = $input.val();
|
||||
$viewMode.html(value === '' ? '<span class="text-muted fst-italic">blank</span>' : `<span class="text-body">${value}</span>`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -193,6 +180,8 @@ $(function() {
|
|||
$('.edit-controls').addClass('d-none');
|
||||
$('.view-mode').show();
|
||||
$('.edit-mode').addClass('d-none');
|
||||
} else {
|
||||
JsMessages.error(response.error || 'Error saving config');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
|
|
Loading…
Reference in New Issue