support custom expiration durations for file and folder shares (closes #26)

This commit is contained in:
Ryan
2025-04-28 20:02:11 -04:00
committed by GitHub
parent 0334e443eb
commit 2e4dbe7f7f
8 changed files with 577 additions and 373 deletions

View File

@@ -4,36 +4,68 @@ import { fileData } from './fileListView.js';
import { t } from './i18n.js';
export function openShareModal(file, folder) {
// Remove any existing modal
const existing = document.getElementById("shareModal");
if (existing) existing.remove();
// Build the modal
const modal = document.createElement("div");
modal.id = "shareModal";
modal.classList.add("modal");
modal.innerHTML = `
<div class="modal-content share-modal-content" style="width: 600px; max-width:90vw;">
<div class="modal-content share-modal-content" style="width:600px;max-width:90vw;">
<div class="modal-header">
<h3>${t("share_file")}: ${escapeHTML(file.name)}</h3>
<span class="close-image-modal" id="closeShareModal" title="Close">&times;</span>
<span id="closeShareModal" title="${t("close")}" class="close-image-modal">&times;</span>
</div>
<div class="modal-body">
<p>${t("set_expiration")}</p>
<select id="shareExpiration">
<option value="30">30 minutes</option>
<option value="60" selected>60 minutes</option>
<option value="120">120 minutes</option>
<option value="180">180 minutes</option>
<option value="240">240 minutes</option>
<option value="1440">1 Day</option>
<select id="shareExpiration" style="width:100%;padding:5px;">
<option value="30">30 ${t("minutes")}</option>
<option value="60" selected>60 ${t("minutes")}</option>
<option value="120">120 ${t("minutes")}</option>
<option value="180">180 ${t("minutes")}</option>
<option value="240">240 ${t("minutes")}</option>
<option value="1440">1 ${t("day")}</option>
<option value="custom">${t("custom")}&hellip;</option>
</select>
<p>${t("password_optional")}</p>
<input type="text" id="sharePassword" placeholder=${t("password_optional")} style="width: 100%;"/>
<br>
<button id="generateShareLinkBtn" class="btn btn-primary" style="margin-top:10px;">${t("generate_share_link")}</button>
<div id="shareLinkDisplay" style="margin-top: 10px; display:none;">
<div id="customExpirationContainer" style="display:none;margin-top:10px;">
<label for="customExpirationValue">${t("duration")}:</label>
<input type="number" id="customExpirationValue" min="1" value="1" style="width:60px;margin:0 8px;"/>
<select id="customExpirationUnit">
<option value="seconds">${t("seconds")}</option>
<option value="minutes" selected>${t("minutes")}</option>
<option value="hours">${t("hours")}</option>
<option value="days">${t("days")}</option>
</select>
<p class="share-warning" style="color:#a33;font-size:0.9em;margin-top:5px;">
${t("custom_duration_warning")}
</p>
</div>
<p style="margin-top:15px;">${t("password_optional")}</p>
<input
type="text"
id="sharePassword"
placeholder="${t("password_optional")}"
style="width:100%;padding:5px;"
/>
<button
id="generateShareLinkBtn"
class="btn btn-primary"
style="margin-top:15px;"
>
${t("generate_share_link")}
</button>
<div id="shareLinkDisplay" style="margin-top:15px;display:none;">
<p>${t("shareable_link")}</p>
<input type="text" id="shareLinkInput" readonly style="width:100%;"/>
<button id="copyShareLinkBtn" class="btn btn-primary" style="margin-top:5px;">${t("copy_link")}</button>
<input type="text" id="shareLinkInput" readonly style="width:100%;padding:5px;"/>
<button id="copyShareLinkBtn" class="btn btn-secondary" style="margin-top:5px;">
${t("copy_link")}
</button>
</div>
</div>
</div>
@@ -41,52 +73,72 @@ export function openShareModal(file, folder) {
document.body.appendChild(modal);
modal.style.display = "block";
document.getElementById("closeShareModal").addEventListener("click", () => {
modal.remove();
});
// Close handler
document.getElementById("closeShareModal")
.addEventListener("click", () => modal.remove());
document.getElementById("generateShareLinkBtn").addEventListener("click", () => {
const expiration = document.getElementById("shareExpiration").value;
const password = document.getElementById("sharePassword").value;
fetch("/api/file/createShareLink.php", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": window.csrfToken
},
body: JSON.stringify({
folder: folder,
file: file.name,
expirationMinutes: parseInt(expiration),
password: password
// Show/hide custom-duration inputs
document.getElementById("shareExpiration")
.addEventListener("change", e => {
const container = document.getElementById("customExpirationContainer");
container.style.display = e.target.value === "custom" ? "block" : "none";
});
// Generate share link
document.getElementById("generateShareLinkBtn")
.addEventListener("click", () => {
const sel = document.getElementById("shareExpiration");
let value, unit;
if (sel.value === "custom") {
value = parseInt(document.getElementById("customExpirationValue").value, 10);
unit = document.getElementById("customExpirationUnit").value;
} else {
value = parseInt(sel.value, 10);
unit = "minutes";
}
const password = document.getElementById("sharePassword").value;
fetch("/api/file/createShareLink.php", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": window.csrfToken
},
body: JSON.stringify({
folder,
file: file.name,
expirationValue: value,
expirationUnit: unit,
password
})
})
})
.then(response => response.json())
.then(res => res.json())
.then(data => {
if (data.token) {
const shareEndpoint = `${window.location.origin}/api/file/share.php`;
const shareUrl = `${shareEndpoint}?token=${encodeURIComponent(data.token)}`;
const displayDiv = document.getElementById("shareLinkDisplay");
const inputField = document.getElementById("shareLinkInput");
inputField.value = shareUrl;
displayDiv.style.display = "block";
const url = `${window.location.origin}/api/file/share.php?token=${encodeURIComponent(data.token)}`;
document.getElementById("shareLinkInput").value = url;
document.getElementById("shareLinkDisplay").style.display = "block";
} else {
showToast("Error generating share link: " + (data.error || "Unknown error"));
showToast(t("error_generating_share") + ": " + (data.error||"Unknown"));
}
})
.catch(err => {
console.error("Error generating share link:", err);
showToast("Error generating share link.");
console.error(err);
showToast(t("error_generating_share"));
});
});
});
document.getElementById("copyShareLinkBtn").addEventListener("click", () => {
const input = document.getElementById("shareLinkInput");
input.select();
document.execCommand("copy");
showToast("Link copied to clipboard!");
});
// Copy to clipboard
document.getElementById("copyShareLinkBtn")
.addEventListener("click", () => {
const input = document.getElementById("shareLinkInput");
input.select();
document.execCommand("copy");
showToast(t("link_copied"));
});
}
export function previewFile(fileUrl, fileName) {