Roadmap / feature ideas¶
A non-exhaustive list of features the module could grow into. These are ideas, not commitments — the order roughly reflects how transformative each one would be relative to what the module does today.
Items 1–3 would change what the module is. Items 4–7 would round out the "document collaboration platform" claim. The rest are polish and integration points.
1. Per-user revocable WebDAV sessions¶
Current state. The lock list shows "who has file X open in
LibreOffice". The per-file revocation lever (drush
webdav:revoke-file) bumps a counter that invalidates every
outstanding URL for that file, across every user. There's no
"log this one user out of this one file" lever for the
URL-token path. The Basic-auth path has no per-session
revocation lever at all today — credentials are the user's live
Drupal password and stay valid until the user changes it (or is
blocked).
What's missing. An admin UI that, for a given lock or user, both releases the open Sabre lock and invalidates outstanding WebDAV URLs for that user only. Today's revocation model is per-file; this would add a per-(user, file) dimension. Pairs naturally with per-user app passwords for the Basic-auth path — instead of "log this user out of WebDAV entirely by making them change their Drupal password", an admin would revoke one named app password (a Cyberduck mount, an automation script, …) without affecting the user's other WebDAV clients or their Drupal login.
Sketch. Extend RevocationStore so the keyed state is
(uid, fid) → counter instead of fid → counter. The HMAC over
the URL incorporates the per-(uid, fid) counter alongside the
master secret. The admin UI grows a "force end session" button
on each lock row. Cost: bigger keyvalue footprint
(O(users × files)), but small in practice — only "touched" pairs
get rows. For Basic auth: a webdav_app_passwords table keyed by
user + named credential, bcrypt-verified. Replace the live
Drupal-password check in BasicAuthenticator with an
app-password lookup. See user-memory note
project_webdav_basic_auth.md for the v2 app-password design.
2. Conflict detection and "save as copy" recovery¶
Current state. WebDAV PUT silently overwrites whatever is on
disk. If two users edit the same file at the same time (lock
expiry, network partition, LibreOffice's autosave behavior), the
losing write is just gone. Sabre's ETag plumbing is in the code
path (EntityFileNode::getETag) but no If-Match check fires
on PUT.
What's missing. A way for the server to refuse a PUT whose client-side ETag doesn't match the current entity revision, plus a UX flow that turns the refusal into "the file changed since you opened it — save as copy under a new name?".
Sketch. In EntityFileNode::put, if the request carries an
If-Match header, compare the supplied ETag to the entity's
current value; on mismatch, throw \Sabre\DAV\Exception\PreconditionFailed.
Add a Drupal status message + a "save as copy" form action when
the user re-opens. LibreOffice surfaces 412 as a "save failed"
dialog; documenting the workflow in
docs/libreoffice-macos.md and docs/libreoffice-windows.md is
part of the feature.
3. DASL search and a virtual "all files" directory¶
Current state. The module's Tree resolves single files by
fid. There's no listable directory. LibreOffice's "Open from
Server…" dialog needs PROPFIND on a directory to be useful.
What's missing. A SEARCH verb handler (RFC 5323 — DASL) that
exposes "files visible to me" or "files of bundle X" as
searchable. Pairs with a virtual /webdav/files/ collection that
PROPFINDs to a list of fids the current user can download.
Sketch. A new Drupal\webdav\Tree\SearchableCollection impl,
backed by an entity-query against File entities filtered through
file_access. Sabre has DASL support out of the box; the work is
on the Drupal side (access checks, paging, search index
integration if Solr / ES is wired).
4. Operator-shared expiring URLs¶
Current state. WebDAV URLs are bound to a session-derived
password and an access webdav permission. There's no way for an
operator to mint a "share this with this person until Friday" URL
for a recipient who doesn't have a Drupal account.
What's missing. A ShareToken config entity (fid + grantee
email + expires_at + signature). On the file's contextual menu,
"share via WebDAV → expires in 7 days → recipient: foo@example".
The recipient gets an email with a one-time URL that mints
short-lived WebDAV credentials when claimed.
Sketch. A new entity, a tiny mail template, a controller that
exchanges a share-token cookie for a per-session WebDAV password.
Distinct from access webdav because the grantee can be
non-Drupal-user.
5. Webhook on PUT¶
Current state. A successful WebDAV PUT updates the File
entity and is observable via Drupal entity hooks, but external
systems (Slack, search indexers, the user's own audit_trail
module) need to subscribe to internal events.
What's missing. A WebDavPutCompletedEvent and a
configurable webhook endpoint. Site builders register a URL; the
module POSTs JSON {fid, uid, mime, bytes_diff, timestamp} after
each PUT.
Sketch. Symfony EventDispatcher event + a small config form +
optional HMAC signing of the webhook body so the receiver can
verify origin. EntityFileNode::put has a natural insertion
point.
6. Trash / soft delete on DELETE¶
Current state. A WebDAV DELETE removes the File entity
outright. Recoverable only from backups.
What's missing. Integration with the contrib trash module
(or a built-in soft-delete table): a WebDAV DELETE becomes a
30-day-recoverable trash event with a "restore" action in the
admin UI.
Sketch. Detect the trash module, wrap the entity delete in
its trash API call if available; otherwise log a watchdog
warning. A small config option toggles the behavior.
7. In-browser WebDAV file explorer¶
Current state. Every WebDAV interaction requires a desktop app handoff (LibreOffice, Word). Users on iPad / Chromebook are out of luck.
What's missing. A /admin/content/webdav-files Drupal page
that PROPFINDs the user's WebDAV tree and renders an inline
explorer with edit-in-Collabora / preview-with-PDF-viewer
options.
Sketch. A controller + a small Vue/Lit JS component
(re-using the existing webdav-link library pattern). Pairs
naturally with item 3 (DASL search) and item 4 (share URLs).
8. Resumable / chunked uploads¶
Current state. Large file PUTs are all-or-nothing. A 100 MB ODP on a flaky network = full re-upload on every retry.
What's missing. RFC 8995-style Content-Range-driven chunked
PUT support. Sabre supports it; Drupal's File API doesn't
natively but bytes can be buffered in
private://tmp/webdav-uploads/<fid>.part.
Sketch. Modify EntityFileNode::put to detect Content-Range
headers, append to a .part file, finalize on the last chunk
(rename to canonical path, run mime sniff, sync entity).
9. Per-file audit trail of WebDAV operations¶
Current state. Drupal watchdog records WebDAV PUT/DELETE at
INFO level, but the messages aren't structured and aren't
tamper-evident.
What's missing. Structured audit-log integration: every PUT /
LOCK / UNLOCK / GET emits a signed record via the audit_trail
contrib module (HMAC tamper-evident log). Particularly valuable
where document provenance matters legally, such as
notarial or records-management workflows.
Sketch. Optional dependency on audit_trail; an event
subscriber on the WebDAV events that emits one audit row per
operation.
10. Quota awareness¶
Current state. LibreOffice's "save" path silently fails when the server runs out of disk; the user only learns when the next PUT returns 507 (or worse — the response body says "Insufficient Storage" but LO interprets it generically).
What's missing. Implement Sabre's IQuota interface so
PROPFIND surfaces DAV:quota-available-bytes /
DAV:quota-used-bytes. LibreOffice reads these before writing
and warns the user proactively.
Sketch. A tiny Drupal\webdav\Quota\DriveQuota service
wired to diskFreeSpace() for the file URI's stream wrapper.
11. Multi-file "queue these to LibreOffice"¶
Current state. One click = one file open. Reviewing 20 contracts in sequence means 20 individual round-trips.
What's missing. A UI affordance (checkbox + button on a file list view) that opens N files in sequence with a tiny inter-open delay. Niche, but useful for bulk document-review workflows.
Sketch. A JS helper that iterates the mint endpoint with a
sleep between iterations and assigns window.location.href
each time. LibreOffice picks them up as separate documents.
12. Cloud-Office handoff (Microsoft Graph / Google Drive)¶
Current state. "Open with LibreOffice" only works if LibreOffice is installed locally. Users on a Chromebook or with "Office Online" subscriptions have no path.
What's missing. A new webdav_link_target plugin family
("cloud") that emits Microsoft Graph deep links / Google Drive
"upload + open" intents instead of libreoffice: scheme URLs.
Sketch. Refactor WebDavLinkTarget into a plugin manager
with two implementations: UriSchemeTarget (current behavior) and
CloudTarget (new). The latter would need per-tenant OAuth
config — non-trivial but tractable.
13. Per-user default editor preferences¶
Current state. Every user sees the same target list — a
.docx shows both "Open with LibreOffice" and "Open with
Microsoft Word" for everyone with access webdav.
What's missing. A per-user setting on /user/{uid}/edit
("My preferred editor: LibreOffice / Word / always ask"). The
formatter renders the preferred target as the primary link and
demotes the alternatives to a dropdown.
Sketch. A user_field on the user entity + a small render-time
selector. Needs care around cache contexts (user would
fragment the cache again — user.permissions doesn't capture
per-user prefs, so this would have to go via a small JS
preference cookie + a client-side reorder, not a render-time
context).
Out-of-scope (intentional)¶
- CalDAV / CardDAV: sabre/dav supports them, but they target a different user need than file editing.
- Real-time co-editing: that's Collabora / OnlyOffice territory. The module hands files off to those when configured; it doesn't host the editor itself.
- Mobile WebDAV clients: documented externally — the module speaks RFC 4918, mobile clients work, but the module won't ship vendor-specific shims.