Commit Graph
100 Commits
Author SHA1 Message Date
Nathan Vasse 9caa1caa3d (backend) add a quota_excluded flag on items
Some items will be created by external services on behalf of users and
should not count against their creator's storage quota. Items flagged as
quota_excluded are now ignored by the creator storage compute backend.

The flag is part of the fields invalidating the cached storage used so
toggling it is reflected immediately in the entitlements.

The covering index is replaced by one whose condition also filters out
quota excluded rows, keeping the aggregation index-only. Both migrations
are safe on a large table: the AddField is catalog-only on PostgreSQL
11+ and the index swap runs CONCURRENTLY, adding the new index before
removing the old one.
2026-08-03 10:10:34 +02:00
Nathan Vasse f239b58e4d (frontend) adapt share e2e helper to the ui-kit 0.28 modal
ui-kit 0.28.0 replaces the share modal plain quick search input with an
invite field: selected users become chips next to a role dropdown and a
dedicated share button. The helper still targeted the old combobox label
and page-level share button, breaking every spec sharing an item.
2026-07-28 12:00:32 +02:00
Nathan Vasse c45b0943c5 (frontend) mock saxen in jest config
ui-kit 0.28.0 pulls in read-excel-file, whose CommonJS build requires
saxen, an ESM-only package that jest cannot parse. No unit test parses
spreadsheets, so a stub parser is enough, following the pretty-bytes
precedent.
2026-07-28 11:43:12 +02:00
Nathan Vasse ab8e0ebc39 (frontend) share an item with contacts imported from a file
Plug the ui-kit share modal file import onto the new batch share
endpoint. The option only shows up when ALLOW_SHARE_IMPORT_FILE is
enabled in the config. Errors are rendered inside the import modal
instead of the global toast so the user can fix the file and retry
without losing context.

The e2e environment enables the flag to cover the flow end to end.
2026-07-28 11:37:10 +02:00
Nathan Vasse 41e796b2a9 ⬆️(frontend) bump ui-kit for the share import modal
The share import modal needs ui-kit latest version.
2026-07-28 11:37:10 +02:00
Nathan Vasse 16f11c12e7 (backend) add an item batch share endpoint
Sharing a folder with many contacts one call at a time is slow and can
leave a partial state when one of them fails. This endpoint accepts a
list of email/role rows, validates them all before any write, then
creates accesses for known users and invitations for unknown emails.
Rows targeting users already covered by an equal or higher role are
skipped and reported so the client can inform the user.

The feature is disabled by default and gated by the new
ALLOW_SHARE_IMPORT_FILE setting exposed in the config endpoint.
2026-07-28 11:26:07 +02:00
Nathan Vasse 22c7eac3aa ♻️(backend) move descendants access sync to a service
The descendants access synchronization logic was private to the accesses
viewset. Extracting it into core/services/accesses.py lets the upcoming
batch share endpoint reuse it without reaching into viewset internals.
2026-07-28 11:25:22 +02:00
Nathan Vasse 9e080f2716 (frontend) cover the homepage messages widget button
The homepage shows the messages widget button when the widget is
configured. A test ensures the loader init command is queued and the
loader script is injected, so a config regression cannot silently
remove the button.
2026-07-28 09:41:07 +02:00
Nathan Vasse 9b545b6087 (frontend) show the messages widget button on the homepage
Anonymous visitors have no help menu, so the homepage loads the
widget floating button instead when the widget is configured. This
gives them the same support entry point as logged-in users.
2026-07-28 09:41:07 +02:00
Nathan Vasse 26197545af (frontend) add a button mode to the messages widget hook
The homepage needs the widget floating button while the help menu
opens the form directly. A WidgetHelper now owns the command queue
and script injection so both entry points share the same bootstrap,
and it fills the legacy queue key so older widget runtimes keep
working. The hook exposes showButton to init the loader script and
canLoadWidget so callers can check the config before rendering.
2026-07-28 09:41:07 +02:00
Nathan Vasse 9120c819ec (frontend) open the messages widget from the help menu
Some instances use the messages widget for support instead of a plain
mailto link. A new supportMessagesWidget flag in the help menu config
lets the "Contact us" entry open the widget, taking precedence over
the support email when both are configured.
2026-07-27 16:35:39 +02:00
Nathan Vasse 635135d1ed ♻️(frontend) reuse the left panel footer on public layouts
The home and simple layouts rendered their own mobile-only panel with
just the gaufre. Reusing the explorer footer keeps the settings entry
and user profile consistent everywhere and removes the duplicate
component. The index page is also reformatted by prettier on the way.
2026-07-23 17:08:12 +02:00
Nathan Vasse d42dd3061a (frontend) refresh the storage gauge on move and duplicate
Duplicating an item consumes storage and moving an item to or from the
root can change who owns its usage. Invalidate the entitlements cache
on success so the gauge reflects the new usage without a reload.
2026-07-23 17:08:12 +02:00
Nathan Vasse 826ad05ce6 🎨(backend) assign the quota with a walrus operator
Apply a review suggestion to keep the quota lookup and its guard on a
single line in the entitlements endpoint.
2026-07-23 17:08:11 +02:00
Nathan Vasse 060707c037 ♻️(backend) invalidate the storage cache from Item.save
Following review, replace the post_save signal with a direct call in
the model method. The invalidation logic stays close to the domain and
is easier to trace than an implicit signal. Bulk queryset updates keep
their explicit invalidation since they bypass save().
2026-07-23 17:08:11 +02:00
Nathan Vasse 1a7fcd4198 ✏️(frontend) fix the spelling of quota exceeded codes
Align the driver enums, quota state, translation keys and e2e fixtures
with the backend error codes now spelled "exceeded".
2026-07-23 17:08:11 +02:00
Nathan Vasse 1d14ef01ba ✏️(backend) fix the spelling of quota exceeded codes
The entitlements enums exposed "excedeed" in both their names and their
wire values. Fix the spelling now, while the codes are only consumed by
this branch, as they are part of the API contract with the frontend.
2026-07-23 17:08:11 +02:00
Nathan Vasse bac1e2dd53 (backend) push usage metrics to DeployCenter entitlements requests
DeployCenter now accepts the usage metrics directly in the entitlements
request body, so we switch from GET to POST and send a "usage_metrics"
list with the user's storage and the aggregated storage of the active
users sharing the same organization claim. This avoids the extra HTTP
roundtrip DeployCenter previously made to our usage metrics API when
resolving entitlements.

