feat(form): pre-flight Submit gate + cap-toast on 403
Two changes to the form tool's submit path:
- Submit button hides when /.profile/access?path=<submission dir>
reports no 'c' verb. The form-status line surfaces a short
explanation so the user knows why the button disappeared.
- 403 on POST routes through zddc.cap.handleForbidden, which
renders an error toast naming the missing verb and offers
Elevate when the path-scoped view reports an elevation grant
covering it. The existing "You are not allowed to submit here"
status line still appears as the in-form indicator.
Also guards shared/cap.js's fetchAccess against file:// URLs —
calling fetch() on a file:// page logs a browser-level error that
shows up as test-runner noise. Short-circuiting to null lets
offline tools (browse on a picked folder, form opened standalone
from a file URL) silently degrade to "no path-scoped info" and
fall back to whatever existing gate they had.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
34208a5bd7
commit
defed434cc
3 changed files with 38 additions and 0 deletions
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue