photoprism/internal/entity
Ömer Duran 6dc0acac13
Settings: Reorganize UI and add an Accessibility section #848 #799 #5429
* Frontend: Add settings for albums, favorites, folders, and media features

* Frontend: Enhance download settings for files and albums #848

* Frontend: Enhance download settings for files and albums #848

* Frontend: Update album download settings to restrict access to super admins

* Frontend: Add album sorting options to settings for super admins

* Frontend: Update download settings visibility

* Frontend: Refine download settings access control for user roles

* Tests: Add album, folder, moment, state, and month order selection options to page-model

* Frontend: Update access control for super admins in settings to include scope check

* Tests: Add new tests for download name and album order settings persistence

* Tests: Implement new tests for default album sort order persistence in settings

* Frontend: Move default album order settings

* Frontend: Add zoom accessibility option in settings #799

* Frontend: Update download name from "Share Identifier" to "Share Friendly" and refine hints for download options in settings

* Tests: Add new acceptance test for disabling album downloads while allowing file downloads in settings

* Frontend: Improve download options layout and hints

* Frontend: Refactor download options condition in settings page

* Frontend: Implement native zoom control in lightbox component

* Settings: Add Collections tab and move Services to its own page #848 #5429

- Add a super-admin Collections tab after Content holding the album
  Download and Sort Order settings; remove those cards from Content.
- Move Services from a settings tab to a dedicated page
  (page/services.vue) with a search toolbar and a Reload / Learn More
  action menu; link it from the navigation settings menu.
- Implement server-side services search (acc_name filter) with tests.
- De-duplicate the picture sort-order options into a shared
  SortOrderOptions builder in options.js.
- Update acceptance/unit tests and page models to match.

* Frontend: Regenerate translation catalogs #848 #5429

Ran `make gettext-extract` to pick up the new settings strings
(Collections, Features, Disable Downloads, and related messages) so
Weblate can translate them. Existing translations are preserved.

* Frontend: Move lightbox viewport-zoom handling into the view helper

- Move the viewport pinch-zoom lock/restore (disableNativeZoom /
  restoreNativeZoom + savedViewportContent) from lightbox.vue onto the
  View singleton in common/view.js.
- Drive it passively from View.apply() via a disableViewportZoom flag in
  the PLightbox case, alongside hideScrollbar / disableScrolling /
  disableNavigationGestures; lightbox.vue no longer references zoom.
- Make save/restore idempotent and extract the viewport constants.
- Cover the helpers and the apply() wiring in view.test.js.

* Settings: Add Accessibility section to General with Reduce Motion #848 #5429

- Move the page-zoom checkbox out of the feature-flags grid into a
  dedicated Accessibility card (super-admin only) with four toggles:
  Open on Hover, Reduce Motion, Hide Scrollbar, and Allow Page Zoom.
- Add a UI.ReduceMotion flag (defaults false) that toggles an
  html.reduce-motion class via config.setReduceMotion and suppresses map
  fly-to animations non-destructively (util.mapAnimateDuration).
- Make Open on Hover apply live by reading shouldOpenOnHover() through a
  reactive computed in the action/auth/lightbox menus; Zoom and Hide
  Scrollbar reload since they are baked into the server-rendered HTML.
- Reword two feature-grid hints to the "pictures" convention and cover
  the new helpers with Go and Vitest tests.

* Frontend: Regenerate translation catalogs #848 #5429

Ran `make gettext-extract` to pick up the new Accessibility settings
strings (Accessibility, Allow Page Zoom, Open on Hover, Hide Scrollbar,
Reduce Motion, and their hints) and the reworded feature-grid hints.
Existing translations are preserved.

---------