The entries reuse the Usage serializers so the pushed body and the
pulled external API cannot drift. The organization serializer now takes
the users queryset to be usable from both call sites, which also moves
the storage computation out of the viewset. The organization claim key
is configurable through a new "organization_claim" backend parameter
defaulting to "siret".
2026-07-23 17:08:11 +02:00
Nathan Vasse d3d9dff6b6 📝(changelog) mention the storage gauge and reorder entries
The frontend storage gauge deserved its own changelog entry, and the
local backend line moves after the already released entries to keep
the section in insertion order.
2026-07-23 17:08:11 +02:00
Nathan Vasse 24863e9e74 (frontend) keep translations in sync across languages
Several keys were missing in some languages (Dutch search filters,
delete menu labels, restore toasts, file picker caption), silently
falling back to English. Sync them and add a test failing whenever a
key exists in one language but not the others, so gaps are caught at
review time instead of in production.
2026-07-23 17:08:11 +02:00
Nathan Vasse be6109c99f (frontend) add the storage gauge and settings modal
Renders the quota returned by the entitlements API as a gauge in the
left panel footer, with a locked state when the organization quota
is reached and a tooltip carrying the error when the quota cannot be
computed. Clicking it opens a settings modal with the detailed gauge
and an optional documentation link. The footer is reworked to host
the gauge on mobile too (user profile and app grid move there), and
the entitlements query is refetched after uploads and hard deletes
so the gauge follows the usage.
2026-07-23 17:08:11 +02:00
Nathan Vasse 027433253f (frontend) show specific quota messages on rejected actions
Uploads, moves to the root and duplications can now be refused by the
backend quota gates. The API error code carries the can_upload reason,
so each surface (upload list, move toast, duplicate toast) maps it to
a dedicated translated message instead of a generic failure, and the
40x redirect is disabled on those calls so the user stays in place
and sees the toast. The move mutation handles its own error feedback
to avoid double toasting through the global handler.
2026-07-23 17:08:11 +02:00
Nathan Vasse 5ed2639e61 ⬆️(frontend) upgrade ui-kit to 0.27.0
Brings the StorageGaugeButton and StorageGaugeInformation components
needed to render the storage quota gauge.
2026-07-23 17:08:11 +02:00
Nathan Vasse e50ace270e (backend) add a storage gauge information link setting
Operators may document how storage quotas work for their users. The
optional FRONTEND_STORAGE_GAUGE_INFORMATION_LINK setting is exposed
through the config API so the frontend gauge can link to that page.
2026-07-23 17:08:11 +02:00
Nathan Vasse a84851efff (backend) gate move-to-root and duplicate on the upload entitlement
Both actions grow the acting user's storage usage without going
through an upload: duplicating makes them creator of a new sized
copy, and moving a file to the root without a direct access
reassigns its creator. An over-quota user could use them to keep
taking ownership of storage. Moving also invalidates the previous
creator's usage cache since the reassignment only triggers the
post_save invalidation for the new creator.
2026-07-23 17:08:10 +02:00
Nathan Vasse 0f7ee3a3ed (backend) add a local entitlements backend with storage limits
Deployments without a DeployCenter service had no way to enforce
storage quotas. This backend computes each user's usage locally and
applies a configurable default limit, overridable per user in the
admin (0 meaning unlimited) and skippable for users created before a
cutoff so quotas can be rolled out to new users only. The quota is
soft: can_upload runs before the file size is known, so one upload
can overshoot before the next one is blocked.
2026-07-23 17:08:10 +02:00
Nathan Vasse 6c61275669 🐛(backend) exclude hard-deleted items from storage computation
Hard-deleted items no longer occupy storage but still counted in the
creator's usage, inflating the quota gauge and blocking uploads for
users who had cleaned up their trash. The new partial covering index
keeps the per-creator sum an index-only scan now that the quota check
runs it on every uncached upload.
2026-07-23 17:08:10 +02:00
Nathan Vasse 780c63bb08 (backend) invalidate per-user storage caches on item writes
The quota gauge reads the storage usage from cache (directly for the
local backend, via DeployCenter's metrics for the remote one). Both
caches must be dropped as soon as a write changes a user's usage,
otherwise the gauge shows stale numbers until the timeout. A post_save
signal covers item saves, and the code paths doing bulk updates that
bypass signals (signup invitations, user reconciliation, hard delete
of a tree) invalidate explicitly for every impacted creator.
2026-07-23 17:08:10 +02:00
Nathan Vasse d9cfb4dae1 (backend) expose the user quota in the entitlements API
The frontend needs the storage usage and limit to render a quota
gauge. Entitlements backends can now return a quota (usage/limit,
locked when the organization quota is reached, or an explicit error
state) and the DeployCenter backend derives the can_upload reason
from the resolve level when the service does not provide one, so
the gauge and the upload errors stay consistent.
2026-07-23 17:08:10 +02:00
Nathan Vasse 99d3a4d20b (backend) expose the can_upload reason as the API error code
The upload gates returned a generic "permission_denied" code whatever
the entitlements backend's refusal reason. Forwarding the backend's
reason as the DRF error code lets the frontend map each rejection to
a specific, translatable message instead of a catch-all one.
2026-07-23 17:08:10 +02:00
Nathan Vasse 4b9eaa4977 ⬆️(frontend) refresh transitive pins in the lockfile
A reinstall picked up new radix-ui and typescript-eslint patch
releases within the ranges already declared, so the lockfile is
updated to keep local installs and CI resolving the same versions.
2026-07-08 14:15:03 +02:00
Nathan Vasse 78501a2881 ♻️(frontend) drop a leftover spread in the category filter options
The reset entry moved out of the options list when the filters
switched to the ui-kit built-in reset, leaving a pointless wrapping
spread and a comment describing an option that no longer exists.
2026-07-08 14:15:03 +02:00
Nathan Vasse 99ce2ebb26 🐛(frontend) clear the modified filter when its option is deselected
The ui-kit Filter emits an empty key when the active option is
deselected. Wrapping it in a structured value anyway made the filter
look active while carrying no usable bounds, so we now propagate
undefined to actually clear it.
2026-07-08 14:15:02 +02:00
Nathan Vasse cdc71c956d (frontend) adapt link reach check to new dropdown markup
The updated ui-kit renders the selected menu item indicator as an
SVG with the c__dropdown-menu-item__check class instead of the old
material-icons checked span, so the share e2e tests could no longer
find the icon and failed in CI.
2026-07-08 14:15:02 +02:00
Nathan Vasse 1241db9d1f 💄(frontend) update search modal spaces
Align the paddings with the one on the sketches.
2026-07-08 14:15:02 +02:00
Nathan Vasse 97bdff4b75 ⬆️(frontend) bump ui-kit version
We need this new version that implement all the required changes
for the filters design review.
2026-07-08 14:14:34 +02:00
Nathan Vasse b4c48a7f81 💄(frontend) let the filters bar scroll on small screens
On mobile the filter buttons overflowed the viewport with no way to
reach the last ones. The bar is now wrapped in a SmartScroller, like
in the search modal, and the mobile background moves to the container
so the surface color covers the whole scrollable area. The explorer
container also takes the full width under the tablet breakpoint.
2026-07-08 11:45:36 +02:00
Nathan Vasse f068dd079b ♻️(frontend) keep the modified filter as a structured value
Storing only the computed date bounds in the filters lost the selected
preset, so the filter could not restore its own state and the custom
range needed extra local state and top-down resets. The filters now
carry a frontend-only "modified" value (preset key or custom range)
and the drivers convert it to updated_at_* query params right before
each request. The custom calendar is hidden on mobile where it does
not have enough room to render comfortably.
2026-07-08 11:45:36 +02:00
Nathan Vasse f6142372a5 ♻️(frontend) use UserSearchFilter for the contact filter
The ui-kit now provides a dedicated user search filter with the reset
button, the empty state and the selected row rendering built in. The
bespoke SearchFilter wiring (manual reset entry, custom item renderer)
is no longer needed.
2026-07-08 11:45:36 +02:00
Nathan Vasse a74053db0a 💄(frontend) rely on the ui-kit built-in filter reset
The Filter component now ships its own reset button, so the custom
reset option at the top of the type and location lists duplicated it.
Icons are also aligned with the design review: mini file icons and
small location icons.
2026-07-08 11:45:36 +02:00
Nathan Vasse 625c5455bd (e2e) follow the user menu button rename
The ui-kit renamed the accessible name of the user menu trigger from
"Open user menu" to "User menu". The tests use this button to detect
that the layout is fully loaded, so the selectors must follow.
2026-07-08 11:45:35 +02:00
Nathan Vasse ab45c32aec (e2e) refresh the wopi after paste canvas baseline
Same cause as the empty doc baseline: the collabora update renders the
canvas 951px wide so the April screenshot fails on the size check. The
new baseline is the image rendered by the CI runner, taken from the
attempt matching the majority of retries (one retry showed a transient
scroll offset that the screenshot polling absorbs at comparison time).
2026-07-08 11:24:42 +02:00
Nathan Vasse 1301c6a8c0 (e2e) retry clearing the silent login retry key
Clearing the django session cookie can trigger a navigation while the
localStorage evaluation runs, which destroys the execution context and
fails the test on webkit. Retrying until the page settles removes the
race.
2026-07-08 11:24:42 +02:00
Nathan Vasse f3b12b5eb0 (e2e) refresh the wopi empty doc canvas baseline
The CI pulls collabora/code:latest and a recent release changed the
canvas rendering: it is now 8px narrower and loses the page border, so
the screenshot comparison fails on the size check before even diffing.
The new baseline is the image produced by the CI runner itself.
2026-07-08 11:24:42 +02:00
Nathan Vasse 44c73de815 (e2e) assert the dropdown check icon via material-icons
The published ui-kit 0.24.0 renders the selected dropdown indicator as
a material icon span (.material-icons.checked). The class used before,
c__dropdown-menu-item__check, only exists in a not yet released ui-kit
version, so the locator matched nothing on the CI.
2026-07-08 11:24:42 +02:00
Nathan Vasse c9f86d91af (e2e) repair the wopi conversion modal tests
These tests never passed since their introduction. The modal title is
not rendered as a heading, so we target the dialog role instead. The
placeholder-injecting routes shadowed the convert ability mock and,
through route.continue(), sent the convert POST to the real backend
which denied it: they now re-inject the ability and use
route.fallback() so earlier mocks still apply. The confirming test
also mocks the placeholder GET, otherwise the poller sees a real 404
and removes the placeholder as a failed conversion.
2026-07-08 11:24:42 +02:00
Nathan Vasse 524bac7bf7 (e2e) assert the dropdown check icon by its class
Since ui-kit 0.24 the selected item indicator in dropdown menus is an
SVG instead of a material icon, so the item text no longer contains
"check" and the share link reach tests fail.
2026-07-08 11:24:42 +02:00
Nathan Vasse 5d969b8d74 (e2e) cover the help menu rendering
We verify the help menu shows the configured options, drops the contact
entry when no support email is set, and stays hidden when the config is
empty. The e2e env gets a help menu config so the menu is reachable.
2026-06-29 18:05:56 +02:00
Nathan Vasse 5c64f2beb6 (frontend) add a help menu in the left panel footer
When the backend provides FRONTEND_HELP_MENU_CONFIG, we render the
ui-kit HelpMenu in the left panel footer so users can reach the
documentation, legal pages and support. The menu is hidden when no
config is set to avoid an empty footer. We also hide the unused
cunningham toast container which would otherwise capture clicks on the
footer.
2026-06-29 18:05:48 +02:00
Nathan Vasse 99bcf05be9 (backend) expose a help menu config to the frontend
The frontend needs a configurable help menu pointing to documentation,
legal pages and a support contact. We expose FRONTEND_HELP_MENU_CONFIG
through the config endpoint so deployments can tune these links without
a frontend rebuild.
2026-06-29 18:05:43 +02:00
Nathan VasseandNicolas Clerc 330a782aab 🐛(frontend) stabilize the custom modification date picker
Committing the picked range on the calendar's onChange called setRange,
which rebuilt the Filter options memo and remounted the calendar. That
detached its OK button mid-interaction, so clicking OK raced against the
remount and timed out on firefox and webkit. We now stash the range in a
ref while picking and only commit it on OK, when the panel closes anyway.
2026-06-29 16:49:52 +02:00
Nathan VasseandNicolas Clerc d0d3083c3d 🐛(frontend) let jest resolve ui-kit exports subpaths
The icons subpath import only exists in the ui-kit package.json
"exports" map. Jest's ts-jest transform forced classic "node"
module resolution, which ignores "exports" and failed to find
"@gouvfr-lasuite/ui-kit/icons". Switching to "bundler" matches
the app tsconfig so the unit tests compile again.
2026-06-29 16:49:52 +02:00
Nathan VasseandNicolas Clerc d1f6fc95f6 (e2e) cover the custom modification date range
The new test picks a custom range through the calendar, asserts the
items are filtered to the selected window, checks the option label
reflects the bounds, and that resetting falls back to the default label.
2026-06-29 16:49:52 +02:00
Nathan VasseandNicolas Clerc 12aceaf73e ♻️(frontend) import icons from the ui-kit icons subpath
The icons now live under the dedicated `@gouvfr-lasuite/ui-kit/icons`
entry point. Importing them from there keeps the icon imports aligned
with the package layout.
2026-06-29 16:49:52 +02:00
Nathan VasseandNicolas Clerc ec51aca603 (frontend) make the search modal filters scroll
The filter row is wrapped in a SmartScroller so it overflows gracefully
when the filters do not fit. The global reset button is dropped since
each filter already exposes its own reset, making it redundant.
2026-06-29 16:49:52 +02:00
Nathan VasseandNicolas Clerc f191788923 (frontend) use a calendar range for the custom modification date
The custom range relied on a detached DateRangePicker rendered below the
filter. We now render a CalendarRange as the custom option's subContent
so the date selection stays inside the filter popover, and the option
label reflects the selected bounds once a range is picked.
2026-06-29 16:49:52 +02:00
Nathan VasseandNicolas Clerc cc39a65f5e ⬆️(frontend) upgrade ui-kit to 0.24.0
We need this last release in order to get access to the upgrade
Filter and SmartScroller.
2026-06-29 16:49:52 +02:00
Nathan VasseandNicolas Clerc c2acb8392e ♻️(frontend) query frequent contacts from the backend
The contact filter used to load the frequent contacts and filter them
locally, falling back to a separate user search above a threshold. The
backend now handles the search through the `q` param, so we pass the
filters straight to the contacts endpoint and let it return the matches.

The query only runs while the popover is open, so navigating folders no
longer triggers contact requests the user never asked for.
2026-06-29 16:49:52 +02:00
Nathan VasseandNicolas Clerc 43bc6ece92 (frontend) reuse clipboard permissions helper on firefox
The dropdown menu test was failing on firefox because
context.grantPermissions does not accept the clipboard-* strings
on that browser. Use the shared helper which skips firefox where
clipboard support is enabled at launch via firefoxUserPrefs.
2026-06-04 11:16:40 +02:00
Nathan VasseandNicolas Clerc f17d7a8e50 (frontend) test public folder CTAs
Cover the folder explorer header on a public folder: authenticated
users see no anonymous CTA; anonymous users see AnonymousCTA with
a login button that redirects to /authenticate/. A third case
exercises the anonymous dropdown menu — copy link writes the
folder URL to the clipboard, and switching language updates the
visible CTA text.
2026-06-04 11:16:40 +02:00
Nathan VasseandNicolas Clerc bdf6c293c3 (frontend) test public file preview CTAs
Cover the contextual CTAs on the standalone file preview page:
authenticated users get MyFilesCTA redirecting to /my-files, and
anonymous users get AnonymousCTA with the default "/" try-out
fallback plus a login button that redirects to /authenticate/.
A dedicated case mocks FRONTEND_EXTERNAL_HOME_URL to ensure the
try-out href follows the config.
2026-06-04 11:16:40 +02:00
Nathan VasseandNicolas Clerc 4adbe9f2b7 (frontend) wire CTAs into header and standalone file preview
UserProfile now renders AnonymousCTA along with a dropdown menu
(copy link, language picker) when no user is authenticated, so
visitors on a public page can still act on it without the user
menu. CustomFilesPreview gains a CONTEXTUAL mode that swaps in
the right CTA based on auth state, used by the standalone file
preview page which is now wrapped in GlobalLayout to expose the
auth context.
2026-06-04 11:16:40 +02:00
Nathan VasseandNicolas Clerc 82e57aefc5 (frontend) add AnonymousCTA and MyFilesCTA components
AnonymousCTA offers a try-out link and a sign-in button to visitors
landing on a public resource. MyFilesCTA is the authenticated
counterpart, sending the user back to their drive. Translations
cover both CTAs and the anonymous dropdown menu that will live next
to them. Components are consumed in a follow-up commit.
2026-06-04 11:16:40 +02:00
Nathan VasseandNicolas Clerc 3088455abf ⬆️(frontend) bump ui-kit to 0.22.0
Required for the DropdownMenu, useDropdownMenu and Folder exports
used by the upcoming anonymous and my-files CTA components.
2026-06-04 11:16:40 +02:00
Nathan Vasse 74b0f84ea0 ♻️(frontend) move mime imports to ui-kit
Some imports were re exported from the mime type file. Let's
import directly from ui-kit.
2026-05-11 14:58:06 +02:00
Nathan Vasse 18d8b22eb7 📝(doc) update changelog
Update the changelog based on the recent commits about
file previewer.
2026-05-11 14:48:22 +02:00
Nathan Vasse 10d72079ae (frontend) replace per-format preview tests with smoke tests
Detailed FilePreview behavior is now covered by Playwright CT inside
the ui-kit repo, so the drive e2e suite only keeps integration smoke
checks: upload -> open -> render for PDF, image, video, audio, plus
prev/next navigation across files.
2026-05-06 17:19:45 +02:00
Nathan Vasse 3ddd7e5db5 ♻️(frontend) replace custom file preview with ui-kit FilePreview
The ui-kit ships a complete FilePreview (PDF, image, video, audio,
WOPI, error/unsupported, navigation, controls), so we drop the in-app
implementation under features/ui/preview/ and keep only a thin
CustomFilesPreview wrapper that maps Item to FilePreviewType, plugs
in the share modal, info sidebar and posthog event.

The WOPI editor is moved to its own features/wopi/ folder and
openWopiInNewTab now takes a FilePreviewType to match the ui-kit
onOpenInEditor callback. The file_preview translation block moves to
the ui-kit too.
2026-05-06 17:19:45 +02:00
Nathan Vasse b7d635b7bf ♻️(frontend) delegate file icons to ui-kit FileIcon
The ui-kit now ships FileIcon along with mime-category and icon-set
utilities, so we drop the local catalog (MIME_MAP, KNOWN_EXTENSIONS,
getMimeCategory, getIconByMimeType, FolderIcon, WorkspaceIcon) and
keep only the drive-specific extensions: folders and suspicious
uploads, exposed through getItemExtendedIcon. ItemIcon falls back to
ui-kit FileIcon for everything else.
2026-05-06 17:19:45 +02:00
Nathan Vasse d99116edb8 ⬆️(frontend) upgrade ui-kit version
Upgrade to the latest version that includes FileIcon, mimetype utils
and FilePreview.
2026-05-06 17:19:45 +02:00
Nathan Vasse 6569287391 (backend) capture item_duplicate posthog event
Duplication is currently invisible to product analytics because the
event is fired from the API rather than the UI. Emit it from the
viewset right after the duplicate row is created so the duplicated
item context (workspace, mimetype, ...) is attached to the event.
2026-05-04 15:58:32 +02:00
Nathan Vasse 1e97297ae2 (frontend) capture column type change posthog event
Track when users customize their column layout so product can see
which columns get adopted vs ignored. The event carries the slot
plus old and new column types to support funnel analysis.
2026-05-04 15:49:53 +02:00
Nathan Vasse 4cc02f7959 (frontend) match event properties in posthog e2e helper
Asserting only on the event name is not enough when the same event is
emitted from multiple call sites with different payloads. Expose
expectEventSentWithProps so specs can pin down the expected slot or
context, and keep the captured properties available alongside the
existing names array.
2026-05-04 15:49:53 +02:00
Nathan Vasse 6049113dfe 🐛(backend) accept CDFV2 mimetype from newer libmagic
Scalingo's Ubuntu runtime ships a newer libmagic that returns
application/CDFV2 for legacy .doc/.xls/.ppt files, where Alpine's
older libmagic (used in our Docker image) returns the older label
application/x-ole-storage. Both describe the same OLE2 / Composite
Document File V2 container.

detect_mimetype() already treats application/x-ole-storage as a
generic content-detection result and falls back to extension-based
detection (yielding application/msword, application/vnd.ms-excel,
etc.). Without the same handling for CDFV2, legacy .doc uploads on
Scalingo are rejected by the mimetype allow-list with
"mimetype not allowed application/CDFV2 for filename ...".
2026-04-29 12:15:02 +02:00
Nathan Vasse 7a95dcf38a 📝(doc) update changelog
Fix the mis-added entries inside the previous release back to
the unreleased one. And add new entry for the pdf mixed pages
size rendering.
2026-04-29 11:37:08 +02:00
Nathan Vasse 679805b244 (frontend) add e2e tests for mixed PDF page sizes
Cover the per-page-dimensions path with a 17-page fixture
spanning portrait, landscape, square, wide-banner and tall-strip
ratios. Asserts that rendered pages and thumbnails match each
page's real aspect ratio, that page-input jumps land precisely
across mixed ratios, and that the virtual-list total height
matches the sum of per-page heights.
2026-04-29 11:02:53 +02:00
Nathan Vasse 7c17132f8e (frontend) render PDF previews at per-page dimensions
Pages were laid out with a hardcoded A4 portrait ratio, so
landscape, square and extreme-ratio pages rendered with wrong
row heights and page-input jumps could land on a neighbour.

A new usePdfPageDimensions hook lazily fetches each page's
viewport from pdfjs and dedupes concurrent requests. The main
viewer and thumbnail sidebar now size rows from the cache and
fall back to A4 only until real dimensions arrive. Page-input
jumps await the prefix's dimensions before computing the scroll
offset so the viewport center lands inside the requested page.
2026-04-29 11:02:53 +02:00
Nathan Vasse 59292f6052 📝(doc) update changelog
Record the metrics filter, entitlement payload additions, and the
frontend disclaimer feature shipped on this branch, alongside the
already-landed organization metrics entry.
2026-04-29 10:39:31 +02:00
Nathan Vasse 019016865a (frontend) add e2e tests for entitlement disclaimers
Cover visibility rules (enabled / disabled / flagged reasons), the
seen-flag lifecycle, the showPotentialOperators branch with and
without signupUrl, and the OK-click close interaction.
2026-04-29 10:39:31 +02:00
Nathan Vasse ef675df5a2 (frontend) add entitlement disclaimer modal
Show a one-shot modal when the user's can_upload is blocked for a
recoverable reason (not_activated / no_organization), with an optional
list of potential operators where they can self-register. Tracks
"seen" per disclaimer in localStorage so users are not nagged on
every load, but re-shows the disclaimer if the underlying entitlement
changes and then regresses.
2026-04-29 10:26:54 +02:00
Nathan Vasse 6a19b0922e (frontend) type entitlement reason and context
Type the new backend fields (\`can_upload.reason\`, \`context\`) and
the new \`FRONTEND_ENTITLEMENTS_DISCLAIMERS\` config key so downstream
components can consume them with no casts.
2026-04-29 10:17:25 +02:00
Nathan Vasse ba4558e1ca (backend) expose FRONTEND_ENTITLEMENTS_DISCLAIMERS config
Add a dict-shaped config exposed to the frontend so operators can
enable or disable entitlement disclaimers per reason without code
changes. Also pin the Test settings to the local entitlements backend
so test runs do not depend on external state.
2026-04-29 10:17:25 +02:00
Nathan Vasse aa099068e9 (backend) add organization metrics to usage API
Add account_type=organization support to the usage metrics
endpoint. When requested, the API filters users by a shared
OIDC claim (account_id_key/account_id_value) and returns
aggregated storage across all matching users.

Add handling of account_id_key/account_id_value pair, allowing
filtering on allowed parameters in a more generic way. At the
moment `account_email` filter is still needed.

Also include the context of the entitlement in the /entitlements
response.
2026-04-29 10:17:25 +02:00
Nathan Vasse 8360aeca88 ♻️(backend) accept a queryset in storage compute backend
Change compute_storage_used signature from a single user
to a queryset of users. This enables aggregating storage
usage across multiple users, which is needed for
organization-level metrics.
2026-04-29 10:17:25 +02:00
Nathan Vasse 62b8341dd7 ♻️(backend) refactor entitlements into backend package
Restructure the entitlements module from flat files into a
backends/ package with a factory pattern. This enables
constructor kwargs injection for backend configuration.

Rename ANCT backend to DeployCenter to match the actual
service name, and rename Dummy backend to Static for clarity.
The factory now passes ENTITLEMENTS_BACKEND_PARAMETERS to
the backend constructor, making configuration more explicit
and testable.

Some request parameters get updated because of changes on
deploy center side that need to be reflected in services.
2026-04-29 10:17:25 +02:00
Nathan Vasse 77a67db6d8 (frontend) add e2e tests for SDK picker selection
Cover the two main picker outcomes: cancelling leaves the item
private, and confirming exposes a publicly reachable URL. The
reach check opens the returned URL in a fresh anonymous context
and waits for the download event, since the backend serves
attachments with Content-Disposition and navigation aborts.

To run the test in CI, build the drive SDK package and start the
sdk-consumer dev server before the playwright job.
2026-04-22 16:00:15 +02:00
Nathan Vasse a12771fe08 🐛(frontend) fix SDK picker link reach promotion
Switch to useMutationUpdateLinkConfiguration and preserve the
item's existing link_role. Also add the missing sdk.explorer.cancel
translations for en and nl so the Cancel button label renders
correctly. This should have been done when migration to this new
API route. The existing update did not have any effect.
2026-04-22 16:00:15 +02:00
Nathan Vasse 352e19580e (backend) make invitation validity duration configurable via env
The invitation validity duration was hardcoded to 7 days. Expose it as
an INVITATION_VALIDITY_DURATION env var so operators can tune it per
deployment without patching settings. Default remains 7 days.
2026-04-22 10:09:31 +02:00
Nathan Vasse 6655b8fbe8 🐛(backend) fix openapi schema for item access endpoints
`ItemAccessViewSet.get_serializer_class` depends on `self.item`, which
reads `self.kwargs["resource_id"]`. This is unavailable during
drf-spectacular introspection, so spectacular fails to resolve the
serializer and drops the requestBody for POST/PUT/PATCH operations.

Declare the schema statically via `@extend_schema` and
`@extend_schema_view` so the generated OpenAPI exposes the proper
request and response payloads for item access endpoints.
2026-04-21 10:24:18 +02:00
Nathan Vasse 26a522f2ae 🔧(ci) exclude ts file from print job
This job was detecting some legit code as false positive
for print statements.
2026-04-20 15:21:01 +02:00
Nathan Vasse eefa620702 (other) route /wopi/<uuid> to the WOPI item page
The WOPI page is a dynamic route keyed by item UUID, so nginx needs to
fall back to the [id].html shell when the URL is hit directly (reload,
external link). Mirrors the existing /explorer/items/[id] rule.
2026-04-20 15:01:56 +02:00
Nathan Vasse a8e71cdb38 (backend) add filename to e2e search fixtures
The search e2e fixture items lacked a filename, which made the seeded
file items diverge from real uploads where the filename is present.
We need the filename to be predictive so we can make assertion in the
e2e tests about the viewer that should open or not.
2026-04-20 15:01:56 +02:00
Nathan Vasse 7280b972c4 ♻️(frontend) small cleanups in file preview and wopi
Drop a redundant clearTimeout guard, reformat the WopiEditorFrame
props, assert launch_url is defined at render time, and add a comment
clarifying the WOPI page spinner branch.
2026-04-20 15:01:42 +02:00
Nathan Vasse 3030f71189 ♻️(frontend) unify preview messages into a shared component
The error, not-supported, suspicious and WOPI placeholder viewers all
rendered the same "icon + title + description + action" layout with
near-duplicate scss. Extract a PreviewMessage component so styling and
backdrop-close behaviour live in one place, and update the e2e
selectors to match the new markup.
2026-04-20 15:01:33 +02:00
Nathan Vasse 788cdba513 (frontend) add e2e tests for preview backdrop close
Cover the backdrop-click-to-close behavior for both image
and PDF viewers: clicking the backdrop closes the preview,
clicking the content does not, and dragging across the image
viewer does not trigger a close.
2026-04-20 15:01:33 +02:00
Nathan Vasse afb4d2862d (frontend) close file preview on backdrop click
Users can now dismiss the preview by clicking the blurry area
around the content. Each viewer marks its backdrop zones with
a data attribute so the handler in FilesPreview can distinguish
backdrop from content clicks. The image viewer also guards
against accidental close during pan-drag gestures.
2026-04-20 15:01:33 +02:00
Nathan Vasse b69e0fc218 (frontend) add e2e test for image print via hidden iframe
Ensures Print on an image creates a hidden iframe that loads the
same preview URL, waits for the image to decode, and calls
window.print() — all without opening a new tab.
2026-04-20 15:01:33 +02:00
Nathan Vasse 49c0821281 (frontend) print images via browser dialog instead of new tab
Previously clicking Print on an image opened it in a new tab,
leaving the user to trigger the print dialog themselves. Now a
hidden iframe loads the image and calls window.print() directly.
2026-04-20 15:01:33 +02:00
Nathan Vasse 140fb16292 (frontend) improve file preview e2e test stability
Switch the non-printable file test from DOCX to an audio
file so it no longer relies on WOPI (which opens in a new
tab). Wait for the PDF page to render before interacting
with the page input to avoid flaky failures. Remove an
unused variable.
2026-04-20 15:01:33 +02:00
Nathan Vasse 3c46a909e5 ♻️(frontend) remove unused props and CSS in preview viewers
Remove the unused isSidebarOpen prop from PdfControls and
strip unnecessary position/min-height rules from the video
player that were left over from earlier layout iterations.
2026-04-20 15:01:33 +02:00