Files
Oxicloud/static/js/fileOperations.js
T

868 lines
34 KiB
JavaScript
Raw Normal View History

2025-03-19 23:28:29 +01:00
/**
* OxiCloud - File Operations Module
* This file handles file and folder operations (create, move, delete, rename, upload)
*/
2026-02-08 22:44:42 +01:00
/**
* Get authorization headers for API requests
* @returns {Object} Headers object with Authorization bearer token
*/
function getAuthHeaders() {
const token = localStorage.getItem('oxicloud_token');
const headers = {};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
return headers;
}
2025-03-19 23:28:29 +01:00
// File Operations Module
const fileOps = {
// ========================================================================
// Upload progress toast helpers
// ========================================================================
/** Show the upload progress toast and reset its contents */
_initUploadToast(totalFiles) {
const toast = document.getElementById('upload-toast');
const body = document.getElementById('upload-toast-body');
const title = document.getElementById('upload-toast-title');
const stats = document.getElementById('upload-toast-stats');
const fill = document.getElementById('upload-toast-overall-fill');
const closeBtn = document.getElementById('upload-toast-close');
body.innerHTML = '';
fill.style.width = '0%';
const uploadingText = (window.i18n && window.i18n.t) ? window.i18n.t('upload.uploading') : 'Uploading...';
title.textContent = uploadingText;
stats.textContent = `0 / ${totalFiles}`;
toast.classList.add('visible');
// Allow user to minimise (hide) the toast; it will re-appear on next upload
closeBtn.onclick = () => toast.classList.remove('visible');
},
/** Add a file row to the toast and return its element references */
_addToastFileRow(fileName) {
const body = document.getElementById('upload-toast-body');
const row = document.createElement('div');
row.className = 'upload-toast-file';
row.innerHTML = `
<span class="upload-toast-file-icon"><i class="fas fa-spinner fa-spin"></i></span>
<div class="upload-toast-file-info">
<div class="upload-toast-file-name" title="${fileName}">${fileName}</div>
<div class="upload-toast-file-bar"><div class="upload-toast-file-fill"></div></div>
</div>
<span class="upload-toast-file-pct">0%</span>
`;
body.appendChild(row);
// Auto-scroll to bottom
body.scrollTop = body.scrollHeight;
return {
row,
icon: row.querySelector('.upload-toast-file-icon'),
fill: row.querySelector('.upload-toast-file-fill'),
pct: row.querySelector('.upload-toast-file-pct'),
};
},
/** Update the overall progress in the toast footer */
_updateOverallProgress(completedCount, totalFiles) {
const fill = document.getElementById('upload-toast-overall-fill');
const stats = document.getElementById('upload-toast-stats');
const pct = totalFiles > 0 ? Math.round((completedCount / totalFiles) * 100) : 0;
fill.style.width = pct + '%';
stats.textContent = `${completedCount} / ${totalFiles}`;
},
/** Mark upload toast as fully complete and auto-hide after a delay */
_finishUploadToast(successCount, totalFiles) {
const title = document.getElementById('upload-toast-title');
const fill = document.getElementById('upload-toast-overall-fill');
fill.style.width = '100%';
const completeText = (window.i18n && window.i18n.t)
? window.i18n.t('upload.complete', { count: successCount, total: totalFiles })
: `${successCount} / ${totalFiles} uploaded`;
title.textContent = completeText;
setTimeout(() => {
const toast = document.getElementById('upload-toast');
toast.classList.remove('visible');
}, 4000);
},
2025-03-19 23:28:29 +01:00
/**
* Upload a single file via XMLHttpRequest with progress events.
* Returns a promise that resolves with { ok, data? }.
*/
_uploadFileXHR(formData, fileRowElements) {
return new Promise((resolve) => {
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable && fileRowElements) {
const pct = Math.round((e.loaded / e.total) * 100);
fileRowElements.fill.style.width = pct + '%';
fileRowElements.pct.textContent = pct + '%';
}
});
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) {
if (fileRowElements) {
fileRowElements.fill.style.width = '100%';
fileRowElements.fill.classList.add('done');
fileRowElements.pct.textContent = '100%';
fileRowElements.icon.innerHTML = '<i class="fas fa-check-circle"></i>';
fileRowElements.icon.classList.add('done');
}
let data = null;
try { data = JSON.parse(xhr.responseText); } catch (_) {}
resolve({ ok: true, data });
} else {
if (fileRowElements) {
fileRowElements.fill.classList.add('error');
fileRowElements.pct.textContent = 'ERR';
fileRowElements.icon.innerHTML = '<i class="fas fa-exclamation-circle"></i>';
fileRowElements.icon.classList.add('error');
}
resolve({ ok: false });
}
});
xhr.addEventListener('error', () => {
if (fileRowElements) {
fileRowElements.fill.classList.add('error');
fileRowElements.pct.textContent = 'ERR';
fileRowElements.icon.innerHTML = '<i class="fas fa-exclamation-circle"></i>';
fileRowElements.icon.classList.add('error');
}
resolve({ ok: false });
});
xhr.open('POST', '/api/files/upload');
// Set auth header
const token = localStorage.getItem('oxicloud_token');
if (token) xhr.setRequestHeader('Authorization', `Bearer ${token}`);
xhr.setRequestHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
xhr.send(formData);
});
},
// ========================================================================
// Upload files (via button or drag-and-drop)
// ========================================================================
/**
* Upload files to the server with real-time progress indication
2025-03-19 23:28:29 +01:00
* @param {FileList} files - Files to upload
*/
async uploadFiles(files) {
const totalFiles = files.length;
if (totalFiles === 0) return;
// Legacy progress bar (inside dropzone) — keep working for drag-drop
2025-03-19 23:28:29 +01:00
const progressBar = document.querySelector('.progress-fill');
const uploadProgressDiv = document.querySelector('.upload-progress');
if (uploadProgressDiv) { uploadProgressDiv.style.display = 'block'; }
if (progressBar) { progressBar.style.width = '0%'; }
// Show upload toast
this._initUploadToast(totalFiles);
2025-03-19 23:28:29 +01:00
let uploadedCount = 0;
let successCount = 0;
2025-03-19 23:28:29 +01:00
for (let i = 0; i < totalFiles; i++) {
const file = files[i];
const formData = new FormData();
2026-02-03 17:59:04 +01:00
const targetFolderId = window.app.currentPath || window.app.userHomeFolderId;
if (targetFolderId) formData.append('folder_id', targetFolderId);
2026-02-03 17:59:04 +01:00
formData.append('file', file);
2025-03-19 23:28:29 +01:00
console.log(`Uploading file to folder: ${targetFolderId || 'root'}`, {
file: file.name, size: file.size
});
2025-03-19 23:28:29 +01:00
// Add row to toast
const rowEls = this._addToastFileRow(file.name);
2025-03-19 23:28:29 +01:00
const result = await this._uploadFileXHR(formData, rowEls);
uploadedCount++;
// Legacy dropzone bar
if (progressBar) {
progressBar.style.width = ((uploadedCount / totalFiles) * 100) + '%';
}
// Toast overall bar
this._updateOverallProgress(uploadedCount, totalFiles);
if (result.ok) {
successCount++;
console.log(`Successfully uploaded ${file.name}`, result.data);
} else {
console.error(`Upload error for ${file.name}`);
window.ui.showNotification('Error', `Error uploading file: ${file.name}`);
2025-03-19 23:28:29 +01:00
}
}
// All done
this._finishUploadToast(successCount, totalFiles);
// Wait for backend to persist, then reload
await new Promise(resolve => setTimeout(resolve, 800));
try {
await window.loadFiles({ forceRefresh: true });
} catch (reloadError) {
console.error('Error reloading files:', reloadError);
}
setTimeout(() => {
const dropzone = document.getElementById('dropzone');
if (dropzone) dropzone.style.display = 'none';
if (uploadProgressDiv) uploadProgressDiv.style.display = 'none';
}, 500);
2025-03-19 23:28:29 +01:00
},
2026-02-08 22:44:42 +01:00
/**
* Upload folder files maintaining directory structure
* Creates subfolders as needed, then uploads files into them
* @param {FileList} files - Files from folder input (with webkitRelativePath)
*/
async uploadFolderFiles(files) {
if (!files || files.length === 0) return;
const progressBar = document.querySelector('.progress-fill');
const uploadProgressDiv = document.querySelector('.upload-progress');
if (uploadProgressDiv) { uploadProgressDiv.style.display = 'block'; }
if (progressBar) { progressBar.style.width = '0%'; }
2026-02-08 22:44:42 +01:00
const currentFolderId = window.app.currentPath || window.app.userHomeFolderId;
// Build folder structure from relative paths
const folderMap = new Map();
folderMap.set('', currentFolderId);
2026-02-08 22:44:42 +01:00
const folderPaths = new Set();
for (const file of files) {
const parts = file.webkitRelativePath.split('/');
for (let i = 1; i < parts.length; i++) {
const path = parts.slice(0, i).join('/');
folderPaths.add(path);
}
}
const sortedPaths = [...folderPaths].sort((a, b) =>
a.split('/').length - b.split('/').length
);
// Create folders first (no progress toast for folder creation)
2026-02-08 22:44:42 +01:00
for (const folderPath of sortedPaths) {
const parts = folderPath.split('/');
const folderName = parts[parts.length - 1];
const parentPath = parts.slice(0, -1).join('/');
const parentId = folderMap.get(parentPath) || currentFolderId;
try {
const response = await fetch('/api/folders', {
method: 'POST',
headers: {
...getAuthHeaders(),
'Content-Type': 'application/json',
'Cache-Control': 'no-cache, no-store, must-revalidate'
},
body: JSON.stringify({
name: folderName,
parent_id: parentId
})
});
if (response.ok) {
const folder = await response.json();
folderMap.set(folderPath, folder.id);
console.log(`Created folder: ${folderPath} -> ${folder.id}`);
} else {
console.error(`Error creating folder ${folderPath}:`, await response.text());
window.ui.showNotification('Error', `Error creating folder: ${folderName}`);
2026-02-08 22:44:42 +01:00
}
} catch (error) {
console.error(`Network error creating folder ${folderPath}:`, error);
}
}
// Upload files with progress toast
2026-02-08 22:44:42 +01:00
const totalFiles = files.length;
this._initUploadToast(totalFiles);
let uploadedCount = 0;
let successCount = 0;
2026-02-08 22:44:42 +01:00
for (let i = 0; i < totalFiles; i++) {
const file = files[i];
const parts = file.webkitRelativePath.split('/');
const parentPath = parts.slice(0, -1).join('/');
const targetFolderId = folderMap.get(parentPath) || currentFolderId;
const formData = new FormData();
formData.append('folder_id', targetFolderId);
formData.append('file', file);
const displayName = file.webkitRelativePath || file.name;
const rowEls = this._addToastFileRow(displayName);
const result = await this._uploadFileXHR(formData, rowEls);
2026-02-08 22:44:42 +01:00
uploadedCount++;
if (progressBar) {
progressBar.style.width = ((uploadedCount / totalFiles) * 100) + '%';
}
this._updateOverallProgress(uploadedCount, totalFiles);
2026-02-08 22:44:42 +01:00
if (result.ok) {
successCount++;
console.log(`Uploaded: ${file.webkitRelativePath}`);
} else {
console.error(`Error uploading ${file.webkitRelativePath}`);
2026-02-08 22:44:42 +01:00
}
}
// Finish
this._finishUploadToast(successCount, totalFiles);
2026-02-08 22:44:42 +01:00
await new Promise(resolve => setTimeout(resolve, 800));
try {
await window.loadFiles({ forceRefresh: true });
} catch (reloadError) {
console.error('Error reloading files:', reloadError);
}
setTimeout(() => {
const dropzone = document.getElementById('dropzone');
if (dropzone) dropzone.style.display = 'none';
if (uploadProgressDiv) uploadProgressDiv.style.display = 'none';
2026-02-08 22:44:42 +01:00
}, 500);
},
2025-03-19 23:28:29 +01:00
/**
* Create a new folder
* @param {string} name - Folder name
*/
async createFolder(name) {
try {
2025-04-01 21:14:09 +02:00
console.log('Creating folder with name:', name);
// Send the actual request to the backend to create the folder
2025-03-19 23:28:29 +01:00
const response = await fetch('/api/folders', {
method: 'POST',
headers: {
2026-02-08 22:44:42 +01:00
...getAuthHeaders(),
2025-04-12 12:37:12 +02:00
'Content-Type': 'application/json',
'Cache-Control': 'no-cache, no-store, must-revalidate'
2025-03-19 23:28:29 +01:00
},
body: JSON.stringify({
name: name,
parent_id: window.app.currentPath || null
})
});
if (response.ok) {
// Get the created folder from the backend
2025-04-12 12:37:12 +02:00
const folder = await response.json();
console.log('Folder created successfully:', folder);
// Add the folder to the view immediately for instant feedback
2025-04-12 12:37:12 +02:00
window.ui.addFolderToView(folder);
// Wait to allow the backend to save the changes
2025-04-12 12:37:12 +02:00
await new Promise(resolve => setTimeout(resolve, 1000));
// Reload files to refresh the view
2025-04-12 12:37:12 +02:00
await window.loadFiles({forceRefresh: true});
window.ui.showNotification('Folder created', `"${name}" created successfully`);
2025-03-19 23:28:29 +01:00
} else {
const errorData = await response.text();
console.error('Create folder error:', errorData);
window.ui.showNotification('Error', 'Error creating the folder');
2025-03-19 23:28:29 +01:00
}
} catch (error) {
console.error('Error creating folder:', error);
window.ui.showNotification('Error', 'Error creating the folder');
2025-03-19 23:28:29 +01:00
}
},
/**
* Move a file to another folder
* @param {string} fileId - File ID
* @param {string} targetFolderId - Target folder ID
* @returns {Promise<boolean>} - Success status
*/
async moveFile(fileId, targetFolderId) {
try {
const response = await fetch(`/api/files/${fileId}/move`, {
method: 'PUT',
headers: {
2026-02-08 22:44:42 +01:00
...getAuthHeaders(),
2025-03-19 23:28:29 +01:00
'Content-Type': 'application/json'
},
body: JSON.stringify({
folder_id: targetFolderId === "" ? null : targetFolderId
})
});
if (response.ok) {
// Reload files after moving
await window.loadFiles();
window.ui.showNotification('File moved', 'File moved successfully');
2025-03-19 23:28:29 +01:00
return true;
} else {
let errorMessage = 'Unknown error';
2025-03-19 23:28:29 +01:00
try {
const errorData = await response.json();
errorMessage = errorData.error || 'Unknown error';
2025-03-19 23:28:29 +01:00
} catch (e) {
errorMessage = 'Error processing server response';
2025-03-19 23:28:29 +01:00
}
window.ui.showNotification('Error', `Error moving the file: ${errorMessage}`);
2025-03-19 23:28:29 +01:00
return false;
}
} catch (error) {
console.error('Error moving file:', error);
window.ui.showNotification('Error', 'Error moving the file');
2025-03-19 23:28:29 +01:00
return false;
}
},
/**
* Move a folder to another folder
* @param {string} folderId - Folder ID
* @param {string} targetFolderId - Target folder ID
* @returns {Promise<boolean>} - Success status
*/
async moveFolder(folderId, targetFolderId) {
try {
const response = await fetch(`/api/folders/${folderId}/move`, {
method: 'PUT',
headers: {
2026-02-08 22:44:42 +01:00
...getAuthHeaders(),
2025-03-19 23:28:29 +01:00
'Content-Type': 'application/json'
},
body: JSON.stringify({
parent_id: targetFolderId === "" ? null : targetFolderId
})
});
if (response.ok) {
// Reload files after moving
await window.loadFiles();
window.ui.showNotification('Folder moved', 'Folder moved successfully');
2025-03-19 23:28:29 +01:00
return true;
} else {
let errorMessage = 'Unknown error';
2025-03-19 23:28:29 +01:00
try {
const errorData = await response.json();
errorMessage = errorData.error || 'Unknown error';
2025-03-19 23:28:29 +01:00
} catch (e) {
errorMessage = 'Error processing server response';
2025-03-19 23:28:29 +01:00
}
window.ui.showNotification('Error', `Error moving the folder: ${errorMessage}`);
2025-03-19 23:28:29 +01:00
return false;
}
} catch (error) {
console.error('Error moving folder:', error);
window.ui.showNotification('Error', 'Error moving the folder');
2025-03-19 23:28:29 +01:00
return false;
}
},
2026-02-08 22:44:42 +01:00
/**
* Rename a file
* @param {string} fileId - File ID
* @param {string} newName - New file name
* @returns {Promise<boolean>} - Success status
*/
async renameFile(fileId, newName) {
try {
console.log(`Renaming file ${fileId} to "${newName}"`);
const response = await fetch(`/api/files/${fileId}/rename`, {
method: 'PUT',
headers: {
...getAuthHeaders(),
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: newName })
});
console.log('Response status:', response.status);
if (response.ok) {
window.ui.showNotification(
window.i18n ? window.i18n.t('notifications.file_renamed') : 'File renamed',
window.i18n ? window.i18n.t('notifications.file_renamed_to', { name: newName }) : `File renamed to "${newName}"`
2026-02-08 22:44:42 +01:00
);
return true;
} else {
const errorText = await response.text();
console.error('Error response:', errorText);
let errorMessage = 'Unknown error';
2026-02-08 22:44:42 +01:00
try {
const errorData = JSON.parse(errorText);
errorMessage = errorData.error || response.statusText;
} catch (e) {
errorMessage = errorText || response.statusText;
}
window.ui.showNotification('Error', `Error renaming the file: ${errorMessage}`);
2026-02-08 22:44:42 +01:00
return false;
}
} catch (error) {
console.error('Error renaming file:', error);
window.ui.showNotification('Error', 'Error renaming the file');
2026-02-08 22:44:42 +01:00
return false;
}
},
2025-03-19 23:28:29 +01:00
/**
* Rename a folder
* @param {string} folderId - Folder ID
* @param {string} newName - New folder name
* @returns {Promise<boolean>} - Success status
*/
async renameFolder(folderId, newName) {
try {
console.log(`Renaming folder ${folderId} to "${newName}"`);
const response = await fetch(`/api/folders/${folderId}/rename`, {
method: 'PUT',
headers: {
2026-02-08 22:44:42 +01:00
...getAuthHeaders(),
2025-03-19 23:28:29 +01:00
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: newName })
});
console.log('Response status:', response.status);
if (response.ok) {
window.ui.showNotification('Folder renamed', `Folder renamed to "${newName}"`);
2025-03-19 23:28:29 +01:00
return true;
} else {
const errorText = await response.text();
console.error('Error response:', errorText);
let errorMessage = 'Unknown error';
2025-03-19 23:28:29 +01:00
try {
// Try to parse as JSON
const errorData = JSON.parse(errorText);
errorMessage = errorData.error || response.statusText;
} catch (e) {
// If not JSON, use text as is
errorMessage = errorText || response.statusText;
}
window.ui.showNotification('Error', `Error renaming the folder: ${errorMessage}`);
2025-03-19 23:28:29 +01:00
return false;
}
} catch (error) {
console.error('Error renaming folder:', error);
window.ui.showNotification('Error', 'Error renaming the folder');
2025-03-19 23:28:29 +01:00
return false;
}
},
/**
2025-03-24 17:49:53 +01:00
* Move a file to trash
2025-03-19 23:28:29 +01:00
* @param {string} fileId - File ID
2025-03-24 17:49:53 +01:00
* @param {string} fileName - File name
2025-03-19 23:28:29 +01:00
* @returns {Promise<boolean>} - Success status
*/
async deleteFile(fileId, fileName) {
2026-02-08 22:44:42 +01:00
const confirmed = await showConfirmDialog({
title: window.i18n ? window.i18n.t('dialogs.confirm_delete') : 'Move to trash',
message: window.i18n ? window.i18n.t('dialogs.confirm_delete_file', { name: fileName }) : `Are you sure you want to move the file "${fileName}" to trash?`,
confirmText: window.i18n ? window.i18n.t('actions.delete') : 'Delete',
2026-02-08 22:44:42 +01:00
});
if (!confirmed) return false;
2025-03-19 23:28:29 +01:00
try {
2025-03-24 17:49:53 +01:00
// Use the trash API endpoint
const response = await fetch(`/api/trash/files/${fileId}`, {
2026-02-08 22:44:42 +01:00
method: 'DELETE',
headers: getAuthHeaders()
2025-03-19 23:28:29 +01:00
});
if (response.ok) {
window.loadFiles();
window.ui.showNotification('File moved to trash', `"${fileName}" moved to trash`);
2025-03-19 23:28:29 +01:00
return true;
} else {
2025-03-24 17:49:53 +01:00
// Fallback to direct deletion if trash fails
const fallbackResponse = await fetch(`/api/files/${fileId}`, {
2026-02-08 22:44:42 +01:00
method: 'DELETE',
headers: getAuthHeaders()
2025-03-24 17:49:53 +01:00
});
if (fallbackResponse.ok) {
window.loadFiles();
window.ui.showNotification('File deleted', `"${fileName}" deleted successfully`);
2025-03-24 17:49:53 +01:00
return true;
} else {
window.ui.showNotification('Error', 'Error deleting the file');
2025-03-24 17:49:53 +01:00
return false;
}
2025-03-19 23:28:29 +01:00
}
} catch (error) {
console.error('Error deleting file:', error);
window.ui.showNotification('Error', 'Error deleting the file');
2025-03-19 23:28:29 +01:00
return false;
}
},
/**
2025-03-24 17:49:53 +01:00
* Move a folder to trash
2025-03-19 23:28:29 +01:00
* @param {string} folderId - Folder ID
* @param {string} folderName - Folder name
* @returns {Promise<boolean>} - Success status
*/
async deleteFolder(folderId, folderName) {
2026-02-08 22:44:42 +01:00
const confirmed = await showConfirmDialog({
title: window.i18n ? window.i18n.t('dialogs.confirm_delete') : 'Move to trash',
message: window.i18n ? window.i18n.t('dialogs.confirm_delete_folder', { name: folderName }) : `Are you sure you want to move the folder "${folderName}" and all its contents to trash?`,
confirmText: window.i18n ? window.i18n.t('actions.delete') : 'Delete',
2026-02-08 22:44:42 +01:00
});
if (!confirmed) return false;
2025-03-19 23:28:29 +01:00
try {
2025-03-24 17:49:53 +01:00
// Use the trash API endpoint
const response = await fetch(`/api/trash/folders/${folderId}`, {
2026-02-08 22:44:42 +01:00
method: 'DELETE',
headers: getAuthHeaders()
2025-03-19 23:28:29 +01:00
});
if (response.ok) {
// If we're inside the folder we just deleted, go back up
if (window.app.currentPath === folderId) {
window.app.currentPath = '';
window.ui.updateBreadcrumb('');
}
window.loadFiles();
window.ui.showNotification('Folder moved to trash', `"${folderName}" moved to trash`);
2025-03-19 23:28:29 +01:00
return true;
} else {
2025-03-24 17:49:53 +01:00
// Fallback to direct deletion if trash fails
const fallbackResponse = await fetch(`/api/folders/${folderId}`, {
2026-02-08 22:44:42 +01:00
method: 'DELETE',
headers: getAuthHeaders()
2025-03-24 17:49:53 +01:00
});
if (fallbackResponse.ok) {
// If we're inside the folder we just deleted, go back up
if (window.app.currentPath === folderId) {
window.app.currentPath = '';
window.ui.updateBreadcrumb('');
}
window.loadFiles();
window.ui.showNotification('Folder deleted', `"${folderName}" deleted successfully`);
2025-03-24 17:49:53 +01:00
return true;
} else {
window.ui.showNotification('Error', 'Error deleting the folder');
2025-03-24 17:49:53 +01:00
return false;
}
2025-03-19 23:28:29 +01:00
}
} catch (error) {
console.error('Error deleting folder:', error);
window.ui.showNotification('Error', 'Error deleting the folder');
2025-03-19 23:28:29 +01:00
return false;
}
2025-03-24 17:49:53 +01:00
},
/**
* Get trash items
* @returns {Promise<Array>} - List of trash items
2025-03-24 17:49:53 +01:00
*/
async getTrashItems() {
try {
2026-02-08 22:44:42 +01:00
const response = await fetch('/api/trash', {
headers: getAuthHeaders()
});
2025-03-24 17:49:53 +01:00
if (response.ok) {
return await response.json();
} else {
console.error('Error fetching trash items:', response.statusText);
return [];
}
} catch (error) {
console.error('Error fetching trash items:', error);
return [];
}
},
/**
* Restore an item from trash
* @param {string} trashId - Trash item ID
* @returns {Promise<boolean>} - Operation success
2025-03-24 17:49:53 +01:00
*/
async restoreFromTrash(trashId) {
try {
const response = await fetch(`/api/trash/${trashId}/restore`, {
method: 'POST',
headers: {
2026-02-08 22:44:42 +01:00
...getAuthHeaders(),
2025-03-24 17:49:53 +01:00
'Content-Type': 'application/json'
},
body: JSON.stringify({})
});
if (response.ok) {
window.ui.showNotification('Item restored', 'Item restored successfully');
2025-03-24 17:49:53 +01:00
return true;
} else {
window.ui.showNotification('Error', 'Error restoring the item');
2025-03-24 17:49:53 +01:00
return false;
}
} catch (error) {
console.error('Error restoring item from trash:', error);
window.ui.showNotification('Error', 'Error restoring the item');
2025-03-24 17:49:53 +01:00
return false;
}
},
/**
* Permanently delete a trash item
* @param {string} trashId - Trash item ID
* @returns {Promise<boolean>} - Operation success
2025-03-24 17:49:53 +01:00
*/
async deletePermanently(trashId) {
2026-02-08 22:44:42 +01:00
const confirmed = await showConfirmDialog({
title: window.i18n ? window.i18n.t('dialogs.confirm_permanent_delete') : 'Delete permanently',
message: window.i18n ? window.i18n.t('dialogs.confirm_permanent_delete_msg') : 'Are you sure you want to permanently delete this item? This action cannot be undone.',
confirmText: window.i18n ? window.i18n.t('actions.delete_permanently') : 'Delete permanently',
2026-02-08 22:44:42 +01:00
});
if (!confirmed) return false;
2025-03-24 17:49:53 +01:00
try {
const response = await fetch(`/api/trash/${trashId}`, {
2026-02-08 22:44:42 +01:00
method: 'DELETE',
headers: getAuthHeaders()
2025-03-24 17:49:53 +01:00
});
if (response.ok) {
window.ui.showNotification('Item deleted', 'Item permanently deleted');
2025-03-24 17:49:53 +01:00
return true;
} else {
window.ui.showNotification('Error', 'Error deleting the item');
2025-03-24 17:49:53 +01:00
return false;
}
} catch (error) {
console.error('Error deleting item permanently:', error);
window.ui.showNotification('Error', 'Error deleting the item');
2025-03-24 17:49:53 +01:00
return false;
}
},
/**
* Empty the trash
* @returns {Promise<boolean>} - Operation success
2025-03-24 17:49:53 +01:00
*/
async emptyTrash() {
2026-02-08 22:44:42 +01:00
const confirmed = await showConfirmDialog({
title: window.i18n ? window.i18n.t('dialogs.confirm_empty_trash') : 'Empty trash',
message: window.i18n ? window.i18n.t('trash.empty_confirm') : 'Are you sure you want to empty the trash? This action will permanently delete all items.',
confirmText: window.i18n ? window.i18n.t('actions.empty_trash') : 'Empty trash',
2026-02-08 22:44:42 +01:00
});
if (!confirmed) return false;
2025-03-24 17:49:53 +01:00
try {
const response = await fetch('/api/trash/empty', {
2026-02-08 22:44:42 +01:00
method: 'DELETE',
headers: getAuthHeaders()
2025-03-24 17:49:53 +01:00
});
if (response.ok) {
window.ui.showNotification('Trash emptied', 'The trash has been emptied successfully');
2025-03-24 17:49:53 +01:00
return true;
} else {
window.ui.showNotification('Error', 'Error emptying the trash');
2025-03-24 17:49:53 +01:00
return false;
}
} catch (error) {
console.error('Error emptying trash:', error);
window.ui.showNotification('Error', 'Error emptying the trash');
2025-03-24 17:49:53 +01:00
return false;
}
2025-04-02 01:22:05 +02:00
},
/**
* Download a file
* @param {string} fileId - File ID
* @param {string} fileName - File name
2025-04-02 01:22:05 +02:00
*/
2026-02-08 22:44:42 +01:00
async downloadFile(fileId, fileName) {
try {
const response = await fetch(`/api/files/${fileId}`, {
headers: getAuthHeaders()
});
if (response.ok) {
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
} else {
window.ui.showNotification('Error', 'Error downloading the file');
2026-02-08 22:44:42 +01:00
}
} catch (error) {
console.error('Error downloading file:', error);
window.ui.showNotification('Error', 'Error downloading the file');
2026-02-08 22:44:42 +01:00
}
2025-04-02 01:22:05 +02:00
},
/**
* Download a folder as ZIP
* @param {string} folderId - Folder ID
* @param {string} folderName - Folder name
2025-04-02 01:22:05 +02:00
*/
async downloadFolder(folderId, folderName) {
try {
// Show notification to user
window.ui.showNotification('Preparing download', 'Preparing the folder for download...');
2025-04-02 01:22:05 +02:00
2026-02-08 22:44:42 +01:00
const response = await fetch(`/api/folders/${folderId}/download?format=zip`, {
headers: getAuthHeaders()
});
if (response.ok) {
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${folderName}.zip`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
} else {
window.ui.showNotification('Error', 'Error downloading the folder');
2026-02-08 22:44:42 +01:00
}
2025-04-02 01:22:05 +02:00
} catch (error) {
console.error('Error downloading folder:', error);
window.ui.showNotification('Error', 'Error downloading the folder');
2025-04-02 01:22:05 +02:00
}
2025-03-19 23:28:29 +01:00
}
};
// Expose file operations module globally
window.fileOps = fileOps;