Co-authored-by: Michael Mayer <michael@photoprism.app>
2026-07-08 14:11:36 +02:00
..
legacy Links: Use canonical trailing-slash form for website URLs 2026-06-24 17:20:30 +02:00
migrate Links: Use canonical trailing-slash form for website URLs 2026-06-24 17:20:30 +02:00
query Links: Use canonical trailing-slash form for website URLs 2026-06-24 17:20:30 +02:00
schema Cleanup: Fix formatting of Markdown files 2026-02-14 12:10:43 +01:00
search Settings: Reorganize UI and add an Accessibility section #848 #799 #5429 2026-07-08 14:11:36 +02:00
sortby Links: Use canonical trailing-slash form for website URLs 2026-06-24 17:20:30 +02:00
testdata/album Auth: Add CLI command to create access tokens for apps #782 #808 #3943 2024-01-05 16:31:07 +01:00
album.go Events: Refactor entity change notifications for sharing features #1307 2026-06-11 15:40:57 +00:00
album_cache.go Docs: Improve code comments in internal/entity/album*.go 2025-10-02 15:59:38 +02:00
album_cache_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
album_fixtures.go Tests: Adapt fixtures to have valid slugs 2025-08-19 19:44:23 +02:00
album_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
album_test.go Events: Refactor entity change notifications for sharing features #1307 2026-06-11 15:40:57 +00:00
album_user.go Logs: Add package pkg/log/status to provide generic outcome constants 2025-10-21 14:42:05 +02:00
album_yaml.go Entity: Clip album, photo, and folder paths via shared ClipPath #5615 2026-06-03 09:34:40 +00:00
album_yaml_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
auth_client.go Cluster: Add a per-instance node DisplayName field 2026-06-09 07:23:05 +00:00
auth_client_add.go OAuth2: Remove client soft delete and fix client add command #213 #3943 2024-01-29 21:08:01 +01:00
auth_client_add_test.go Auth: Refactor cluster configuration and provisioning API endpoints #98 2025-09-24 08:28:38 +02:00
auth_client_data.go Cluster: Declare per-node group config via instance env/flags 2026-06-12 18:52:54 +00:00
auth_client_fixtures.go Backend: Move unix package to /pkg/time/unix 2024-07-03 09:31:27 +02:00
auth_client_fixtures_test.go Auth: Refactor cluster configuration and provisioning API endpoints #98 2025-09-24 08:28:38 +02:00
auth_client_test.go Cluster: Add a per-instance node DisplayName field 2026-06-09 07:23:05 +00:00
auth_session.go Auth: Store session login_at as NULL when no client IP is set #5707 2026-06-29 23:28:12 +00:00
auth_session_cache.go Logs: Add package pkg/log/status to provide generic outcome constants 2025-10-21 14:42:05 +02:00
auth_session_cache_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
auth_session_client.go Account: Generate app password from the UI #808 #4114 2024-04-08 10:44:43 +02:00
auth_session_client_test.go Auth: Store session login_at as NULL when no client IP is set #5707 2026-06-29 23:28:12 +00:00
auth_session_data.go Cluster: Collect OIDC groups at login for group-based admission 2026-06-12 16:08:38 +00:00
auth_session_data_test.go Cluster: Collect OIDC groups at login for group-based admission 2026-06-12 16:08:38 +00:00
auth_session_delete.go Logs: Add package pkg/log/status to provide generic outcome constants 2025-10-21 14:42:05 +02:00
auth_session_delete_test.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
auth_session_fixtures.go Auth: Store session login_at as NULL when no client IP is set #5707 2026-06-29 23:28:12 +00:00
auth_session_fixtures_test.go Auth: Refactor cluster configuration and provisioning API endpoints #98 2025-09-24 08:28:38 +02:00
auth_session_jwt.go Cluster: Improve usage of canonical terms in code/docs based on glossary 2026-02-23 16:16:29 +01:00
auth_session_jwt_test.go Lint: Resolve gosec findings and add embedding dist benchmark 2026-03-03 17:17:49 +01:00
auth_session_login.go Logs: Add package pkg/log/status to provide generic outcome constants 2025-10-21 14:42:05 +02:00
auth_session_login_test.go Lint: Resolve gosec findings and add embedding dist benchmark 2026-03-03 17:17:49 +01:00
auth_session_report.go Auth: Refactor user management API and CLI commands #98 2023-03-08 23:30:39 +01:00
auth_session_report_test.go Tests: Add unit tests 2023-08-09 15:56:16 +02:00
auth_session_test.go Auth: Gate app passwords behind a settings feature flag #5647 2026-06-09 17:36:24 +00:00
auth_tokens.go Security: Add gosec fixes with shared URL and fs validation helpers 2026-03-03 16:38:41 +01:00
auth_tokens_test.go Tests: Add unit tests 2023-08-09 15:56:16 +02:00
auth_user.go Users: Apply 2FA changes for the cluster service principal 2026-06-29 21:59:48 +00:00
auth_user_add.go OIDC: Improve CLI commands and add AuthIssuer to users and sessions #782 2024-07-09 11:01:59 +02:00
auth_user_cli.go Auth: Revoke derived sessions & gate app passwords by login state #5647 2026-06-15 23:41:01 +00:00
auth_user_default.go Logs: Add package pkg/log/status to provide generic outcome constants 2025-10-21 14:42:05 +02:00
auth_user_details.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
auth_user_details_test.go Lint: Resolve gosec findings and add embedding dist benchmark 2026-03-03 17:17:49 +01:00
auth_user_fixtures.go Tests: Add unit tests #4973 2025-05-02 18:39:40 +02:00
auth_user_fixtures_test.go Auth: Refactor cluster configuration and provisioning API endpoints #98 2025-09-24 08:28:38 +02:00
auth_user_legacy.go Auth: Improve legacy user search command and add tests #98 2022-10-14 02:48:23 +02:00
auth_user_legacy_test.go Auth: Improve legacy user search command and add tests #98 2022-10-14 02:48:23 +02:00
auth_user_report.go Auth: Refactor user management API and CLI commands #98 2023-03-08 23:30:39 +01:00
auth_user_report_test.go Tests: Add unit tests 2023-08-11 14:48:28 +02:00
auth_user_settings.go Users: Set the user UID when creating user settings 2026-05-31 00:23:18 +00:00
auth_user_settings_test.go Users: Set the user UID when creating user settings 2026-05-31 00:23:18 +00:00
auth_user_share.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
auth_user_share_fixtures.go Search: Change time_format to RFC3339 and add "edited" filter #4300 2024-05-27 09:16:21 +02:00
auth_user_share_fixtures_test.go Auth: Refactor cluster configuration and provisioning API endpoints #98 2025-09-24 08:28:38 +02:00
auth_user_share_test.go Tests: Use valid fixture IDs so entity tests pass on MariaDB 2026-05-31 00:23:23 +00:00
auth_user_test.go Users: Apply 2FA changes for the cluster service principal 2026-06-29 21:59:48 +00:00
camera.go Events: UID-only camera/lens/country payloads & people refetch #5663 #1307 2026-06-15 11:37:26 +00:00
camera_cache.go Metadata: Add Camera Make and Model updates via CLI & API #5663 #5656 2026-06-15 09:25:44 +00:00
camera_fixtures.go Search: Change time_format to RFC3339 and add "edited" filter #4300 2024-05-27 09:16:21 +02:00
camera_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
camera_makes.go Metadata: Add Camera Make and Model updates via CLI & API #5663 #5656 2026-06-15 09:25:44 +00:00
camera_models.go Metadata: Add Camera Make and Model updates via CLI & API #5663 #5656 2026-06-15 09:25:44 +00:00
camera_test.go Events: UID-only camera/lens/country payloads & people refetch #5663 #1307 2026-06-15 11:37:26 +00:00
camera_types.go Scans: Add scan detection for epson scanner #5703 2025-06-26 11:42:22 +02:00
camera_types_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
category.go Docs: Update code comments in internal/entity/category.go and details.go 2025-10-02 16:19:57 +02:00
category_fixtures.go Backend: Update label photo count and refactor entity fixtures 2020-05-10 19:43:49 +02:00
category_test.go Code clean-up (make fmt) 2019-12-21 17:24:29 +01:00
cell.go Entity: Fix cell lookups 2026-03-07 11:48:20 +01:00
cell_fixtures.go Tests: Refactor unit tests after uid change #4560 2024-10-21 13:16:06 +02:00
cell_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
cell_test.go Entity: Fix cell lookups 2026-03-07 11:48:20 +01:00
clip.go Entity: Rename Sanitize* to Clip* and string.go to clip.go 2026-06-03 09:50:56 +00:00
clip_test.go Entity: Rename Sanitize* to Clip* and string.go to clip.go 2026-06-03 09:50:56 +00:00
country.go Events: UID-only camera/lens/country payloads & people refetch #5663 #1307 2026-06-15 11:37:26 +00:00
country_cache.go Index: Improve save / update functions, remove orphans 2021-02-06 16:30:30 +01:00
country_fixtures.go Backend: Refactor test fixtures 2020-05-11 14:49:53 +02:00
country_test.go Events: UID-only camera/lens/country payloads & people refetch #5663 #1307 2026-06-15 11:37:26 +00:00
db.go Entity: Store created and updated timestamps with second precision 2026-05-31 01:13:27 +00:00
db_conn.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
db_conn_test.go Auth: Open album share links in the regular user interface #98 #782 2022-10-02 11:38:30 +02:00
deprecated.go Routing: Prefix frontend UI routes with /library #840 #2466 2022-10-15 21:54:11 +02:00
details.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
details_fixtures.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
details_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
details_test.go Tests: Use valid fixture IDs so entity tests pass on MariaDB 2026-05-31 00:23:23 +00:00
duplicate.go Auth: Session and ACL enhancements #98 #1746 2022-09-28 09:01:17 +02:00
duplicate_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
entity.go Links: Use canonical trailing-slash form for website URLs 2026-06-24 17:20:30 +02:00
entity_const.go Assets: Rename example fixtures directory to "samples" 2026-03-06 18:47:33 +01:00
entity_count.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
entity_count_test.go Download: Add Disabled, Originals, MediaRaw & MediaSidecar Flags #2234 2022-04-15 09:42:07 +02:00
entity_counts.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
entity_counts_test.go Entity: Fix nil-DB race in async count/cover updates #5551 2026-05-03 10:49:28 +00:00
entity_errors.go API: Ensure slugs are not empty before saving/creating labels #4761 2025-02-04 03:35:01 +01:00
entity_fixtures.go API: Rename /batch/photos endpoint to /batch/photos/edit #271 2025-05-04 14:09:23 +02:00
entity_init.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
entity_save.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
entity_save_test.go Lint: Resolve gosec findings and add embedding dist benchmark 2026-03-03 17:17:49 +01:00
entity_tables.go Security: Add gosec fixes with shared URL and fs validation helpers 2026-03-03 16:38:41 +01:00
entity_test.go TestMain: Handle "defer" before os.Exit #5330 2025-11-21 18:58:36 +01:00
entity_time.go Auth: Store session login_at as NULL when no client IP is set #5707 2026-06-29 23:28:12 +00:00
entity_time_test.go Auth: Store session login_at as NULL when no client IP is set #5707 2026-06-29 23:28:12 +00:00
entity_update.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
entity_update_test.go Entity: Store created and updated timestamps with second precision 2026-05-31 01:13:27 +00:00
entity_values.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
entity_values_test.go CLI: Add "photoprism vision reset" command to reset metadata #5233 2025-09-29 18:59:52 +02:00
error.go Backend: Call clip.Bytes directly and drop the txt.ClipBytes facade #5638 2026-06-03 09:50:56 +00:00
face.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
face_fixtures.go Search: Change time_format to RFC3339 and add "edited" filter #4300 2024-05-27 09:16:21 +02:00
face_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
face_test.go AI: Rename SampleRadius to ClusterRadius and increase cap to 0.42 #5167 2025-10-26 10:08:59 +01:00
faces.go Backend: Rename /internal/tensorflow to /internal/ai 2024-07-02 08:08:14 +02:00
faces_test.go Security: Use individual preview tokens for each user account #98 2022-10-13 22:11:02 +02:00
file.go Entity: Rename Sanitize* to Clip* and string.go to clip.go 2026-06-03 09:50:56 +00:00
file_fixtures.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
file_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
file_json.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
file_json_test.go People: Add SubjectNames() to file entity #22 2021-09-02 11:12:42 +02:00
file_metadata.go Videos: Refactor File.UpdateVideoInfos() method #926 #3588 #3559 2023-08-18 09:12:38 +02:00
file_share.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
file_share_fixtures.go Routing: Prefix frontend UI routes with /library #840 #2466 2022-10-15 21:54:11 +02:00
file_share_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
file_share_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
file_sync.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
file_sync_fixtures.go Routing: Prefix frontend UI routes with /library #840 #2466 2022-10-15 21:54:11 +02:00
file_sync_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
file_sync_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
file_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
fixtures.go API: Rename /batch/photos endpoint to /batch/photos/edit #271 2025-05-04 14:09:23 +02:00
folder.go Entity: Clip album, photo, and folder paths via shared ClipPath #5615 2026-06-03 09:34:40 +00:00
folder_album_slug.go Folders: Fix slug collision on long ASCII paths #5614 #5615 2026-05-26 02:34:48 +00:00
folder_album_slug_test.go Folders: Fix slug collision on long ASCII paths #5614 #5615 2026-05-26 02:34:48 +00:00
folder_fixtures.go People: Improve face clustering and indexing #22 2021-08-19 21:12:38 +02:00
folder_test.go Folders: Improve slug collision handling for Unicode paths #5366 2026-03-07 11:16:21 +01:00
keyword.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
keyword_cache.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
keyword_cache_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
keyword_fixtures.go Tests: Add fixtures 2022-03-30 18:21:14 +02:00
keyword_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
keyword_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
label.go Events: Refactor entity change notifications for sharing features #1307 2026-06-11 15:40:57 +00:00
label_cache.go Entity: Fix label homophone lookups and photo-label caching #5227 #5238 2026-03-07 11:43:25 +01:00
label_cache_test.go Entity: Fix label homophone lookups and photo-label caching #5227 #5238 2026-03-07 11:43:25 +01:00
label_fixtures.go Tests: Adapt fixtures to have valid slugs 2025-08-19 19:44:23 +02:00
label_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
label_search.go Labels: Reuse renamed label when re-added by previous name #5531 2026-05-04 21:13:57 +00:00
label_search_test.go Labels: Reuse renamed label when re-added by previous name #5531 2026-05-04 21:13:57 +00:00
label_slug.go Backend: Compact verbose comments in entity and photoprism workers 2026-05-15 17:50:08 +00:00
label_slug_test.go Labels: Reuse renamed label when re-added by previous name #5531 2026-05-04 21:13:57 +00:00
label_test.go Events: Refactor entity change notifications for sharing features #1307 2026-06-11 15:40:57 +00:00
lens.go Events: UID-only camera/lens/country payloads & people refetch #5663 #1307 2026-06-15 11:37:26 +00:00
lens_cache.go Metadata: Add Camera Make and Model updates via CLI & API #5663 #5656 2026-06-15 09:25:44 +00:00
lens_fixtures.go Metadata: Add Lens Make and Model updates via CLI & API #5644 #5656 2026-06-15 09:37:32 +02:00
lens_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
lens_models.go Cards View: Improve camera and lens information #2040 #3077 #3816 2023-10-13 11:35:43 +02:00
lens_test.go Events: UID-only camera/lens/country payloads & people refetch #5663 #1307 2026-06-15 11:37:26 +00:00
link.go Logs: Improve log messages in API and share audit paths 2026-03-08 12:53:20 +01:00
link_fixtures.go Security: Add gosec fixes with shared URL and fs validation helpers 2026-03-03 16:38:41 +01:00
link_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
marker.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
marker_fixtures.go Backend: Move crop package to /internal/thumb/crop 2024-07-02 07:45:16 +02:00
marker_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
marker_json.go People: Add face dist to marker API JSON #22 2021-09-23 14:03:35 +02:00
marker_json_test.go People: Add SubjectNames() to file entity #22 2021-09-02 11:12:42 +02:00
marker_test.go Entity: Store created and updated timestamps with second precision 2026-05-31 01:13:27 +00:00
markers.go AI: Do not add portrait or people labels from face markers #5232 2025-10-05 04:37:20 +02:00
markers_test.go AI: Improve the generation, sorting, and filtering of labels #5232 2025-10-02 13:08:52 +02:00
mysql8_test.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
passcode.go Auth: Refine passcode recovery code comparison 2026-06-11 09:37:40 +00:00
passcode_fixtures.go Tests: Add unit tests 2024-07-17 16:38:34 +02:00
passcode_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
passcode_json.go Security: Add gosec fixes with shared URL and fs validation helpers 2026-03-03 16:38:41 +01:00
passcode_json_test.go Tests: Add unit tests #808 #3943 2024-03-27 17:01:17 +01:00
passcode_test.go Auth: Refine passcode recovery code comparison 2026-06-11 09:37:40 +00:00
password.go Entities: Improve Stringer interface implementations for logging #4490 2024-08-29 10:35:18 +02:00
password_fixtures.go Tests: Add unit tests #4973 2025-05-02 18:39:40 +02:00
password_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
password_test.go Tests: Use valid fixture IDs so entity tests pass on MariaDB 2026-05-31 00:23:23 +00:00
photo.go Index: Improve support for metadata in XMP sidecar files #2260 #2828 #5563 2026-06-17 01:53:28 +02:00
photo_album.go Docs: Improve code comments in internal/entity/photo*.go 2025-10-02 15:52:05 +02:00
photo_album_fixtures.go Tests: Fix typo in test fixtures 2025-12-02 11:02:05 +01:00
photo_album_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
photo_album_test.go Tests: Use valid fixture IDs so entity tests pass on MariaDB 2026-05-31 00:23:23 +00:00
photo_caption.go Docs: Improve code comments in internal/entity/photo*.go 2025-10-02 15:52:05 +02:00
photo_caption_test.go Tests: Add unit tests 2025-07-22 13:01:22 +02:00
photo_datetime.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
photo_datetime_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
photo_estimate.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
photo_estimate_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
photo_fixtures.go Tests: Fix typo in test fixtures 2025-12-02 11:02:05 +01:00
photo_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
photo_interface.go Albums: Remove photo from review when adding it to an album #4229 2024-05-09 16:00:53 +02:00
photo_keyword.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
photo_keyword_fixtures.go Correct KeywordID's with extra Zero 2024-10-27 11:13:03 +01:00
photo_keyword_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
photo_label.go Entity: Fix photo-label caching #5227 #5238 2026-03-07 11:48:07 +01:00
photo_label_test.go Tests: Use valid fixture IDs so entity tests pass on MariaDB 2026-05-31 00:23:23 +00:00
photo_location.go Places: Fix minor GPS boundary overshoots in S2 mapping #5445 2026-02-10 17:29:51 +01:00
photo_location_test.go Places: Update entity/photo_location_test.go #5445 2026-02-10 17:36:47 +01:00
photo_merge.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
photo_merge_test.go Lint: Resolve gosec findings and add embedding dist benchmark 2026-03-03 17:17:49 +01:00
photo_optimize.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
photo_optimize_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
photo_quality.go Media: Improve layered TIFF & PSD preview support #5383 2026-03-06 18:14:48 +01:00
photo_quality_test.go Media: Improve layered TIFF & PSD preview support #5383 2026-03-06 18:14:48 +01:00
photo_reset.go CLI: Add "photoprism vision reset" command to reset metadata #5233 2025-09-29 18:59:52 +02:00
photo_reset_test.go CLI: Add "photoprism vision reset" command to reset metadata #5233 2025-09-29 18:59:52 +02:00
photo_scope_test.go Photos: Reduce identifying metadata for shared-only sessions #1307 2026-05-29 08:20:26 +00:00
photo_test.go Tests: Add unit tests for recent metadata, auth, and config changes #5697 2026-06-26 14:12:59 +02:00
photo_title.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
photo_title_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
photo_user.go Logs: Add package pkg/log/status to provide generic outcome constants 2025-10-21 14:42:05 +02:00
photo_vision.go AI: Add IndexedAt time to indicate when a photo was fully indexed #5167 2025-10-07 13:57:01 +02:00
photo_yaml.go Security: Add gosec fixes with shared URL and fs validation helpers 2026-03-03 16:38:41 +01:00
photo_yaml_test.go Tests: Add unit tests #4294 2024-05-28 12:48:24 +02:00
photos.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
photos_test.go API: Add /batch/photos endpoint to get metadata of selected photos #271 2025-05-03 12:43:01 +02:00
place.go Backend: Rename /internal/remote package to /internal/service 2024-07-02 08:00:27 +02:00
place_fixtures.go Tests: Refactor unit tests after uid change #4560 2024-10-21 13:16:06 +02:00
place_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
place_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
reaction.go CLI: Add "photoprism vision reset" command to reset metadata #5233 2025-09-29 18:59:52 +02:00
reaction_fixtures.go Search: Change time_format to RFC3339 and add "edited" filter #4300 2024-05-27 09:16:21 +02:00
reaction_test.go Auth: Rename database tables and delete temporary tables #98 #782 2022-10-02 22:09:02 +02:00
README.md Entity: Document VARBINARY index-prefix limit and byte-exact paths #5614 #5615 2026-06-01 11:42:59 +00:00
service.go Backend: Clip VARBINARY text columns by bytes to avoid write errors #5638 2026-06-03 08:04:40 +00:00
service_fixtures.go Search: Change time_format to RFC3339 and add "edited" filter #4300 2024-05-27 09:16:21 +02:00
service_test.go Entity: Store created and updated timestamps with second precision 2026-05-31 01:13:27 +00:00
src.go Faces: Change search to return all face markers, regardless of src #5167 2025-10-07 20:01:43 +02:00
src_test.go Faces: Change search to return all face markers, regardless of src #5167 2025-10-07 20:01:43 +02:00
string_keyvalue.go People: Show real name instead of uid in logs #1438 #2182 2022-04-04 14:21:43 +02:00
string_map.go Security: Use individual preview tokens for each user account #98 2022-10-13 22:11:02 +02:00
string_map_test.go Security: Use individual preview tokens for each user account #98 2022-10-13 22:11:02 +02:00
subject.go Events: Refactor entity change notifications for sharing features #1307 2026-06-11 15:40:57 +00:00
subject_fixtures.go API: Update Thumb/ThumbSrc for subjects and labels #4151 2025-10-03 23:17:07 +02:00
subject_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
subject_names.go People: Show real name instead of uid in logs #1438 #2182 2022-04-04 14:21:43 +02:00
subject_person.go People: Show real name instead of uid in logs #1438 #2182 2022-04-04 14:21:43 +02:00
subject_person_test.go People: Improve face thumbnails on overview page #22 2021-09-17 18:51:24 +02:00
subject_test.go Events: Refactor entity change notifications for sharing features #1307 2026-06-11 15:40:57 +00:00
subjects.go Revert "People: Add subject cache and show real name in logs #1438 #2182" 2022-04-04 08:54:03 +02:00
subjects_test.go Security: Use individual preview tokens for each user account #98 2022-10-13 22:11:02 +02:00

