PHP Classes

File: templates/admin/dosdoors_config.twig

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   templates/admin/dosdoors_config.twig   Download  
File: templates/admin/dosdoors_config.twig
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 17,458 bytes
 

Contents

Class file image Download
{% extends "base.twig" %} {% block title %}{{ t('ui.admin.dosdoors_config.page_title', {}, locale, ['common']) }}{% endblock %} {% block content %} <div class="container-fluid"> <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> <h1 class="h2">{{ t('ui.admin.dosdoors_config.heading', {}, locale, ['common']) }}</h1> </div> <div class="alert alert-info"> <i class="fas fa-info-circle"></i> {{ t('ui.admin.dosdoors_config.info_text_1', {}, locale, ['common']) }} <code>config/dosdoors.json</code>. {{ t('ui.admin.dosdoors_config.info_text_2', {}, locale, ['common']) }} <code>dosbox-bridge/dos/doors/</code> {{ t('ui.admin.dosdoors_config.info_text_3', {}, locale, ['common']) }} <code>dosdoor.jsn</code> {{ t('ui.admin.dosdoors_config.info_text_4', {}, locale, ['common']) }} </div> <div id="statusAlert"></div> <div class="row g-4"> <!-- Available Doors List --> <div class="col-lg-4"> <div class="card shadow"> <div class="card-header"> <h6 class="mb-0">{{ t('ui.admin.dosdoors_config.installed_doors', {}, locale, ['common']) }}</h6> </div> <div class="card-body" id="doorsList"> <div class="text-center text-muted"> <div class="spinner-border spinner-border-sm" role="status"> <span class="visually-hidden">{{ t('ui.common.loading', {}, locale, ['common']) }}</span> </div> {{ t('ui.admin.dosdoors_config.loading_doors', {}, locale, ['common']) }} </div> </div> </div> <div class="card shadow mt-3"> <div class="card-header"> <h6 class="mb-0">{{ t('ui.admin.dosdoors_config.quick_actions', {}, locale, ['common']) }}</h6> </div> <div class="card-body"> <button class="btn btn-sm btn-outline-primary w-100 mb-2" onclick="enableAllDoors()"> <i class="fas fa-check-circle"></i> {{ t('ui.admin.dosdoors_config.enable_all_doors', {}, locale, ['common']) }} </button> <button class="btn btn-sm btn-outline-secondary w-100" onclick="disableAllDoors()"> <i class="fas fa-times-circle"></i> {{ t('ui.admin.dosdoors_config.disable_all_doors', {}, locale, ['common']) }} </button> </div> </div> </div> <!-- Configuration Editor --> <div class="col-lg-8"> <div class="card shadow"> <div class="card-header d-flex justify-content-between align-items-center"> <h6 class="mb-0">{{ t('ui.admin.dosdoors_config.config_filename', {}, locale, ['common']) }}</h6> <div class="btn-group"> <button class="btn btn-sm btn-outline-secondary" onclick="formatJson()"> <i class="fas fa-align-left"></i> {{ t('ui.admin.dosdoors_config.format', {}, locale, ['common']) }} </button> <button class="btn btn-sm btn-outline-primary" onclick="saveConfig()" id="saveBtn"> <i class="fas fa-save"></i> {{ t('ui.common.save', {}, locale, ['common']) }} </button> </div> </div> <div class="card-body"> <div id="editorAlert"></div> <textarea id="configEditor" class="form-control font-monospace" rows="20" spellcheck="false"></textarea> <div class="d-flex justify-content-between align-items-center mt-2"> <small class="text-muted" id="jsonStatus">{{ t('ui.admin.dosdoors_config.loading_config', {}, locale, ['common']) }}</small> <small class="text-muted">{{ t('ui.admin.dosdoors_config.json_validation_before_save', {}, locale, ['common']) }}</small> </div> </div> </div> <!-- Door Configuration Help --> <div class="card shadow mt-3"> <div class="card-header"> <h6 class="mb-0">{{ t('ui.admin.dosdoors_config.configuration_options', {}, locale, ['common']) }}</h6> </div> <div class="card-body"> <table class="table table-sm table-borderless"> <tr> <td class="text-nowrap"><code>enabled</code></td> <td>{{ t('ui.admin.dosdoors_config.option_enabled_help', {}, locale, ['common']) }}</td> </tr> <tr> <td class="text-nowrap"><code>credit_cost</code></td> <td>{{ t('ui.admin.dosdoors_config.option_credit_cost_help', {}, locale, ['common']) }}</td> </tr> <tr> <td class="text-nowrap"><code>max_time_minutes</code></td> <td>{{ t('ui.admin.dosdoors_config.option_max_time_help', {}, locale, ['common']) }}</td> </tr> <tr> <td class="text-nowrap"><code>cpu_cycles</code></td> <td>{{ t('ui.admin.dosdoors_config.option_cpu_cycles_help', {}, locale, ['common']) }}</td> </tr> <tr> <td class="text-nowrap"><code>max_concurrent_sessions</code></td> <td>{{ t('ui.admin.dosdoors_config.option_max_concurrent_help', {}, locale, ['common']) }}</td> </tr> </table> </div> </div> </div> </div> </div> {% endblock %} {% block scripts %} <script> let doorsState = { config: {}, availableDoors: [], configExists: false }; function uiT(key, fallback, params = {}) { if (window.t) { return window.t(key, params, fallback); } return fallback; } document.addEventListener('DOMContentLoaded', function() { loadI18nNamespaces(['common', 'errors']).then(function() { loadConfig(); loadAvailableDoors(); }); document.getElementById('configEditor').addEventListener('input', () => { updateJsonStatus(); renderDoorsList(); }); }); function loadConfig() { fetch('/admin/api/dosdoors-config') .then(response => response.json()) .then(data => { if (!data.success) { throw new Error(apiError(data, uiT('ui.admin.dosdoors_config.load_config_failed', 'Failed to load config'))); } doorsState.config = data.config || {}; doorsState.configExists = data.exists; const editor = document.getElementById('configEditor'); editor.value = JSON.stringify(doorsState.config, null, 4); updateJsonStatus(); renderDoorsList(); }) .catch(error => { showAlert('danger', uiT('ui.admin.dosdoors_config.load_config_error_prefix', 'Error loading config: ') + error.message); }); } function loadAvailableDoors() { fetch('/admin/api/dosdoors-available') .then(response => response.json()) .then(data => { if (!data.success) { throw new Error(apiError(data, uiT('ui.admin.dosdoors_config.load_doors_failed', 'Failed to load doors'))); } doorsState.availableDoors = data.doors || []; renderDoorsList(); }) .catch(error => { showAlert('danger', uiT('ui.admin.dosdoors_config.load_doors_error_prefix', 'Error loading available doors: ') + error.message); }); } function renderDoorsList() { const container = document.getElementById('doorsList'); if (doorsState.availableDoors.length === 0) { container.innerHTML = '<div class="text-muted">' + uiT('ui.admin.dosdoors_config.no_doors_found_prefix', 'No doors found. Install doors in') + ' <code>dosbox-bridge/dos/doors/</code></div>'; return; } // Parse current editor config let currentConfig = {}; try { const editorValue = document.getElementById('configEditor').value.trim(); if (editorValue) { currentConfig = JSON.parse(editorValue); // Handle edge case: config is an array instead of object if (Array.isArray(currentConfig)) { currentConfig = {}; } } } catch (e) { currentConfig = doorsState.config; } let html = '<div class="list-group list-group-flush">'; doorsState.availableDoors.forEach(door => { const doorConfig = currentConfig[door.id] || {}; const enabled = doorConfig.enabled || false; const cost = doorConfig.credit_cost || 0; const statusClass = enabled ? 'success' : 'secondary'; const statusIcon = enabled ? 'fa-check-circle' : 'fa-circle'; html += ` <div class="list-group-item"> <div class="d-flex justify-content-between align-items-start"> <div class="flex-grow-1"> <div class="d-flex align-items-center gap-2 mb-1"> <i class="fas ${statusIcon} text-${statusClass}"></i> <strong>${door.name}</strong> </div> <div class="small text-muted">${door.description || uiT('ui.admin.dosdoors_config.no_description', 'No description')}</div> <div class="small mt-1"> <span class="badge bg-secondary">${door.id}</span> ${cost > 0 ? `<span class="badge bg-info">${cost} ${uiT('ui.admin.dosdoors_config.credits', 'credits')}</span>` : '<span class="badge bg-success">' + uiT('ui.admin.dosdoors_config.free', 'Free') + '</span>'} </div> </div> <div class="btn-group-vertical btn-group-sm"> <button class="btn btn-outline-${enabled ? 'success' : 'secondary'}" onclick="toggleDoor('${door.id}', ${!enabled})" title="${enabled ? uiT('ui.admin.dosdoors_config.disable', 'Disable') : uiT('ui.admin.dosdoors_config.enable', 'Enable')}"> <i class="fas fa-power-off"></i> </button> </div> </div> </div> `; }); html += '</div>'; container.innerHTML = html; } function toggleDoor(doorId, enable) { try { const editor = document.getElementById('configEditor'); let config = JSON.parse(editor.value || '{}'); // Handle edge case: config is an array instead of object if (Array.isArray(config)) { config = {}; } if (!config[doorId]) { // Initialize with defaults if not exists config[doorId] = { enabled: enable, credit_cost: 0, max_time_minutes: 30, cpu_cycles: 10000, max_concurrent_sessions: 10 }; } else { config[doorId].enabled = enable; } editor.value = JSON.stringify(config, null, 4); updateJsonStatus(); renderDoorsList(); } catch (e) { showEditorAlert('danger', uiT('ui.admin.dosdoors_config.invalid_json_toggle', 'Invalid JSON - cannot toggle door')); } } function enableAllDoors() { try { const editor = document.getElementById('configEditor'); let config = JSON.parse(editor.value || '{}'); // Handle edge case: config is an array instead of object if (Array.isArray(config)) { config = {}; } doorsState.availableDoors.forEach(door => { if (!config[door.id]) { config[door.id] = { enabled: true, credit_cost: 0, max_time_minutes: 30, cpu_cycles: 10000, max_concurrent_sessions: 10 }; } else { config[door.id].enabled = true; } }); editor.value = JSON.stringify(config, null, 4); updateJsonStatus(); renderDoorsList(); showEditorAlert('success', uiT('ui.admin.dosdoors_config.enabled_all_editor', 'All doors enabled in editor. Click Save to apply.')); } catch (e) { showEditorAlert('danger', uiT('ui.admin.dosdoors_config.invalid_json_enable_all', 'Invalid JSON - cannot enable all')); } } function disableAllDoors() { try { const editor = document.getElementById('configEditor'); let config = JSON.parse(editor.value || '{}'); Object.keys(config).forEach(doorId => { config[doorId].enabled = false; }); editor.value = JSON.stringify(config, null, 4); updateJsonStatus(); renderDoorsList(); showEditorAlert('warning', uiT('ui.admin.dosdoors_config.disabled_all_editor', 'All doors disabled in editor. Click Save to apply.')); } catch (e) { showEditorAlert('danger', uiT('ui.admin.dosdoors_config.invalid_json_disable_all', 'Invalid JSON - cannot disable all')); } } function formatJson() { try { const editor = document.getElementById('configEditor'); const config = JSON.parse(editor.value); editor.value = JSON.stringify(config, null, 4); updateJsonStatus(); showEditorAlert('success', uiT('ui.admin.dosdoors_config.json_formatted', 'JSON formatted successfully')); } catch (e) { showEditorAlert('danger', uiT('ui.admin.dosdoors_config.cannot_format_prefix', 'Cannot format - invalid JSON: ') + e.message); } } function updateJsonStatus() { const editor = document.getElementById('configEditor'); const status = document.getElementById('jsonStatus'); try { const value = editor.value.trim(); if (!value) { status.textContent = uiT('ui.admin.dosdoors_config.empty_config', 'Empty configuration'); status.className = 'text-warning'; return; } JSON.parse(value); status.textContent = uiT('ui.admin.dosdoors_config.valid_json', 'Valid JSON'); status.className = 'text-success'; } catch (e) { status.textContent = uiT('ui.admin.dosdoors_config.invalid_json_prefix', 'Invalid JSON: ') + e.message; status.className = 'text-danger'; } } function saveConfig() { const editor = document.getElementById('configEditor'); try { const config = JSON.parse(editor.value); document.getElementById('saveBtn').disabled = true; fetch('/admin/api/dosdoors-config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ config: config }) }) .then(response => response.json()) .then(data => { if (!data.success) { throw new Error(apiError(data, uiT('ui.admin.dosdoors_config.save_failed', 'Save failed'))); } doorsState.config = data.config; const successMessage = window.getApiMessage ? window.getApiMessage(data, uiT('ui.admin.dosdoors_config.saved_success', 'Configuration saved successfully!')) : uiT('ui.admin.dosdoors_config.saved_success', 'Configuration saved successfully!'); showAlert('success', successMessage); renderDoorsList(); }) .catch(error => { showAlert('danger', uiT('ui.admin.dosdoors_config.save_error_prefix', 'Error saving config: ') + error.message); }) .finally(() => { document.getElementById('saveBtn').disabled = false; }); } catch (e) { showEditorAlert('danger', uiT('ui.admin.dosdoors_config.cannot_save_invalid_json_prefix', 'Cannot save - invalid JSON: ') + e.message); } } function showAlert(type, message) { const alert = document.getElementById('statusAlert'); alert.innerHTML = ` <div class="alert alert-${type} alert-dismissible fade show" role="alert"> ${message} <button type="button" class="btn-close" data-bs-dismiss="alert"></button> </div> `; } function showEditorAlert(type, message) { const alert = document.getElementById('editorAlert'); alert.innerHTML = ` <div class="alert alert-${type} alert-dismissible fade show" role="alert"> ${message} <button type="button" class="btn-close" data-bs-dismiss="alert"></button> </div> `; setTimeout(() => { alert.innerHTML = ''; }, 5000); } function apiError(payload, fallback) { if (window.getApiErrorMessage) { return window.getApiErrorMessage(payload, fallback); } if (payload && payload.error) { return String(payload.error); } return fallback; } </script> {% endblock %}