Allow renaming and moving folders in web file manager (PR #1617 by alkk)
This commit is contained in:
@@ -46,13 +46,42 @@ unsigned long wsLastCompleteAt = 0;
|
||||
|
||||
// Helper function to clear epub cache after upload
|
||||
void clearEpubCacheIfNeeded(const String& filePath) {
|
||||
// Only clear cache for .epub files
|
||||
if (FsHelpers::hasEpubExtension(filePath)) {
|
||||
Epub(filePath.c_str(), "/.crosspoint").clearCache();
|
||||
LOG_DBG("WEB", "Cleared epub cache for: %s", filePath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively clear epub caches for all EPUBs inside a directory
|
||||
void clearEpubCachesInDirectory(const String& dirPath) {
|
||||
esp_task_wdt_reset();
|
||||
yield();
|
||||
FsFile dir = Storage.open(dirPath.c_str());
|
||||
if (!dir || !dir.isDirectory()) {
|
||||
if (dir) dir.close();
|
||||
return;
|
||||
}
|
||||
char name[500];
|
||||
FsFile entry = dir.openNextFile();
|
||||
while (entry) {
|
||||
esp_task_wdt_reset();
|
||||
yield();
|
||||
entry.getName(name, sizeof(name));
|
||||
String childPath = dirPath;
|
||||
if (!childPath.endsWith("/")) childPath += "/";
|
||||
childPath += name;
|
||||
if (entry.isDirectory()) {
|
||||
entry.close();
|
||||
clearEpubCachesInDirectory(childPath);
|
||||
} else {
|
||||
entry.close();
|
||||
clearEpubCacheIfNeeded(childPath);
|
||||
}
|
||||
entry = dir.openNextFile();
|
||||
}
|
||||
dir.close();
|
||||
}
|
||||
|
||||
String normalizeWebPath(const String& inputPath) {
|
||||
if (inputPath.isEmpty() || inputPath == "/") {
|
||||
return "/";
|
||||
@@ -849,11 +878,7 @@ void CrossPointWebServer::handleRename() const {
|
||||
server->send(500, "text/plain", "Failed to open file");
|
||||
return;
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
file.close();
|
||||
server->send(400, "text/plain", "Only files can be renamed");
|
||||
return;
|
||||
}
|
||||
const bool isDir = file.isDirectory();
|
||||
|
||||
String parentPath = itemPath.substring(0, itemPath.lastIndexOf('/'));
|
||||
if (parentPath.isEmpty()) {
|
||||
@@ -871,16 +896,20 @@ void CrossPointWebServer::handleRename() const {
|
||||
return;
|
||||
}
|
||||
|
||||
clearEpubCacheIfNeeded(itemPath);
|
||||
if (isDir) {
|
||||
clearEpubCachesInDirectory(itemPath);
|
||||
} else {
|
||||
clearEpubCacheIfNeeded(itemPath);
|
||||
}
|
||||
const bool success = file.rename(newPath.c_str());
|
||||
file.close();
|
||||
|
||||
if (success) {
|
||||
LOG_DBG("WEB", "Renamed file: %s -> %s", itemPath.c_str(), newPath.c_str());
|
||||
LOG_DBG("WEB", "Renamed: %s -> %s", itemPath.c_str(), newPath.c_str());
|
||||
server->send(200, "text/plain", "Renamed successfully");
|
||||
} else {
|
||||
LOG_ERR("WEB", "Failed to rename file: %s -> %s", itemPath.c_str(), newPath.c_str());
|
||||
server->send(500, "text/plain", "Failed to rename file");
|
||||
LOG_ERR("WEB", "Failed to rename: %s -> %s", itemPath.c_str(), newPath.c_str());
|
||||
server->send(500, "text/plain", "Failed to rename");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -925,10 +954,18 @@ void CrossPointWebServer::handleMove() const {
|
||||
server->send(500, "text/plain", "Failed to open file");
|
||||
return;
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
file.close();
|
||||
server->send(400, "text/plain", "Only files can be moved");
|
||||
return;
|
||||
const bool isDir = file.isDirectory();
|
||||
|
||||
if (isDir) {
|
||||
String destWithSlash = destPath;
|
||||
if (!destWithSlash.endsWith("/")) destWithSlash += "/";
|
||||
String itemWithSlash = itemPath;
|
||||
if (!itemWithSlash.endsWith("/")) itemWithSlash += "/";
|
||||
if (destPath == itemPath || destWithSlash.startsWith(itemWithSlash)) {
|
||||
file.close();
|
||||
server->send(400, "text/plain", "Cannot move folder into itself");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Storage.exists(destPath.c_str())) {
|
||||
@@ -964,16 +1001,20 @@ void CrossPointWebServer::handleMove() const {
|
||||
return;
|
||||
}
|
||||
|
||||
clearEpubCacheIfNeeded(itemPath);
|
||||
if (isDir) {
|
||||
clearEpubCachesInDirectory(itemPath);
|
||||
} else {
|
||||
clearEpubCacheIfNeeded(itemPath);
|
||||
}
|
||||
const bool success = file.rename(newPath.c_str());
|
||||
file.close();
|
||||
|
||||
if (success) {
|
||||
LOG_DBG("WEB", "Moved file: %s -> %s", itemPath.c_str(), newPath.c_str());
|
||||
LOG_DBG("WEB", "Moved: %s -> %s", itemPath.c_str(), newPath.c_str());
|
||||
server->send(200, "text/plain", "Moved successfully");
|
||||
} else {
|
||||
LOG_ERR("WEB", "Failed to move file: %s -> %s", itemPath.c_str(), newPath.c_str());
|
||||
server->send(500, "text/plain", "Failed to move file");
|
||||
LOG_ERR("WEB", "Failed to move: %s -> %s", itemPath.c_str(), newPath.c_str());
|
||||
server->send(500, "text/plain", "Failed to move");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2197,7 +2197,11 @@
|
||||
fileTableContent += `<td><span class="file-icon">📁</span><a href="/files?path=${encodeURIComponent(folderPath)}" class="folder-link">${escapeHtml(file.name)}</a><span class="folder-badge">FOLDER</span></td>`;
|
||||
fileTableContent += '<td>Folder</td>';
|
||||
fileTableContent += '<td>-</td>';
|
||||
fileTableContent += `<td class="actions-col"><div class="action-icon-group"><button class="delete-btn" onclick="openDeleteModal('${file.name.replaceAll("'", "\\'")}', '${folderPath.replaceAll("'", "\\'")}', true)" title="Delete folder">🗑️</button></div></td>`;
|
||||
fileTableContent += `<td class="actions-col"><div class="action-icon-group">`;
|
||||
fileTableContent += `<button class="move-btn" onclick="openMoveModal('${file.name.replaceAll("'", "\\'")}', '${folderPath.replaceAll("'", "\\'")}', true)" title="Move folder">📂</button>`;
|
||||
fileTableContent += `<button class="rename-btn" onclick="openRenameModal('${file.name.replaceAll("'", "\\'")}', '${folderPath.replaceAll("'", "\\'")}', true)" title="Rename folder">✏️</button>`;
|
||||
fileTableContent += `<button class="delete-btn" onclick="openDeleteModal('${file.name.replaceAll("'", "\\'")}', '${folderPath.replaceAll("'", "\\'")}', true)" title="Delete folder">🗑️</button>`;
|
||||
fileTableContent += `</div></td>`;
|
||||
fileTableContent += '</tr>';
|
||||
} else {
|
||||
let filePath = currentPath;
|
||||
@@ -5367,8 +5371,9 @@
|
||||
}
|
||||
|
||||
// Rename functions
|
||||
function openRenameModal(name, path) {
|
||||
document.getElementById('renameItemName').textContent = '📄 ' + name;
|
||||
function openRenameModal(name, path, isFolder = false) {
|
||||
const icon = isFolder ? '📁' : '📄';
|
||||
document.getElementById('renameItemName').textContent = icon + ' ' + name;
|
||||
document.getElementById('renameItemPath').value = path;
|
||||
document.getElementById('renameNewName').value = name;
|
||||
document.getElementById('renameModal').classList.add('open');
|
||||
@@ -5482,8 +5487,9 @@
|
||||
});
|
||||
}
|
||||
|
||||
function openMoveModal(name, path) {
|
||||
document.getElementById('moveItemName').textContent = '📄 ' + name;
|
||||
function openMoveModal(name, path, isFolder = false) {
|
||||
const icon = isFolder ? '📁' : '📄';
|
||||
document.getElementById('moveItemName').textContent = icon + ' ' + name;
|
||||
document.getElementById('moveItemPath').value = path;
|
||||
document.getElementById('moveDestPath').value = currentPath === '/' ? '/' : currentPath;
|
||||
document.getElementById('moveModal').classList.add('open');
|
||||
|
||||
Reference in New Issue
Block a user