PhotoPrism — Database Entities

Last Updated: June 1, 2026

Overview

internal/entity holds the GORM models (Photo, File, Album, Label, Face, User, Client, Session, Service, Marker, …), their query and create/update helpers, the test fixtures (*_fixtures.go), and the migration helpers under migrate/. Models map to the database via GORM v1 (github.com/jinzhu/gorm) and are shared by the API, workers, and CLI.

Timestamps

Created and updated timestamps are stored as SQL DATETIME without fractional seconds (DATETIME_PRECISION = 0). To keep in-memory and persisted values in sync, the package sets GORM's timestamp source to second precision in db.go:

gorm.NowFunc = Now // entity.Now() == UTC().Truncate(time.Second)

Time helpers in entity_time.go:

  • UTC() — current time in UTC, full sub-second precision. Use for elapsed-time measurements, not for values that get persisted.
  • Now() — UTC truncated to whole seconds. This is what GORM writes to created_at / updated_at.
  • TimeStamp() — pointer to Now(), for nullable *time.Time columns.
  • Time(s) — parses an RFC 3339 string to a second-precision UTC time, or nil.

Implications:

  • Do not rely on sub-second ordering of persisted timestamps. Two rows created and updated within the same wall-clock second compare equal, so created_at / updated_at cannot disambiguate them. There is no monotonic auto-increment ID on UID-keyed models (e.g. Client), so there is no reliable intra-second tiebreaker — give rows distinct times when ordering must be deterministic.
  • Because both SQLite and MariaDB now receive second-precision values, timestamp behavior is identical across drivers. A timestamp assertion that passes on SQLite will pass on MariaDB.

