Skip to content

Feat/external apps integrations - #262

Open
diocas wants to merge 9 commits into
cernboxfrom
feat/external_apps_integrations
Open

Feat/external apps integrations#262
diocas wants to merge 9 commits into
cernboxfrom
feat/external_apps_integrations

Conversation

@diocas

@diocas diocas commented Jul 27, 2026

Copy link
Copy Markdown

External apps integrations like mentions and file picker.

diocas and others added 9 commits July 27, 2026 13:55
Lets a browser-local localStorage preference (key
"preferredOfficeAppName") win over the backend/admin-configured
hasPriority default when deciding which app opens an office file from
the file listing - but only among the actions actually available for
that resource, so it's a no-op when the preferred app doesn't support
a given file type. Written by the office-app-feedback web-extension's
"make this the default for this browser" option.

Deliberately reads localStorage inline rather than through a proper
store - this is meant to be temporary (a couple of months) while
piloting alternative office apps, and removed once no longer needed.
Does not affect the legacy external-app bookmark redirect
(web-app-external/src/Redirect.vue), which keeps using the backend
default as before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
When the app-open response carries a forced_viewmode_reason, a warning
banner already told the user why they're stuck in a forced view mode.
This adds a "Switch to <app>" action to that banner: it reads the name
of the app currently holding the lock, matches it against the external
apps actually configured (appProviderService.appNames), and - if there's
a match other than the app already open - lets the user jump straight
into that app for concurrent editing instead of staying read-only.
….vue

Introduces the app-agnostic scaffolding for the office postMessage
integration: OfficePostMessageContext/OfficePostMessageRegistration/
OfficePostMessageFactory (types.ts), the registry that picks the right
per-app handler by app name and delegates message handling/resource-change
notifications to it (registry.ts), and postMessageToIframe, the one place
that builds and posts WOPI-shaped messages back to the app iframe.

App.vue wires this in: registers/unregisters the handler on mount/unmount,
forwards window "message" events from the resolved app origin into the
registry, warns before unload if the active handler reports pending
mentions, and reacts to isAppLoaded (MS365's "connection issues" alert,
Collabora's first-open modal) - none of these behaviors do anything until
the per-app handlers and shared functions land in the following commits.

Remove Collabora Experimental Banner.

Moves isOfficeAlertClosed/showOfficeAlert out of App.vue into their own
composable, mirroring useCollaboraModal.ts's shape (an is*Closed computed
plus a show* function). Takes App.vue's generic showAlert DOM-builder as a
dependency rather than duplicating it, since that builder is also used by
the unrelated file-locked warning (showWarningAlert) and stays in App.vue.
Collabora Online's WOPI postMessage protocol - App_LoadingStatus (replies
Host_PostmessageReady), Doc_ModifiedStatus/UI_Close (flush mentions),
UI_SaveAs/Action_Save_Resp (save-as + navigate to the renamed file),
UI_InsertGraphic/UI_InsertFile/UI_PickLink (file/link picker round-trips),
and UI_Mention (autocomplete + queueing selected mentions). Delegates to
useOfficeFileOperations.ts and useMentionNotifications.ts for the actual
behavior, landing in a following commit.
EuroOffice's (OnlyOffice-engine) WOPI postMessage protocol, confirmed
against api.onlyoffice.com's WOPI postmessage docs: App_LoadingStatus
(replies Host_PostmessageReady, exposes isLoaded), UI_Sharing (opens the
share dialog via useShareDialog.ts, landing in a following commit).
UI_Close/UI_FileVersions/File_Rename/Edit_Notification stay documented
stubs - the docs don't specify their Values payloads. Blur_Focus/Grab_Focus
are intentionally not handled - they're host -> iframe only per the docs,
nothing to receive.
Microsoft 365 for the web's WOPI postMessage protocol: App_LoadingStatus
(replies Host_PostmessageReady - MS365 disables some of its own UI, e.g.
Share, until it gets this back; exposes isLoaded), UI_Edit (switches to
write mode), UI_Sharing (opens the share dialog via useShareDialog.ts).
UI_Close/UI_FileVersions/File_Rename/Edit_Notification stay documented
stubs pending backend confirmation of their exact behavior/payloads.
Behavior shared by more than one app's postMessage handler, implemented
once and reused rather than duplicated per app:

- useOfficeFileOperations.ts: saveAs, insertGraphic/insertFile (open the
  embed-mode file picker, read the download URL straight off the picked
  resource) and insertLink (privateLink), used by Collabora today.
- useMentionNotifications.ts: resolves @mention autocomplete candidates,
  queues a pick without granting access yet, and once the comment is
  actually finished (not merely picked from the list), grants access to
  anyone who doesn't already have it and flushes the queue via
  POST {serverUrl}/app/mentions. Used by Collabora today.
- useShareDialog.ts: opens the share dialog, used by EuroOffice and MS365.
- useCollaboraModal.ts: tracks whether the first-open "office" modal has
  been dismissed (localStorage-backed), used by App.vue for Collabora.

include username in mention labels, fall back to service/secondary accounts

- Office apps (e.g. Collabora) re-match the typed query against the label
  they were given for each candidate - a label with only the display name
  doesn't contain what was typed if someone searches by login/username, so
  the entry silently fails to show up as a match. Labels are now
  "Name Surname (username)" when onPremisesSamAccountName is available.
- CERN: the default user search only covers primary (personal) accounts.
  When it comes up empty, resolveMentionCandidates now also searches service
  and secondary accounts (userType eq 'Service'/'Secondary') - same account
  types InviteCollaboratorForm.vue's per-role-type filters already use, as
  two separate calls since the graph API's $filter doesn't support "or" -
  only on the empty-result path, to avoid the extra round-trips on the
  common case.
…shared functions

Unit tests for everything added across the preceding commits: registry
registration/dispatch/teardown, each app's postMessage dispatch table,
useMentionNotifications' full mention-then-share-then-notify flow,
useOfficeFileOperations, useShareDialog, useCollaboraModal, and App.vue's
handler registration, origin filtering, and beforeunload mentions warning.

@rawe0 rawe0 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If everything works then I'm okay with merging this, but maybe we should look over the logic for searching for users a bit.

factory: useCollaboraPostMessages
},
{
// the exact app-name string CERNBox's backend reports for EuroOffice is unconfirmed

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can confirm the app name is EuroOffice :)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also don't think we need OnlyOffice here? (I don't think we will use it...)

hideActions: true,
customComponentAttrs: () => ({
parentFolderLink: getParentFolderLink(currentResource),
allowedFileTypes: ['image/png', 'image/gif', 'image/jpeg', 'image/svg'],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe 'image/jpg' too?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants