diff --git a/form/js/main.js b/form/js/main.js index ca1fbaf..0923197 100644 --- a/form/js/main.js +++ b/form/js/main.js @@ -79,6 +79,29 @@ const submitBtn = document.getElementById('submit-btn'); if (submitBtn) { submitBtn.addEventListener('click', app.modules.post.submit); + // Pre-flight gate: hide Submit when the cascade denies + // create at the submission directory. Server still + // enforces on POST — this just avoids dangling an + // affordance that would 403. Submission directory is the + // parent of submitUrl; fall back to the page URL when + // submitUrl is absent (file:// / no-context mode). + if (window.zddc && window.zddc.cap && app.context && app.context.submitUrl) { + const subUrl = app.context.submitUrl; + const dir = subUrl.replace(/\/[^\/]*$/, '/') || subUrl; + window.zddc.cap.at(dir).then(function (view) { + if (!view) return; + const verbs = view.path_verbs || ''; + if (verbs.indexOf('c') === -1) { + submitBtn.hidden = true; + const status = document.getElementById('form-status'); + if (status) { + status.textContent = "You don't have permission to submit here."; + status.hidden = false; + status.classList.add('is-error'); + } + } + }); + } } } diff --git a/form/js/post.js b/form/js/post.js index c061089..5a39360 100644 --- a/form/js/post.js +++ b/form/js/post.js @@ -56,6 +56,12 @@ showStatus('Please correct the errors below.', 'error'); } else if (res.status === 403) { showStatus('You are not allowed to submit here.', 'error'); + if (window.zddc && window.zddc.cap) { + window.zddc.cap.handleForbidden(res, { + context: 'Submit', + path: app.context.submitUrl + }); + } } else if (res.status === 409) { showStatus('A submission with this filename already exists.', 'error'); } else { diff --git a/shared/cap.js b/shared/cap.js index 369b960..0d53d2f 100644 --- a/shared/cap.js +++ b/shared/cap.js @@ -34,6 +34,15 @@ var pathCache = new Map(); // path → AccessView (or null sentinel) async function fetchAccess(path) { + // file:// pages have no server to fetch /.profile/access from; + // calling fetch() there logs a browser-level error before our + // catch even runs. Short-circuit so offline tools (browse on + // a picked folder, form opened from a file URL) silently + // degrade to "no path-scoped info, fall back to existing + // gating signals". + if (location.protocol !== 'http:' && location.protocol !== 'https:') { + return null; + } try { var url = '/.profile/access'; if (path) url += '?path=' + encodeURIComponent(path);