When a test needs to prove a write advanced a timestamp, prefer one of:

  • Seed the starting value clearly in the past (e.g. Now().Add(-time.Hour)) and assert the new value is greater. This stays meaningful and distinguishes a real bump from a no-op.

  • Compare with Time.Sub() and assert the difference falls in a sane range, rather than a strict Before/After. A same-second save legitimately yields a zero delta:

    elapsed := after.Sub(before)
    assert.GreaterOrEqual(t, elapsed, time.Duration(0))
    assert.Less(t, elapsed, time.Minute)
    

Testing

Tests default to SQLite. To exercise the models against MariaDB (which is stricter and is the production database for some subsystems such as the cluster registry):

mysql < scripts/sql/reset-acceptance.sql
PHOTOPRISM_TEST_DRIVER="mysql" \
PHOTOPRISM_TEST_DSN="root:photoprism@tcp(mariadb:4001)/acceptance?charset=utf8mb4,utf8&collation=utf8mb4_unicode_ci&parseTime=true" \
go test ./internal/entity/... -count=1 -tags="slow,develop"

MariaDB strict mode rejects inserts that SQLite quietly accepts, so a test that only ran on SQLite can fail here:

  • Primary keys must be set. An empty PK ("" UID, zero ID) triggers Error 1364: Field '<col>' doesn't have a default value. Use a valid ID/UID, not a placeholder like "1234".
  • Values must fit the column. Oversized strings give Error 1406: Data too long; out-of-range integers give Error 1264: Out of range value (e.g. photo_id is INT UNSIGNED, max 4294967295).
  • UID format (see pkg/rnd/uid.go): a one-byte prefix + 6 base36 time chars + 9 base36 random, 16 chars total (p… photo, a… album, c… client, u… user, l… label). Reuse existing fixtures for foreign-key safety; use a throwaway but in-range value only where a real reference would overwrite seeded data (e.g. a synthetic photo_id so a Details row does not attach to a real photo).
  • Fixtures live in *_fixtures.go, but some join rows are created indirectly from a parent fixture's embedded slice (e.g. a photos_labels row from a Photo fixture's Labels). Verify a combination is free against the seeded database, not just the fixtures file.
  • List-style global queries (WHERE … <> '' with no per-test scope) are not isolated on the shared acceptance database: rows from other tests in the same run leak in, so a len(list) == N assertion that holds on a per-test SQLite file can fail on MariaDB.

