Feat/overview and wopi#7
Draft
moodyjmz wants to merge 56 commits into
Draft
Conversation
Add @mdi/js, @nextcloud/{auth,files,l10n,router}, and axios needed for the
overview feature. Bump nextcloud min-version to 33 / max-version to 35 to
match the APIs the overview relies on.
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Port the three services from richdocuments to TypeScript with full type definitions. officeFiles.ts uses DAV SEARCH (depth:infinity) with a MAX_DISPLAY_FILES render guard; config.ts persists grid-view preference to localStorage; templates.ts wraps the NC core templates OCS API. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Port FileCard (slot-based card with preview/icon/name/subname) and TemplateSection (scrollable template row with ResizeObserver arrows and per-type gradient backgrounds) from richdocuments to Vue 3 script setup with TypeScript. Replaces vue-material-design-icons chevrons with @mdi/js paths in TemplateSection. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Port OfficeOverview from richdocuments Options API to Vue 3 script setup with TypeScript. All vue-material-design-icons icons replaced with @mdi/js paths via NcIconSvgWrapper. App-id references switched from richdocuments to office. previewEnabled state dropped; per-card @error fallback handles missing previews. Grid-view mode read from localStorage via config.ts. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
- Override vue-eslint-parser to use @typescript-eslint/parser as inner parser so TypeScript generics in <script setup lang="ts"> are accepted - Remove direct `webdav` import from officeFiles.ts; use inline structural type instead — `webdav` is a transitive dep and the import was type-only - Add .stylelintrc.cjs extending @nextcloud/stylelint-config (was absent) - Update plan doc: axios → @nextcloud/axios correction All three checks now pass clean: eslint, stylelint, tsc --noEmit. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
- Add src/global.d.ts to type window.OCA.Viewer - Replace regex mime→theme detection in TemplateSection with an explicit MIME_THEME map - Extract OcsErrorResponse type to templates.ts and use it in the catch block - Add activeCategoryName computed, guard files section with v-else-if="activeCreator" to eliminate the three activeCreator! non-null assertions - Add activeFilter intentionally-not-reset comment in watch(activeCreator) - Prefer component.focus() over $el.querySelector in the dialog nextTick - Add validateFilename() to block / \\ and NUL chars before the API call - Document the flat cache design constraint in officeFiles.ts - Remove dead restoreCreator parameter from fetchAll - Add TTI comment on the module-level fetchAll() call Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
…tion Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
…n validation Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
After stripping WOPI template placeholders the URL may have no '?' yet, so appending with '&' produced a malformed URL like `https://editor/path&wopisrc=...`. Check for an existing '?' and use '?' as the separator when absent. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
File extensions are user-controlled (derived from the filename) and
were interpolated directly into XPath predicates. Validate the extension
against a strict [a-zA-Z0-9]{1,20} allowlist before use.
Also pass LIBXML_NONET | LIBXML_NOCDATA to SimpleXMLElement to block
network requests during XML parsing of the discovery response.
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
An unconstrained wopi_url allowed any scheme (file://, gopher://) and any host including cloud metadata endpoints. Reject non-http/https schemes and reject URLs that embed credentials. allow_local_address remains enabled intentionally: self-hosted deployments often run the editor and Nextcloud on the same host/network. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Range requests with start >= fileSize now return 416 (Range Not Satisfiable) per RFC 7233 §4.4 instead of silently streaming wrong data. Added try/finally around both range-stream handles so they are closed on exception. Also wrapped php://input in try/finally in putFile so the handle is always released even when early-return or an exception fires. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Without a cleanup job the office_wopi table grows without bound — one row per file open per 10-hour TTL window. Add a TimedJob that runs hourly and batch-deletes expired rows (500 at a time) via a new WopiMapper::deleteByIds() helper. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
WOPI locks are file-level (one opaque lock_id per file, 30-minute TTL) and distinct from the per-token session data in office_wopi. Separate table keeps the semantics clean. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
WopiLock stores a single opaque lock_id per file with a 30-minute TTL (WOPI spec). WopiLockMapper provides findByFileId, upsertLock (create or refresh), getExpiredLockIds, and deleteByIds for the cleanup job. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
POST wopi/files/{fileId} dispatches on X-WOPI-Override header:
- LOCK: acquire lock; idempotent for same lock_id; 409 on conflict
- UNLOCK: verify lock_id then release; 409 on mismatch
- REFRESH_LOCK: extend TTL; 409 on mismatch
- GET_LOCK: return current lock_id (empty string if unlocked)
- LOCK + X-WOPI-OldLock: UnlockAndRelock (atomic swap)
All conflict responses carry X-WOPI-Lock and X-WOPI-LockFailureReason
headers per WOPI spec so the editor can surface a meaningful error.
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Without these guards PutFile accepted any write regardless of whether another client held the file lock, silently discarding concurrent edits. - X-WOPI-Lock must match the current stored lock when one exists; returns 409 with X-WOPI-Lock + X-WOPI-LockFailureReason on mismatch. - X-WOPI-ItemVersion (when provided) must match the file mtime; returns 409 with current X-WOPI-ItemVersion when file changed out-of-band. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
The same hourly job now clears both office_wopi (expired tokens) and office_wopi_locks (expired file locks) so neither table grows without bound. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
NC35 removed registerJob() and registerSettings() from IRegistrationContext. Declare the CleanupJob background job and the Admin settings class in appinfo/info.xml instead, which is the supported mechanism from NC35 onwards. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
NC's putContent() closes the stream handle internally. The finally block called fclose() on an already-invalid resource, producing a PHP warning and a 500 response. Guard with is_resource() before closing. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
…ponses StreamResponse defaults to text/html; override on all GetFile paths (full, range, zero-byte) for WOPI spec compliance. Signed-off-by: James Manuel <james.manuel@nextcloud.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
executeOperation dispatched LOCK/UNLOCK/REFRESH_LOCK without checking canwrite, allowing read-only tokens (including read-only guest shares) to acquire and hold locks. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Migration 1002 adds hide_download BOOLEAN (default false) to oc_office_wopi. WopiMapper::generateGuestToken and TokenManager:: generateGuestToken accept the flag; CheckFileInfo derives HideExportOption, DisablePrint, DisableExport and UserCanNotWriteRelative from it at response time. Signed-off-by: James Manuel <james.manuel@nextcloud.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
GET /apps/office/open/share/{shareToken} opens a file in the WOPI editor
via a Nextcloud public share link. Handles:
- File shares (share node is a File)
- Folder shares (resolve target by fileId or path parameter)
- Password-protected shares (checks public_link_authenticated session
key in both legacy string and current array-of-IDs formats)
Generates a guest WOPI token stamped with the share's canWrite and
hideDownload flags. Returns the same editor TemplateResponse shape
as EditorController::open.
Signed-off-by: James Manuel <james.manuel@nextcloud.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
HideExportOption, DisablePrint and DisableExport are now derived from the token's hide_download field. UserCanNotWriteRelative is true for both guests and hideDownload tokens, preventing Save As from writing back to the owner's storage. Signed-off-by: James Manuel <james.manuel@nextcloud.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
- Strip control chars and cap guestName at 64 bytes in ShareController to prevent log injection and oversized display names - Add boundary comment on getFirstNodeById in resolveFile - Document three known gaps in PHASE3_DECISIONS.md (password UI, authenticated-user-through-share, federated shares) Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Parses app/@name attributes from the cached discovery XML to return all MIME types the editor advertises. Returns [] on cache miss or parse failure so callers degrade gracefully. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
KG2: authenticated NC users visiting a share link now get a full user
token via TokenManager::generateToken() rather than a guest token.
hideDownload is not applied on the user path — download restrictions
target unauthenticated third parties, not collaborators with direct access.
KG1: password-protected share without a session now redirects to
/s/{token} for the NC password challenge instead of returning 401 JSON.
Authenticated users bypass the password check entirely.
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Register LoadAdditionalScriptsListener to load the office-file-actions script on every Files app page. Supported MIME types from DiscoveryService are injected as initial state so the file action can filter correctly without a per-file HTTP round-trip. Falls back to an empty MIME list if discovery is unavailable, preventing the listener from blocking page render. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Adds a file-actions.ts entry that registers a DEFAULT file action for all MIME types advertised by the editor's discovery XML. MIME list is injected as initial state by LoadAdditionalScriptsListener. - Authenticated file access routes to EditorController::open - Public share context (isPublicShare()) routes to ShareController using getSharingToken() from @nextcloud/sharing/public Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Combines feat/euro-office-overview (file/template overview UI) with feat/wopi-phase-4 (WOPI connector backend). Resolved conflicts in info.xml (min-version 33) and package.json (union of dependencies). Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Avoids routing through the Files shortlink (/f/{fileid}) which loses
the overview as the referrer and lands the user in the Files app URL.
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
window.close() only works for popup windows. For full-page navigation, fall back to the office overview if there is no history to go back to. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
The initial skeleton used a cloud/weather icon. Replace with the standard NC document (folded-corner page) icon to match the app's purpose. Signed-off-by: James Manuel <james.manuel@nextcloud.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Read the editor URL from NC initial state (injected by PageController)
instead of hardcoding /apps/office/open. When no editor URL is provided
(overview-only deployment), fall back to NC's /f/{fileid} file shortlink
which routes through the Files app and triggers whatever default file
action is registered (e.g. eurooffice).
This removes the cross-branch dependency introduced in the previous
navigation fix: the overview branch no longer assumes the WOPI editor
route exists. The combined/WOPI branch will provide the concrete URL
via PageController::index().
Behaviour summary:
- With WOPI backend: direct navigation to editor, history.back() returns
to overview
- Without WOPI (e.g. eurooffice): routes via /f/{fileid} → Files app →
registered default action; return-to-overview depends on that editor
Signed-off-by: James Manuel <james.manuel@nextcloud.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
The overview Vue component reads 'editor-url' from NC initial state and
falls back to /f/{fileid} when null. On this combined branch the WOPI
EditorController exists, so inject the concrete route so the overview
navigates directly to the editor with clean history.back() behaviour.
Signed-off-by: James Manuel <james.manuel@nextcloud.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Documents the complete user flow, WOPI protocol sequence, key classes, local dev setup, and Phase 3 share support with known gaps. Signed-off-by: James Manuel <james.manuel@nextcloud.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Combined overview and WOPI for testing only - better to work on separate branches