refactor: clean ONLYOFFICE integration - remove workarounds, follow standard pattern

This commit is contained in:
root
2026-07-17 08:56:38 +00:00
parent 7883c46b15
commit a962f29f55
4 changed files with 92 additions and 324 deletions

View File

@@ -693,14 +693,4 @@ function formatDate(d) {
}
// Initial load
loadDashboard();
// Restore active page after an ONLYOFFICE editor close triggered a page reload
(function() {
var returnPage = sessionStorage.getItem('returnPage');
if (returnPage) {
sessionStorage.removeItem('returnPage');
var link = document.querySelector('[data-page="' + returnPage + '"]');
if (link) link.click();
}
})();
loadDashboard();

View File

@@ -263,56 +263,34 @@ async function loadOnlyofficeTemplates() {
// Force save the document
function forceSaveOnlyoffice() {
if (onlyofficeEditorInstance) {
// The ONLYOFFICE editor's onSave event will trigger the callback
// which saves the file to our backend
toast('Saving... changes will be stored automatically');
}
}
// Close the editor and clean up
// Close the editor — standard ONLYOFFICE teardown
function closeOnlyofficeEditor() {
if (onlyofficeEditorInstance) {
try {
onlyofficeEditorInstance.destroyEditor();
} catch (e) {
console.error('Error closing editor:', e);
}
try { onlyofficeEditorInstance.destroyEditor(); } catch (e) {}
onlyofficeEditorInstance = null;
}
closeModal();
// Reload the page to clear ONLYOFFICE's in-memory state.
// Save the active page so the user returns to Templates automatically
sessionStorage.setItem('returnPage', 'templates');
// Wait 2 seconds for the save callback (status=2) to complete
// on our backend before reloading the page
setTimeout(() => {
location.reload();
}, 2000);
loadOnlyofficeTemplates();
}
// Open ONLYOFFICE editor in a modal
async function openOnlyofficeEditor(templateId, templateName) {
// If there's a previous editor instance still lingering, destroy it first
if (onlyofficeEditorInstance) {
try { onlyofficeEditorInstance.destroyEditor(); } catch (e) {}
onlyofficeEditorInstance = null;
}
try {
// Get the JWT-signed editor config from our backend
// Add cache-buster to prevent browser caching the old config
const cacheBuster = Date.now();
const resp = await fetch(ONLYOFFICE_API + '/' + templateId + '/config?cb=' + cacheBuster);
if (!resp.ok) {
const err = await resp.json();
throw new Error(err.error || 'Failed to get editor config');
}
const data = await resp.json();
const config = data.config;
const onlyofficeUrl = data.onlyofficeUrl;
// Fetch JWT-signed config from backend (cache-busted)
const resp = await fetch(ONLYOFFICE_API + '/' + templateId + '/config?cb=' + Date.now());
if (!resp.ok) throw new Error((await resp.json()).error || 'Failed to get config');
const { config, onlyofficeUrl } = await resp.json();
// Build the editor modal with a unique container ID each time
// so ONLYOFFICE never reuses a cached DOM element
// Unique container ID per session
const containerId = 'oo-editor-' + Date.now();
const body = `
<div class="flex-between mb-16">
@@ -327,26 +305,19 @@ async function openOnlyofficeEditor(templateId, templateName) {
`;
showModal(body, '', 'xlarge');
// Load ONLYOFFICE API script - force fresh load each time
// Remove old script to force ONLYOFFICE to reload API.js from server
const oldScript = document.getElementById('onlyoffice-api-script');
if (oldScript) oldScript.remove();
// Reload with cache-buster so ONLYOFFICE gets fresh API state
const scriptBuster = Date.now();
const scriptUrl = onlyofficeUrl + '/web-apps/apps/api/documents/api.js?cb=' + scriptBuster;
await new Promise((resolve, reject) => {
const script = document.createElement('script');
script.id = 'onlyoffice-api-script';
script.src = scriptUrl;
script.onload = resolve;
script.onerror = () => reject(new Error('Failed to load ONLYOFFICE API from ' + scriptUrl));
document.head.appendChild(script);
// Load ONLYOFFICE API script (once)
if (typeof DocsAPI === 'undefined') {
await new Promise((resolve, reject) => {
const s = document.createElement('script');
s.src = onlyofficeUrl + '/web-apps/apps/api/documents/api.js';
s.onload = resolve;
s.onerror = () => reject(new Error('Failed to load ONLYOFFICE API'));
document.head.appendChild(s);
});
// Initialize the ONLYOFFICE editor
}
if (typeof DocsAPI === 'undefined' || !DocsAPI.DocEditor) {
throw new Error('ONLYOFFICE API not loaded. Check that the server is running at ' + onlyofficeUrl);
throw new Error('ONLYOFFICE API not loaded');
}
onlyofficeEditorInstance = new DocsAPI.DocEditor(containerId, {
@@ -354,30 +325,11 @@ async function openOnlyofficeEditor(templateId, templateName) {
width: '100%',
height: '800px',
events: {
onAppReady: function() {
console.log('ONLYOFFICE editor ready');
},
onDocumentReady: function() {
console.log('Document loaded in editor');
// Inject CSS into ONLYOFFICE iframe to hide the logo
try {
var iframe = document.getElementById(containerId).querySelector('iframe');
if (iframe && iframe.contentDocument) {
var style = iframe.contentDocument.createElement('style');
style.textContent = '#header-logo { display: none !important; }';
iframe.contentDocument.head.appendChild(style);
}
} catch(e) {
console.log('Could not inject CSS into iframe:', e);
}
},
onSave: function(event) {
toast('Document saved successfully');
// Reload the templates list
onSave: function() {
toast('Document saved');
loadOnlyofficeTemplates();
},
onError: function(event) {
console.error('ONLYOFFICE error:', event);
toast('Editor error: ' + (event.data?.errorDescription || 'unknown'), 'error');
},
},
@@ -386,21 +338,22 @@ async function openOnlyofficeEditor(templateId, templateName) {
toast('Failed to open editor: ' + e.message, 'error');
}
}
// Initialize when templates page is active
document.addEventListener('DOMContentLoaded', () => {
const templatesPage = document.getElementById('page-templates');
if (templatesPage) {
// Check on initial load (page may already be active from sessionStorage restore)
if (!templatesPage) return;
if (templatesPage.classList.contains('active')) {
checkOnlyofficeStatus();
loadOnlyofficeTemplates();
}
const observer = new MutationObserver(() => {
if (templatesPage.classList.contains('active')) {
checkOnlyofficeStatus();
loadOnlyofficeTemplates();
}
// Also watch for future tab switches
const observer = new MutationObserver(() => {
if (templatesPage.classList.contains('active')) {
checkOnlyofficeStatus();
loadOnlyofficeTemplates();
}
});
observer.observe(templatesPage, { attributes: true, attributeFilter: ['class'] });
}
});
observer.observe(templatesPage, { attributes: true, attributeFilter: ['class'] });
});

View File

@@ -139,7 +139,7 @@
<!-- Modals -->
<div class="modal-overlay" id="modal-overlay"></div>
<script src="/static/app.js?v=10"></script>
<script src="/static/carbone.js?v=10"></script>
<script src="/static/app.js?v=11"></script>
<script src="/static/carbone.js?v=11"></script>
</body>
</html>