Collation & Emoji

MariaDB's utf8mb4_unicode_ci assigns most emoji the same collation weight, so an SQL =, <>, or LIKE on a utf8mb4 column treats distinct emoji as equal (e.g. test/🪞 matches test/🎃). SQLite compares text byte-exact, so this only reproduces on MariaDB.

  • utf8mb4 columns that collapse: albums.album_title, display/name text (*_name, *_title).
  • VARBINARY columns that stay byte-exact: albums.album_slug, albums.album_filter, albums.album_path, photos.photo_path, and every *_uid. A utf8mb4 column compared against a VARBINARY column is byte-exact (the binary operand wins).

The durable fix for an identity/path column is to make it VARBINARYalbum_path is VARBINARY(1024) so it matches photos.photo_path and album_path = ? lookups are byte-exact at the database. Where a utf8mb4 column must stay, keep the SQL but re-verify the match byte-exact in Go before accepting it (see FindFolderAlbum / findFolderAlbumByPath, whose Go re-check is retained as defense-in-depth even now that album_path is VARBINARY). For self-join SQL where a Go re-check is awkward, HEX(col) = HEX(col) compares byte-exact on both MariaDB and SQLite. Legacy folder slugs drop emoji entirely (slug.Make("ins/🪞") == "ins") and long paths truncate to ClipSlug runes, so distinct folders can still collide on album_slug; folder albums are therefore deduplicated by album_filter (the byte-exact serialized path), not by slug (see query.RemoveDuplicateMoments).

VARBINARY Index Prefix Limit

InnoDB caps an index key prefix at 767 bytes on the COMPACT/REDUNDANT row formats, and only allows up to 3072 bytes on DYNAMIC/COMPRESSED. On a VARBINARY column the prefix is counted in bytes (on utf8mb4 it is counted in characters, i.e. up to 4 bytes each), so converting a long text column to VARBINARY can push an existing prefix index over the limit on older or non-DYNAMIC installs. Keep prefix indexes on long VARBINARY path/filter columns at ≤ 767 bytes; the project convention is 512 (albums.album_filter(512), albums.album_path(512)). A prefix index only narrows candidate rows — the full-column comparison stays exact — so a shorter prefix costs nothing for correctness.