Compare commits

...
Author SHA1 Message Date
Mads Rasmussen 5d2b902baf Add entity context per table row 2026-02-12 09:39:33 +01:00
Andy ButlandandGitHub 74858e2c44 Repositories: Fix GetAllContentTypeIds query on content type to generate correct SQL (#21612)
* Fixes the failing repository method ContentTypeRepository.GetAllContentTypeIds.

* Revert syntax change.
2026-02-12 06:40:12 +01:00
Niels Lyngsø 0b2516050f add data-marks 2026-02-11 21:44:34 +01:00
a6c363001c Chore: Hide generated files from GitHub PR diffs (#21713)
Mark hey-api generated client code and OpenApi.json as linguist-generated
so they are collapsed by default in GitHub diffs and excluded from
language statistics.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 12:36:54 +00:00
Mads Rasmussen 4286daa4b5 Lazy-init picker modal route 2026-02-11 13:12:51 +01:00
Niels Lyngsø e194776103 Merge branch 'release/17.2'
# Conflicts:
#	src/Umbraco.Web.UI.Client/package.json
#	src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts
#	version.json
2026-02-11 12:51:59 +01:00
551865b79e Entity Data Picker: Register non-editor manifests statically (#21721)
fix(web): register entity data picker non-editor manifests statically

The entity data picker's picker infrastructure manifests (collection menu,
item, search, tree) were only registered dynamically via the entry point,
meaning they were unavailable until a picker data source was detected.
Split the registration so these manifests are registered statically through
the main property-editors manifest tree, while only property editor manifests
remain dynamically registered.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 11:45:24 +00:00
028e9affd9 Long Running Operations: Increase type column length and handle rebuilds of Examine indexes with long names (closes #21666) (#21715)
* Update length of type column in LongRunningOperation

* Adding truncation

* Update src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Used constants.
Handled case where long strings with the same first 200 characters could end up clashing (very unlikely, but we can be defensive).
Introduced a TruncateWithUniqueHash extension method to support this.

* Reverted comment removal.

* Rename migration class.

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-02-11 11:36:57 +00:00
b1ef0153f3 Block Workspace: Inline Editing Workspace to awaits all Content Type compositions before setting initial active tab (#21719)
* fix contentTypeLoaded reaction in Block Workspace without a router

* Update src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-content-no-router.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* revert check with todo comment

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-11 11:25:45 +00:00
114dc9bc91 API Docs: Fix and optimize DocFX API documentation pipeline (#21707)
* Use dotnet tool instead

* Uses pipeline build artifacts to reduce compilation time

* Fixed name

* Remove --noRestore

* Move DocFX metadata generation to Build stage

* Updated to use dlls

* Docs: Fix DocFX CSS reference for newer DocFX version

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Added conditions for generating DocFX metadata

* use glob pattern for DLLs

* Move DocFX to Build_Docs stage with separate DLLs artifac

* Fixes based on comments

* Undo commented out Upload C# Docs job

* Removed Build_Docs from Nuget Release so our docs isnt blocking

* Added dependsOn so Upload_API_Docs is only done when Build_Docs are finished

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-11 10:00:07 +01:00
Niels LyngsøGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>nielslyngsoe
a1bcabf44e (fixes #21178) (#21672)
* Initial plan

* Add tab badges for validation errors in block workspace

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

* Build successful - frontend changes compiled

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

* remove double naming

* remove double naming in no router block workspace

* fixing code

* debugger

* binding view contexts

* inline mode binding

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>
2026-02-10 14:29:38 +00:00
Andy ButlandandGitHub f3595a8ae6 User Management: Avoid discard changes dialog after enabling/disabling a user (closes #19019) (#21702)
Avoid discard changes modal when enabling or disabling a user.
2026-02-10 14:17:23 +00:00
Niels LyngsøandGitHub bfee99c5b0 Block Workspace: Tabs navigation, Cherry-pick from #21672 (#21693)
* cherry-pick from #21672

* cherry pick tab rendering to handle one more case

* move the root route down for it to stay an empty path.

* Revert empty root path commit

* fullPath for root includes 'root'

* revert claude settings commit

* refactor accordingly to feedback
2026-02-10 14:17:14 +00:00
Andy Butland ed963b2dbb Bumped version to 17.2.0-rc2. 2026-02-10 15:16:52 +01:00
Niels Lyngsø fc43d5316f Merge branch 'release/17.2' 2026-02-10 14:42:35 +01:00
Niels LyngsøandGitHub 79dfe76286 Block Workspace: renme root-tab to 'generic' (#21699)
* renme to generic

* only use label
2026-02-10 13:31:01 +00:00
Andy ButlandandGitHub ecb4ecdc1a Developer Experience: Clarify obsoletion warning messages (#21695)
* Updates all obsoletion messages to use a softer expression of intent rather than stating explicit removal in a particular version.

* Code review feedback.

* Updates from code review.
2026-02-10 11:47:49 +00:00
Laura NetoandGitHub 78d6229b8e Task: Regenerate OpenAPI definition and backoffice client SDK (#21694)
chore(api): regenerate OpenApi.json and backoffice client SDK

The OpenAPI definition and backoffice TypeScript client were out of
sync with recent Management API changes already on main. Regenerated
to bring them up to date.
2026-02-10 10:01:46 +00:00
e035c8541a Developer Experience: Clarify nullability for BackOfficeAuthenticationBuilder.SchemeForBackOffice (closes #21689) (#21690)
* #21689: signature fix

* #21689: namespace fix

* Update src/Umbraco.Cms.Api.Management/Security/BackOfficeAuthenticationBuilder.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-02-10 09:47:45 +01:00
b5ec111351 Docs: Add breaking changes avoidance policy to CLAUDE.md (#21683)
* Updated Claude memory files with details on how to handle binary breaking changes.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-10 09:20:37 +01:00
6db77ec155 Security settings: Add UserPassword and MemberPassword properties to SecuritySettings for appsettings.json IntelliSense (#21681)
* Fix suggestion appsettings issue

* Add todo comment to remove UserPasswordConfigurationSettings and MemberPasswordConfigurationSettings

* Apply suggestions from code review

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-02-10 08:00:53 +00:00
Niels LyngsøandGitHub eea17292c8 Extension Slot: JS Docs update (#21645)
* remove not existing script from Claude settings

* append JS docs for extension-slot and extension-with-api-slot
2026-02-09 15:07:03 +00:00
Niels LyngsøandGitHub 21a17a4d74 Block Grid Editor: improved extension initialization for Inline Mode Blocks (#21661)
improve extension initialization life cycle
2026-02-09 15:54:14 +01:00
c4d4c17d63 Enable usage of umb-input-entity-data element without registering a propertyEditorDataSource. (#21686)
* resolve Property Editor Data Source apis in the Property Editor UI

* load input-entity-data globally

* Update src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/property-editor/entity-data-picker-property-editor-ui.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Create/teardown data-source API on alias change

* Initialize _dataSourceConfig inside extractor

* Allow clearing entity data picker data source

---------

Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-09 15:50:34 +01:00
Niels LyngsøandGitHub 49be5b6e9b Block Grid Editor: adjust styling of default Grid Block views (Fixes #16375) (#21685)
adjust styling of default Grid Block views
2026-02-09 13:19:08 +00:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>AndyButlandOwain WilliamsAndy ButlandCopilot
22f7cefcdf Add endpoint descriptions to all Management API controllers (#20690)
* Initial plan

* Add EndpointSummary and EndpointDescription to all Management API endpoints

Co-authored-by: AndyButland <1993459+AndyButland@users.noreply.github.com>

* Add missing using directive for EndpointSummary attributes

Co-authored-by: AndyButland <1993459+AndyButland@users.noreply.github.com>

* Fix English compound words and add missing Webhook endpoint descriptions

Co-authored-by: AndyButland <1993459+AndyButland@users.noreply.github.com>

* Add endpoint descriptions to remaining 359 Management API endpoints

Co-authored-by: AndyButland <1993459+AndyButland@users.noreply.github.com>

* Add final endpoint descriptions - 434 of 441 endpoints now documented

Co-authored-by: AndyButland <1993459+AndyButland@users.noreply.github.com>

* Fix concatenated words, duplicate attributes, and missing using statements

Co-authored-by: AndyButland <1993459+AndyButland@users.noreply.github.com>

* Fix using statement alphabetical order and remove all duplicate attributes

Co-authored-by: AndyButland <1993459+AndyButland@users.noreply.github.com>

* Revert "Fix using statement alphabetical order and remove all duplicate attributes"

Co-authored-by: AndyButland <1993459+AndyButland@users.noreply.github.com>

* Fix using statement order and remove duplicate attributes

Co-authored-by: AndyButland <1993459+AndyButland@users.noreply.github.com>

* Add endpoint descriptions to final 7 Management API endpoints

Co-authored-by: AndyButland <1993459+AndyButland@users.noreply.github.com>

* Simplify endpoint descriptions - remove redundant phrases and fix compound words

Co-authored-by: AndyButland <1993459+AndyButland@users.noreply.github.com>

* Revert Create and Update controller description simplifications

Co-authored-by: AndyButland <1993459+AndyButland@users.noreply.github.com>

* Updated to Controllers/Document controllers (#20917)

* [ADD] Cultures Summary and Description

* tidy up from previous merge issue

* update wording on notifications controller

* UpdateDomainsController wording update

* Update the UpdateNotificationsController description / summary

* UpdatePublicAccessDocumentController update

* naming Document Blueprint as referenced in the Docs

* renaming Document Blueprint to match Group Name

* Document Blueprint updates

* Document Type Folder controllers

* Document Type Item endpoints

* Aligned casing.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix compound words, incorrect pluralisiation and other endpoint documentation issues.

* Add descriptions to controllers missing them.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: AndyButland <1993459+AndyButland@users.noreply.github.com>
Co-authored-by: Owain Williams <owaingdwilliams@gmail.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-09 12:35:40 +01:00
56b657d389 Content Type Properties: make content type property responsive (#21559)
make content type property responsive

Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
2026-02-09 09:45:06 +00:00
899beaa9d1 Initial update to a couple of Management API endpoints (#20549)
* Update README.md with information about the forum

Making a small change to the Readme to signpost the Forum now that it's the place to go for help/questions

* [TASK] Initial update of some Management API endpoints

* Update src/Umbraco.Cms.Api.Management/Controllers/Culture/AllCultureController.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/UpdatePreventCleanupDocumentVersionController.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/ByKeyDocumentVersionController.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/AllDocumentVersionController.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* [UPDATE] Small grammer update

* Update src/Umbraco.Cms.Api.Management/Controllers/Culture/AllCultureController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/Culture/AllCultureController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/AllDocumentVersionController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/AllDocumentVersionController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/ByKeyDocumentVersionController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/ByKeyDocumentVersionController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/RollbackDocumentVersionController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/RollbackDocumentVersionController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/UpdatePreventCleanupDocumentVersionController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/UpdatePreventCleanupDocumentVersionController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* [WIP] Data Types

* [WIP] Update ByKey and Configuration DataTypes

* [AMEND] Add additional Summary and Descritpion to API endpoints on DataTypes

* [AMEND] Filter / Folder / Item / References / Tree API update

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/ConfigurationDataTypeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/CopyDataTypeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/CopyDataTypeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/DeleteDataTypeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Tree/RootDataTypeTreeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Tree/SiblingsDataTypeTreeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Tree/SiblingsDataTypeTreeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/UpdateDataTypeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/UpdateDataTypeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/DeleteDataTypeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Filter/FilterDataTypeFilterController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Folder/ByKeyDataTypeFolderController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Tree/ChildrenDataTypeTreeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Folder/ByKeyDataTypeFolderController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Folder/DeleteDataTypeFolderController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Folder/UpdateDataTypeFolderController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Folder/DeleteDataTypeFolderController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Tree/ChildrenDataTypeTreeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Folder/UpdateDataTypeFolderController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Folder/CreateDataTypeFolderController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Item/ItemDatatypeItemController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Item/ItemDatatypeItemController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Tree/AncestorsDataTypeTreeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Item/SearchDataTypeItemController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/MoveDataTypeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/MoveDataTypeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/References/ReferencedByDataTypeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Tree/AncestorsDataTypeTreeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/References/ReferencedByDataTypeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/DataType/Tree/RootDataTypeTreeController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

---------

Co-authored-by: Owain Williams <ow@initials.co.uk>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-02-09 09:39:45 +01:00
4070158f8c Preset property value: acceptance test (#21498)
* Add tests for property presest value

* save method'

* update accepntance test

* ad timeout to preset value test

* Updated yaml file to copy all .cs files but still keep folder structure

* remove timeout from preset value test

* adding time wait to tests

* adding more timeout

* Adding slow test

* update test

* Format code

* Format code and add more afterEach step to clean language

* Remove test.slow() as it is unnecessary

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Nhu Dinh <hnd@umbraco.dk>
Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>
2026-02-09 08:09:10 +00:00
f5f6ee9bb3 Tiptap RTE: Add clipboard copy/paste support for RTE blocks (#21604)
* Localize "Copy to clipboard" button label

in other Block editor components.

* Adds "Copy to clipboard" action to RTE Block component

* Check if Clipboard Property Context is available

If not, don't show the action button.

* Register "Clipboard Property Context" for Tiptap RTE

we can't make this generic for all RTEs,
since it is bound to the property-editor UI alias.

* Implemented RTE Block's `copyToClipboard()` method

* Added Clipboard Property Value Translators

for Tiptap RTE Blocks.

* Block RTE: fix clipboard paste data structure mismatch

Change paste translator to output UmbPropertyEditorRteValueType (with
markup and blocks) instead of UmbBlockRteValueModel (flat structure).
This ensures the cloner receives the correct type and can properly
regenerate content keys.

Also optimize the cloner to skip DOM parsing when markup is empty,
which is always the case for clipboard paste operations.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* TipTap: debounce block updates to prevent race condition

Add debounceTime to the contents observable to batch rapid emissions
when pasting multiple blocks from clipboard. This prevents the
#updateBlocks method from being called multiple times in quick
succession.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Block RTE: address code review feedback

- Add missing await on insert() and insertFromRtePropertyValues()
- Remove redundant optional chaining on blockContentTypes.every()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-02-09 07:09:31 +00:00
Niels LyngsøandGitHub 145f054c2e Block Editor: UX Flow when creating one block / inline editing (#20836)
fix create ux-flow
2026-02-07 09:34:14 +01:00
023e00a975 21599: Removed the icon from redirect URL dashboard as per request in… (#21665)
21599: Removed the icon from redirect URL dashboard as per request in the issue discussion

Co-authored-by: Pasang Tamang <45009265+pasangtamang@users.noreply.github.com>
2026-02-06 19:19:03 +01:00
Niels LyngsøandGitHub 89ff306db4 Block Editors: Sync Validation Messages when in Inline Mode (Fixes #21518) (#21669)
* setup auto report for blocks validation in inline mode

* Single + grid implementation
2026-02-06 18:56:57 +01:00
32dbaf5f57 Collection: Add description support to default collection item card and ref elements (#21654)
* Add optional description to collection items

* Add optional description support to default item ref

---------

Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2026-02-06 17:09:35 +00:00
Mads RasmussenandGitHub 9892b35373 Collection: Fix undefined take in collection filter and hide pagination when all items are shown (#21662)
* Hide pagination when all items are shown

* Add a fallback page size
2026-02-06 17:34:21 +01:00
Mads RasmussenandGitHub bc53a73039 Extension Insights: Fix collection by avoiding shallow copy of manifests (#21660)
* fix problem with shallow copy because of js module in object

* nest manifest data
2026-02-06 17:32:27 +01:00
Nicklas KramerandGitHub 3527c01a70 Media Item Search: Fixing missing attribute for constructor (#21659)
Adding missing attribute to constructor
2026-02-06 12:19:28 +01:00
087cc8db51 Backoffice NPM: use looser peerDependencies version ranges for plugin compatibility (#21644)
* feat(backoffice): use looser version ranges for peerDependencies

Convert hoisted dependencies to peerDependencies with more permissive version
ranges that allow plugin developers to use different versions without npm conflicts.

Version range strategy:
- Pre-release (0.x.y): >=X.Y.Z <1.0.0
  Example: @hey-api/openapi-ts 0.85.0 → >=0.85.0 <1.0.0
  Allows plugins to use 0.85.0, 0.91.1, 0.99.99 without conflicts

- Stable (major.x.y where major ≥1): major.x.x
  Example: lit ^3.3.1 → 3.x.x
  Allows any patch/minor within the major version

This allows plugin developers to:
- Use @hey-api/openapi-ts 0.91.1 while backoffice uses 0.85.0
- Install compatible deduplicated versions when available
- Override versions when needed for their specific use case

Types remain available from peerDependencies (automatically installed by npm 7+).
When @hey-api reaches 1.0.0, the range will automatically become ^1.0.0.

https://claude.ai/code/session_01CBpcwXYZjzexKkM9Cf57Kb

* refactor(backoffice): use semver package for version parsing in cleanse script

Replace regex-based version parsing with the semver package used by npm itself.
This ensures version parsing is consistent with npm's own semver handling and is
more robust for edge cases.

Also update the version range logic to be more explicit and correct:
- Pre-release (0.x.y): >=X.Y.Z <1.0.0
- Stable (1+.x.y): >=X.Y.Z <NEXT_MAJOR.0.0

This ensures plugin developers use at least the tested version and prevents
accidental downgrades to incompatible minor versions.

https://claude.ai/code/session_01CBpcwXYZjzexKkM9Cf57Kb

* chore: formats file

* chore: lockfile

* fix(backoffice): use semver.minVersion to parse version ranges

Fix parsing of version ranges like ^0.85.0 by using semver.minVersion() instead
of semver.parse(). The parse() function only handles exact versions, while
minVersion() extracts the minimum version from a range.

Example transformations:
- ^0.85.0 → 0.85.0 → >=0.85.0 <1.0.0
- ^3.3.1 → 3.3.1 → >=3.3.1 <4.0.0

https://claude.ai/code/session_01CBpcwXYZjzexKkM9Cf57Kb

* refactor(backoffice): keep caret ranges for stable package versions

Optimize the version range conversion logic:

- Stable versions (major ≥ 1) with caret (e.g., ^3.3.1): Keep as-is
  The caret already implements the desired range: >=3.3.1 <4.0.0

- Pre-release versions (0.x.y): Convert to explicit range
  ^0.85.0 → >=0.85.0 <1.0.0 (caret only allows 0.85.z, not 0.91.z)

- Exact versions (e.g., 3.16.0): Convert to range
  3.16.0 → >=3.16.0 <4.0.0

This simplifies the published package.json while maintaining the same semantics
and is more explicit about the intent.

Examples of published peerDependencies:
- lit: ^3.3.1 (unchanged, already has correct range)
- rxjs: ^7.8.2 (unchanged)
- @hey-api/openapi-ts: >=0.85.0 <1.0.0 (converted from ^0.85.0)
- @tiptap/core: >=3.16.0 <4.0.0 (converted from 3.16.0)

https://claude.ai/code/session_01CBpcwXYZjzexKkM9Cf57Kb

* refactor(backoffice): use caret for stable exact versions

Simplify stable exact versions (e.g., 3.16.0) by adding a caret prefix (^3.16.0)
instead of explicit range (>=3.16.0 <4.0.0). Both are semantically identical for
stable versions but caret is more concise and conventional.

Updated version range logic:
- Stable with caret (^3.3.1): Keep as-is
- Pre-release with caret (^0.85.0): Convert to >=0.85.0 <1.0.0
- Stable exact version (3.16.0): Convert to ^3.16.0

Examples of published peerDependencies:
- lit: ^3.3.1
- rxjs: ^7.8.2
- @hey-api/openapi-ts: >=0.85.0 <1.0.0
- @tiptap/core: ^3.16.0 (now with caret)

https://claude.ai/code/session_01CBpcwXYZjzexKkM9Cf57Kb

* refactor(backoffice): ensure all pre-release versions get explicit range

Reorganize version conversion logic for clarity:

1. All pre-release (0.x.y) versions → explicit range: >=X.Y.Z <1.0.0
   - Examples: ^0.85.0 → >=0.85.0 <1.0.0, 0.85.0 → >=0.85.0 <1.0.0

2. Stable versions with caret (^3.3.1) → keep as-is

3. Stable versions exact (3.16.0) → add caret: ^3.16.0

This ensures pre-release version constraints are properly loosened for plugins
while maintaining stability guarantees.

https://claude.ai/code/session_01CBpcwXYZjzexKkM9Cf57Kb

* treat all modifiers the same

* docs: add backoffice npm package structure documentation

Add comprehensive section to CLAUDE.md explaining:
- Backoffice npm package architecture and plugin model
- Dependency hoisting strategy and version range logic
- How pre-release versions are handled vs stable versions
- Importmap as single source of truth for runtime
- Plugin development implications and expectations

Clarifies that while npm versions constrain types, the actual runtime comes
from importmap, and plugin developers should declare explicit dependencies
rather than relying on transitive deps.

https://claude.ai/code/session_01CBpcwXYZjzexKkM9Cf57Kb

* docs: add npm package publishing guide to backoffice CLAUDE.md

Add comprehensive section explaining:
- Why backoffice uses peerDependencies (importmap provides runtime)
- Dependency hoisting strategy and version range conversion logic
- How pre-release versions are handled differently from stable versions
- Example published peerDependencies showing final output
- Plugin developer guide with dos and don'ts
- Key files involved in the publishing process

Provides clear guidance for plugin developers on version compatibility
and explains the importmap-as-single-source-of-truth architecture.

https://claude.ai/code/session_01CBpcwXYZjzexKkM9Cf57Kb

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-05 11:25:30 +00:00
Niels LyngsøandGitHub 004569146e Block Catalogue: adapt size to the amount of BlockTypes (#21619)
adaptive Block Catalogue size based on amount of BlockType
2026-02-05 11:48:26 +01:00
Niels Lyngsø dfb46be645 remove not existing script from Claude settings 2026-02-05 11:11:40 +01:00
dfd915c6e3 Block List/Grid: Add "Clear" property action. (#21436)
* Enable Clear action for BlockList and BlockGrid editors.

* Add has-value condition to property action manifests.

* Clear manager state when value is undefined.

* Add clear kind property action.

* Register clear property action in block list/grid.

* Remove has value condition.

* Remove unused import.

* Fix missing export.

* move clear controller into kinds/clear folder

* Update manifests.ts

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
2026-02-05 10:10:04 +00:00
Andy Butland ccc815d711 Merge branch 'release/17.2' 2026-02-05 08:23:39 +01:00
Andy ButlandandLan Nguyen Thuy 13fb4a4e09 OEmbed providers: Tighten up resource URL matching for providers (#21583)
* edit regex for oembed flickr

* Apply stricter matching with domain to all embed providers, and validate with unit tests.

* Resolved warnings and added further unit tests.

* Further tightened the URL matching regex for two providers.

* Add regex caching to OEmbedService and unit tests to verify behaviour.

* Restore flickr short URL domain.

* Use https in requests to oembed providers.

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-02-05 07:14:43 +01:00
cd5ef5ff4d OEmbed providers: Tighten up resource URL matching for providers (#21583)
* edit regex for oembed flickr

* Apply stricter matching with domain to all embed providers, and validate with unit tests.

* Resolved warnings and added further unit tests.

* Further tightened the URL matching regex for two providers.

* Add regex caching to OEmbedService and unit tests to verify behaviour.

* Restore flickr short URL domain.

* Use https in requests to oembed providers.

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-02-05 06:00:49 +00:00
35944c6cca External Login Providers: Fixes deleting a member with more than 1 external login provider causes an error. (#21625)
* Correcting flawed sql statement.

* Integration tests for the fix

* Resolve warnings in MemberServiceTests.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-02-04 20:57:23 +00:00
3a965677e0 Tests: Fix permission controller tests to use correct entity keys (#21613)
* Tests: Fix permission controller tests to use correct entity keys

The GetDocumentPermissionsCurrentUserController, GetMediaPermissionsCurrentUserController,
and GetPermissionsCurrentUserController tests were incorrectly creating user data and
passing user keys to the GetPermissions method. These controllers expect document/media
keys, not user keys.

Updated the tests to create the appropriate content/media types and entities, then pass
the correct keys to properly test the permission endpoints.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Andreas Zerbst <73799582+andr317c@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-04 18:23:35 +00:00
9b99d36cbf Picker: Add pagination to search results (#21593)
* add paging UI to picker search results

* implement paging in collection picker data source example

* Hide pagination when all items loaded

* Use paging object for search requests

* Forward paging params in server search queries

* Set default page size in PickerSearchManager

* Use args.paging for skip/take in search

* Update src/Umbraco.Web.UI.Client/src/packages/core/picker/search/picker-search-result.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Reset pagination page when updating query

* dim the box while searching

* Delay loader appearance with fade-in

* Track executed search query and use in results to prevent UI flickering when entering in the search field

* Skip update when dataType is undefined

* Cancel tree loads on context destroy

* fix pagination labels

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-02-04 10:53:44 +01:00
af359e9f40 Code Documentation: Add XML documentation to all public members in Umbraco.Core (#21471)
* Umbraco.Core: add XML documentation to all public members

Add comprehensive XML documentation comments to all public classes,
interfaces, methods, properties, constructors, and enums in Umbraco.Core
to resolve SA1600 StyleCop warnings.

- Document ~2,500+ files across all folders (Services, Models,
  Notifications, Configuration, Cache, etc.)
- Use <summary>, <param>, <returns>, <remarks> tags as appropriate
- Apply <inheritdoc/> for interface implementations
- Use <see cref="..."/> for type references
- Preserve all existing comments

This eliminates approximately 15,500 SA1600 warnings from the project.

* Revert any code changes in the PR.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-02-04 09:35:20 +00:00
2917e5be97 Routing: Fix URL aliases not stored for variant content with shared alias property in DocumentUrlAliasService (#21571)
* Fix issue where URL aliases on variant content with a shared property were not being recorded.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-04 09:54:20 +01:00
Niels LyngsøandGitHub 1ab1311a90 Duplicate to: switch to split icon (#21616)
switch to split icon, from 'enter', to make distinguishable from move
2026-02-04 09:02:29 +01:00
bd3d2e1a3f Tiptap RTE: Add delete action support for RTE blocks (#21615)
Block RTE: Add delete action with undo support

Adds a delete button to RTE block entries that removes blocks from
both the editor HTML and the block manager data. Implements an
HTML-first deletion approach that enables Ctrl+Z undo support by
leveraging the existing _filterUnusedBlocks mechanism.

- Add delete button to block-rte-entry action bar
- Add pendingDeletions state to manager for HTML-first deletion flow
- Modify entries context to use pending deletion mechanism
- Add Tiptap API observer to process pending deletions
- Remove blocks from editor via ProseMirror transactions

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 09:00:21 +01:00
3a1f308e9c Updates Umbraco templates, removes framework choice, makes LTS version a wildcard (#21430)
* Updates UmbracoProject template

Removes the framework choice from the template configuration as it's not used and was out of date.

Updates the LTS version to a wildcard to allow minor version updates and mean this doesn't need to be updated all the time!

Updates the description in the dotnet version generated property

* Removes unnecessary build flag

* Remove Custom Version symbol

It doesn't show up in the template anyway and has been marked as obsolete

* Remove framework from Extension template also as not used

* fix: update dotnet new syntax in pipeline

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: NguyenThuyLan <116753400+NguyenThuyLan@users.noreply.github.com>
2026-02-04 12:56:57 +07:00
f9abf0ad18 Management API: Add endpoints, service and repository methods for retrieving the allowed parents for content types (#21586)
* Document Types returns a list of allowed parent keys

* Media types included

* Minor fixes to namespace etc.

* Tests

* Fixing breaking change

* Correcting requested changes

* Corrected requested changes

* Removed unnecessary usings, aligned naming between service, repository and tests.
Add a new status for the default implementation (NotImplemented felt more correct than NotFound).
Updated inheritance in service layer so we maintain the NotFound behaviour for member types.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-02-03 15:36:08 +01:00
b99547db50 Documents: Remove deprecated entityType from property values (closes #21567) (#21609)
Documents: Remove deprecated entityType from property values

The entityType property on property values was causing "Unsaved Changes"
modal to appear after saving documents with RTE blocks. This occurred
because the server data source added entityType when reading, but
setPropertyValue did not preserve it when updating values.

Since entityType on UmbElementValueModel is deprecated and marked for
removal in v18, the cleanest fix is to stop adding it in the server
data source mapping.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 14:18:10 +01:00
3ab7a03254 Content Editor: Fix display of validation hint badge on tabs (#21595)
* Fix display of validation hint related to a tab.

* Update position of the badge.

* Change position for last tab.

---------

Co-authored-by: engjlr <enl@umbraco.dk>
Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
2026-02-03 10:13:50 +00:00
Niels LyngsøandGitHub aa334ad3bc MCP: Basic setup for MCP in sourcecode (#21061)
Basic setup for MCP
2026-02-03 10:59:04 +01:00
dependabot[bot]andJacob Overgaard 21ece43091 Bump lodash
Bumps the npm_and_yarn group with 1 update in the /tests/Umbraco.Tests.AcceptanceTest directory: [lodash](https://github.com/lodash/lodash).


Updates `lodash` from 4.17.21 to 4.17.23
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 09:26:03 +01:00
Andy ButlandandGitHub 9915fd3cc5 Media Types: Add public constant for Folder media type GUID (#21597)
* Add and use a constant for the folder media type GUID identifier.

* Use defined constant and avoid lookup for folder media type when searching for media items.
2026-02-03 09:16:11 +01:00
32e7b13944 Performance: Implement key-based caching for data type and template repositories (#21280)
* Add failing tests illustrating the lack of data type caching by key.

* Implement cache by key in data type repository.

* Apply same for template repository look-ups by key.

* Add tests verifying that content types are already cached by Id and key.

* Use correct default for creator Id in data type builder for tests.

* Use non-obsolete constructor in test.

* Ensured a deleted data type or template is cleared from the by key cache.

* Add IReadRepository implementations

* Utilize by-key repo access in service layers

* Fix test

* Safeguard against potential null reference exception

---------

Co-authored-by: kjac <kja@umbraco.dk>
2026-02-03 08:25:52 +01:00
50d089c2f9 Repositories: Quote table, column and alias names (closes #21451) (#21577)
* quote table, column and alias names with SqlSyntaxProvider methods in raw sql

* refactoring private methods into new file as internal methods,
refactor new extensions into another file

* refactor GetAlias method

* Double check the change

* improve code health

* change new static classes into public static partial class NPocoSqlExtensions

* resolve some Copilot review suggestions

* revert Copilot suggestion because it decreases code health

* revert test

* revert refactoring for CodeScene

* delete obsolete Test

* remove new methods and updates, which are not relevat for this PR

* prepare for additional states in the future

* don't mix string building methods

* fix SQL injection danger

* fix test for reverted methods

* another SqlSyntax issue

* Add additional unit and integration tests verifying the refactorings made in the PR.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-02-03 07:36:57 +01:00
Nhu DinhandGitHub df37ae9839 E2E: QA Bumped version of test helper to fix the failing tests (#21596) 2026-02-02 21:07:20 +07:00
769cd808f1 TipTap: Avoid empty target attribute on links (#21572)
* Remove the default empty target tag for links, so the target attribute is only output when it has a value.

* Tiptap Link extensions: defaults `target` value to `null`

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
2026-02-02 12:42:02 +00:00
Jacob OvergaardandGitHub 6aeffe7a6e build(deps): bumps @umbraco-ui/uui to 1.17.0-rc.5 (#21569) 2026-02-02 12:40:30 +00:00
Andy ButlandandGitHub 574c9bed6c Content Types: Fix deletion of properties without containers (closes #21566) (#21585)
Fix issue with deleting properties that are not in containers.
2026-01-30 14:41:09 +01:00
Niels LyngsøandGitHub 8f0555182c Content Type Designer: transfer root properties when creating first tab. (#21582)
* transfer root properties when creating a new tab

* fix controller lifecycle
2026-01-30 13:28:57 +01:00
Niels Lyngsø 731b10b07a formatting 2026-01-30 11:13:10 +01:00
Niels Lyngsø f1d32e41e0 lint 2026-01-30 11:12:35 +01:00
dc218ac1bf Repositories: Use FirstOrDefault over ExecuteScalar for GUID and nullable types (closes #21448) (#21552)
* Squash merged  "v173/20453-21446-21448-FirstOrDefault-vs-ExecuteScalar" into "v173/21448-FirstOrDefault-vs-ExecuteScalar"

* resolce Copilot code review comments

* revert to ExecuteScalar<string>

* revert to Database.ExecuteScalar<string>

* revert .FirstOrDefault<long>(query) and its async variant to .ExecuteScalar<long>(query). It is fine for PostgreSql too.

* Remove the test added for verifying NPoco behaviour (it's not needed in the code base moving forward)

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-01-29 15:38:48 +00:00
Jacob Overgaard 79cce7b574 bumps lockfile 2026-01-29 12:39:59 +01:00
Jacob Overgaard feb2827d16 Merge branch 'release/17.2' 2026-01-29 12:39:27 +01:00
Jacob Overgaard ae10b6685a generates api types 2026-01-29 12:12:31 +01:00
Andy Butland 6122cfc201 Bump version to 17.3.0-rc. 2026-01-28 14:22:09 +01:00
Niels LyngsøandGitHub c1b0672ce0 Content Value Transformation: Clean out values when property-type variation transforms (#21557)
* cleanup values of property when property type variation changes

* implement handling segments in transformation
2026-01-28 14:16:13 +01:00
8efdfcd341 Backoffice: Exclude invariant options for culture-variant properties in preset builder (#21555)
* Exclude invariant options for culture-variant properties in preset builder

* Add unit test verifying the fix.

* added a few more unit tests

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2026-01-28 11:30:54 +00:00
3c7a1ad2de Entity Signs: Refactor Entity Sign Bundle to use Popover API. (#21490)
* Refactor entity sign tooltip to use popover API.

* Refactor popover positioning and styling logic.

* Add preview icons to entity sign bundle.

* Improve entity sign preview rendering and popover state.

* Refactor entity sign popover and sign container styles.

* Remove unused index parameter.

* Update menu item background color styles.

* Revert commented lines.

* Refactor entity sign popover rendering logic.

* Refactor popover sign creation into separate method.

* keep previews on hover

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
2026-01-28 10:37:20 +00:00
5485a31f75 Tiptap RTE: Resolves inline blocks being set as dirty (closes #17749) (#21546)
* Resolves RTE inline blocks being flagged as dirty

Fixes #17749

* Deprecated `displayInline` field

* Update src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/block/block.tiptap-api.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-28 09:52:45 +00:00
Jacob OvergaardandGitHub 345a075706 Dependencies: Bumps @umbraco-ui/uui to 1.17.0-rc.4 (#21538)
* build(deps): bumps @umbraco-ui/uui to 1.17.0-rc.2

* build(deps-dev): bumps @umbraco-ui/uui to rc.3 to fix deps mess

* build(deps): bumps @umbraco-ui/uui to 1.17.0-rc.4
2026-01-28 09:52:08 +00:00
Niels LyngsøandGitHub 046c7d207d Content Type Designer: Property Layout updates (#21544)
* remove alias id

* adjust spacing and sizing for improved space in the layout
2026-01-28 09:26:37 +00:00
a5a67a4381 Translations: Missing translations in user permission (#21541)
* Fix issue localization of user permission

* add localize to create button

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
2026-01-28 10:13:39 +01:00
Andy ButlandandGitHub e259cd0fd3 External Logins: Handle duplicate key race condition intermittently triggered in ExternalLoginRepository (#21551)
Handle duplicate key race condition in ExternalLoginRepository.
2026-01-28 06:27:27 +01:00
bohdansolovieandGitHub b5559eb9e8 View Engines: Make ProfilingViewEngine._inner private and modernize string formatting (#21550)
improvement(web): make ProfilingViewEngine._inner private and modernize string formatting

- Changed internal readonly Inner field to private readonly _inner field
- Replaced string.Format calls with string interpolation
- Removed TODO comment
2026-01-27 17:51:24 +01:00
f2c351df64 Skip leading whitespace in ufm parser (#21509)
* Skip leading whitespace in ufm parser

* UFM: Update start function to also skip leading whitespace

The tokenizer was updated to allow whitespace after opening braces,
but the start function still used a string pattern without whitespace
tolerance. This updates start to use a pre-compiled regex that matches
the tokenizer behavior, and adds an additional test case for the
documentation example format.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
2026-01-27 16:02:27 +00:00
Andy Butland 4e6481abd9 Bumped test dependencies to latest minor or patch. 2026-01-27 15:30:43 +01:00
Andy Butland b40c17582a Bump dependency on SixLabors.ImageSharp for ImageSharp2. 2026-01-27 15:30:13 +01:00
Lee KelleherandGitHub af7a21723d Tiptap RTE: Upgraded to latest v3.x (#21493)
* Upgraded Tiptap to v3.13.0

* Remove eslint disable comments

* Update notes in externals

* `TextDirection` is now part of Tiptap core

* Upgraded Tiptap to v3.16.0

* The `addOptions()` typing error still persists in v3.16.0

* Resolved the export issue

* Removed unrequired `@ts-expect-error`

This came from an upstream merge.
2026-01-27 12:04:48 +00:00
Jacob OvergaardandGitHub fc5a8a0536 build(deps-dev): bumps login dependencies to latest (#21539) 2026-01-27 11:39:33 +01:00
7d813667c3 Content/Media: Fix deadlock when performing certain operations in parallel (closes #21125) (#21526)
* Move MediaTree write lock before MediaSavingNotification to prevent deadlock

Fixes a deadlock that could occur when saving multiple media items in parallel
when a MediaSavingNotification handler acquires a MediaTree read lock. The
previous ordering allowed two threads to each acquire read locks in their
notification handlers, then both attempt to upgrade to write locks, causing
a classic lock upgrade deadlock in SQL Server.

By acquiring the write lock before publishing the notification, the deadlock
scenario is avoided. Since the write lock is lazy, it only materializes at the
database level when actual queries are made, so notification handlers doing
in-memory work won't hold the lock.

* Apply same fix to MediaService.Delete method

* Apply same fix to DeleteVersions, DeleteVersion, and Sort methods

* Apply same fix to ContentService methods

Move WriteLock before notifications in:
- Save (single and batch)
- Delete
- DeleteVersions
- DeleteVersion
- Copy

* Apply the same pattern to MemberService.

* Add integration tests to verify the fix.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-01-27 11:08:35 +01:00
Andy ButlandandGitHub 138818acde Members: Fix misleading error message on change password with incorrect current password (#21504)
* Provide correct validation error message on member change password with incorrect current password.

* Rework to use MembersErrorDescriber.
2026-01-27 10:37:28 +01:00
Nhu DinhandGitHub 79753eaf82 E2E: QA Updated acceptance tests for collection view search and document type group (#21522) 2026-01-27 16:00:12 +07:00
Andy ButlandandGitHub 647aa08586 Dependencies: Bump to latest minor/patch versions (#21540)
Bump dependencies to the latest minor or patch.
2026-01-27 08:34:18 +00:00
Nhu DinhandGitHub 9deffead21 E2E: QA Added acceptance tests for multi url picker validation message (#21226)
* Added tests for multi url picker validation message

* Added more tests - not done

* Updated more tests for multi url picker validation message

* Removed unused file

* Bumped version

* Make tests run in the pipeline

* Reverted npm command
2026-01-27 08:31:41 +00:00
Andy ButlandandGitHub d5d93ff1e0 Templates: Allow underscore as first character in template alias (closes #21534) (#21536)
Use a custom regex for validating template aliases that allows underscores.
2026-01-27 08:32:37 +01:00
7ad3d2f68f Content picker: Fix bug where dynamic root children are not correctly available for selection (closes #21477 and #21537) (#21535)
Fix bug content picker dynamic root children not selected properly

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
2026-01-27 08:27:52 +01:00
Andreas ZerbstandGitHub 2fb1221d68 Dotnet Template: Fix trailing comma in appsettings.json (#21529)
Fix trailing comma in global settings
2026-01-27 03:34:18 +00:00
efb8aaf87b Dark mode: Added color variable to code block in the system information dialog to make it readable (#21532)
* added color variable to code-block to make it readable in dark mode

* Update src/Umbraco.Web.UI.Client/src/packages/core/components/code-block/code-block.element.ts

Co-authored-by: Andy Butland <abutland73@gmail.com>

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-01-26 18:07:17 +00:00
2e80b996cf Media: Prevent creation of media with GUID v7 keys when using incompatible path scheme (closes #21440) (#21457)
* Prevent creation of media items with GUID version 7 keys when a media scheme is registered that doesn't support this GUID version.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix log message formatting.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2026-01-26 16:29:37 +01:00
Andy ButlandandGitHub 883b6a4d5d Models Builder: Fix nested generic type handling in WriteClrType (#21429)
* Add support to models builder for nested generic types.

* Fixed existing warnings, added further tests, renamed tests for clarity.

* Add defensive validation for generic brackets passed to SplitGenericArguments.

* Fix failing unit tests.
2026-01-26 15:32:48 +01:00
6946783b74 Content types: Allow adding composition with clashing property alias when property is being removed (closes #21298) (#21527)
* Content types: Allow adding composition with clashing property alias when property is being removed

* Further assert on property coming from the composition.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-01-26 14:32:34 +00:00
ad759e9311 Block editors: Fix false pending changes indicator for invariant block editor with culture-variant blocks (closes #21223) (#21292)
* Block editors: Fix false pending changes indicator for invariant BlockList with culture-variant blocks (closes #21223)

When a document with a culture-variant content type has an invariant BlockList property containing culture-variant blocks, and you publish all languages for the first time, the content would incorrectly show as having unpublished changes.

The root cause was inconsistent JSON serialization order between EditedValue and PublishedValue. Two fixes were applied:

1. Sort block item values by culture before serialization in both `FromEditor` and `MergePartialPropertyValueForCulture` to ensure consistent ordering.

2. Add `[JsonIgnore]` to `BlockItemData.Udi` property since this computed property differs between save and publish paths.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/PropertyEditors/BlockListElementLevelVariationTests.Publishing.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fixed failing integration tests.

* Fix backwards compatibility for legacy UDI format in JSON deserializatio

* Tidy up, remove unused parameters.

* Fixed failing E2E test with copy blocks.

* Separate handling of udi and values in deserialization from current and legacy format, to correctly fix previously failing integeration and E2E tests.

* Fixed failing unit test.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-26 14:51:16 +01:00
00794f48c5 Tiptap RTE: Adds link (umbLink) support to styleMenu API (#21494)
* Adds link (`umbLink`) support to the Style Menu api

* Tiptap RTE: Fix toggleClassName to handle multi-class strings

The toggleClassName command now properly tokenizes the className parameter
to handle space-separated classes (e.g., "btn btn-primary"). Previously,
the entire string was treated as a single token, causing duplicates and
preventing class removal.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Tiptap RTE: Add ensureUmbLink command for idempotent link creation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 14:24:29 +01:00
Niels LyngsøandGitHub 2c5466755a Block List Editor: Describe that Single Block Mode is deprecated (#21512)
mark Single Block Mode as deprecated
2026-01-26 13:07:54 +00:00
DreamandGitHub 7438dd7c84 Backoffice: Redirect to list view after entity deletion (#21456)
Backoffice: Redirect to list view after entity deletion

When an entity is deleted from its detail workspace, the UI now redirects
to the parent list view and shows a success notification instead of staying
on the deleted entity's page showing a 404 error.

Changes:
- Dispatch UmbEntityDeletedEvent after successful deletion
- Show success notification toast on deletion
- Listen for delete event in workspace editor and navigate to backPath
2026-01-26 12:54:25 +00:00
Andy ButlandandGitHub 868ac50b79 Media: Only add deleted suffix to URLs for trashed media when recycle bin protection is enabled (#21412)
Fix protection for media URLs such that they only apply for trashed media.
2026-01-26 13:41:01 +01:00
Sven GeusensandGitHub 897b6ccac6 Load Balancing: Tracking difference CM and CD redirect and post-logout URIs in load-balanced environments (#21432)
* Integration tests for #21138

* Make OpenId redirect and postlogout uris support load balanced environments

* Applied review suggestions

* Fix unit test mocks
2026-01-26 12:08:42 +01:00
997df3d92b Performance: Optimize property retrieval and authorization checks in collection views (#21470)
* Introduce new method overloads and repository implentation, such that a collection view response only loads properties it needs.

* Use non-obsolete method overloads throughout.

* Add unit tests to verify property value retrieval.

* Don't load templates for collection view content retrieval.

* Optimize access checks by verifying the full collection rather than one at a time, and avoid the need to retrieve full content items.

* Added obsoletion messages and aligned behaviour of content and media permission service checks.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Return key in TreeEntityPath collection response, avoiding a later look-up of the key by Id.

* Resolve breaking changes to interfaces.

* Fix further breaking change.

* Additional assert for test verifying property loading for a non-existing property.

* Refactored repositories to avoid having method parameters related to templates on non-document and base content repositories.

* Remove check that verifies all provided keys are found when doing permission checks (although arguably correct, it's a behavioural change, and can also be argued it's corect as is).

* Introduce variable for permission set permissions.

* Provide functional default implementation on FilterAuthorizedAsync.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-26 11:53:53 +01:00
e4df87123c Testing: Ensure ordering for paged descendants tests (closes #21446) (#21447)
* Squash merge Squash merged v173/20453-fix-more-sql-syntax-issues int v173/20453-fix-more-sql-syntax-issues-squash (copy of main)

* fix 2 unit test

* Replace nameof(DTO.COLUMN_NAME) by constant, because it leads to casing issues for case sensitive databses

* fix Copilot review comments

* resolve review comments

* replace more hard coded strings

* fix test

* fix review comments

* fix database schema

* fix database schema

* fix database schema and ResultColumn reference names

* Update src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeAllowedContentTypeDto.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* add comment  from review

* fix two reference column names

* fix breaking change

* fix typo

* Remove unnecessary attributes

* mark 2 unsused DTO classes as obsolete

* reverted change of class UnionHelperDto adding [Column("...")] attributes again, because some integration tests for PostgreSQL provider fail without them. Again a case sensitivty issue.

* replace nameof reference names,
make all column name const consistent

* use NPoco dto instead of raw sql,
extend ISqlSyntaxProvider to handle some sql issues

* reduce complexity

* remove currently unsused extensions to ISqlSyntax

* add missing methods to ISqlSyntaxProvider and SqlSyntaxProviderBase

* add another missing methods to ISqlSyntaxProvider and SqlSyntaxProviderBase

* fix Copilot review comments and build errors

* update ISqlSyntaxProvider and SqlSyntaxProviderBase

* ensure GetPagedDescendants returns ordered by path entities as default

* resolve review comments

* fix review comments

* Update src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.PublishedCache.HybridCache/Persistence/DatabaseCacheRepository.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix Copilot comment

* fix wrong Copilot suggestion

* quote more column names

* resolve review

* Apply suggestions from code review

* synced interface and base class

* Revert "synced interface and base class". For an interface's default implementation, NotImplementedException makes more sense.

This reverts commit cf01cd01fc.

* Fixed remaining code warnings in DatabaseSchemaCreator.

* follow Cotpilot's review suggestion

* revert implementation and fix test

* use default

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-26 09:36:13 +00:00
c6370e39a7 Recycle Bin: Adds emptyRecycleBin collection action kind for Documents and Media (#21482)
* Adds reusable `emptyRecycleBin` `collectionAction` kind

* Adds `emptyRecycleBin` collection-action to documents

* Adds `emptyRecycleBin` collection-action to media

* Removes `api` export

since the condition is eagerly loaded.

* Fixes type annotations and JSDoc comments

- Uses correct generic type `UmbCollectionHasItemsConditionConfig` in `UmbCollectionHasItemsCondition`
- Corrects JSDoc `@augments` tag in `UmbEmptyRecycleBinCollectionAction`

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fixed linting errors

* Refactors execute() to reduce cyclomatic complexity

Extracts tree refresh logic into private #reloadChildrenOfEntity() method.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/manifests.ts

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Removed code comment

as caused ambiguity.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-01-26 09:25:37 +00:00
08863332d2 Disabled the generation and upload off the docfx csharp api docs. (#21521)
* Disabled the generation and upload off the docfx csharp api docs.

* Add comment explaining why job is disabled

* Added comment on second job

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-01-26 09:07:00 +00:00
f483946c4d Code Quality: Added missing documentation to the Umbraco.Cms.Api.Common project (#21465)
* Added missing code documentation to the Umbraco.Cms.Api.Common project

* Remove duplicate XML summary for All constant

Removed duplicate XML summary documentation for the All constant.

* Removed inline comments no longer required now the information has been moved to XML header remarks

* Fix indentation on refactored path segment extraction in SubTypesSelector

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-01-26 07:28:56 +00:00
e7624364ed Database Providers: Add support for providers offering sequence and null casting support (closes #21418) (#21419)
* Squash merge Squash merged v173/20453-fix-more-sql-syntax-issues int v173/20453-fix-more-sql-syntax-issues-squash (copy of main)

* fix 2 unit test

* Replace nameof(DTO.COLUMN_NAME) by constant, because it leads to casing issues for case sensitive databses

* fix Copilot review comments

* resolve review comments

* replace more hard coded strings

* fix test

* fix review comments

* fix database schema

* fix database schema

* fix database schema and ResultColumn reference names

* Update src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeAllowedContentTypeDto.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* add comment  from review

* fix two reference column names

* fix breaking change

* fix typo

* Remove unnecessary attributes

* mark 2 unsused DTO classes as obsolete

* reverted change of class UnionHelperDto adding [Column("...")] attributes again, because some integration tests for PostgreSQL provider fail without them. Again a case sensitivty issue.

* replace nameof reference names,
make all column name const consistent

* use NPoco dto instead of raw sql,
extend ISqlSyntaxProvider to handle some sql issues

* reduce complexity

* remove currently unsused extensions to ISqlSyntax

* add missing methods to ISqlSyntaxProvider and SqlSyntaxProviderBase

* add another missing methods to ISqlSyntaxProvider and SqlSyntaxProviderBase

* fix Copilot review comments and build errors

* update ISqlSyntaxProvider and SqlSyntaxProviderBase

* resolve review comments

* fix review comments

* Update src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.PublishedCache.HybridCache/Persistence/DatabaseCacheRepository.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix Copilot comment

* fix wrong Copilot suggestion

* quote more column names

* resolve review

* Apply suggestions from code review

* synced interface and base class

* Revert "synced interface and base class". For an interface's default implementation, NotImplementedException makes more sense.

This reverts commit cf01cd01fc.

* Fixed remaining code warnings in DatabaseSchemaCreator.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-24 09:16:15 +00:00
83300221fe Log Viewer: Fix polling interval reset when changing intervals (closes #21507) (#21508)
* fix(log-viewer): prevent polling toggle reset when changing interval

Fixes issue where changing polling interval would reset the button to 'Polling' state instead of applying the new interval immediately.

- Remove togglePolling() call from closePoolingPopover() method
- Update setPollingInterval() to restart polling with new interval if already enabled

Fixes #21507

* refactor(log-viewer): extract polling start logic and fix regression

- Extract polling start logic into #startPolling() helper method
- Fix regression: enable and start polling when interval is selected while polling is off
- Update togglePolling() to use the helper method for consistency

Addresses feedback on PR #21508

---------

Co-authored-by: Gittensor Miner <miner@gittensor.io>
2026-01-23 17:17:02 +01:00
af81e258b4 Fix for the client side circular dependency. (#21464)
* Fix for the client side circular dependency.

This should fix the circular dependency without causing any breaking changes to the public APIs.

This issue is detailed here:
https://github.com/umbraco/Umbraco-CMS/issues/21463

* refactor UMB_MODAL_MANAGER_CONTEXT to avoid circular dependency

---------

Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2026-01-23 15:53:42 +00:00
aa4a33251a Content Types: Root properties (#21500)
implement root properties

Co-authored-by: Andreas Zerbst <73799582+andr317c@users.noreply.github.com>
2026-01-23 12:47:17 +00:00
Mads RasmussenandGitHub e7c63b19b9 Modal: Add 'in modal' condition to modal package (#21503)
* Add 'is modal' condition to modal package

Introduces a new 'is modal' condition for extension manifests, allowing actions to be conditionally permitted based on modal context. Updates user collection action manifests to use this condition, preventing certain actions when inside a modal. Includes implementation, configuration, manifest registration, and tests for the new condition.

* rename from is modal to in modal
2026-01-23 12:27:36 +00:00
Niels LyngsøandGitHub 16f9bc7cd7 Block List & Block Single: Use property value in validation check (Fixes #21313) (#21491)
use this.value as source for the validation
2026-01-23 12:12:20 +00:00
Niels LyngsøandGitHub 3b5e938492 Property Value Preset Builder: accept variant options (#21382)
property value preset builder to use variant options
2026-01-23 10:04:37 +00:00
ea76efafc1 Dictionary: Add configurable value search functionality (#21200)
* added dicationary value search active only with config param set

* Removed code smell, by reducing nesting

* Renamed configuration value to EnableValueSearch.
Added integration tests to verify search results.

* update query to return correct values for each language in the overview

* Use OptionsMonitor and add additional assert to verify fix to indication of which languages have translations.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-01-23 09:47:39 +01:00
1a68d39272 Variants Sorting: Sort by language name (fix #21408) (#21435)
* Sort at last by language name

* ensure document language picker is sorted as variant selector

* Update src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Client/src/packages/documents/documents/utils.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* refactor to avoid inline methods

* transform into a function

* revert config file commit

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-23 09:47:14 +01:00
Andy ButlandandGitHub fee3cc7352 Redirect Tracking: Handle empty string in redirect tracker when restoring from recycle bin (#21488)
Handle empty string in redirect tracker when restoring from recycle bin.
2026-01-23 14:25:51 +09:00
Nhu DinhandGitHub 3f3362e0f3 E2E: QA Updated tests for granular permission in content to match the changes (#21478) 2026-01-23 04:07:50 +00:00
Nicklas KramerandGitHub 8f55885829 Data Types: Allows transparency for the approved colors in the color picker. (#21495)
Fixing regex and adding test
2026-01-23 10:34:20 +09:00
8f6ebcdcf6 Picker: Support embedded Collections in the Collection Item Picker Modal (#21392)
* Add alias property to collection config interface

Introduced an 'alias' property to the UmbCollectionItemPickerModalCollectionConfig interface

* render collection element when modal is configured with an alias

* expose a picker modal route

* use collection in use picker

* adjust spacing

* add config option for selectOnly

* dynamic modal alias

* support selectable entity item ref

* wip entity data picker collection + ref and card views

* Add entity collection item card extension type + default elements

* implement user collection item card

* fix selection events

* map to prop

* add prop/attr for href

* add support for which detail properties to show

* update type import

* Update src/Umbraco.Web.UI.Client/src/packages/core/collection/item/entity-collection-item-card/entity-collection-item-card.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* import card in correct file

* Fix event listener binding for selection events

* implement disabled property for collection item cards

* init commit of collection item ref extension

* fix imports

* add element interface

* Implement UmbEntityCollectionItemElement interface in item cards

Added the UmbEntityCollectionItemElement interface to document and user collection item card elements for improved type safety and consistency. Updated type exports to include the new interface.

* Update collection item ref to use uui-ref-node

Replaces the placeholder div with a uui-ref-node component, passing relevant item properties and event handlers. Adds dynamic icon rendering using umb-icon.

* Refactor entity collection item elements to use shared base

Introduces a new abstract base class for entity collection item elements, consolidating shared logic for card and ref variants. Updates card and ref element implementations to extend the new base, and refactors extension manifest interfaces for consistency. This improves maintainability and reduces code duplication.

* use class instead of magic string

* Use entity collection item card in picker view

Replaces the placeholder card markup with the <umb-entity-collection-item-card> component, enabling selection and deselection functionality for items in the entity data picker card collection view.

* Update entity item ref to collection item ref

Replaces <umb-entity-item-ref> with <umb-entity-collection-item-ref> in the picker collection view. Adjusts event handlers and select-only logic to improve selection behavior and component consistency.

* utilise ref and card kind for picker views

* introduce ref and card collection view kinds

* Utilise card kind for user collection view

* Add item-specific href support to collection views

Introduces a requestItemHref method to collection contexts for retrieving item-specific hrefs. Updates card, ref, and user table collection views to use these hrefs, enabling dynamic linking for collection items. Refactors user table name column layout to accept href via value prop instead of constructing it internally.

* Update ManifestCollectionView import path

Changed the import of ManifestCollectionView from '../extensions/types.js' to '../view/types.js' to reflect its new location.

* remove unused

* use size medium for entity collection item picker

* use box

* render entity actions

* use edit path builder for user links

* rename method

* Revert "rename method"

This reverts commit 4df577688e.

* Update collection-default.context.ts

* make type lint ignore unused args with an underscore

* temp remove unused

* only make collection vie selectable if there are any registered bulk actions

* don't render name link if there is no href

* fix imports

* Render selection actions only if bulk actions exist

* use selectable state

* Update language-table-collection-view.element.ts

* Update language-table-collection-view.element.ts

* Update card-collection-view.element.ts

* clean up

* Refactor collection views to use shared base class

* refactor(collection): parallelize href fetching and make method private

* docs(examples): update collection example to use card and ref kinds

* docs(examples): add icon property to collection example data model

* Update src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Client/src/packages/core/collection/view/types.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update collection-bulk-action.manager.test.ts

* Removed duplicate and redundant '@typescript-eslint/no-unused-vars' rule definitions, consolidating the configuration to use only 'argsIgnorePattern'.

* Handle missing user href in name column layout

Replaces the user name link with a span when the href property is not provided, preventing broken links in the user table name column layout.

* Update user-table-name-column-layout.element.ts

* pass modal data and value to routable modal

* Update picker-input.context.ts

* support selectableFilter

* scaffolding of a collection text filter extension

* Refactor collection text filter to use API interface

* Fix incorrect tag

* Update types.ts

* Update collection-text-filter.extension.ts

* Add cancelation to debounced search on destroy

* clean up

* add js docs

* two way binding of filter value

* clean up

* Add collection text filter manifest example

Introduced a new filter manifest for the example collection and updated the main manifests file to include it. This enables a text filter extension for the example collection.

* Delete unused element and context

* Update src/Umbraco.Web.UI.Client/src/packages/user/user-group/collection/user-group-collection.context-token.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update user-group-table-collection-view.element.ts

* support search for tree item and collection item pickers

* add spacing between collection ref items

* add margin between picker search result items

* remove spacing after last item

* remove padding in search results

* Update collection-item-picker-modal.element.ts

* move select only logic to collection selection manager

* add tests for collection selection manager

* change to filter label instead of search

* delete unused user grid collection view

* Select-only mode is now only disabled when all items are deselected, rather than on every deselection.

* prepare umb table for pickers

* utilize UmbCollectionViewElementBase in user table collection view

* remove console log

* handle select all and select item from same event

* bulk actions workaround

* add bulk action in collections feature toggle

* remove unused method

* make fields optional to avoid a breaking change

* remove unused import

* fix typescript errors

* adjust search styling

* hide with css

* fix ts errors

* Add modal data support to picker input context

Introduces methods to set and get modal data in UmbPickerInputContext, allowing base configuration for picker modals. Updates modal data handling to merge stored modal data with provided data for both direct picker opening and modal route setup.

* Fix bulk action manager test initialization

Added calls to setConfig in tests to properly initialize the observer before subscribing to hasBulkActions. Simplified the test logic for checking emissions when actions are present.

* Update tree-picker-modal.element.ts

* Update picker-search-result.element.ts

* Update src/Umbraco.Web.UI.Client/src/packages/core/collection/view/umb-collection-view-element-base.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Use ifDefined for modal route in user input button

* Use ifDefined for href binding in entity data picker

* Fix collection alias binding in item picker modal

* wire up user table collection view with selectableFilter

* clean up controller aliases

* Update collection-item-picker-modal.element.ts

* Update collection-item-picker-modal.element.ts

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-22 15:24:09 +00:00
Andy ButlandandGitHub eca3e91af0 Management API: Fix document URLs returning all languages for invariant content (closes #21459) (#21473)
Fix display of all languages for URLs for invariant documents.
2026-01-22 11:41:01 +01:00
Niels LyngsøandGitHub 718e35f483 Media: Picker Modal types export (Fixes #21265) (#21329)
fix media modal exports
2026-01-22 10:03:18 +00:00
Niels LyngsøandGitHub ca8f6f59bd Entity Signs: Embed Api & Element for performance (#21480)
embed api & element
2026-01-22 09:18:31 +00:00
338f650274 Content-Type Designer: Transfer tab when moving property to inhertied tab (Fixes #20789) (#21234)
* enable async method

* ensure container is local to the owner content type

* no need to await anyhow

* handle moved groups

* Update src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor-properties.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-22 07:09:45 +01:00
Lee KelleherandGitHub 995e3bde6f Task: De-duplicate TypeScript class names (#21474)
De-duplicate TypeScript class names
2026-01-21 20:57:16 +01:00
7f162201e6 Fixes #20665 - Password change error msg (#21257)
* Fixes #20665 - Password change error msg

In order to show the right validation message:
 - the repository code always notifies the validation failure message
    (or a default failure message if none is received)
 - in the data-source code, tryExecute is called with the option
    to disable the default notification

* Return the original error instead of faking success

---------

Co-authored-by: Emma L Garland <1649855+emmagarland@users.noreply.github.com>
2026-01-21 13:48:25 +00:00
d7dbe39dd3 Routing: Add DocumentUrlAliasService for optimized URL alias lookups (closes #21383) (#21396)
* Implement document alias cache and service to optimize content finder by alias.

* Renamed to DocumentUrlAlias. Fixed issues on start-up.

* Remove tracking of root ancestor.

* Optimize cache key, tidy up tests, move domain matching to content finder.

* Handle language and document deletes.

* Align further with document URL service.

* Code tidy.

* Fixed comment.

* Refactor scope handling to avoid nested scopes

Extract CreateOrUpdateAliasesInternalAsync to process documents without
creating their own scope. Both CreateOrUpdateAliasesAsync and
CreateOrUpdateAliasesWithDescendantsAsync now create a single scope
and call the internal method, avoiding unnecessary nested scope creation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Extract CreateOrUpdateAliasesInternalAsync to process documents without
creating their own scope.

* Only return a document for a match under a domain if the document is found under the domain of the current request.

* Fix failing integration tests.

* Apply suggestions from code review.

* Ensured language to culture code map is updated when a language isn't found in the cached map.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 13:52:45 +01:00
b2801e6555 Backoffice: Fix event listener memory leaks in auth, dropzone, actions, and router (#21458)
fix(backoffice): resolve event listener memory leaks in auth, dropzone, actions, and router

Fixes memory leaks in 4 components where event listeners registered with .bind(this) could not be properly removed because each .bind() call creates a new function reference.

Changes:
- auth.context.ts: Convert #onStorageEvent to arrow function property
- dropzone-media.element.ts: Convert 4 drag handlers to arrow function properties
- entity-actions-dropdown.element.ts: Convert handler and add disconnectedCallback
- router-slot.element.ts: Convert handler and add proper cleanup in disconnectedCallback

Solution: Arrow function properties maintain consistent references while preserving 'this' context, enabling proper listener removal.

Testing:
- Added unit tests for auth.context.ts
- All builds pass
- Linter passes
- No breaking changes

Documentation:
- Added "Event Listener Cleanup Pattern" section to clean-code.md
- Added "Event Handler Guidelines" section to style-guide.md

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-21 11:12:09 +00:00
594b64e2a9 Tiptap RTE: Optimize umb-input-tiptap initialization and rendering (#21070)
* refactor(rte): Replace misleading Promise.all with sequential awaits

The inner awaits in Promise.all([await ..., await ...]) made the operations
sequential anyway. Since #loadEditor() depends on _extensions being populated,
sequential execution is correct - this change makes the intent clearer.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* perf(rte): Cache toolbar and statusbar emptiness checks

Instead of calling .flat() on every render to check if toolbar/statusbar
have items, compute the boolean once when values are set in #loadEditor().
This avoids unnecessary array operations during render cycles.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* perf(rte): Pre-compute extension styles during initialization

Instead of calling unsafeCSS() on each style during every render cycle,
collect and process styles once in #loadEditor() and store the result
in _extensionStyles. This avoids repeated CSS processing during renders.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

# Conflicts:
#	src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-21 10:19:21 +01:00
acd0aae48e DevOps: Adds check:duplicate-class-names devops script (#21460)
* Adds `check:duplicate-class-names` devops script

* DevOps: Improve `check:duplicate-class-names` script

- Fix example path in JSDoc comment
- Add support for `export default class` declarations
- Add `--ignore-stories` flag to exclude story files from detection

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Added try/catch on reading file contents

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-01-21 10:18:52 +01:00
Jacob OvergaardandGitHub d4c3813410 Dotnet Template: Removes unused setting SanitizeTinyMce (#21467)
chore: removes unused setting `SanitizeTinyMce`
2026-01-21 08:53:59 +00:00
Nikolaj GeisleandGitHub 5d664538f0 Examine: Check for registered populators before emptying indexes (#21455)
* Check for registered populators before emptying indexes

* Update can rebuild to also use HasRegisteredPopulator
2026-01-21 08:33:03 +01:00
Andy ButlandandGitHub c8f897879c Performance: Fix thread safety and optimize cache updates in PublishStatusService after content changes (#21415)
* Resolved potential thread safety issues with PublishStatusService.

* Only update published status in content cache refresher if within a publish or unpublish operation.
2026-01-21 06:41:55 +00:00
cee47613a6 Move media-type guid strings into constants partial (#21461)
* move media-type guid strings into constants partial

* missed one.

* Update src/Umbraco.Core/Constants-MediaTypes.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Add member type GUID constants too.

* Removed member type incorrectly recorded as a built-in data type.

* Reuse constant in obsolete GUID constant.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-01-21 06:25:45 +00:00
ac1a0a46df Document URL Cache: Ensure URLs are rebuilt after upgrade and prevent duplicate initialization (closes #21337) (#21379)
* Remove rebuild of document URLs during migration, instead ensuring they will run after migration is complete and Umbraco is running.

* Avoid unnecessary second rebuild of document URL cache after startup with migration that has already triggered a rebuild.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-21 06:54:56 +01:00
8f571fe51b Rollback: Add toggle for diff display (closes #18518) (#21426)
* Add a toggle, defaulted to off, for display of diffs on the rollback view.

* Used only label for checkbox.

* Align formatting across translations for diffHelp key.

* Changed the checkbox to a toggle

UI semantics, checkboxes imply selection, whereas toggles imply activation.

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
2026-01-20 15:44:04 +00:00
Niels LyngsøandGitHub a3b4e922d9 Performance: Use import maps to save requests (#21363)
Use import maps to save requests
2026-01-20 14:27:11 +00:00
fd7a2c6a5a Content picker: Prevent selection of document/member type containers when configuring allowed types (closes #21356) (#21357)
* Prevent selection of document and member type folders when selecting allowed types for the content picker.

* Added fix for Media Types

* Set `documentTypesOnly` on `umb-input-document-type`

so to disallow selecting element-types.

* Linting

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
2026-01-20 12:50:37 +00:00
fd09a24559 Media: Unable to see the "Access denied" view when deep-linking to restricted media nodes (#21442)
* fix: aligns media workspace with document workspace to handle "variants" when calculating routes, which fixes an issue where the "Access denied" view would not be shown

* fix: clear root access flag when selecting specific start nodes

When selecting specific document or media start nodes for a user, the UI now automatically sets hasDocumentRootAccess/hasMediaRootAccess to false.

Previously, if a user group had "Has access to all items" enabled, selecting specific start nodes on the individual user wouldn't clear the root access flag. This caused the backend to add -1 (root access) to the start node list, overriding the specific node selections.

This ensures user-specific start node permissions properly override group-level root access settings.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: add length check to prevent rendering router with empty routes array

The render method now checks both that _routes exists AND has length > 0 before rendering the router-slot. An empty array is truthy, so without the length check, the router-slot could be rendered with an empty routes array, causing runtime errors.

This aligns with the original render logic and prevents the TypeError when media tests run.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix E2E test URL construction for media workspace deep-linking

The test was constructing an invalid URL by appending the workspace path
directly to the current URL, which included '/collection'. This resulted in:
/umbraco/section/media/collection/workspace/media/edit/ (invalid)

Instead of the correct:
/umbraco/section/media/workspace/media/edit/

The fix removes '/collection' before appending the workspace path, ensuring
the test actually navigates to the workspace editor where the 'Access denied'
view is properly displayed.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Make all tests for media start node run in the pipeline - remember to revert before merging

* Revert npm command before merging

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Nhu Dinh <hnd@umbraco.dk>
2026-01-20 10:42:12 +00:00
2eb2e33f2f Document Tree: Filter tree items based on user browse permissions (closes #21141) (#21173)
* Document Tree: Filter tree items based on user browse permissions

- Add FilterTreeEntities virtual methods to EntityTreeControllerBase for filtering tree entities with total count adjustments
- Override FilterTreeEntities in DocumentTreeControllerBase to filter by ActionBrowse permission
- Extract filtering logic into IDocumentPermissionFilterService for testability
- Add unit tests for DocumentPermissionFilterService

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-20 10:57:13 +01:00
ddffd6ec1e HybridCache: Optimize content type change cache rebuild to resolve SQL timeouts (#21207)
* Complete the scope when no runnable job found. Without this I'm seeing timeouts and lock contention if a long-running document type save operation is running when the first distributed job is requested.

* Run serialization steps of rebuild of content cache in parallel for a small but not insignficant speed optimization.

* Add integration tests for database cache rebuild.

* Optimize rebuild of databaes and memory cache after content type update.

* Add debug log for running distributed job.

* Apply memory cache clear optimization to media.

* Optimize MediaCacheService.RebuildMemoryCacheByContentTypeAsync with lightweight query

Use GetMediaKeysByContentTypeKeys to fetch only media keys instead of loading full ContentCacheNode objects. This matches the same optimization applied to DocumentCacheService.

Also refactors Rebuild() to reuse RebuildMemoryCacheByContentTypeAsync for the memory cache clearing step.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Further updates from code review.

* Further tests for variant documents, composed documents and message pack serialization.

* Fixed failing integration tests.

* Clear the cacje level published content cache on content type change.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-20 09:15:40 +01:00
e206f26e5e Content Picker: Provide "content root" origin for dynamic root (closes #21134) (#21161)
* Provide "content root" origin for dynamic root.

* Update src/Umbraco.Core/DynamicRoot/Origin/ContentRootDynamicRootOriginFinder.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Code tidy.

* Add integration test

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Sven Geusens <geusens@gmail.com>
Co-authored-by: Sven Geusens <sge@umbraco.dk>
2026-01-20 06:56:23 +01:00
Chris HoustonandGitHub cbaab6a6f3 Code Quality: Adding XML documentation to Umbraco.Cms.Persistence.EFCore.SQLServer & Umbraco.Cms.Persistence.EFCore.SQLite (#21439)
Adding XML documentation to these two projects.

- Umbraco.Cms.Persistence.EFCore.SQLServer
- Umbraco.Cms.Persistence.EFCore.SQLite
2026-01-20 06:50:24 +01:00
Chris HoustonandGitHub 4f62771135 Code Quality: Resolve 128 SA1600 documentation warnings in Umbraco.Cms.Persistence.Sqlite (#21438)
* fix: Resolve 128 SA1600 documentation warnings in Umbraco.Cms.Persistence.Sqlite

- Added XML documentation comments to interceptors, mappers, and services
- Added TODO (V18) comments to SqliteSyntaxProvider.Format methods (CS0114)
- Updated .csproj TODO comment to follow V18 convention
- CS0114 warnings remain suppressed as fix would be binary breaking

* Fixed the issues Copilot complained about with the documentation and..

Fixed two IDE0270 warnings (null check simplification).
2026-01-20 06:48:32 +01:00
Chris HoustonandGitHub a4e72cbdb1 Code Quality: Adding all missing XML documentation for the Umbraco.Cms.Persistence.EFCore project (#21437)
Adding all missing XML documentation for the Persistence.EFCore project
2026-01-20 06:42:00 +01:00
Chris HoustonandGitHub 8d75277297 Code Quality: Fix CS0659 and CS0661 build warnings in Item test class by removing legacy test and setup of little value (#21399)
* Code Quality: Fix CS0659 and CS0661 build warnings in Item test class

The Item class in test project defined Equals override and equality operators without implementing GetHashCode, causing CS0659 and CS0661 compiler warnings.

Added GetHashCode implementation using RuntimeHelpers.GetHashCode(this) for consistent reference-based equality matching the existing operators behavior.

Removed CS0659/CS0661 from WarningsNotAsErrors in test project as they are no longer needed.

* Code Quality: Remove unused test infrastructure classes

Remove Item, OrderItem, and SimpleOrder classes along with the SimpleOrder_Returns_Null_On_FirstOrDefault_When_Empty test.

These ~370 lines of test infrastructure existed only for a single trivial test that verified FirstOrDefault() returns null on an empty collection - behavior already tested on actual Umbraco collections in the same file.
2026-01-20 06:32:51 +01:00
Andy ButlandandGitHub d4fe1b3783 StringExtensions: Refactor into partial classes and optimize methods (#21370)
* Refactor StringExtensions into multiple files using partial classes.

* Tidy/complete XML header comments.

* Fixed warnings in string extension methods.

* Add unit tests for IsLowerCase and IsUpperCase and optimize the methods.

* Add unit tests for ReplaceNonAlphanumericChars and optimize the method.

* Add unit tests for StringWhitespace and optimize the method.

* Add unit tests for StripHtml and DecodeFromHex and optimize the methods.
Fix too aggressive regex for StripHTML to ensure works only on HTML tags.

* Add unit tests for EnsureStartsWith and EnsureENdsWith and optimize the methods.

* Add unit tests for ToSingleLine and StripNewLines and optimize the methods.

* Fix issues raised in code review.
2026-01-19 19:18:39 +01:00
Andreas ZerbstandGitHub e767914bd0 E2E: QA Added separate emails for the login tests (#21443) 2026-01-19 14:33:56 +00:00
7acdc6ec0b Router: Destroy route component when disconnected (Fixes #21272) (#21318)
destroy route component when disconnected

Co-authored-by: Mads Rasmussen <madsr@hey.com>
2026-01-19 11:11:26 +01:00
Nhu DinhandGitHub 00971bb55e E2E: QA Fixed failing tests for setting up content notifications (#21441) 2026-01-19 08:39:27 +00:00
Chris HoustonandGitHub 9832603525 Code Quality: Resolve SA1649 warnings (#21401)
* Added the SA1649 to the "No Warnings" section.

Stylecop is trying to enforce filenames that are like:

CancellableObjectEventArgs{TEventObject}.cs

However Umbraco uses CancellableObjectEventArgs.cs

Unless a policy decision is make to follow this stylecop rule, I think it is better to add this rule to the " NoWarn " section, so we don't see it appear at all.

* Revert accidental package-lock.json change

* Renaming files to match the StyleCop patterns.

Except two which would end up having the same names as other file, so these have been renamed as LegacyIScope & LegacyIScopeProvider, with local Pragma warnings disabled for this Style Cop rule.

* Removing the SA1649 from Warnings NOT as Errors.

In other words, if you turn on show warnings as errors, these will show as errors, rather than being suppressed.
2026-01-17 14:35:01 +01:00
Jacob OvergaardandGitHub e485056b8d Bulk Publish: Filter variant options to applicable cultures only (closes #19147) (#21163) 2026-01-16 19:25:22 +01:00
Andy ButlandandGitHub 28a3884adb Revert binary breaking changes from PR #21236 (#21434)
* Revert binary breaking changes from #21236 and comment with a TODO for the next major.

* Further TODO.
2026-01-16 17:46:59 +01:00
Mads RasmussenandGitHub 0eaca92615 Backoffice Performance: Inline entry point modules to reduce JS chunk count (#21380)
* change to static import

* add support for passing modules to manifest js property

* Replaces dynamic imports of entry-point.js with static imports across all manifests

* Support statically imported modules in loader functions

Extended loadManifestApi and loadManifestElement to handle already resolved module objects (statically imported modules) in addition to dynamic imports. Updated type definitions in utils.ts to include module export types for loader properties.

* Add tests for loadManifest* functions in extension-api

Introduces unit tests for loadManifestApi, loadManifestElement, and loadManifestPlainJs functions. These tests cover various scenarios including direct class constructors, dynamic and static imports, export prioritization, and edge cases for null and undefined inputs.
2026-01-16 15:18:59 +00:00
Engiber LozadaandGitHub 9ed3186cc2 Content Workspace: Add condition to detect when a content workspace has finished loading. (#21290)
* Added folder and files for the new condition.

* Registered the condition.

* Added an example to test the condition.

* Added the condition in one of examples.

* Renamed condition.

* Fixed linting error.
2026-01-16 15:59:01 +01:00
10c5df892f Notification Container: Make toast notifications announced by screen readers in Chrome. (#21028)
* fix(a11y): Toast notifications not announced by screen readers in Chrome

- Move screen reader live region from Shadow DOM to Light DOM (document.body)
  Chrome doesn't reliably detect ARIA live regions inside Shadow DOM
- Use role="alert" with fresh elements for each announcement instead of
  updating text content of an existing live region
- Fix invalid aria-role="true" attribute (was invalid HTML)
- Fix missing backslash in unicode escape '\u00A0'

The previous implementation had the live region nested 3 levels deep in
Shadow DOM, which Safari handled but Chrome ignored. Creating a new
alert element in Light DOM for each announcement is the most reliable
method across browsers.

Closes #14521

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Removed comment.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-16 15:57:46 +01:00
Niels Lyngsø 90be5d9958 lint fixes 2026-01-16 14:51:20 +01:00
Bjarne FyrstenborgandGitHub 4d5f9ec7ce Focal point: Utility functions (#21264)
Focal point utils
2026-01-16 13:20:19 +00:00
Andy ButlandandGitHub 07e25d681b Media Picker: Respect start node when drag+dropping files directly onto picker (closes #21422) (#21423)
Pass parent unique to the media to the media picker dropzone.
2026-01-16 11:51:45 +01:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>iOvergaardEngiber Lozada
ba7f84c96f Thumbnails: Fix image thumbnails cropping to allow the entire image to be shown as a thumbnail (closes #20347) (#21288)
* Initial plan

* Change object-fit from cover to contain for image thumbnails

Co-authored-by: iOvergaard <752371+iOvergaard@users.noreply.github.com>

* Add visual demonstration of the fix

Co-authored-by: iOvergaard <752371+iOvergaard@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: iOvergaard <752371+iOvergaard@users.noreply.github.com>
Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
2026-01-16 11:50:26 +01:00
Dirk SeefeldandGitHub f36947a947 Replace nameof() by constants of DTO (closes #21303) (#21344) 2026-01-16 10:59:03 +01:00
JeavonandGitHub f6230cd122 Update Umbraco and Starter Kit versions in templates (#21395)
* Update Umbraco version in starterkits template

LTS and Latest should both install 17.0.0, at the moment latest uses Umbraco v17.1.0 but a starter kit version 17.0.0-rc1 which is not a good combo

* Update LTS in template to 17.1.0
2026-01-16 10:04:48 +01:00
a2c743ca43 Persistence Model: Replace some hard coded strings in DTOs (#21327)
* Squash merge Squash merged v173/20453-fix-more-sql-syntax-issues int v173/20453-fix-more-sql-syntax-issues-squash (copy of main)

* fix 2 unit test

* fix Copilot review comments

* resolve review comments

* replace more hard coded strings

* fix test

* fix review comments

* fix database schema

* fix database schema and ResultColumn reference names

* Update src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeAllowedContentTypeDto.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* add comment  from review

* fix two reference column names

* fix breaking change

* fix typo

* Remove unnecessary attributes

* mark 2 unsused DTO classes as obsolete

* reverted change of class UnionHelperDto adding [Column("...")] attributes again, because some integration tests for PostgreSQL provider fail without them. Again a case sensitivty issue.

* replace nameof reference names,
make all column name const consistent

* Update obsoletion messages

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-01-16 06:18:53 +00:00
Niels Lyngsø d72d66edfd no delay on forbidden-text animation 2026-01-15 16:35:00 +01:00
9c50476b17 Tree Navigation: Add visual indicators for items with restricted access (#21365)
* Tree pickers: Implement noAccess property UI handling for user start nodes

- Add noAccess observable to document and media tree item contexts
- Add visual styling (grayed out, italic) for noAccess items in tree views
- Update document and media picker input contexts to prevent selection of noAccess items
- Items with noAccess are shown for navigation but cannot be selected in pickers

This implements the UI handling for Feature 63060 "Handle Start Nodes"

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix noAccess implementation and add E2E tests

This commit combines all improvements made to the noAccess property feature:

1. Refactored to use Lit lifecycle methods (updated()) instead of property watchers
2. Added click and keyboard event handlers to prevent navigation
3. Removed disabled attribute that was blocking tree expansion
4. Added comprehensive E2E tests for document and media trees

Critical bug fix: Removed disabled attribute that prevented expansion
- The disabled attribute was blocking ALL interactions including expanding
  tree items to show accessible children underneath noAccess ancestors
- Now only sets aria-disabled="true" for screen readers and removes href
- Click and keyboard event handlers still prevent navigation as intended
- Users can now properly navigate through noAccess ancestors to reach
  their accessible child nodes

E2E test coverage:
- Display noAccess styling (opacity, italic)
- Prevent navigation when clicking noAccess nodes
- Allow expansion of noAccess nodes to show children
- Picker tests skipped pending infrastructure improvements

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Remove aria-disabled manipulation that interferes with tree expansion

The previous implementation set aria-disabled="true" and removed href
from the menu-item in #updateMenuItemAccessibility(). This approach
caused issues with tree expansion functionality.

Removed:
- #updateMenuItemAccessibility() method
- updated() lifecycle hook that called it
- UUIMenuItemElement import (no longer needed)

The click and keyboard event handlers already prevent navigation to
noAccess nodes, so additional DOM manipulation is not necessary.

Test results:
 4 passing: Display styling and prevent navigation work correctly
 2 failing: These appear to be backend issues:
   1. Document expansion: Caret button disabled (backend marking noAccess
      items as not selectable, which disables entire menu-item)
   2. Media expansion: Child media folder incorrectly has noAccess attribute
      (backend data issue - child should be accessible as it's the start node)

The UI implementation is sound. The remaining test failures indicate
backend API issues that need investigation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix path comparison bug in UserStartNodeEntitiesService (similar to #21162)

This fixes the same path comparison bug we fixed in PR #21162 but in C# string
comparisons instead of SQL queries.

## Root Cause
Path comparisons without trailing commas caused false matches:
- Path "-1,1001" incorrectly matched prefix "-1,100"
- This marked nodes as ancestors/descendants when they weren't related

## Examples of False Matches
- child.Path = "-1,1001", startNodePath = "-1,100"
  - OLD: "-1,1001".StartsWith("-1,100") = TRUE (bug!)
  - NEW: "-1,1001,".StartsWith("-1,100,") = FALSE (correct!)

- child.Path = "-1,100", startNodePath = "-1,1001"
  - OLD: "-1,1001".StartsWith("-1,100") = TRUE (bug!)
  - NEW: "-1,1001,".StartsWith("-1,100,") = FALSE (correct!)

## Fix Applied (Two Locations)
1. Line 146 (ancestor check): Added comma suffix to child.Path
2. Line 226 (IsDescendantOrSelf): Added comma suffix to both paths

This matches the pattern already used correctly in lines 92 and 191 of the
same file, and mirrors the SQL fix from PR #21162.

## Test Impact
This should fix the failing E2E test where child media folders were incorrectly
marked as noAccess when they were actually the user's start node.

Related: #21162

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix remaining merge conflict markers in media-tree-item.element.ts

* Remove E2E agent markdown file (moved to personal space)

* test: adds mock data for noAccess

* feat: moves noAccess subscriber to base class

* test: adds mock data for media

* feat: moves no-access styling to the base class

* fix: media tree items should inherit styling from the base class

* feat: observes noAccess from children and reports back to the base class

* test: spec file should use undefined instead of null

* docs: add comprehensive comments explaining noAccess opt-in pattern

- Document why noAccess is not in base interface (breaking change)
- Explain opt-in pattern with code examples
- Add JSDoc comments to property, event handlers, and CSS
- Reference accessibility considerations (keyboard users)
- Link child class implementations to base class documentation

* test: adds timeout for URL to settle

* fix: allow clicks on accessible children of noAccess tree items

When a tree item has noAccess, child tree items are rendered in its slot.
Previously, the parent's click handler blocked ALL clicks due to event bubbling,
preventing users from navigating to accessible descendants.

Now checks if click originated from a child tree item element using closest().
If it's a child, allow the click. Only block clicks on the noAccess item itself.

Applied to both mouse clicks and keyboard navigation (Enter/Space).

This enables users to navigate through noAccess ancestors to reach their
accessible start nodes (e.g., Root[noAccess] → Child[noAccess] → Grandchild[accessible]).

Fixes tests:
- should allow expansion of noAccess ancestor node to show children (documents)
- should allow expansion of noAccess ancestor media node to show children (media)

* compare with the closest element to see if we are clicking on the element that is blocked or a sub-element that is not

* fix: adds forbidden route in case of no variants

* test: corrects label locator

* test: adds test to check if you can click or deeplink to restricted media

* test: removes .only

* test: removes duplicated tests

* test: adds test for document no-access

* test: add unit tests for user start node path comparison logic

Adds comprehensive unit tests documenting the path comparison fix that prevents
false matches when node IDs are numeric prefixes of other IDs (e.g., 100 vs 1001).

The fix uses trailing commas on both paths to ensure accurate comparison:
- Without fix: "-1,100".StartsWith("-1,10") = true  (incorrect)
- With fix: "-1,100,".StartsWith("-1,10,") = false  (correct)

Tests cover:
- Numeric prefix edge cases (1 vs 10, 10 vs 100, 100 vs 1001)
- Self comparison (start node itself)
- Descendant relationships
- Deep path hierarchies
- Demonstrates the bug without the fix for documentation

19 test cases total, all passing.

* test: removes .only

* fix: do not overwrite forbidden route

* docs: fixes line number in comment

* test: fixes comment

* feat: uses isSelectableContext to disable and scrub 'href' from base element

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-15 16:29:21 +01:00
7dca122a80 NPM: Move Umbraco Package Schema and custom-elements to root level for IDE discoverability (closes #16667) (#17866)
* Adjust build scripts for custom elements and JSON schema generation to be placed at root level, add generation to build for npm and update .gitignore

* fix: updates umbraco package schema location

* git ignores

* fix: outputs the vscode custom elements file at root

* fix: adds generated files to output

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2026-01-15 15:11:26 +00:00
e7de6a8afb Backoffice: Fix login logo popover to display Umbraco branding (closes #21078) (#21413)
* fix(backoffice): use hardcoded Umbraco logo in header popover

Fixes issue where the backoffice header logo popover incorrectly
displayed the LoginLogoImageAlternative setting instead of showing
the Umbraco branding.

Changes:
- Added hardcoded umbraco-logo.svg asset to client project
- Updated backoffice-header-logo component to reference static logo
- Wrapped logo in link to umbraco.com
- Removed dependency on BackOfficeLogo endpoint for popover

The small header logo button still uses <umb-app-logo> and remains
customizable via the BackOfficeLogo setting.

Closes #62866

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore: removes link to umbraco.com

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-15 15:27:12 +01:00
d6de4a611a Media Picker: Uploaded files should automatically be selected (closes #21115) (#21409)
* fix(media-picker): auto-select uploaded media items

When uploading media in the media picker modal, uploaded items are now
automatically selected. This works for both single and multiple selection
modes, and correctly handles paginated folders where uploaded items may
not be visible on the current page.

Closes #21115

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(media-picker): navigate to last page after upload

Uploaded media items get the highest SortOrder, placing them on the last
page. This change navigates to the last page after upload so users can
see their newly uploaded items, which are also auto-selected.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-picker/media-picker-modal.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-15 13:06:00 +01:00
Nhu DinhandGitHub 65d7989c23 E2E: QA Add acceptance tests for user group description (#21404)
* Added tests to create a user group with description

* Clean up

* Moved tests for user group description to other class

* Bumped version

* Make tests run in the pipeline

* Reverted npm command
2026-01-15 10:54:37 +00:00
226162d8f2 Performance: Optimize refresh of hybrid cache for a document by retrieving draft and published in single query (#21407)
* Optimize retrieval of ContentCacheNode for draft and publish in when refreshing the hybrid cache.

* Fixed issue with XML header documentation tags.

* Use is null for consistency

---------

Co-authored-by: mole <nikolajlauridsen@protonmail.ch>
2026-01-15 10:52:21 +00:00
dependabot[bot]andJacob Overgaard b484403b1c Bump the npm_and_yarn group across 2 directories with 1 update
Bumps the npm_and_yarn group with 1 update in the /src/Umbraco.Web.UI.Client directory: [diff](https://github.com/kpdecker/jsdiff).
Bumps the npm_and_yarn group with 1 update in the /src/Umbraco.Web.UI.Client/src/packages/core directory: [diff](https://github.com/kpdecker/jsdiff).


Updates `diff` from 7.0.0 to 8.0.3
- [Changelog](https://github.com/kpdecker/jsdiff/blob/master/release-notes.md)
- [Commits](https://github.com/kpdecker/jsdiff/compare/7.0.0...v8.0.3)

Updates `diff` from 7.0.0 to 8.0.3
- [Changelog](https://github.com/kpdecker/jsdiff/blob/master/release-notes.md)
- [Commits](https://github.com/kpdecker/jsdiff/compare/7.0.0...v8.0.3)

---
updated-dependencies:
- dependency-name: diff
  dependency-version: 8.0.3
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: diff
  dependency-version: 8.0.3
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-15 11:22:54 +01:00
f000b00c65 Server Events: Add runtime state check and error handling to ServerEventRouter (#21406)
Add resilience to ServerEventRouter to prevent failures during unattended
install/upgrade when SignalR (especially Azure SignalR) is configured.

Changes:
- Skip server event routing when runtime level is not Run (Install/Upgrade)
- Add try-catch with warning logging for graceful degradation on SignalR failures
- Add backwards-compatible obsolete constructor using StaticServiceProvider pattern
- Add unit tests for runtime level checks

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 10:20:06 +00:00
Chris HoustonandGitHub 720774d219 Code Quality: Fix CS1574 and CS0419 XML documentation warnings (#21400)
Docs: Fix CS1574 and CS0419 XML documentation warnings

Fixed 17 build warnings related to XML documentation cref attributes:

CS1574 (cref attribute could not be resolved):
- IApiMediaQueryService: Changed see cref to paramref for path parameter
- Permission resources: Removed cross-assembly cref to handlers in Management API
- ContentService: Removed reference to non-existent SaveAndPublish method
- ModelsBuilderModeValidator: Fixed cref to Constants.ModelsBuilder.ModelsModes.Nothing
- ImageCropperPropertyValueEditor: Fixed cref to TemporaryFileUploadValueBase.Src
- NPocoSqlServerDatabaseExtensions: Removed cref to external NPoco method
- RegisteredReloadableLogger: Removed cref to non-existent RefreshingRazorViewEngine
- FriendlyPublishedContentExtensions: Removed cref to non-existent AncestorOrSelf methods
- ManagementApiControllerBase: Removed cref to inherited Forbid() method
- BackOfficeExternalLoginProviderErrorMiddleware: Fixed namespace in cref
- HasScheduleFlagProvider: Fixed typo HasScheduleSignProvider -> HasScheduleFlagProvider

CS0419 (ambiguous cref reference):
- ServiceCollectionExtensions: Specified exact overload ConfigureUmbracoDefaults(IHostBuilder)
- IUserStartNodeEntitiesService: Replaced ambiguous GetPagedChildren cref with plain text
2026-01-15 06:45:13 +01:00
Chris HoustonandGitHub e389e3c505 Removing a variable that is not being used - fixes warning CS0168 (#21398) 2026-01-15 06:30:52 +01:00
9bf54ca9cb UI: Refactor breadcrumb URLs to use Path Constants (#21179)
- Add UMB_WORKSPACE_EDIT_PATH_PATTERN and UMB_WORKSPACE_EDIT_VARIANT_PATH_PATTERN
  to core workspace paths for generic edit URL generation
- Fix UmbPathPattern to support multi-level chaining via toAbsolutePatternString()
- Refactor workspace-menu-breadcrumb to use new path patterns
- Refactor menu-variant-tree-structure-workspace-context-base to use new patterns
- Refactor tree-item-context-base to use UMB_WORKSPACE_EDIT_PATH_PATTERN
- Refactor user-grid-collection-view to use existing UMB_EDIT_USER_WORKSPACE_PATH_PATTERN
- Remove outdated TODO about encoding uniques (handled at data source)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Mads Rasmussen <madsr@hey.com>
2026-01-14 11:08:49 +01:00
59675a083d Code Quality: Fix StyleCop warnings SA1116, SA1401, SA1649, SA1405, SA1121, SA1130, SA1306, SA1028, SA1400, SA1106 (#21377)
* fix(stylecop): resolve SA1106 - remove empty statement

* fix(stylecop): resolve SA1400 - add missing access modifiers

* fix(stylecop): resolve SA1028 - remove trailing whitespace

* fix(stylecop): resolve SA1306 - rename fields to lowercase

* fix(stylecop): resolve SA1130 - use lambda syntax

* fix(stylecop): resolve SA1121 - use built-in type aliases

* fix(stylecop): resolve SA1405 - add messages to Debug.Assert calls

* fix(stylecop): resolve SA1649 - rename files to match type names (partial)

* fix(stylecop): resolve SA1401 - convert fields to const/readonly (partial)

* fix(stylecop): resolve SA1116 - reformat multi-line parameters (partial)

* fix(stylecop): revert breaking changes, add V18 TODO comments

* fix: correct TODO comment for SA1306 - should rename to _completed

* Standardize API file names across modules - No code changes, file names.

- Extracts login model to a dedicated file and preserves binding behavior
- Renames multiple API files to align with updated conventions ( Just to match their names in the code, not changing the actual API names, i.e. no breaking changes )
- Updates DI extensions, mappings, and OpenAPI helpers to follow new naming
- Adjusts tests for consistent formatting and readability
- Preserves behavior; no logic changes, references kept intact

* fix(tests): refactor UserEmail to virtual property pattern

- Convert protected field _userEmail to virtual property UserEmail
- Remove dead code (_userEmail += "groupName" executed after request)
- Update derived test classes to use property instead of field
- Maintains original name to avoid breaking changes
- Follows best practice: virtual property allows derived class override

This was originally changed in my PR from UserEmail to _userEmail, so changing it back to ensure no breaking change, even though this is in a test class.

* Committing small fix to prevent a breaking change, adding commit for future removal.

* Renames helper class and removes BOM

Renames internal helper to follow naming conventions without the T prefix
Removes stray BOM from header to ensure clean compilation
No runtime behavior changes

* Split Physical FileSystem interface into it's own file.

* Split the IContentQueryService into it's own file

Also updated XML docs.

* Reverting the package-lock.json

* Updated the typo for Permision -> Permission

Updated the file name and class to: AddUserGroup2PermissionTable

This should be safe to do so as migrations are logged with their GUID's not the class names.

* Update src/Umbraco.Core/Scoping/CoreScope.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Reverting a binary change.

* Reverted rename of public migration class.

* Revert name in migration plan.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-01-14 09:17:47 +00:00
Sebastiaan JanssenandGitHub a4a6c37c8b Only run this scheduled job on the original repo, not on forks 2026-01-14 10:01:23 +01:00
Andreas ZerbstandGitHub 396a8edd91 E2E: QA updated test helpers to fix flaky acceptance tests (#21373)
* Bumped helpers

* Updated tests

* Bumped version

* Bumped version again to fix flaky renaming test
2026-01-14 08:58:05 +01:00
Andy ButlandandGitHub de87a71bc8 Migrations: Ensure description column is added before earlier User Group migration runs (#21378)
* Ensure the description field added in a later migration for user groups is available when the earlier migration on this table runs.

* Update implementation of fix to store and use the state of UserGroupDto at the time of migrations.
2026-01-14 07:59:39 +01:00
abe772b963 Collection: Introduce Collection Text Filter Extension (#21172)
* scaffolding of a collection text filter extension

* Refactor collection text filter to use API interface

* Fix incorrect tag

* Update types.ts

* Update collection-text-filter.extension.ts

* Add cancelation to debounced search on destroy

* clean up

* add js docs

* two way binding of filter value

* clean up

* Add collection text filter manifest example

Introduced a new filter manifest for the example collection and updated the main manifests file to include it. This enables a text filter extension for the example collection.

* Delete unused element and context

* Update src/Umbraco.Web.UI.Client/src/packages/user/user-group/collection/user-group-collection.context-token.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update user-group-table-collection-view.element.ts

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-13 20:56:27 +01:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>nielslyngsoeNiels Lyngsø
3342d31270 Add loading indicator and error handling to Member Public Access Modal (#21087)
* Initial plan

* Add loading indicator and error handling to public access modal

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

* Fix test for public access modal element

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2026-01-13 14:24:09 +00:00
Niels LyngsøandGitHub 7557f7bfa5 Content Type Designer: make inherited property appear more like the local, to take less focus (#21229)
make inherited property appear more like the local, otherwise it takes too much attention.
2026-01-13 13:40:54 +00:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>nielslyngsoeNiels Lyngsø
dc459bca67 Add loading indicator to composition picker modal (#21086)
* Initial plan

* Add loading indicator to composition picker modal

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

* Document implementation and verify changes

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

* Address code review feedback - add accessibility and documentation

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2026-01-13 13:14:40 +00:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>nielslyngsoeMads Rasmussen
2a7478e7f4 Add loading indicator to data type picker flow modal (#21085)
* Initial plan

* Add loading indicator to data-type-picker-flow-modal

- Added _isLoading state variable
- Updated #getDataTypes() to set loading state with try-finally
- Added uui-loader component in #renderGrid() when loading
- Created test file with basic tests
- Fixed linter warnings

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

* Refactor: Replace inline styles with CSS class for loader

- Added .loader-container CSS class
- Removed inline styles from loader div
- Improves maintainability and follows best practices

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

* Improve tests and revert unrelated package-lock.json changes

- Test observable behavior (loader element) instead of private properties
- Added tests for loader visibility during loading states
- Added test for loader removal after loading completes
- Reverted unintended changes to Umbraco.Web.UI.Login/package-lock.json

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

* Refactor: Extract helper function in tests for cleaner code

- Added setLoadingState helper function to reduce code duplication
- Updated comments to reflect testing reactive state property
- Improved test readability and maintainability

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

* delete test file not testing anything

* show loading indicator in search field instead

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>
Co-authored-by: Mads Rasmussen <madsr@hey.com>
2026-01-13 13:05:59 +00:00
Niels LyngsøandGitHub 7b5a087ea5 Sidebar: Make scale grab-area smaller (#21228)
make split view scale area smaller
2026-01-13 13:57:54 +01:00
Andy ButlandandGitHub fefb9ca7aa Docs: Add branch naming convention details to CLAUDE.md (#21311)
* Update Claude memory files to provide further instruction on branch naming convention.

* Resolved points raised in code review.
2026-01-13 13:50:15 +01:00
Andy ButlandandGitHub 8f584e1a49 Tests: Fix intermittent failure in RecycleBinMediaProtectionHelperTests (#21332)
Use thread-safe collections in unit tests verifying functionality that uses parallel processing.
2026-01-13 10:20:35 +01:00
de02456cf8 E2E: QA Added acceptance tests for removing not-found items (#21371)
* Added tests for removing a not-found member picker

* Added tests for adding thumbnail to block

* Updated tests to match the test helper changes

* Refactor code to avoid duplication

* Added tests for removing a thumbnail from a block list/grid and refactor code

* Bumped version

* Bumped version and make tests run in the pipeline

* Update smokeTest command to use '@smoke' filter

---------

Co-authored-by: Andreas Zerbst <73799582+andr317c@users.noreply.github.com>
2026-01-13 03:57:04 +00:00
f47a456515 Datatype Collection: Add fallback icon for datatype (#21269)
Add fallback icon for datatype

Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
2026-01-12 15:22:41 +01:00
engjlr 234907aedb Merge branch 'main' of https://github.com/umbraco/Umbraco-CMS 2026-01-12 13:57:57 +01:00
CopilotJacob Overgaardcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>nielslyngsoeNiels LyngsøEngiber Lozadaengjlr
e3ff3ea3b9 Add loader and error handling to MFA modal (#21089)
* Initial plan

* Add loader and error handling to MFA modal

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

* Use localized error messages in MFA modal

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

* Refactor render logic into separate #renderContent method

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

* Revert src/Umbraco.Web.UI.Login/package-lock.json to main branch version

* Removed unused variable.

* Center the loader in MFA modal.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
Co-authored-by: engjlr <enl@umbraco.dk>
2026-01-12 13:51:59 +01:00
Jacob OvergaardandClaude Sonnet 4.5 6050bdd40f Tree pickers: Implement noAccess property UI handling for user start nodes
- Add noAccess observable to document and media tree item contexts
- Add visual styling (grayed out, italic, cursor: not-allowed) for noAccess items in tree views
- Implement click prevention to block navigation when noAccess is true
- Update document and media picker input contexts with type-safe guards to prevent selection of noAccess items
- Create type guard utilities (isDocumentTreeItem, isMediaTreeItem) in separate utils files
- Items with noAccess are shown for navigation but cannot be selected or opened

This implements Task 63363 for Feature 63060 "Handle Start Nodes"

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-12 10:52:10 +01:00
8c23cb2ffd Block Grid: Resolve translation keys for group names (closes #20696) (#21362)
* Block Grid: Resolve translation keys for group names (closes #20696)

Add localization support for block group names in two locations:
- Block Grid area type permission combobox options
- Block catalogue modal group headers

Translation keys (e.g., #content_isPublished) used as group names
are now properly resolved instead of displaying the raw key.

* fix: moves group.name to mapper so search works

* fix: localizes block types in permissions element too

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-12 08:16:08 +00:00
faba205025 Collections: fix create action causing full page navigation (#21366)
Fix collection create action causing full page navigation instead of SPA routing

When a collection create action had an href, clicking it would trigger a full
browser navigation instead of using history.pushState for SPA routing. This was
caused by event.stopPropagation() being called before the early return when an
href was present, preventing the global ensureAnchorHistory() listener from
intercepting the click event.

The fix moves stopPropagation() to only execute when we're actually handling
the click via execute() (no href), allowing href-based navigation to bubble
to the window-level router listener for proper SPA navigation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-12 07:55:55 +01:00
Chris HoustonandGitHub 35c940364c Code Quality: Fix SA1500, SA1111 and SA1134 StyleCop warnings (#21369)
style: fix SA1500, SA1111, SA1134 StyleCop warnings

Fixed 194 StyleCop analyzer warnings across the codebase:

- SA1500: Move opening braces to their own line for multi-line statements (80 warnings)
- SA1111: Move closing parenthesis to same line as last parameter (50 warnings)
- SA1134: Place each attribute on its own line (64 warnings)

Also added XML documentation to any public APIs within the edited files.
2026-01-12 07:06:56 +01:00
Andy ButlandandGitHub f4ff1da043 Cache: Resolve thread safety issues in RepositoryCacheKeys and FullDataSetRepositoryCachePolicy (closes #21350) (#21355)
* Verify and resolve thread safety issues in RepositoryCacheKeys.

* Verify and resolve thread safety issues in FullDataSetRepositoryCachePolicy.
2026-01-12 06:58:13 +01:00
Niels Lyngsø 30aea87e28 correct comments 2026-01-09 21:33:01 +01:00
Niels LyngsøandGitHub 93229fd457 Mobile navigation: enable horizontal scroll on small screens (#21354)
* make backoffice minimum 800px wide

* minimum 920px
2026-01-09 14:19:47 +00:00
4f29bcd8d6 Fix #20769, added support for clip-text for UmbTableColumn (#20808)
Co-authored-by: Markus Johansson <markus@obviuse.se>
Co-authored-by: Mads Rasmussen <madsr@hey.com>
2026-01-09 13:26:42 +00:00
Niels LyngsøandGitHub 80dddc430c declare events in the global event interface map (#21349) 2026-01-09 13:12:41 +01:00
5a33a55f21 Block workspace: Enforce "AllowEditInvariantFromNonDefault" in variant blocks (closes #20633) (#20868)
* implement preventEditInvariantFromNonDefault for variant blocks

* remove unused

* refactor to enforce both document and block case from the document module

* remove implementation from doc workspace

* prevent editing invariant blocks from non default

* remove unused imports

* more explicit class names

* Refactor invariant edit guard rule creation

Extracted the creation of the invariant property edit guard rule into a reusable _createRule method in the controller base class. Updated block and document workspace controllers to use this method

* Refactor invariant edit rule logic into base controller

Moved the logic for observing properties and variant options and applying property guard rules into a new _observeAndApplyRule method in the base controller. Updated document block and workspace controllers to use this shared method, reducing code duplication and improving maintainability.

* Refactor invariant block edit check into helper methods

Extracted logic for checking invariant blocks and default language datasets into private async methods for better readability and maintainability. This refactor also corrects a context check typo and improves error handling.

* remove unused

---------

Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2026-01-09 09:51:05 +00:00
Mads RasmussenandGitHub 4276e903d7 Fix MSW typescript compile errors (#21319)
Refactor mock handlers to use typed request/response models
2026-01-09 08:50:16 +01:00
Andy ButlandandGitHub c5098f6df3 Multi-node picker: Validate content type when filter is configured but object type is not (closes #21338) (#21342)
Validate content type for multi-node picker when content types are defined but the object type is not (consider as being for a document).
2026-01-09 06:48:42 +01:00
aa0d636503 Entity Signs: rounded infobox top corners (#21341)
* correct styling for entity sign infobox top corners

* Used shorthand notation for padding.

---------

Co-authored-by: engjlr <enl@umbraco.dk>
2026-01-08 17:47:14 +00:00
aka James4uandGitHub a0e1908d2b fix(slider): enforce Maximum Value configuration (closes #21323) (#21339)
- Fixed inverted logic in #parseNumber method
- Changed Number.isFinite(num) ? undefined : num to Number.isFinite(num) ? num : undefined
- Previously, valid max values (e.g., 50) were being ignored and defaulting to 100
- Now correctly parses and uses the configured Maximum Value from data type settings

This ensures the Maximum Value setting in Slider datatype configuration
is properly enforced in the slider UI component.
2026-01-08 17:49:12 +01:00
3e57bb8128 Multi-link picker: Allow drag and drop into empty link picker (closes #21295) (#21325)
* fix(backoffice): allow drag and drop into empty link picker (closes #21295)

* refactor: use classMap to avoid empty class attribute

* refactor: use margin/padding trick for drop zone

* refactor: use CSS :has() selector instead of class

* also enable it for the Document input

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2026-01-08 15:24:42 +01:00
Dirk SeefeldandGitHub 0094c19780 Gitignore: Exclude acceptance test results from git (#21336)
* exclude acceptance test results from git

* reorder .gitignore
2026-01-08 13:01:03 +00:00
Andy ButlandandGitHub ed162feddf Server Events: Route document updated event when public access entries are modified (closes #21237) (#21310)
* Route server events for public access updates to indicate an update to the protected document.

* Add unit tests for all ServerEventSender notification handlers.

* Adds additional test recommended in code review.

* Addressed parameter name issue raised in code review.
2026-01-08 09:31:27 +01:00
Andreas ZerbstandGitHub 28e9607910 QA: E2E skip running sqlite acceptance test on nightly pipeline (#21331)
* Added skip condition for SQLite

* Added missing comma

* Added missing bracket

* Followed naming convention of parameters and added comments
2026-01-08 08:32:02 +01:00
Andy Butland b8d02067fe Merge branch 'release/17.1'
# Conflicts:
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
2026-01-08 06:36:50 +01:00
3fd311bce8 E2E: QA added temporary waits to flaky acceptance (#21324)
* Added wait

* Additional fixes

* More fixes

---------

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>
2026-01-08 03:13:54 +00:00
calmandGitHub 55b77157ec Backoffice entity actions: prevent entity actions dropdown from closing on first click (#21322)
fix(backoffice): prevent entity actions dropdown from closing on first open (closes #21320)
2026-01-07 17:04:12 +01:00
calmandAndreas Zerbst 2832436a0e Fix login validation messages not showing on submit (#21306)
Co-authored-by: Andreas Zerbst <73799582+andr317c@users.noreply.github.com>
(cherry picked from commit 4df4ee9c31)
2026-01-07 16:06:51 +01:00
906cbb50d8 Removed margin-top to address the loader icon shifting when entering … (#21301)
* Removed margin-top to address the loader icon shifting when entering text

* Space

---------

Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
2026-01-07 13:39:08 +00:00
Engiber LozadaandGitHub 93dff1a7a3 Entity Sings: Add localization keys to core entity signs. (#21302)
Improving localization support for core entity signs.
2026-01-07 14:26:31 +01:00
Niels Lyngsø 962db17628 lint fixes 2026-01-07 13:36:57 +01:00
4df4ee9c31 Fix login validation messages not showing on submit (#21306)
Co-authored-by: Andreas Zerbst <73799582+andr317c@users.noreply.github.com>
2026-01-07 13:34:57 +01:00
Andreas ZerbstandGitHub c37fab8e4e E2E: QA Update acceptance to use new assertion pattern (#21297)
* Updated tests

* Updated culture

* Updated tests to use new assertion helpers

* Updated helpers

* Updated tests

* Updated tests

* Updated tests

* Bumped version

* Bumped version
2026-01-07 12:19:02 +00:00
bfc8053c11 Docker: Fix Docker template healthcheck, bind mounts, and HTTPS support (fixes #21278) (#21299)
* Fix test entrypoint

* Fix healthcheck.sh

* Ensure bind mounts gets created on host

* Fix bind mounts permission issues

* Generate self signed cert for dev

* Only generate cert for localhost

* Fixup docker compose file

* Update templates/UmbracoProject/entrypoint.sh

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update templates/UmbracoProject/Dockerfile

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix APP_UID runtime availability and optimize chown performance

- Export APP_UID as ENV so it's available at container runtime
  (ARG values from .NET base image are only available at build time)
- Only run chown -R when directory ownership differs from APP_UID
  to avoid slow recursive operations on large directories

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 12:56:58 +01:00
Andy ButlandandGitHub 462d63b055 Media: Fix files not deleted from disk when recycle bin protection is enabled (#21309)
* Fix bug where with recycle bin media protection on, the files on disk aren't deleted when the recycle bin is emptied.

* Fix display of media URL when in recycle bin and protection of trashed media is enabled.
2026-01-07 11:20:09 +00:00
50b2c8e03b Performance: Only flush ID/Key map in ContentCacheRefresher on content deletion (#21283)
* Clean-up of ContentCacheRefresher: resolved warnings and tidied up code and comments.

* Only flush the ID/Key map when content is deleted.

* Update src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-07 12:14:36 +01:00
Andy Butland 2564d04941 Merge branch 'main' of https://github.com/umbraco/Umbraco-CMS 2026-01-07 11:40:47 +01:00
Andy Butland ca3091e92e Fixed version of Microsoft.Extensions.Caching.Hybrid from incorrect one added in earlier commit. 2026-01-07 11:40:33 +01:00
Niels Lyngsø 036225a76d early return if manager is not present 2026-01-07 11:39:49 +01:00
307f3be505 User group: add description to user group (discussions/14986) (#21057)
* Adding description to user groups

* Add description to dto and test and umbraco plan

* update unit test for user group

* remove change from link picker

* edit icon ui for user group

* Fixed table exists check in migration.

* update description in table user groups

* update user group editor css

* update description default

* remove description column element

* add ignore large method

* remove codesence

* update user group descriptions default

* Added description to constructor of ReadOnlyUserGroup.

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Andreas Zerbst <73799582+andr317c@users.noreply.github.com>
2026-01-07 11:17:04 +01:00
Niels LyngsøandNiels Lyngsø b5fb17682c Hotfix: refactor of #21221 (#21293)
* refactor reload method to entity-detail

* refactor property value transfering when variation changes
2026-01-07 10:53:11 +01:00
Andy ButlandandNiels Lyngsø 83882a94bd Bumped version to 17.1.0.
# Conflicts:
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
2026-01-07 10:53:11 +01:00
0c5156c7b3 Content: Fix property variation change breaking document save via Infinite Editing (closes #21195) (#21221)
When changing a property's variation setting (Shared/Invariant ↔ Variant) via Infinite Editing,
the document would fail to save with a 404 error.

This fix:
- Adds value migration fallback logic in UmbPropertyValuePresetVariantBuilderController
  to find values when culture/segment doesn't match exactly
- Overrides reload() in UmbContentDetailWorkspaceContextBase to process incoming data
  through _processIncomingData() for proper value transformation
- Detects property variation changes and triggers document reload to migrate values

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 10:52:52 +01:00
Kenn JacobsenandGitHub c859b2883d Members: Fix IMemberService.GetByKeysAsync() (#21312)
Fix IMemberService.GetByKeysAsync()
2026-01-07 07:54:20 +00:00
2aa49a3004 Performance: Avoid database lookup in UserIdKeyResolver for super-user (#21281)
* Avoid an unnecessary look-up for the super-user when resolving ID from key and vice versa.
Utilise an async database methods given the enclosing method is async.

* Applied suggestions from code review.

* Revered async amend (caused pipeline failures with integration tests).

* Apply suggestions from code review

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

---------

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
2026-01-07 08:37:59 +01:00
Andy ButlandandGitHub 54a1364a88 Dependencies: Update Microsoft packages to 10.0.1 and pin vulnerable transitive dependencies (closes #21122) (#21285)
* Added transitive dependency references to specific libraries where direct dependencies depend on vulnerable versions.

* Enable CentralPackageTransitivePinningEnabled.

* Update MS dependencies to 10.0.1 patch versions.

* Removed System.Text.Encodings.Web.

* Add TODOs for pinned dependency removal.
2026-01-07 06:58:12 +00:00
603788a039 Extension insights: Hide icon from table collection (#21268)
Hide icon from table collection

Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
2026-01-06 15:00:24 +00:00
dependabot[bot]andJacob Overgaard c8454684d0 Bump qs
Bumps the npm_and_yarn group with 1 update in the /src/Umbraco.Web.UI.Client directory: [qs](https://github.com/ljharb/qs).


Updates `qs` from 6.14.0 to 6.14.1
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.0...v6.14.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-06 15:46:22 +01:00
Niels LyngsøandGitHub 21736159af Hotfix: refactor of #21221 (#21293)
* refactor reload method to entity-detail

* refactor property value transfering when variation changes
2026-01-06 15:45:33 +01:00
848df27482 Auto close focus leave (#20700)
* working on a auto closing ... modal when focus leaves

* remove appsetting for local development

* rewrites the focus function to check if the shadow dom exsits before trying to set focus

* Fix JSON formatting in appsettings.Development.template.json

* Simplify focus logic in entity action list

Refactored the focus method to directly focus the first menu item after it is rendered, removing unnecessary nested updateComplete checks and focusing logic for the label button.

* Remove unused 'nothing' import and minor formatting

* Call ext.component?.focus() instead of chaining updateComplete promises.

* Update entity-action-list.element.ts

---------

Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
Co-authored-by: engjlr <enl@umbraco.dk>
2026-01-06 13:43:25 +00:00
6ed4075eeb Added localize label for entity sign bundle (#21252)
* Added localize for entity sign bundle label

* Updated how entity-sign-bundle.element handles label localize

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Removed unnecessary conditional check for localize entity-sign-bundle

* Moved localize into render method for entity-sign-bundle.element

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
2026-01-06 14:00:48 +01:00
0da1fa116e Markdown Conversion: Remove hard dependency on deprecated library and replace with IMarkdownToHtmlConverter abstraction (closes #21238 and #19500) (#21242)
* Add IMarkdownToHtmlConverter abstraction with Markdig and HeyRed implementations

- Add IMarkdownToHtmlConverter interface in Umbraco.Core.Strings
- Add MarkdigMarkdownToHtmlConverter using the Markdig library (new default)
- Add HeyRedMarkdownToHtmlConverter using HeyRed.MarkdownSharp (deprecated, for backwards compatibility)
- Update HealthChecks.MarkdownToHtmlConverter to use the new abstraction
- Update MarkdownEditorValueConverter to use the new abstraction
- Add unit tests for both markdown converter implementations
- Add unit tests for HealthChecks.MarkdownToHtmlConverter syntax highlighting

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from code review.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-06 08:43:47 +01:00
Andy Butland 4e2fdfc269 Bumped version to 17.1.0. 2026-01-06 07:52:42 +01:00
Nhu DinhandGitHub 51cec0e41f E2E: QA Updated acceptance tests for max length validation message (#21230)
* Updated max length validation message

* Fixed format
2026-01-05 17:26:14 +07:00
b563561d2a Management API: Scope notification headers and add document inclusion abstraction (closes #21231, #21241 and #21240) (#21244)
* Adds a check to ensure notifications are only added to relevant CMS management API endpoints.

* Add null check for tagging actions to handle null management API GroupName.

* Provide abstraction for the DocInclusionPredicate when configuring SwaggerGenOptions.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-05 11:16:41 +01:00
c003751466 Content Type Cache: Clear ContentTypeCommonRepository cache when data types change (closes #21261) (#21289)
Cache: Clear ContentTypeCommonRepository cache when data types change

When a data type's EditorAlias is changed (e.g., BlockList to SingleBlock), the ContentTypeCommonRepository 5-minute cache was not being cleared. This caused content types to return stale PropertyEditorAlias values until server restart, leading to validation using the wrong property editor validators.

The fix adds IContentTypeCommonRepository as a dependency to DataTypeCacheRefresher and calls ClearCache() in RefreshInternal(), matching the pattern already used in ContentTypeCacheRefresher and TemplateCacheRefresher.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 18:02:36 +09:00
caa01b5122 Code quality: resolve build warnings CS0169 and CS0649 (#21258)
* Fixed the 22 warnings.

Also updated the XML documentation where required.

* Updating XML docs.

* Noted version in obsoletion message.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-01-04 11:52:25 +00:00
Chris HoustonandGitHub b14a7ca3e3 Code Quality: Fix ASP0019 warnings by replacing Headers.Add with Headers.Append (#21260)
Use Append for response headers

- Replaces Add with Append when adding response headers to conform to IHeaderDictionary usage.
- Applies across unauthorized handling, redirects, and custom header signaling.
- Updates tests to use Append consistently.
- Removes ASP0019 from warnings in project configs to reflect new approach.
2025-12-31 12:00:15 +01:00
Chris HoustonandGitHub 0803ab8bda Build Warnings: Suppress ASPDEPR003 warnings in DevelopmentMode.Backoffice (#21259)
Silences deprecation warning in dev mode

Silences deprecated runtime-compile warning in dev mode
Keeps development-time runtime compilation enabled for in-memory dev setup
Relates to ASPDEPR003
2025-12-31 11:29:55 +01:00
Nhu DinhandGitHub ac86d97c4e E2E: QA Update acceptance tests to use refactored UI helpers (#21266)
* Bumped version

* Updated tests to match new changes

* Bumped version

* Bumped version

* Use isSuccessNotificationVisible instead of doesSuccessNotificationHaveText

* Updated tests to avoid flaky tests

* Bumped version of test helper

* Bumped version of test helper

* Bumped version

* Bumped version

* Bumped version

* Update waits to avoid hardcoded values
2025-12-31 14:47:53 +07:00
cbde6af9c0 Code quality: Fixing XML docs issues CS1570, CS1572, CS1723, & CS1575 (#21256)
* FIxing XML docs issues CS1570, CS1572, CS1723, & CS1575

* Update ContentImagingSettings.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update ITypedSingleBlockListProcessor.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update PublishedContentExtensions.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-26 10:57:16 +01:00
Chris HoustonandGitHub da8a532080 Code quality: Remove obsolete serialization constructors (fixing SYSLIB0051 warnings) (#21235)
* fix(SYSLIB0051): Remove obsolete serialization constructors

Removes obsolete formatter-based serialization constructors from exception
classes to resolve SYSLIB0051 warnings. This is NOT a breaking change as:

- Binary serialization is deprecated in modern .NET
- No BinaryFormatter usage exists in the codebase
- Microsoft recommends removing these obsolete constructors

Affected files:
- AuthorizationException.cs
- BootFailedException.cs
- ConfigurationException.cs
- PanicException.cs
- UnattendedInstallException.cs
- RetryLimitExceededException.cs
- IncompleteMigrationExpressionException.cs
- HttpUmbracoFormRouteStringException.cs
- ModelBindingException.cs

Also removes SYSLIB0051 from WarningsNotAsErrors in project files.

* fix(SYSLIB0051): Mark obsolete serialization constructors for removal in v19
2025-12-24 15:46:29 +01:00
cf7623e5bd Code quality: Resolve CS0108 compiler warnings by adding explicit 'new' keyword (#21236)
* fix: Resolve CS0108 compiler warnings by adding explicit 'new' keyword

fix: Resolve CS0108 compiler warnings by adding explicit 'new' keyword to hiding members

- Add 'new' modifier to Empty properties in BlockGridModel, BlockListModel, RichTextBlockModel
- Add 'new' modifier to DeepCloneWithResetIdentities in IContentType, IMediaType
- Add 'new' modifier to Save/GetById methods in IContentService, IMediaService, IMemberService
- Add 'new' modifier to EFCore interface members (IAmbientEFCoreScopeStack, IEFCoreScope)
- Add 'new' modifier to ExternalLoginSignInResult.NotAllowed and CreateMediaTypeRequestModel.Collection
- Improve XML documentation for edited public members

Note: CS0114 warnings (virtual/override) were intentionally not fixed as they may be breaking changes. Will add more details to the PR.

* fix: Resolve CS0108 compiler warnings by adding explicit 'new' keyword

fix: Resolve CS0108 compiler warnings by adding explicit 'new' keyword to hiding members

- Add 'new' modifier to Empty properties in BlockGridModel, BlockListModel, RichTextBlockModel
- Add 'new' modifier to DeepCloneWithResetIdentities in IContentType, IMediaType
- Add 'new' modifier to Save/GetById methods in IContentService, IMediaService, IMemberService
- Add 'new' modifier to EFCore interface members (IAmbientEFCoreScopeStack, IEFCoreScope)
- Add 'new' modifier to ExternalLoginSignInResult.NotAllowed and CreateMediaTypeRequestModel.Collection
- Improve XML documentation for edited public members

Note: CS0114 warnings (virtual/override) were intentionally not fixed as they may be breaking changes. Will add more details to the PR.

* Modifying the PR based on feedback from Andy.

Files Modified (Removed Duplicate Members)
src/Umbraco.Cms.Api.Management/ViewModels/MediaType/CreateMediaTypeRequestModel.cs
Removed duplicate Collection property (already defined in base class ContentTypeModelBase)
src/Umbraco.Core/Services/IContentService.cs
Removed duplicate GetById(Guid key) method (already in IContentServiceBase<IContent>)
Removed duplicate Save(IEnumerable<IContent> contents, ...) method (already in IContentServiceBase<IContent>)
src/Umbraco.Core/Services/IMediaService.cs
Removed duplicate GetById(Guid key) method (already in IContentServiceBase<IMedia>)
Removed duplicate Save(IEnumerable<IMedia> medias, ...) method (already in IContentServiceBase<IMedia>)
src/Umbraco.Core/Services/IMemberService.cs
Removed duplicate Save(IEnumerable<IMember> members, ...) method (already in IContentServiceBase<IMember>)
Files Left Unchanged (Keeping new keyword)
The following files were correctly fixed with the new keyword because they intentionally hide base members to return more specific types:
BlockGridModel.cs, BlockListModel.cs, RichTextBlockModel.cs - Empty returns specific type
IContentType.cs, IMediaType.cs - DeepCloneWithResetIdentities returns specific interface
ExternalLoginSignInResult.cs - NotAllowed returns ExternalLoginSignInResult instead of SignInResult
IEFCoreScope.cs, IAmbientEfCoreScopeStack.cs - re-declarations for documentation purposes

* fix: Resolve CS0108 compiler warnings by adding explicit 'new' keyword

fix: Resolve CS0108 compiler warnings by adding explicit 'new' keyword to hiding members

- Add 'new' modifier to Empty properties in BlockGridModel, BlockListModel, RichTextBlockModel
- Add 'new' modifier to DeepCloneWithResetIdentities in IContentType, IMediaType
- Add 'new' modifier to Save/GetById methods in IContentService, IMediaService, IMemberService
- Add 'new' modifier to EFCore interface members (IAmbientEFCoreScopeStack, IEFCoreScope)
- Add 'new' modifier to ExternalLoginSignInResult.NotAllowed and CreateMediaTypeRequestModel.Collection
- Improve XML documentation for edited public members

Note: CS0114 warnings (virtual/override) were intentionally not fixed as they may be breaking changes. Will add more details to the PR.

* Modifying the PR based on feedback from Andy.

Files Modified (Removed Duplicate Members)
src/Umbraco.Cms.Api.Management/ViewModels/MediaType/CreateMediaTypeRequestModel.cs
Removed duplicate Collection property (already defined in base class ContentTypeModelBase)
src/Umbraco.Core/Services/IContentService.cs
Removed duplicate GetById(Guid key) method (already in IContentServiceBase<IContent>)
Removed duplicate Save(IEnumerable<IContent> contents, ...) method (already in IContentServiceBase<IContent>)
src/Umbraco.Core/Services/IMediaService.cs
Removed duplicate GetById(Guid key) method (already in IContentServiceBase<IMedia>)
Removed duplicate Save(IEnumerable<IMedia> medias, ...) method (already in IContentServiceBase<IMedia>)
src/Umbraco.Core/Services/IMemberService.cs
Removed duplicate Save(IEnumerable<IMember> members, ...) method (already in IContentServiceBase<IMember>)
Files Left Unchanged (Keeping new keyword)
The following files were correctly fixed with the new keyword because they intentionally hide base members to return more specific types:
BlockGridModel.cs, BlockListModel.cs, RichTextBlockModel.cs - Empty returns specific type
IContentType.cs, IMediaType.cs - DeepCloneWithResetIdentities returns specific interface
ExternalLoginSignInResult.cs - NotAllowed returns ExternalLoginSignInResult instead of SignInResult
IEFCoreScope.cs, IAmbientEfCoreScopeStack.cs - re-declarations for documentation purposes

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Reverted the one of the changes and updated some XML doc tags.

* Should have committed these files.

Corrected a name space in the test files.

* Remove warning on missing access modifiers on interface members.

* Revert breaking namespace change.

* Fixed build errors following namespace reversion.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-24 13:11:03 +01:00
8a47072b9b Code quality: Replace obsolete APIs in Umbraco.TestData controllers (#21251)
* fix(tests): Replace obsolete APIs in Umbraco.TestData controllers

Replaced deprecated synchronous service methods with their modern async
equivalents in the TestData controllers to eliminate CS0618 warnings.

Changes:
- LoadTestController: Replace IFileService with ITemplateService,
  use IDataTypeService.GetAsync(Guid) instead of GetDataType(int),
  use IContentTypeService.CreateAsync() instead of Save()
- SegmentTestController: Use IContentTypeService.UpdateAsync()
  instead of Save()
- UmbracoTestDataController: Use IContentTypeService.CreateAsync()
  and UpdateAsync() instead of Save()

Also adds comprehensive XML documentation to all three controllers
including class summaries, constructor parameters, and method
documentation.

The controllers now use:
- Constants.DataTypes.Guids.TextstringGuid instead of magic int -88
- Constants.Security.SuperUserKey for async service operations
- Task<IActionResult> return types where async operations are used

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Further amends from code review.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-24 09:36:45 +00:00
Chris HoustonandGitHub 6726e6b208 Benchmarks: Fix obsolete BenchmarkDotNet API warnings by replacing depreciated function calls. (#21250)
* fix(benchmarks): resolve obsolete BenchmarkDotNet API warnings

Update BenchmarkDotNet configuration to use current API methods:

- Replace deprecated `ManualConfig.Add()` with `AddDiagnoser()` for memory diagnostics
- Replace deprecated `ConfigExtensions.With()` with `AddJob()` for job configuration
- Initialize `_totalItemCount` field to suppress CS0649 warning

These changes resolve 4 compiler warnings (CS0618, CS0649) in the benchmark project
without any functional changes.

* Removing the benchmark artifacts and adding the folder to gitignore
2025-12-24 10:25:35 +01:00
27209c2295 Code documentation: Added XML Docs to the files in the Umbraco.Cms.Imaging.ImageSharp2 project (#21249)
* Added XML Docs to the files in Umbraco.Cms.Imaging.ImageSharp2

This commit fixes the missing XML documentation with the Umbraco.Cms.Imaging.ImageSharp2 project.

Now this project should have no build warnings.

* Update src/Umbraco.Cms.Imaging.ImageSharp2/UmbracoBuilderExtensions.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-24 09:34:56 +01:00
Chris HoustonandGitHub 6c40dc0d25 Added XML Docs to the files in the Umbraco.Cms.Imaging.ImageSharp (#21248)
The only build errors in this project were missing XML documentation, so I've now updated each of the files.
2025-12-24 09:33:27 +01:00
Chris HoustonandGitHub fe06cd28bf Code Quality: Fixing the build warnings in the Umbraco.Tests.Common project (#21245)
* Fixing the build warnings in the Umbraco.Tests.Common Project

The main warning was about the file name not matching the class, this was because an interface was being defined first within the class file.

This should either be moved into its own file, or to the bottom of the file to fix this warning. Rather than creating a new file, I've moved the interface to the bottom, but if you'd prefer a new file, just let me know :)

Also removed the TODO in the project file and the WarningsNotAsErrors as they have all been resolved.

* Moved the interface into it's own file.

* Removed the interfaces folder.
2025-12-23 18:11:51 +00:00
Chris HoustonandGitHub 4083ceb63a fix: Update RenderNoContentController to use non-obsolete constructor (#21246)
- Replace obsolete constructor call that used IUmbracoContextAccessor
- Use new constructor with IDocumentUrlService instead
- Add using for Umbraco.Cms.Core.Services namespace
- Remove unused Umbraco.Cms.Core.Web namespace

The obsolete constructor was scheduled for removal in Umbraco 18.
This is an internal change only - ControllersAsServicesComposer is not
shipped with Umbraco.Templates package.
2025-12-23 16:07:52 +00:00
69e7151079 Media Picker: Always include folders when searching for media (#21216)
* Always include folders when searching for media. Closes #21149

* Address copilot comments

* Resolved breaking changes.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-12-23 10:16:32 +00:00
Chris HoustonandGitHub 74c71f977c Fixes all SA1117 warnings and adding more XML documentation comments. (#21224)
This commit fixes around 1,000 build warnings related to SA1117, this warning is related to ensuring each parameter passed into a function is on a new line OR all on one line.

I have also added XML code comments to any public function within these files and fixed some that had invalid / old comments.
2025-12-23 10:34:40 +01:00
18fafaa0f3 Content: Fix property variation change breaking document save via Infinite Editing (closes #21195) (#21221)
When changing a property's variation setting (Shared/Invariant ↔ Variant) via Infinite Editing,
the document would fail to save with a 404 error.

This fix:
- Adds value migration fallback logic in UmbPropertyValuePresetVariantBuilderController
  to find values when culture/segment doesn't match exactly
- Overrides reload() in UmbContentDetailWorkspaceContextBase to process incoming data
  through _processIncomingData() for proper value transformation
- Detects property variation changes and triggers document reload to migrate values

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 16:34:47 +01:00
Niels Lyngsø f12da6e4cb Merge branch 'v16/dev' 2025-12-22 13:25:22 +01:00
Niels Lyngsø c19cc92690 optical space adjustment for content-type-designer property 2025-12-22 13:25:06 +01:00
Andy ButlandandZeegaan 2d35e32920 HybridCache: Clear published content cache on content type change (#21225)
* Clear the cacje level published content cache on content type change.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
(cherry picked from commit 396ebdd48d)
2025-12-22 15:45:06 +09:00
396ebdd48d HybridCache: Clear published content cache on content type change (#21225)
* Clear the cacje level published content cache on content type change.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-22 15:14:21 +09:00
e91c2e659d User Avatar: The "Change Photo" button is not working in all cases (#21206)
fix(user): fix avatar change and remove functionality

- Fix "Change photo" button not working on subsequent clicks by using
  { once: true } on the event listener and resetting the input value
- Fix avatar not disappearing after removal by clearing _imgSrc when
  imgUrls is set to empty array

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 09:55:25 +01:00
Andy Butland 5bb265b3a4 Merge branch 'release/17.1' 2025-12-20 09:38:59 +01:00
72e85ecaf6 TextBox: Fix max length validation message showing wrong exceeded count (#21219)
The validation message was showing the total character count instead of how
many characters exceeded the limit. For example, with max=4 and input="12345",
it showed "5 too many" instead of "1 too many".

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 09:21:26 +01:00
296102c0bd Content Type Workspace: Fix navigation blocked after save (#21218)
Content Type Workspace: Sync current data after save to prevent navigation blocking

After saving a content type, only `_data.setPersisted()` was called without
`_data.setCurrent()`. An observer kept `current` in sync with the structure's
ownerContentType, but timing mismatches caused `persisted` and `current` to
differ, triggering false "unsaved changes" detection and blocking navigation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 09:10:52 +01:00
c9dcaa808b Document Permissions: Export UmbDocumentUserPermissionCondition from package (closes #21199) (#21217)
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 08:58:37 +01:00
Chris HoustonandGitHub 107fb48002 Cryptographic Functions: Fixes obsolete .NET API warnings (SYSLIB0023, SYSLIB0045, SYSLIB0021, SYSLIB0013, SYSLIB0012) by replacing deprecated cryptographic and reflection APIs with their modern equivalents. (#21213)
Fixed various obsolete .NET API calls.

These all show up as warnings when you try to build the Umbraco solution:

- SYSLIB0045 (6) - Use proper HashAlgorithm creation
- SYSLIB0023 (4) - Replace RNGCryptoServiceProvider with RandomNumberGenerator
- SYSLIB0021 (8) - Replace deprecated crypto types
- SYSLIB0013 (4) - Replace Uri.EscapeUriString
2025-12-19 21:19:41 +01:00
Chris HoustonandGitHub 7b449658d3 Fix build warning CS0252 - unintended reference comparison in tests (#21212)
Fix unintended reference comparison in tests

Explicitly casts the mock property to string in a predicate to prevent reference equality checks. Adds constraints for storage type and editor to align with expected data characteristics and improve test reliability. Keeps mock service behavior unchanged; minor formatting tweak included.
2025-12-19 21:06:55 +01:00
add5bd31c9 Management API: Fixes the warning CS8524 and tidying up the "Set Status Redirect Url Management Controller" (#21211)
* Fixes the warning CS8524 and tidying up the SetStatusRedirectUrlManagementController

I have added throwing an error in the switch statement if neither of the know enums is submitted to the API, in theory this should never happen.

The other option would be to simply return false, but I think throwing the exception is better as it will alert whoever is calling the API that their call is invalid.

* Update src/Umbraco.Cms.Api.Management/Controllers/RedirectUrlManagement/SetStatusRedirectUrlManagementController.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-19 21:04:53 +01:00
Chris HoustonandGitHub 72447b040a Resolves all CA2017 warnings and improves log message clarity (#21210)
Improves log message clarity

- Refines a warning when a property type alias is missing, clarifying that a default value is returned.
- Improves backoffice token revocation log by including contextual client id for easier troubleshooting.
- Corrects model generation error logging by passing the exception as the first argument for consistency.
2025-12-19 18:31:45 +01:00
Chris HoustonandGitHub b13544083f Docs: Fix CS1573 warnings - add missing XML param tags (#21209)
Add missing parameter documentation tags to resolve CS1573 compiler warnings:

- Add cancellationToken param tags to 24 API Management controllers

- Add payloadType param tags to 6 Webhook extension files

- Fix various missing param tags in Core and Infrastructure files
2025-12-19 18:25:08 +01:00
Nhu DinhandGitHub 70d1728843 E2E: QA Added acceptance tests for content delivery API (#21095)
* Added tests for media delivery api

* Added tests for content delivery api

* Fixed import

* Added tests for Content Delivery API

* Updated skip tag and issue link for the failing tests

* Bumped version

* Refactor code for content delivery api tests

* Moved repeat steps to beforeEach and added more waits

* Bumped version

* Added more waits

* Updated variable names

* Grouped tests

* Renamed

* Fixed names
2025-12-19 10:16:19 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Jacob Overgaard
c08e39064f Storybook: Bumps storybook from 9.0.14 to 10.1.10 (#21208)
* Bump storybook

Bumps the npm_and_yarn group with 1 update in the /src/Umbraco.Web.UI.Client directory: [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/core).


Updates `storybook` from 9.0.14 to 9.1.17
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v9.1.17/code/core)

---
updated-dependencies:
- dependency-name: storybook
  dependency-version: 9.1.17
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps-dev): bumps storybook from v9 to v10

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-12-19 10:13:31 +00:00
Nhu DinhandGitHub dfb7507ae2 E2E: QA Added acceptance tests for rendering content with invariant blocks (#21180)
* Added tests for rendering content with block list

* Updated tests for rendering content with block lists

* Updated message when has no block lists

* Added tests for rendering content with block grid

* Updated code

* Bumped version

* Changed npm command to make tests run in the pipeline

* Fixed comment

* Reverted command
2025-12-19 09:25:17 +00:00
d749af8974 Upgrade MSW from 1.3.5 to 2.12.4 (#21096)
* upgrade msw, migrate all interceptors, and update backoffice integration

* Fix msw test runner integration

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-12-19 10:19:10 +01:00
Nhu DinhandGitHub 14fe6f4888 E2E: QA Fixed failing tests for the current user profile (#21214)
* Added more waits

* Make tests run in the pipeline

* Reverted npm command
2025-12-19 09:18:45 +00:00
Chris HoustonandGitHub 0d4f24300a fix(code-quality): resolve CS0628 warnings - change protected to private in sealed classes. (#21193)
* fix(code-quality): resolve CS0628 warnings - change protected to private in sealed classes

Changed protected members to private in sealed classes across 26 files. Protected members in sealed classes serve no purpose since sealed classes cannot be inherited. This eliminates 57 CS0628 compiler warnings.

* fix: use public for NUnit SetUp methods per Copilot review

NUnit requires SetUp methods to be at least protected. Using public
avoids CS0628 while allowing NUnit to discover and execute the methods.

* Clean up

- Renames internal fields to clearer names and aligns with conventions
- Changes internal cache holder to use auto-properties for state ( fixes another build warning )
- Removes an unused exception type from the loader and cleans up unused usings
- Adds "Umbraco" to the list of known spellings in .vsCode settings file, amazed this wasn't already there :)

* Improvements to fix CodeScene Code Health Review issues.

- Introduces debug-only logging helpers and routes all log messages through them for consistency
- Centralizes retrieval of discoverable types and scanning logic to simplify paths
- Aligns logging of cached vs non-cached and slow paths with new helpers
- Documents data-holding structure used to store type lists for clarity

* Refactoring to make CodeScene happy, removing code duplication :)
2025-12-18 19:28:13 +01:00
Engiber LozadaandGitHub ebb6590bad Data Types: Add condition to hide delete action in non deletable data types. (#21184)
* Created a condition that check non deletable data types.

* Export condition.

* Updated export path.
2025-12-18 09:58:29 +00:00
Niels LyngsøandGitHub 7b85109606 News Dashboard: Update styling to fit with new style (#21185)
styling of the dashboard
2025-12-18 09:51:11 +01:00
Chris HoustonandGitHub 0e60602746 Tests: Fix CS4014 warnings - add missing await operators (#21194)
Fix unawaited async calls in tests

- Converts setup to async and awaits data creation
- Awaits operation status update to fix potential unawaited calls
- Updates nested validation tests to use await for helper results
- Removes stray BOM character in a test file
- Improves overall async flow, addressing CS4014
Relates to CS4014
2025-12-18 07:03:23 +01:00
Mads RasmussenandGitHub e08a60e74d Performance: Embed Store API in Manifests to lower number of network request (#21191)
Refactor manifests to use direct store imports to minimize the number of lazy loaded items on startup
2025-12-17 23:13:39 +01:00
Niels LyngsøandGitHub 4c7abc41b2 Performance: Bundle Js Libs (#21187)
bundle libs
2025-12-17 21:41:58 +00:00
Niels LyngsøandGitHub 45ae5aaac0 Performance: Embeds the API of selected extra Conditions (#21188)
embeds the API of selected Conditions
2025-12-17 21:43:08 +01:00
Niels LyngsøandGitHub 30d9288f12 Culture and Hostnames: Load all languages (#21169)
make sure we take all languages
2025-12-17 16:01:20 +01:00
Kenn JacobsenandAndy Butland 4aa506100e Indexing: Gracefully handle element property variance changes at index time (#21183)
Gracefully handle element property variance changes at index time
2025-12-17 15:53:14 +01:00
700a19f4b9 Block level variance: fix values being polluted when changing variance before publish (#21121)
* TDD solution to Block level variance publishing changing to no variance retaining block level variance values and vica versa

* Add similar tests for the other block editors

* Apply suggestions from code review

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Amend the test scenarios

* Simplify the merge clean-up to remove all misaligned values.

* Fix failing test (remove false assumptions)

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-12-17 15:52:56 +01:00
Niels Lyngsø e27b661b3f update version in package lock 2025-12-17 15:51:12 +01:00
Kenn JacobsenandGitHub 75e4b2b18d Indexing: Gracefully handle element property variance changes at index time (#21183)
Gracefully handle element property variance changes at index time
2025-12-17 15:44:09 +01:00
7b9bef9d2a Content Types: Fix property variation change when content exists only in non-default language (#21182)
* Content Types: Fix property variation change when content exists only in non-default language (closes #11771)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add tests for AllowEditInvariantFromNonDefault enabled scenario

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 15:19:38 +01:00
638e181334 Content Types: Fix property variation change when content exists only in non-default language (#21182)
* Content Types: Fix property variation change when content exists only in non-default language (closes #11771)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add tests for AllowEditInvariantFromNonDefault enabled scenario

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 15:18:14 +01:00
254400bb58 Block level variance: fix values being polluted when changing variance before publish (#21121)
* TDD solution to Block level variance publishing changing to no variance retaining block level variance values and vica versa

* Add similar tests for the other block editors

* Apply suggestions from code review

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Amend the test scenarios

* Simplify the merge clean-up to remove all misaligned values.

* Fix failing test (remove false assumptions)

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-12-17 13:51:19 +01:00
3bcdcc56c8 Cache: Add null checks for entities that may no longer exist during cache refresh (#21181)
* Add null checks in case content no longer exists

* Add logging statement

* Add integration tests for null handling in cache refresh services

Tests verify that DocumentUrlService and PublishStatusService
do not throw exceptions when called with non-existent content keys,
which can happen when processing stale cache instructions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add integration tests for stale cache instruction handling

Tests simulate the scenario where a cache instruction is processed
for content that has been deleted (stale instruction). This can happen when:
1. Content is saved (cache instruction queued)
2. Content is deleted before instruction is processed
3. Server restarts and processes the stale instruction

Tests cover:
- ContentCacheRefresher with RefreshBranch for deleted content
- ContentCacheRefresher with RefreshNode for deleted content
- MediaCacheRefresher with RefreshBranch for deleted media
- Processing instructions for content that never existed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 11:44:07 +01:00
Nhu DinhandGitHub dd6ae8bd25 E2E: QA Added acceptance tests for removing a not-found content picker (#21177)
* Added tests for removing a not-found content picker

* Added comment

* Bumped version

* Change npm command to make tests run in the pipeline

* Reverted npm command
2025-12-17 09:38:39 +00:00
Andreas ZerbstandGitHub aa32035830 E2E QA: Updated integration test that was missing directory setup (#21167)
* Added base class

* Added implementation of base class

* Added missing parameter

* Added try-catch around delete operations in Purge() to ignore locked files during cleanup.
2025-12-17 09:57:17 +01:00
911d2fe67b Content: Fix name() and getName() to use active variant (closes #20759) (#21171)
* Content: Fix name() and getName() to use active variant when no variantId provided

When calling `name()` or `getName()` without a variantId argument, the methods
now correctly return the name of the first active variant from the split view
instead of always returning the first variant in the data array.

The `name()` method now returns a reactive observable that updates when the
active variant changes, using `mergeObservables` to combine the split view's
active variant observable with the variants data.

The `getName()` method now uses `splitView.getActiveVariants()[0]` to get the
current active variant synchronously, with a fallback to the first variant if
no active variant is set.

This fixes an issue where block previews could not reactively observe the
document name because the callback parameter was always empty.

Closes #20759

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: use UmbVariantId.compare() for consistent variant matching

Address PR review feedback by using UmbVariantId.Create() and compare()
instead of direct property comparison. This ensures consistent behavior
with the existing variant comparison pattern used throughout the codebase.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: formatting

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 08:48:42 +01:00
Sven GeusensandAndy Butland 235fa3b743 Added a migration to remove the property regex validation length limit (#21175) 2025-12-17 06:49:02 +01:00
Sven GeusensandGitHub 81685bf0e2 Added a migration to remove the property regex validation length limit (#21175) 2025-12-17 06:48:36 +01:00
Andy Butland 9fb018276a Bumped version to 17.2.0-rc. 2025-12-17 06:41:09 +01:00
Andy ButlandandGitHub a9c6f97f7b Models builder: Re-provide support for casting a published member to the models builder type (closes #21135) (#21150)
* Re-provide support for casting a published member to the models builder type.

* Used new constructor for MemberManager in unit tests.

* Fix failing unit tests.
2025-12-17 06:37:15 +01:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>nielslyngsoe
044cd1da57 Add loader to Culture and Hostnames modal while data loads (#21084)
* Initial plan

* Add loader to culture-and-hostnames modal with undefined state tracking

Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: nielslyngsoe <6791648+nielslyngsoe@users.noreply.github.com>
2025-12-16 14:06:50 +00:00
Niels LyngsøandGitHub 871d590f9a refactor multi url picker validation (#21143)
* refactor multi url picker validation

* fix line break
2025-12-16 13:36:33 +00:00
b207fa31cd Relations: Fix descendants query to exclude parent item (#21162)
* Relations: Fix descendants query to exclude parent item

The GetPagedDescendantsInReferences query was using a path LIKE without
the comma delimiter, causing it to match both the parent item and its
descendants. This resulted in the trash confirmation dialog showing
the item being deleted in the "descending items with dependencies"
list.

Changed the WhereLike call to use ",%" suffix instead of just "%",
so the query only matches actual descendants (items whose path
starts with the parent's path followed by a comma).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add integration test to verify behaviour.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-12-16 13:44:21 +01:00
Laura NetoandGitHub dab88d7019 Document Saving: Fix SQL Server deadlock during concurrent updates (#21156)
Fix SQL Server deadlock during concurrent document updates

Add ReadLock on ContentTree in RefreshMemoryCacheAsync to prevent
deadlocks between cache refresh SELECT queries and concurrent document
UPDATE operations. The deadlock occurred because the operations accessed
umbracoNode and umbracoDocument tables in different orders.
2025-12-16 11:42:49 +01:00
5bd35288f5 Tree: Fix race condition when loading tree with hideTreeRoot enabled (#21160)
The #debouncedLoadTree method was not awaiting repository initialization
before calling loadChildren() when hideTreeRoot was true. This caused a
"repository is missing" error in tree pickers like the Static File picker
used for block thumbnails.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 10:35:09 +00:00
c2730e7308 UI: Prevent headline overflow in umb-body-layout (#21140)
* fix: add title attribute and prevent headline overflow in body layout

* Added min-width property.

---------

Co-authored-by: engjlr <enl@umbraco.dk>
Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
2025-12-16 09:24:56 +00:00
Andreas ZerbstandGitHub 6419585443 E2E: QA Added acceptance tests for issue #20815 (#21151)
* Added tests for making sure that nested variants properties are updated

* Updated failing test

* Bumped version

* Cleaned
2025-12-16 08:38:39 +00:00
Nhu DinhandGitHub dc61f93afe E2E: Added acceptance tests for regression issues (#21098)
* Added tests for regression issue #20962

* Added tests for regression issue #20520

* Added tests for rendering content with RTE

* Added @release tags

* Bumped version

* Make new added tests run in the pipeline

* Updated dataTypeName

* Fixed comments

* Reverted npm command
2025-12-16 07:43:48 +00:00
MoleandGitHub 7982641262 Distributed Background Jobs: Catch exceptions in job loop to improve application resilience (#21099)
* Set exit code when exception is thrown

* Catch any error instead so we don't stop the application

* Handle exception when ensuring jobs
2025-12-16 06:56:04 +01:00
85806fd7b5 Performance: Re-introduce lazy locks (#21102)
* fix: add write lock at outer scope to prevent deadlocks in content publishing

Acquire a write lock on ContentTree at the start of PublishAsync to prevent
deadlocks. Previously, inner scopes would acquire read locks first (via
repository operations), then attempt to upgrade to write locks, causing
deadlocks when multiple transactions tried this simultaneously.

By acquiring the write lock at the outer scope, we ensure consistent lock
ordering and prevent the deadlock scenario.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Re-enable lazy locks

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 06:53:34 +01:00
Laura NetoandGitHub 57cec15928 Content Types: Introduce schema service to support future schema generation (#21031)
* Introduce new content type schema service and models

To be used in the future for content type schema generation.

* Do not fail when content type is not in cache, simply ignore

* Added unit and integration tests

* Fix failing unit tests

* Addressing comments from code review
2025-12-15 20:52:46 +01:00
Andy Butland 59aaa00bab Merge branch 'v16/dev'
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/assets/lang/en.ts
#	src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-picker-modal/icon-picker-modal.element.ts
#	src/Umbraco.Web.UI.Client/src/packages/performance-profiling/dashboard-performance-profiling.element.ts
2025-12-15 19:57:30 +01:00
a429170842 Extend RTE output in Delivery API for better support for multi-site URL resolution (#20846)
* add content key and type to link info on RTE delivery API data. Fix tests to accomodate changes

* convert field type content-id -> destination-id. convert field type content-type -> link-type and use LinkType enum instead.

* revert unintended find and replace changes

* apply patch by kjac

* fix unit tests for RTE parsers

---------

Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2025-12-15 15:23:11 +01:00
+2 0845d267e6 Block Editors: variantId inheritance fix (#21101)
* block context example

* fix clone method

* implement contextual variant id

* use contextual variant id

* ensure currentExposeOf uses elementType configuration

* make hasExposeOf use ElementType configuration for variatId

* Update src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* rename to displayVariant

* use variantid in this case

* update readme

* return false

* remove unused imports

* return undefined

* append UmbWorkspaceViewElement interface

* remove test

* fallback to false

* fallback to false

* refactor what is not exposed.

* Fix #20944: Updating UI Slider number properties to accept decimal values (#20945)

* updating UI slider properties min, max, initial1, initial2 and step to accept decimal values

* Changes based on Copilot suggestions

* Used a smaller step value configuration.

---------

Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
Co-authored-by: engjlr <enl@umbraco.dk>

* Manifests: Fix misnaming and mis-registering of the document validation manifest (closes #21128) (#21139)

Fix misnaming and mis-registering of the document validation manifest.

* Performance: Optimize memory footprint of document URL cache (closes #21055) (#21066)

* Optimize memory footprint of document URL cache.

* Update tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DocumentUrlServiceTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Used properties, added some further comments.

* Fixed failing integration tests.

* Ensure no edge case exists where the culture code to language Id map isn't up to date with the newly created languages.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Block List: Sort mode (#21060)

* Block List: added sort-mode

* Fix missing closing bracket

* register sort mode toolbar element on start up

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts

* Update nightly build to include 16 as 17 is now main (#21144)

* Global search items missing Umbraco url segment (#20266)

* Add /umbraco url Segament for searched mapped results to aviod 404 og navigation away from backoffice

* Applied changes from code review.

---------

Co-authored-by: Lucas Bach Bisgaard <lucas.bisgaard@kraftvaerk.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>

* import

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jason Andrae <jasona@emergentsoftware.net>
Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
Co-authored-by: engjlr <enl@umbraco.dk>
Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Lee Kelleher <leekelleher@users.noreply.github.com>
Co-authored-by: Mads Rasmussen <madsr@hey.com>
Co-authored-by: Sven Geusens <sge@umbraco.dk>
Co-authored-by: Lucas Bach Bisgaard <rammi@rammi.dk>
Co-authored-by: Lucas Bach Bisgaard <lucas.bisgaard@kraftvaerk.com>
2025-12-15 13:31:51 +00:00
6c743e8270 Tiptap RTE: Uses UmbracoCssPath global setting when loading CSS files (closes #20877) (#21106)
* get full path of file

* get configuration umbracosspath from BE and use it when get css file

* fix lint error

* update for test fail

* add obsolete constructor

* Update src/Umbraco.Cms.Api.Management/Controllers/Server/ConfigurationServerController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update src/Umbraco.Cms.Api.Management/Controllers/Server/ConfigurationServerController.cs

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Added `umbracoCssPath` to mock Server handler

* Added `umbracoCssPath` to Server Connection controller

* Removed the Tiptap Config Repository/Store code

we can simplify this by reusing the Server Context.

* Used `UMB_SERVER_CONTEXT` to get the `umbracoCssPath`

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-12-15 14:14:40 +01:00
d47464e2a7 Global search items missing Umbraco url segment (#20266)
* Add /umbraco url Segament for searched mapped results to aviod 404 og navigation away from backoffice

* Applied changes from code review.

---------

Co-authored-by: Lucas Bach Bisgaard <lucas.bisgaard@kraftvaerk.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-12-15 11:51:29 +00:00
Sven GeusensandGitHub e8adb30411 Update nightly build to include 16 as 17 is now main (#21144) 2025-12-15 11:33:33 +01:00
fb1e52a172 Block List: Sort mode (#21060)
* Block List: added sort-mode

* Fix missing closing bracket

* register sort mode toolbar element on start up

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
2025-12-15 11:22:11 +01:00
452c56b882 Performance: Optimize memory footprint of document URL cache (closes #21055) (#21066)
* Optimize memory footprint of document URL cache.

* Update tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DocumentUrlServiceTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Used properties, added some further comments.

* Fixed failing integration tests.

* Ensure no edge case exists where the culture code to language Id map isn't up to date with the newly created languages.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-15 11:19:44 +01:00
Andy ButlandandGitHub aeca0d0f29 Manifests: Fix misnaming and mis-registering of the document validation manifest (closes #21128) (#21139)
Fix misnaming and mis-registering of the document validation manifest.
2025-12-15 09:10:29 +00:00
fa23e1874c Fix #20944: Updating UI Slider number properties to accept decimal values (#20945)
* updating UI slider properties min, max, initial1, initial2 and step to accept decimal values

* Changes based on Copilot suggestions

* Used a smaller step value configuration.

---------

Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
Co-authored-by: engjlr <enl@umbraco.dk>
2025-12-15 09:00:12 +00:00
Sven GeusensandGitHub 906f8e4664 QA Added unit tests for HideBackOfficeTokensHandler (#21067)
* Added unittests for the HideBackOfficeTokensHandler
2025-12-15 09:43:39 +01:00
63fd7ddc52 Repositories: Optimize repository caches to populate for both int and GUID keys (#21124)
* Populate repository cache by GUID when retrieving documents by integer ID, to avoid database hit on subsequent retrieval by key.

* Apply same for media repository.

* Use existing helper to centralise generation of repository cache keys.

* Minor clean-up.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Applied suggestions from code review.

* Apply suggestions from code review

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

* Applied suggestions from code review.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
2025-12-15 07:17:57 +01:00
5569607a81 Workspace: Fix browser title not being set correctly (#21126)
* Block Workspace: Only update view title when opened in modal context

Prevents unintended document title updates when block workspace is used
outside of modal context. Also adds early return in view controller when
no local title is available to avoid setting an empty title prefix.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* View Controller: Reactivate parent view when child is removed

When a non-inheriting child view (like a modal) is removed, the parent
view may have been deactivated. This change ensures the parent view is
reactivated to restore the browser title when the modal closes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Update src/Umbraco.Web.UI.Client/src/packages/core/view/context/view.controller.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-12 15:44:46 +00:00
9a0d2d1f97 Update src/Umbraco.Web.UI.Client/src/packages/tiptap/CLAUDE.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-12 16:30:33 +01:00
leekelleherandJacob Overgaard 99a1a612ce Corrected directory structure layout 2025-12-12 16:30:33 +01:00
leekelleherandJacob Overgaard 0119d8d4da docs: Add CLAUDE.md documentation for Tiptap RTE 2025-12-12 16:30:33 +01:00
Nhu DinhandGitHub 0e90b072c8 E2E: QA Fixing some failing tests related to culture and hostname, log viewer and user (#21132)
* Bumped version

* Updated tests to match new changes

* Bumped version

* Bumped version
2025-12-12 20:02:46 +07:00
08a95b64fe Docs: Add PR naming convention to CLAUDE.md (#21114)
* Docs: Add PR naming convention to CLAUDE.md

Integrate the PR naming guidelines from PullRequestNaming.md into the
Pull Request Process section of CLAUDE.md for better discoverability.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Remove area

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 12:06:46 +00:00
Nicklas KramerandGitHub 08aa10b4f7 Media: Fixing media folder not getting deleted when deleting media files. (#21059)
* Introducing lock and logic to find directory to be deleted

* Obsoleting old ctor

* Expanding obsolete comment

* Adding new parameter to tests

* Adding database to failing integration tests.

* Adding more tests

* Fixing flawed logic

* Cleanup on FileSystemsTests.cs
2025-12-12 17:32:27 +09:00
Lee KelleherandGitHub 8e3764e4fb UFM: Resolves "missing filter" console warning (#21118)
UFM Filter base, check that the component is connected to the DOM

otherwise the context no longer exists and the `render` still runs,
giving a bunch of "missing filter" warnings.
2025-12-11 13:28:32 +01:00
Andy Butland 7492009dc6 Merge branch 'release/17.0'
# Conflicts:
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
2025-12-11 07:07:24 +01:00
Bjarne FyrstenborgandGitHub 23e8ee51d3 Add option to hide colors from icon picker (#20650)
* Add option to hide colors from icon picker

* Hide colors

* Hide colors from config
2025-12-11 06:40:28 +01:00
30a57dd5f5 Content Publishing: Fix deadlocks by acquiring WriteLock at outer scope (#21105)
fix: add write lock at outer scope to prevent deadlocks in content publishing

Acquire a write lock on ContentTree at the start of PublishAsync to prevent
deadlocks. Previously, inner scopes would acquire read locks first (via
repository operations), then attempt to upgrade to write locks, causing
deadlocks when multiple transactions tried this simultaneously.

By acquiring the write lock at the outer scope, we ensure consistent lock
ordering and prevent the deadlock scenario.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 20:59:38 +01:00
3fa053ae4c Delivery API: Adding allow list for content types (#21111)
* Adding allow content type alias settings and validator

* Creating private helper method for tests

* Revisiting logic for disallow types

* Adding tests for validator

* Obsolete unnecessary methods and overloads.

* Fix warning and update naming in tests.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-12-10 16:43:39 +00:00
7b13e23025 Templating: Creating a doctype with template now yields a strongly typed template (closes #20443) (#20688)
* Adjust the document type creation flow so that a template can be created for a content type

Add id, name and alias to the request payload to allow creating multiple templates for the same document type

Small adjustments

Remove unused import and unnecessary async

Switched content type template creation to content type controller

Missing constant export

# Conflicts:
#	src/Umbraco.Web.UI.Client/src/packages/core/backend-api/sdk.gen.ts

* Add default implementation for CreateForContentTypeAsync

* Small adjustments from code review

* Introduce InvalidTemplateAlias content type operation status

* Add tests for CreateTemplateAsync and fix alias validation

- Add integration tests for ContentTypeService.CreateTemplateAsync:
  - Success case with template association
  - NotFound status for non-existent content type
  - InvalidTemplateAlias for empty and too-long aliases
  - Default template assignment verification

- Fix bug in TemplateService.CreateAsync where alias validation
  occurred after GetViewContent call, causing ArgumentNullException
  for invalid aliases instead of returning InvalidAlias status

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 17:36:42 +01:00
Laura NetoandGitHub 8c187a699f Long Running Operations: Ensure eager write lock (#21113)
Use EagerWriteLock for long running operations

Switch from WriteLock to EagerWriteLock when acquiring the lock for
long running operations to ensure proper lock acquisition timing.
2025-12-10 15:24:45 +00:00
e08d80f084 Add Context7 claim file (#21110)
Adds context7.json to claim this repository for Context7 documentation service.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Phil Whittaker <pjw@umbraco.dk>
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-10 15:32:00 +01:00
f5e6d85769 Distributed Background Jobs: Add initialization logging to DistributedJobService (#21112)
* Distributed Background Jobs: Add initialization logging to DistributedJobService

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Use registered instead of intitialized

* Update src/Umbraco.Infrastructure/Services/Implement/DistributedJobService.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Be consistent in log messages

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-10 15:03:34 +01:00
497c31ef2c Sync: Fix SyncBootStateAccessor to use ILastSyncedManager to prevent unnecessary cold boots (#21109)
* Sync: Fix SyncBootStateAccessor to use ILastSyncedManager

Update SyncBootStateAccessor to use the new ILastSyncedManager interface
instead of the deprecated LastSyncedFileManager, which was causing cold
boots to be triggered every time.

- Replace LastSyncedFileManager field with ILastSyncedManager
- Add new primary constructor accepting ILastSyncedManager
- Add obsolete constructors for backwards compatibility using StaticServiceProvider
- Update GetSyncBootState to use GetLastSyncedExternalAsync

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Sync: Add integration tests for SyncBootStateAccessor

Add integration tests to verify SyncBootStateAccessor correctly
determines cold boot vs warm boot state based on ILastSyncedManager.

- Test cold boot when no last synced ID exists
- Test warm boot when last synced external ID matches a cache instruction

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 14:38:09 +01:00
1a4ca0ba4d Sync: Fix SyncBootStateAccessor to use ILastSyncedManager to prevent unnecessary cold boots (#21109)
* Sync: Fix SyncBootStateAccessor to use ILastSyncedManager

Update SyncBootStateAccessor to use the new ILastSyncedManager interface
instead of the deprecated LastSyncedFileManager, which was causing cold
boots to be triggered every time.

- Replace LastSyncedFileManager field with ILastSyncedManager
- Add new primary constructor accepting ILastSyncedManager
- Add obsolete constructors for backwards compatibility using StaticServiceProvider
- Update GetSyncBootState to use GetLastSyncedExternalAsync

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Sync: Add integration tests for SyncBootStateAccessor

Add integration tests to verify SyncBootStateAccessor correctly
determines cold boot vs warm boot state based on ILastSyncedManager.

- Test cold boot when no last synced ID exists
- Test warm boot when last synced external ID matches a cache instruction

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 13:02:10 +00:00
8626ae4f2e Distributed Background Jobs: Improve distributed background job locking behavior and performance (#21100)
* Don't take jobs that's already running and handle stale jobs

* Add tests

* Add bulk insert and delete methods to repository

* Optimize EnsureJobsAsync to use batch inserts

* Add EnsureJobs tests

* Remember to keep old constructor

* Minor cleanup

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
2025-12-10 11:23:38 +01:00
Lotte PitcherandAndy Butland 78dfcf97b7 Dotnet new templates: Fix placeholders and port in umbraco-extension template (#20956)
* reset placeholders and port number in umbraco-extension template

* add readme instructions on how to test templates locally
2025-12-10 07:06:29 +01:00
Niels LyngsøandGitHub f8d6a97196 Blocks: localize group headline (#21090)
localize block group headline
2025-12-09 09:17:20 +01:00
c910f544ee Improve deletion logic in UmbracoContentIndex (#21091)
* Improve deletion logic in UmbracoContentIndex

When re-indexing a node it will first delete it, however in many cases the query to delete doesn't return anything, however a delete command is still executed with an empty integer collection. We can avoid allocating and iterating lists and avoid the deletion call all together if the collection is empty.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Clean-up and warning resolution whilst we are modifying class.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-09 08:01:48 +00:00
Niels Lyngsø 0d599f6639 formatting 2025-12-08 15:23:24 +01:00
cbc832d6f3 Collection: Introduce Card and Ref Collection View kinds (#21037)
* Add entity collection item card extension type + default elements

* implement user collection item card

* fix selection events

* map to prop

* add prop/attr for href

* add support for which detail properties to show

* update type import

* Update src/Umbraco.Web.UI.Client/src/packages/core/collection/item/entity-collection-item-card/entity-collection-item-card.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* import card in correct file

* Fix event listener binding for selection events

* implement disabled property for collection item cards

* init commit of collection item ref extension

* fix imports

* add element interface

* Implement UmbEntityCollectionItemElement interface in item cards

Added the UmbEntityCollectionItemElement interface to document and user collection item card elements for improved type safety and consistency. Updated type exports to include the new interface.

* Update collection item ref to use uui-ref-node

Replaces the placeholder div with a uui-ref-node component, passing relevant item properties and event handlers. Adds dynamic icon rendering using umb-icon.

* Refactor entity collection item elements to use shared base

Introduces a new abstract base class for entity collection item elements, consolidating shared logic for card and ref variants. Updates card and ref element implementations to extend the new base, and refactors extension manifest interfaces for consistency. This improves maintainability and reduces code duplication.

* use class instead of magic string

* introduce ref and card collection view kinds

* Utilise card kind for user collection view

* Add item-specific href support to collection views

Introduces a requestItemHref method to collection contexts for retrieving item-specific hrefs. Updates card, ref, and user table collection views to use these hrefs, enabling dynamic linking for collection items. Refactors user table name column layout to accept href via value prop instead of constructing it internally.

* Update ManifestCollectionView import path

Changed the import of ManifestCollectionView from '../extensions/types.js' to '../view/types.js' to reflect its new location.

* use box

* render entity actions

* use edit path builder for user links

* rename method

* Revert "rename method"

This reverts commit 4df577688e.

* Update collection-default.context.ts

* make type lint ignore unused args with an underscore

* temp remove unused

* only make collection vie selectable if there are any registered bulk actions

* don't render name link if there is no href

* fix imports

* use selectable state

* Update language-table-collection-view.element.ts

* Update card-collection-view.element.ts

* clean up

* Refactor collection views to use shared base class

* refactor(collection): parallelize href fetching and make method private

* docs(examples): update collection example to use card and ref kinds

* docs(examples): add icon property to collection example data model

* Update src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Client/src/packages/core/collection/view/types.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update collection-bulk-action.manager.test.ts

* Removed duplicate and redundant '@typescript-eslint/no-unused-vars' rule definitions, consolidating the configuration to use only 'argsIgnorePattern'.

* Handle missing user href in name column layout

Replaces the user name link with a span when the href property is not provided, preventing broken links in the user table name column layout.

* Update user-table-name-column-layout.element.ts

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-08 11:18:28 +00:00
Jacob Overgaard 4bf83a70c1 build: adds keywords to package.json 2025-12-08 10:35:55 +01:00
d7d52dd488 fix(rte): Deduplicate Tiptap extensions to prevent duplicate name warnings
When multiple Umbraco extensions (e.g., BulletList and OrderedList) include
the same Tiptap extension (ListItem), duplicates were added to the extensions
array, causing Tiptap to log warnings. This change uses a Map to deduplicate
extensions by their name property before passing them to the Editor.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 09:53:37 +01:00
475010148b Added 'mandatory' tag as a visual indicator for webhook events being … (#21075)
* Added 'mandatory' tag as a visual indicator for webhook events being mandatory.

* Map the webhook validation for no events specified to a specific API response problem details message.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-12-05 15:44:33 +00:00
830250b682 Docs: Update outdated branch references from contrib to main (#21072)
The contributing documentation still referenced the old `contrib` branch,
which was causing AI tools to incorrectly use it as the base branch for
comparisons. Updated all references to use `main` instead.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-05 14:24:40 +00:00
fd4ed2cfc1 Back-office auth: Calculate token cookie names at request time (Closes #21050) (#21056)
* Back-office auth: Calculate token cookie names at request time

The __Host- cookie prefix enforces secure cookies at browser level,
which caused cookies to be rejected when running over HTTP in local
development environments even when UseHttps was set to false.

Cookie names are now calculated per-request based on both the UseHttps
setting and whether the current request is over HTTPS, matching the
logic used for the Secure cookie option.

* Update src/Umbraco.Cms.Api.Common/DependencyInjection/HideBackOfficeTokensHandler.cs

Co-authored-by: Sven Geusens <sge@umbraco.dk>

---------

Co-authored-by: Sven Geusens <sge@umbraco.dk>
2025-12-05 11:04:31 +01:00
Nicklas KramerandGitHub 15b2cb7bd1 News Dashboard: Adding functionality to overwrite the cache duration (#21064)
* Adding functionality to overwrite the cacheduration for NewsDashboard

* Making the extension its own class, as to avoid having to inherit the entire service.

* Changing options to duration and adding interface
2025-12-05 10:43:43 +01:00
Andy ButlandandGitHub 5683ae9e4b oEmbed Providers: Updated the X oEmbed provider to use the x.com domain (closes #21052) (#21053)
* Updated the X oEmbed provider to use the x.com domain.

* Fixed issues raised in code review.
2025-12-05 10:38:58 +01:00
b6580ed40c E2E: QA updated current accceptance test project README to match current project (#21063)
* Updated readme

* Update tests/Umbraco.Tests.AcceptanceTest/README.md

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Update tests/Umbraco.Tests.AcceptanceTest/README.md

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

---------

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>
2025-12-05 07:43:34 +00:00
Andy Butland 9340842ee2 Bump version to 17.0.2. 2025-12-05 06:47:46 +01:00
Lotte PitcherandGitHub 61a69852e3 Dotnet new templates: Fix placeholders and port in umbraco-extension template (#20956)
* reset placeholders and port number in umbraco-extension template

* add readme instructions on how to test templates locally
2025-12-04 15:34:57 +01:00
e6c7ef8904 Segments: Only validate segment values for cultures they are defined for (closes #21029) (#21033)
* Only validate segment values for cultures they are defined for.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Integration test suppressions.

* Remove previous implementation using ISegmentService and rely on values provided in the model to determine segments with cultures.

* Omit null culture and remove passing but unrealistic tests.

* Fixed nullability error.

* Apply suggestions from code review

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>

* Relocated function following code review.

* Reset unchanged files.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-12-04 12:54:44 +01:00
effccef81d Collections: Add selection mode toggle in UmbTableElement update lifecycle (#20486)
* Add selection mode toggle in UmbTableElement update lifecycle

* Update src/Umbraco.Web.UI.Client/src/packages/core/components/table/table.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Markup attribute consistency

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-12-04 11:25:19 +00:00
88f04cd722 Document Tree: Fix undefined name for variants without fallback. (#21046)
* fix(backoffice): Tree menu item shows undefined for variant names without fallback

When a document variant has no name set and there's no fallback language
configured, the tree now falls back to the first variant with any name
instead of displaying "undefined".

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(backoffice): Show (Untitled) when no variant has a name

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-04 10:01:57 +00:00
Nicklas KramerandGitHub 26efa520bc Packaging: Fixing bad serialization for data types in packages (#21043)
* Changing data type serialization to datatype

* Moving and correcting comment
2025-12-04 10:52:23 +01:00
d8c03c426e Property Editors: Fix localization of user-provided labels (closes #20974) (#21045)
* fix: uses localization string() to localize user-provided labels

* fix: localizes placeholder as well

* Refinements to the Toggle input

The localizations can happen in the `config` setter,
then they don't need to re-get the localization each re-render.

Added a `when` directive to show/hide the label `<span>` tag.

Removed `_currentLabel` as unused.

* Refinements to the Textbox input

The localizations can happen in the `config` setter,
then they don't need to re-get the localization each re-render.

Refactored the `uui-input` attributes/properties.

* Refinements to the Number input

The localizations can happen in the `config` setter,
then they don't need to re-get the localization each re-render.

Refactored the `uui-input` attributes/properties.

* Update src/Umbraco.Web.UI.Client/src/packages/core/components/input-toggle/input-toggle.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Client/src/packages/property-editors/number/property-editor-ui-number.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Updates based on Copilot feedback

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
Co-authored-by: Lee Kelleher <leekelleher@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-04 09:20:33 +00:00
455e7027a0 Block Grid: Sort mode (#20869)
* Added `icon-sort`

from Lucide's "arrow-down-up" icon.

* Added "Sort" package

with property action and context.

* Adds the "sort" property action and context to the Block Grid property

* [WIP] Observing sort mode toggle on Block Grid editor

* [WIP] Further work on Block Grid editor sort-mode

* Added "umb-sort-mode-toolbar" component

* Fixed typo of private method "renderNoting"

* Renamed "sort" property-action to "sort-mode"

* Corrected bad copypasta!

* Renamed "Sort" package to "Sorter"

to include the Sorter controller and maintain backwards-compatibility.

* Code updates based on @copilot feedback

* Fixed circular references

* Removed reference to "sorter/index.ts"

that I'd missed when relocating the package.

* Moved "sorter" back into "core" package

* Moved the "sort" property-action to a combined "property-actions" location

Fixed up other code, use of constants and manifest clarity.

* rename with claude code (#21036)

* rename with claude code

* include property action in name

* renaming

* Rename sortingMode to isSortMode in property sort context

Refactored property sort mode context and related components to use 'isSortMode'

* add jsdocs

* Update vite.config.ts

* no need to export as element

* add tests

* Ordered the tsconfig namespaces

* Reverted the relocation of the "sorter" controller files

* Import ordering

* Reverted some code style tweaks

* Renamed `sortingMode` to `isSortMode`

* Renamed `sortModeEnabled` to `isSortMode`

* Add tests for property sort mode action

* add js docs

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
2025-12-04 09:03:58 +00:00
Jacob OvergaardandGitHub 26679d17db build(deps): bumps monaco-editor from 0.54.0 to 0.55.1 (#21054) 2025-12-04 08:51:45 +00:00
Andreas ZerbstandGitHub 7b462c17f9 E2E: QA updated flaky acceptance tests (#21012)
* Updated tests

* Updated tests

* Cleane up

* Bumped version of test helpers

* Updated tests

* Bumped test helpers
2025-12-04 08:26:47 +00:00
cb454372f2 Debug mode: Marks UMB-DEBUG cookie as HttpOnly and Secure (#21032)
* fix: sets profiling cookie to httpOnly and strict in order to run non-secure

* fix: adds extra message to explain when you can set a cookie

* fix: simplify cookie explanation comment in WebProfilerRepository

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: checks that the profiler is actually enabled and/or disabled and warns the user if that is not the case

* Update src/Umbraco.Web.UI.Client/src/assets/lang/en.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-04 08:55:39 +01:00
84fecd3521 Backoffice: CTRL+Click to open in a new tab should work on Linux (closes #21009) (#21027)
* fix: uses 'href' as property instead of attribute

* build: runs on PR to release branches

* Content references: Avoid requesting references for content that is not yet persisted server side (#21035)

* Avoid requesting references for content that is not yet persisted server side.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* refactor to use condition

* revert

* danish translations

* da translation

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>

* fix: CTRL+Click now opens links in new tab on Linux

The router's anchor click handler incorrectly assumed non-Windows
platforms use Meta (⌘) key for "open in new tab". This broke
CTRL+Click on Linux, which uses CTRL like Windows.

Changed detection from "is Windows" to "is Mac" so Linux correctly
uses CTRL+Click while Mac continues to use Meta+Click.

Also replaced deprecated navigator.platform with navigator.userAgent.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-03 15:03:18 +00:00
Jacob Overgaard c35fcf181b Merge remote-tracking branch 'origin/release/17.0' 2025-12-03 15:10:47 +01:00
86411e4ae3 Content references: Avoid requesting references for content that is not yet persisted server side (#21035)
* Avoid requesting references for content that is not yet persisted server side.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* refactor to use condition

* revert

* danish translations

* da translation

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2025-12-03 12:25:06 +00:00
6104ae60ce Image cropper modal import missing component (#20651)
* Import missing component

* Handle nullable type

* Vertically center image

* Add minimum width for SVG without dimensions

* 100% height until max height

* 100% height minus top/bottom padding

* Revert "100% height minus top/bottom padding"

This reverts commit 67ada4c70f4b75dfcfa2b54ce139ec7465a17ce1.

* Revert "Handle nullable type"

This reverts commit 3130e11a4be83a18b5a7d8c1c24ee23c94d8765d.

* Removed flexbox style

* Fixed circular dependency

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-12-02 17:07:01 +00:00
bcec927e64 Modal: Remove unused uui-dialog element in modal component (#21030)
refactor(backoffice): remove unused uui-dialog element in modal component

Remove dead code that created an unnecessary uui-dialog element inside uui-modal-dialog. The uui-modal-dialog component already manages its own internal dialog element, making the manual creation redundant.

This aligns the dialog implementation with the sidebar implementation pattern, where container elements manage their own internal structure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 18:04:19 +01:00
Andy Butland 5e1f758117 Merge branch 'release/17.0' 2025-12-02 15:57:09 +01:00
Andy Butland b0e5cd768d Reverted accidental change to Program.cs. 2025-12-02 15:56:30 +01:00
Andy Butland a9d8d13735 Merge branch 'release/17.0'
# Conflicts:
#	tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml
2025-12-02 15:42:32 +01:00
Niels LyngsøandGitHub 857f2900bb Segments: Fix for processing data for Segments-variants (#21018)
* refactor to load segments before processing incoming data

* clean up

* remove unused segment promise
2025-12-02 15:00:25 +01:00
876fc5fff9 Collection: Introduce Collection Item Ref extension type (#20994)
* Add entity collection item card extension type + default elements

* implement user collection item card

* fix selection events

* map to prop

* add prop/attr for href

* add support for which detail properties to show

* update type import

* Update src/Umbraco.Web.UI.Client/src/packages/core/collection/item/entity-collection-item-card/entity-collection-item-card.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* import card in correct file

* Fix event listener binding for selection events

* implement disabled property for collection item cards

* init commit of collection item ref extension

* fix imports

* add element interface

* Implement UmbEntityCollectionItemElement interface in item cards

Added the UmbEntityCollectionItemElement interface to document and user collection item card elements for improved type safety and consistency. Updated type exports to include the new interface.

* Update collection item ref to use uui-ref-node

Replaces the placeholder div with a uui-ref-node component, passing relevant item properties and event handlers. Adds dynamic icon rendering using umb-icon.

* Refactor entity collection item elements to use shared base

Introduces a new abstract base class for entity collection item elements, consolidating shared logic for card and ref variants. Updates card and ref element implementations to extend the new base, and refactors extension manifest interfaces for consistency. This improves maintainability and reduces code duplication.

* use class instead of magic string

* Use ifDefined for href in item card element

* Fix href attribute handling in collection item ref

* Make meta property optional in ManifestEntityCollectionItemBase

* Use ifDefined for href binding in document card

* Fix user card href binding with ifDefined

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-02 13:07:52 +00:00
c612fcc866 Localization: Adds termOrDefault() method to accept a fallback value (#20947)
* feat: adds `termOrDefault` to be able to safely fall back to a value if the translation does not exist

* feat: accepts 'null' as fallback

* feat: uses 'termOrDefault' to do a safe null-check and uses 'willUpdate' to contain number of re-renders

* feat: uses null-check to determine if key is set

* chore: accidental rename of variable

* uses `when()` to evaluate

* revert commits

* fix: improves the fallback mechanism

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-02 11:58:25 +00:00
Niels LyngsøandGitHub c885922a64 Block: open-interaction only available when Content is Editable (#20833)
Only interactive-open area when editable
2025-12-02 10:57:17 +00:00
1cdc15efda Collection: Introduce Collection Item Card extension type (#20954)
* Add entity collection item card extension type + default elements

* implement user collection item card

* fix selection events

* map to prop

* add prop/attr for href

* add support for which detail properties to show

* update type import

* Update src/Umbraco.Web.UI.Client/src/packages/core/collection/item/entity-collection-item-card/entity-collection-item-card.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* import card in correct file

* Fix event listener binding for selection events

* implement disabled property for collection item cards

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-02 10:43:43 +00:00
Kenn JacobsenandGitHub 657ccbd104 Delivery API: Retain the Delivery API login redirect behavior in .NET 10 (closes #21000) (#21023)
* Retain the Delivery API login redirect behavior in .NET 10

* Retrofit fix for backwards compatability
2025-12-02 11:02:18 +01:00
e0999c186b Chore: Move old icons into legacy folder, make new folder for custom. (#20990)
* move legacy icons into a folder

* regenerate icons

* icon compile script for custom

* Reverts "icon-company" to use "building-2" from latest Lucide version

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-12-02 09:49:23 +00:00
da66cbdcf4 Property Editors: Added form control and mandatory support(User, Member, Member Group) (#20672)
* Implement form control for user picker property editor.

* Added form control support to member picker property editor.

* Added form control support to member group picker property editor and removed super.value.

* Reverted max state to infinity.

* Removed console.log inside the render.

* Removed duplicated import.

* Import missing input components in member picker tests

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2025-12-02 07:44:08 +00:00
Andy ButlandandZeegaan 7d0101170e Management API: Return not found from request for content references when entity does not exist (closes #20997) (#20999)
* Return not found when request for content references when entity does not exist.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Move check for entity existence from controller to the service.

* Update OpenApi.json.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Addressed points raised in code review.

* Update OpenApi.json

* Resolved breaking changes.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
(cherry picked from commit da94e0953b)
2025-12-02 13:28:50 +09:00
da94e0953b Management API: Return not found from request for content references when entity does not exist (closes #20997) (#20999)
* Return not found when request for content references when entity does not exist.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Move check for entity existence from controller to the service.

* Update OpenApi.json.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Addressed points raised in code review.

* Update OpenApi.json

* Resolved breaking changes.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-02 13:25:43 +09:00
Andy ButlandandZeegaan 706ac2d8f6 Static files: Fix tree to only provide items from expected folders (closes #20962) (#21001)
* Applies checks for root folders to static file tree service.

* Add integration tests.

* Fix ancestor test.

* Amends from code review.

* Integration test compatibility suppressions.

* Reverted breaking change in test base class.

(cherry picked from commit 84c15ff4d7)
2025-12-02 10:22:15 +09:00
Andy ButlandandGitHub 84c15ff4d7 Static files: Fix tree to only provide items from expected folders (closes #20962) (#21001)
* Applies checks for root folders to static file tree service.

* Add integration tests.

* Fix ancestor test.

* Amends from code review.

* Integration test compatibility suppressions.

* Reverted breaking change in test base class.
2025-12-02 10:20:08 +09:00
Andy ButlandandZeegaan f408d2a1b3 Migrations: Optimise ConvertLocalLinks migration to process data in pages, to avoid having to load all property data into memory (#21003)
* Optimize ConvertLocalLinks migration to process data in pages, to avoid having to load all property data into memory.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Updated obsoletion warning.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
(cherry picked from commit 742de79f46)
2025-12-02 10:18:48 +09:00
742de79f46 Migrations: Optimise ConvertLocalLinks migration to process data in pages, to avoid having to load all property data into memory (#21003)
* Optimize ConvertLocalLinks migration to process data in pages, to avoid having to load all property data into memory.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Updated obsoletion warning.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-02 10:09:54 +09:00
Jacob OvergaardandGitHub 1694e3bad2 Tree: Fix incorrect error notification when deleting last child (closes #20977) (#20985)
* Fix infinite recursion and incorrect error notifications in tree children loading

This commit addresses two critical issues in the tree item children manager:

1. **Infinite recursion vulnerability**: The #resetChildren() method called
   loadChildren(), which could recursively call #resetChildren() again if
   the underlying issue persisted, creating an infinite loop.

2. **Inappropriate error messages**: The "Menu loading failed" notification
   was shown even in legitimate scenarios, such as when deleting the last
   child of a node, where an empty tree is the expected outcome.

Changes made:

- Add ResetReason type ('error' | 'empty' | 'fallback') to differentiate
  between error states and expected empty states

- Extract #loadChildrenWithOffsetPagination() as a terminal fallback method
  that uses only offset pagination and never calls #resetChildren(),
  structurally preventing recursion

- Update #resetChildren() to:
  - Accept a reason parameter to determine whether to show error notification
  - Reset all retry counters (#loadChildrenRetries, #loadPrevItemsRetries,
    #loadNextItemsRetries) to ensure clean state
  - Call #loadChildrenWithOffsetPagination() instead of loadChildren()
  - Only show error notification when reason is 'error'

- Update all call sites of #resetChildren() with appropriate reasons:
  - 'error' when retries are exhausted (actual failures)
  - 'empty' or 'fallback' when no new target is found (may be expected,
    e.g., after deleting items)

The fix makes infinite recursion structurally impossible by creating a
one-way flow: target-based loading can fall back to #resetChildren(),
which calls offset-only loading that never recurses back.

* Fix undefined items array causing tree to break after deletion

This fixes the root cause of issue #20977 where deleting a document type
would cause the tree to "forever load" with a JavaScript error.

The error occurred in #getTargetResultHasValidParents() which called .every()
on data without checking if it was undefined. When the API returned undefined
items (e.g., after deleting the last child), this caused:

TypeError: can't access property "every", e is undefined

The fix adds a guard to check if data is undefined before calling .every(),
returning false in that case to trigger the proper error handling flow.

* Address code review feedback on terminal fallback method

- Change error throwing to silent return for graceful failure handling
- Remove target pagination state updates from offset-only loading method
- Update JSDoc to clarify that method does not throw errors
2025-12-01 20:12:23 +01:00
Andy Butland 35f73a8a31 Migrations: Set a long timeout by default on the migration of system dates (closes #21013) (#21022)
Set a long timeout by default on the migration of system dates.
2025-12-01 19:24:26 +01:00
Andy ButlandandGitHub 24b3f11329 Migrations: Set a long timeout by default on the migration of system dates (closes #21013) (#21022)
Set a long timeout by default on the migration of system dates.
2025-12-01 19:13:55 +01:00
Andy Butland 025a4e056f Merge branch 'release/17.0'
# Conflicts:
#	src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
#	version.json
2025-12-01 19:06:16 +01:00
Andy Butland 822a2fcf70 Migrations: Ensure umbracoPropertyData column casing (#21015)
* Add migration to fix umbracoPropertyData column casing.

* Improve migration with column existence check and logging

- Add ILogger to log when column is renamed
- Check if column exists with incorrect casing before renaming
- Use fluent Rename API instead of raw SQL
- Add XML remarks documentation

?? Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Clarify what old and new column name really is

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: kjac <kja@umbraco.dk>
# Conflicts:
#	src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
2025-12-01 19:01:55 +01:00
34aabb8413 Migrations: Ensure umbracoPropertyData column casing (#21015)
* Add migration to fix umbracoPropertyData column casing.

* Improve migration with column existence check and logging

- Add ILogger to log when column is renamed
- Check if column exists with incorrect casing before renaming
- Use fluent Rename API instead of raw SQL
- Add XML remarks documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Clarify what old and new column name really is

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: kjac <kja@umbraco.dk>
2025-12-01 17:47:31 +00:00
Laura NetoandGitHub 1d59e20daa Delivery API: Missing Member Open API security scheme references (#21020)
Use AddComponent for OpenAPI security scheme registration

Fixes security requirements being serialized as empty objects in the
OpenAPI document by using the document's AddComponent method instead
of directly manipulating the SecuritySchemes dictionary.
2025-12-01 17:24:28 +01:00
Mads RasmussenandGitHub 1c922f34f5 Table Collection View: Update table view icon to 'icon-table' (#20970)
Update table view icon to 'icon-table'

Replaces the 'icon-list' icon with 'icon-table' for all table view manifests across multiple packages to improve consistency and better represent the table view visually.
2025-12-01 16:09:55 +00:00
Mads RasmussenandGitHub 18bbf5609b Data Type Workspace: Enable client mandatory field validation for configuration properties (#20799)
* Add validation property to PropertyEditorSettingsProperty

Introduces a 'validation' field to the PropertyEditorSettingsProperty interface, allowing configuration of mandatory status and custom mandatory messages for property editor settings.

* Pass validation property to umb-property component
2025-12-01 16:03:14 +00:00
0210e942ed Folder Workspace: Support menu expansion and breadcrumbs (closes #20675) (#20712)
* add menu context and breadcrumbs for document type folders

* add menu context and breadcrumbs for media type folders

* add menu context and breadcrumbs for media type folders

* add menu context and breadcrumbs for partial view folders

* add menu context and breadcrumbs for partial view folders

* add menu context and breadcrumbs for script folders

* Register menu structure workspace contexts and breadcrumbs for document blueprints

* fix menu alias

* remove from blueprints

* fix wrong path when navigating from an inner folder to an outer

* remove debugger

* fix structure link between variant to invariant

* fix up path generation

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2025-12-01 14:00:00 +00:00
bbc0f1f894 Upgrade: Deprecates Mangement API controller for defunct our.umbraco.com version checker (#21011)
* fix: deprecates the upgrade checker

* fix: removes any deprecated UI that no longer has a function for upgrade checks in the backoffice

* chore: generates new api types

* chore: deprecates types

* chore: returns direct task

* docs: explains deprecation

* chore: deprecated model

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-12-01 12:50:51 +00:00
Mole 75b40e79a2 Cache: Add awaits to memory cache rebuilds to fix race conditions (#20960)
* Await rebuilds and fix multiple open DataReaders

* Add additional missing awaits

(cherry picked from commit eaf5960a4d)
2025-12-01 13:46:23 +01:00
09844204b7 History: Take URL objects into consideration when storing Backoffice history (#20986)
* fix: allows URL to be passed to navigator

* Also adds fix to Block workspace

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-12-01 12:29:07 +00:00
Kenn JacobsenandGitHub 1c4b4c90c9 Rendering: Don't use element cache level on snapshot cache level properties (#21006)
Don't use element cache level on snapshot cache level propreties
2025-12-01 12:52:35 +01:00
820c34432a Preview: Fix preview showing published version when Save and Preview is clicked multiple times (closes #20981) (#20992)
* Fix preview showing published version when Save and Preview is clicked multiple times

Fixes #20981

When clicking "Save and Preview" multiple times, the preview tab would show the published version instead of the latest saved version. This occurred because:

1. Each "Save and Preview" creates a new preview session with a new token
2. The preview window is reused (via named window target)
3. Without a URL change, the browser doesn't reload and misses the new session token
4. The stale page gets redirected to the published URL

Solution: Add a cache-busting parameter (?rnd=timestamp) to the preview URL, forcing the browser to reload and pick up the new preview session token. This aligns with how SignalR refreshes work.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Improve Save and Preview to avoid full page reloads when preview is already open

When clicking "Save and Preview" multiple times with a preview tab already open, the entire preview tab would reload. This enhancement makes it behave like the "Save" button - only the iframe reloads, not the entire preview wrapper.

Changes:
- Store reference to preview window when opened
- Check if preview window is still open before creating new session
- If open, just focus it and let SignalR handle the iframe refresh
- If closed, create new preview session and open new window

This provides a smoother UX where subsequent saves don't cause the preview frame and controls to reload, only the content iframe refreshes via SignalR.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Close preview window when ending preview session

Changes the "End Preview" behavior to close the preview tab instead of navigating to the published URL. This provides a cleaner UX and ensures subsequent "Save and Preview" actions will always create a fresh preview session.

Benefits:
- Eliminates edge case where preview window remains open but is no longer in preview mode
- Simpler behavior - preview session ends and window closes
- Users can use "Preview website" button if they want to view published page

Also removes unnecessary await on SignalR connection.stop() to prevent blocking if the connection cleanup hangs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix preview cookie expiration and add proper error handling

This commit addresses cookie management issues in the preview system:

1. **Cookie Expiration API Enhancement**
   - Added `ExpireCookie` overload with security parameters (httpOnly, secure, sameSiteMode)
   - Added `SetCookieValue` overload with optional expires parameter
   - Marked old methods as obsolete for removal in Umbraco 19
   - Ensures cookies are expired with matching security attributes

2. **PreviewService Cookie Handling**
   - Changed to use new `ExpireCookie` method with explicit security attributes
   - Maintains `Secure=true` and `SameSite=None` for cross-site scenarios
   - Uses new `SetCookieValue` overload with explicit expires parameter
   - Properly expires preview cookies when ending preview session

3. **Frontend Error Handling**
   - Added try-catch around preview window reference checks
   - Handles stale window references gracefully
   - Prevents potential errors from accessing closed window properties

These changes ensure preview cookies are properly managed throughout their
lifecycle and support both same-site and cross-site scenarios (e.g., when
the backoffice is on a different domain/port during development).

Fixes #20981

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Track document ID for preview window to prevent reusing window across different documents

When navigating from one document to another in the backoffice, the preview window reference was being reused even though it was showing a different document. This meant clicking "Save and Preview" would just focus the existing window without updating it to show the new document.

Now we track which document the preview window is showing and only reuse the window if:
1. The window is still open
2. The window is showing the same document

This ensures each document gets its own preview session while still avoiding unnecessary full page reloads when repeatedly previewing the same document.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Remove updates to ICookieManager and use Cookies.Delete to remove cookie.

* Fix file not found on click to save and preview.

* Removed further currently unnecessary updates to the cookie manager interface and implementation.

* Fixed failing unit test.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-12-01 11:16:35 +00:00
b40ea0df8c Content Type Workspace: Create condition that checks content type uniques. (#20906)
* Created condition for workspace content type unique.

* Changed import.

* Revert import.

* Updated name in the alias example and also import.

* Update src/Umbraco.Web.UI.Client/examples/entity-content-type-condition/index.ts

Co-authored-by: Mads Rasmussen <madsr@hey.com>

* Update src/Umbraco.Web.UI.Client/examples/entity-content-type-condition/workspace-view-unique.element.ts

Co-authored-by: Mads Rasmussen <madsr@hey.com>

* Moved the manifest definition to the manifest file.

* Changed default export.

* Updated example element to render the real GUID.

* Fixed import.

* Replaced CONTENT_WORKSPACE for PROPERTY_STRUCTURE_WORKSPACE context.

* Moved content type unique condition to the content type folder.

* Fixed import.

* final adjustments

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
2025-12-01 11:01:09 +01:00
050b37ed1a Installer: Removes unused telemetry functionality (#20995)
* fix: removes the non-functioning installer telemetry and obsoletes all InstallHelper functionality

* fix: deprecates related cookie

* fix: adds ActivatorUtilitiesConstructor for DI

* fix: obsoletes and removes more telemetry functionality

* fix: removes uneeded modifier

* docs: removes docs

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-28 22:21:04 +01:00
f99e9394f8 Culture and Hostnames: Add ability to sort hostnames (closes #20691) (#20826)
* Adding the sorter controller, and fixing some ui elements so you are able to drag the hostname elements around to sort them

* Fixed sorting

* Changed the html structure and tweaked around with the css to make it look better.
Added a description for the Culture section.
Alligned the rendered text to allign better with the name "Culture and Hostnames"

* Update src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/modal/culture-and-hostnames-modal.element.ts

Forgot to remove this after I was done testing

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/modal/culture-and-hostnames-modal.element.ts

Changing grid-gap to just gap

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Removed the disabled and readonly props I added since they are not needed.
Removed the conditional rendering that was attached to the readonly and disabled properties

* Removed the item id from the element and changed css and sorter logic to target the hostname-item class instead

* Updated test

* Bumped helpers

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Andreas Zerbst <73799582+andr317c@users.noreply.github.com>
Co-authored-by: Andreas Zerbst <andr317c@live.dk>
2025-11-28 10:06:20 +01:00
Andy ButlandandGitHub 9c038bc68b Re-enable package validation (#20964)
* Re-enable package validation.

* Remove unnecessary supressions file.

* Removed unnecessary suppressions.

* Restored and obsoleted all overload.
2025-11-28 09:48:06 +01:00
e6b99938db Delivery API: Only add default strategy if delivery API is not registered. (#20982)
* Only add if not already present

* Update src/Umbraco.Cms.Api.Management/DependencyInjection/WebhooksBuilderExtensions.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-11-28 08:25:06 +01:00
Zeegaan 5ab93454d9 Bump version 2025-11-28 10:58:04 +09:00
Warren BuckleyandGitHub f44e9328d7 Extensions: Adds all yet unused Lit directives to @umbraco-cms/backoffice/external/lit (closes #20961) (#20963)
* Adds choose directive to @umbraco-cms/backoffice/external/lit

This can then allow choose to be imported like so
import { html, customElement, LitElement, property, css, choose } from '@umbraco-cms/backoffice/external/lit';

* Exports all of Lits directives for @umbraco-cms/backoffice/external/lit

Also puts them in alphabetical order to help add any new ones Lit may add in the future
2025-11-27 15:22:50 +00:00
Jacob Overgaard ec11714b4b Merge remote-tracking branch 'origin/v16/dev' 2025-11-27 15:46:27 +01:00
Lee KelleherandGitHub 615cb76288 Block editors: adds prefix to workspace title (closes #20588) (#20884)
* Adds `$settings` to the block workspace label renderer

* Adds a prefix to the Block workspace title

* Imports tidy-up
2025-11-27 15:39:27 +01:00
Sven GeusensandGitHub c61bcca066 Add Claude memory files for all relevant project files (#20959)
* Regenerate delivery api claud memory file for updated file lines and inclusion of Secure Cookie-Based Token Storage

* Add delivery api memory file

* claude memory file for in memory modelsbuilder project

* Claud memory file for Imagesharp project

* Claude memory file for legacy image sharp project

* Claude memory files for Persistence projects

* Remaining claude memory files
2025-11-27 10:47:19 +01:00
MoleandGitHub eaf5960a4d Cache: Add awaits to memory cache rebuilds to fix race conditions (#20960)
* Await rebuilds and fix multiple open DataReaders

* Add additional missing awaits
2025-11-26 15:02:07 +01:00
Niels LyngsøandGitHub ff8cbb8b81 PropertyGuards: improved argument names and JSDocs (#20949)
improved argument names and JSDocs
2025-11-26 10:44:38 +01:00
Niels LyngsøandGitHub a364c9e86d Content Property: Remove unused 'entityType'-property (#20948)
remove unused property
2025-11-26 10:41:41 +01:00
ce98184178 Log Viewer: Enhances the donut chart to be responsive, link to log search, and show numbers directly (#20928)
* Log Viewer: Refactor log types chart to use Lit repeat directive

- Import and use repeat directive for better performance
- Add _logLevelKeys state property to track log level keys
- Update setLogLevelCount() to populate _logLevelKeys
- Replace .map() with repeat() in render method for legend and donut slices
- Update willUpdate to observe both filter and response changes
- Resolves TODO comment about using repeat directive

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Donut Chart: Add inline numbers and fix tooltip positioning

- Add showInlineNumbers property to optionally display numbers inside slices
- Implement #getTextPosition() method to calculate text position at slice center
- Render SVG text elements when showInlineNumbers is enabled
- Fix tooltip positioning to appear near cursor (changed from x-10, y-70 to x+10, y+10)
- Recalculate container bounds on each mouse move to handle window resize
- Add pointer-events: none to tooltip to prevent mouse interference
- Add CSS styling for slice numbers (user-select: none)
- Enable inline numbers by default in log viewer log types chart

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Donut Chart: Add clickable slices and visible description

- Add href property to donut-slice element for clickable slices
- Wrap SVG paths in <a> tags when href is provided
- Update Circle interface to include href property
- Add showDescription property to optionally display description text
- Render description as visible text below the chart
- Add CSS styling for description text
- Update log-types-chart to build search URLs with log level and date range
- Observe dateRange from context to build accurate search URLs
- Enable clickable slices and visible description in log-types-chart

Now clicking on a donut slice navigates to the search view filtered by that log level and the current date range.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: uses whole link

* Log Viewer: Fix log types chart layout for larger screens

Add media query to switch from column to row layout on screens wider than 768px. This ensures the legend and donut chart are displayed side by side on desktop resolutions instead of stacked vertically.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* chore: improves mock function

* chore: formatting

* fix: ensures the donut chart works responsively

* feat: adds support for SVGAElement in the router

* adds key for description

* chore: adds test data

* feat: displays numbers in the legend instead of the chart

* chore: restores functionality with lower-cased keys

* fix: adds translation to 'log messages'

* chore: removes unused method

* feat: ensures that the log levels follow the generated LogLevelModel enum from the server, which requires to map the keys as JSON camelCase's the keys

* fix: uses correct property

* fix: reverts back to the original behavior to calculate a relative URL (rather than the automatic .toString() that gets a qualified URL)

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: uses fullUrl for router

* fix: properly translates new aria-label

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-26 09:28:44 +00:00
Nhu DinhandGitHub 69e2f8df74 E2E: QA Added acceptance tests for notification emails (#20918)
* Added tests for notification emails for content

* Bumped version

* Updated tests for notification permission in content

* Added appsettings.json for smtp tests

* Added smtp test project

* Updated nightly E2E test pipeline yaml file to run smtp project in the pipeline

* Fixed command to run smtp4dev in Docker

* Fixed pipeline

* Only run smtp tests on Linux

* Debugged

* Debugging

* Added step to stop smtp4dev container

* Debugging

* Updated port

* Reverted tests

* Added more tests for notification emails

* Formatted code
2025-11-26 07:34:36 +00:00
Andy Butland bd33246525 Merge branch 'release/17.0'
# Conflicts:
#	src/Umbraco.Web.UI.Client/package-lock.json
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
2025-11-25 15:58:55 +01:00
ef282a5211 docs: Add CLAUDE.md documentation for key .NET projects (#20841)
* docs: Add CLAUDE.md documentation for key .NET projects

Add comprehensive CLAUDE.md files for major Umbraco projects:
- Root CLAUDE.md: Multi-project repository overview
- Umbraco.Core: Interface contracts and domain models
- Umbraco.Infrastructure: Implementation layer (NPoco, migrations, services)
- Umbraco.Cms.Api.Common: Shared API infrastructure
- Umbraco.Cms.Api.Management: Management API (1,317 files, 54 domains)
- Umbraco.Web.UI.Client: Frontend with split docs structure

Each file includes:
- Architecture and design patterns
- Project-specific workflows
- Edge cases and gotchas
- Commands and setup
- Technical debt tracking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Update src/Umbraco.Cms.Api.Management/CLAUDE.md

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>

* docs: Update CLAUDE.md with accurate persistence and auth info

- Clarify NPoco is current and fully supported (not legacy)
- Document EF Core as future direction with ongoing migration
- Add secure cookie-based token storage details for v17+
- Update OpenIddict authentication documentation
- Update API versioning (v1.0 and v1.1)
- Minor documentation cleanups (community links, descriptions)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Update src/Umbraco.Web.UI.Client/docs/agentic-workflow.md

* Update src/Umbraco.Web.UI.Client/docs/agentic-workflow.md

* Apply suggestions from code review

* Clarifications and duplicate removal.

---------

Co-authored-by: Phil Whittaker <pjw@umbraco.dk>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Sven Geusens <geusens@gmail.com>
2025-11-25 14:37:49 +00:00
Andy ButlandandGitHub 1240d845d4 Dependencies: Updates some dependencies to latest minor or patch releases (#20953)
Updates some dependencies to latest minor or patch releases for 17.1.
2025-11-25 13:55:15 +01:00
20cfdb9c0a Login Screen: Fix css for login screen dark mode (#20922) (#20946)
* Fix css for login screen dark mode

* Removes the outer-layout wrapper, moving the flexbox to the host

Sets the fallback for `--umb-auth-backdrop` to `--uui-color-surface`

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-11-25 12:17:53 +00:00
Lee KelleherandGitHub 4fe60f360f Block Catalogue: Localizes block-type name/description before render (closes #20890) (#20904)
Block Catalogue: Localizes block-type name/description before render

Fixes #20890.
2025-11-25 10:33:11 +00:00
Jacob OvergaardandGitHub 439c1dccd3 Log Viewer: Adds localization in the Backoffice UI + cleans up unused keys (#20923)
* fix: adds localization to the log viewer

* fix: missing log viewer keys for English

* fix: translations for Danish

* fix: removes unused keys and replaces polling keys

* fix: lowercases values to match what was there before
2025-11-25 10:17:32 +00:00
a41c48ad6c Multi URL Picker: change validation for url and anchor/querystring (#20920)
* Fix validation for url and anchor of multi url picker

* fix codesence warning

* remove redundant validation

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
2025-11-25 10:07:21 +00:00
Andy Butland da502e06ae Bump version to 17.0.0. 2025-11-25 09:53:36 +01:00
Andy ButlandandGitHub cf265e248b Examine Management: Allow selection of all available fields in Examine search results, and fix layout issue when not all records have all fields (closes #20878 and #20879) (#20909)
* Allowed selection of all available fields in Examine search results, and fix layout issue when not all records have all fields.

* Updates from code review.
2025-11-24 17:57:10 +00:00
Mads RasmussenandGitHub fd5077d823 Example Docs: Add menu item examples (#20910)
Add menu item registration examples

Introduces example implementations for registering action, link, and entity menu items in the backoffice. Includes manifests and API to demonstrate how to extend the menu system.
2025-11-24 16:32:53 +00:00
3d90fffccd UFM: Update contentName component to respect current backoffice culture. (#20927)
* Updated the content-name element to use the DocumentItemDataResolver.

* Import sorting

* Defaults the entity-type to "document"

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-11-24 16:23:35 +00:00
Jacob Overgaard 8f2532a28a Merge remote-tracking branch 'origin/release/17.0' 2025-11-24 16:32:38 +01:00
Jacob Overgaard bfa3ff4042 Merge remote-tracking branch 'origin/v16/dev' 2025-11-24 16:31:29 +01:00
2b7efbe861 Localization: Restores region-specific cultures (#20939)
* Adds localization manifests for region-specific cultures

This is to support backwards-compatibility and v13 upgradability.

* Removed `uiCulture` from Vietnamese localizations

since it duplicated the English fallback texts.

* 'en' localization file formatting

* Update src/Umbraco.Web.UI.Client/src/assets/lang/en.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-24 14:41:24 +01:00
Niels LyngsøandGitHub 4138262a19 Examples: updated naming of example (#20938)
updated naming of example
2025-11-24 12:38:10 +00:00
Niels LyngsøandGitHub ea840bcfde Front-end Example: Initial name example (#20899)
initial name example
2025-11-24 12:37:43 +00:00
9beed532a9 Update Swashbuckle to v10 (#20925)
* Update Swashbuckle to v10

* Regenerate backoffice api client

* Add missing space for consistency

* Simplify nullability check

* Small improvement

Didn't notice that these classes were internal, so tried keeping compatibility, but it wasn't needed.

* Fix failing integration test

* Apply suggestions from code review

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Remove unnecessary comma

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-24 12:06:03 +01:00
Jacob OvergaardandGitHub 215c4dc540 build(deps): bump @microsoft/signalr to 10.0.0 (#20932) 2025-11-24 10:23:08 +00:00
Jacob OvergaardandGitHub f19aaaee52 build(deps): bump marked to 17.0.1 (#20933) 2025-11-24 10:07:45 +00:00
Andy ButlandandGitHub dc50a6e8c3 Update further dependencies for 17 (#20935)
Update further dependencies.
2025-11-24 09:42:35 +00:00
12611942ff Permissions: Protect GetIdsFromPathReversed against invalid program exception (#20930)
* Updates to protect GetIdsFromPathReversed against invalid program exception.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-24 10:19:37 +01:00
04f98a758d Log viewer: Improves search functionality and code quality (#20913)
* fix: adds correct fallback for dates to avoid console error

* fix: resolves a TODO by using UmbStringState over rxjs Subject

* Refactor log viewer search to use UmbStringState and improve architecture

- Replace RxJS Subject with UmbStringState to follow Umbraco patterns
- Move debounced search observation to messages list component
  - Only triggers when component is mounted (logs are visible)
  - Prevents unnecessary API calls on other views
- Simplify search input to just update context state
- Add semantic form structure with role="search" for accessibility
- Add visually-hidden submit button for keyboard navigation
- Allow re-running same query via form submission (bypasses debounce)
- Follow same architecture pattern as date range selector

This resolves the TODO to not use RxJS directly and significantly improves
separation of concerns where the data consumer (messages list) owns the
fetching logic.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Add visible refresh button to log viewer search input

- Add refresh button with icon-refresh next to save and clear buttons
- Allows users to re-run search with same query (bypasses debounce)
- Remove form structure that couldn't work due to Shadow DOM boundaries
- Simplify parent component by removing form submission logic
- Keep role="search" for accessibility

The refresh button provides a more discoverable UI than the hidden submit
button approach and avoids Shadow DOM event bubbling issues.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix debouncing by adding local state in search input

- Add local UmbStringState to debounce user input (250ms)
- Only update context filterExpression after debounce
- Remove debouncing from messages list (now handled at input level)
- Saved searches and refresh button still bypass debounce for immediate feedback

This restores the expected debouncing behavior while maintaining the clean
architecture where the messages list triggers searches based on context changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* chore: cleans up in docs

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-24 08:44:45 +00:00
Nhu DinhandGitHub bb88be9d2e E2E: QA Updated .fixme() acceptance tests (#20919)
* Added steps to wait for upload to complete

* Update tests for content with media picker

* Removed fixme tags

* Updated fixme tests

* Bumped version
2025-11-24 12:56:52 +07:00
Nhu DinhandGitHub 60b886dc9c E2E: Updated the failing Default Config tests (#20818)
* Updated block list tests as the “Add Block” button is hidden after reaching the maximum limit.

* Updated validation option due to UI changes

* Updated tests for current user profile as waitForNetworkToBeIdle() is removed

* Fixed flaky tests

* Bumped version

* Updated tests for current user profile

* Bumped version
2025-11-24 12:54:36 +07:00
908974c6ac Preserve existing Examine FieldDefinitionCollection if it already exists (#20931)
* Preserve existing Examine FieldDefinitionCollection if it already exists (#20267)

* Fix missing bracket

* Minor tidy/addition of comments; addition of unit tests.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-11-24 09:53:38 +09:00
Niels LyngsøandGitHub 488f373fea Properties: Implement container queries (#20832)
implement container queries for properties
2025-11-21 13:10:36 +01:00
35acfcb7e2 Trashbin: introduce a empty trash icon (#20629)
* add empty trash icon and use it for empty trash-bin and delete from trash-bin

* package lock

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-11-21 13:05:49 +01:00
Niels LyngsøandGitHub 2939eb4f81 Bock Type Card: make actions stand clear from thumbnail/color (#20895)
clean up and make actions stand clear from custom background colors/images
2025-11-21 11:19:34 +01:00
netaddicts-councilandGitHub 2f02bee421 Sets the default name for the content listview workspace name to 'Child items' instead or 'Collection' (#20907)
Sets the default name for thecontent listview workspace name to 'Child items' instead of 'Collection' (Fixes #20860)
2025-11-20 16:03:25 +01:00
6a7360aded Member types: Implement containers (#20706)
* Add MemberType/MemberTypeContainer to supported EntityContainer object types

* Implement MemberTypeContainerRepository

* Update and add member type container API endpoints

* Complete server and client-side implementation for member type container support.

* Fix FE linting errors.

* Export folder constants.

* Applied suggestions from code review.

* Updated management API authorization tests for member types.

* Resolved breaking change on copy member type controller.

* Allow content types to be moved to own folder without error.

* Use flag providers for member type siblings endpoint.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-11-20 11:28:03 +01:00
Niels LyngsøandGitHub f70f6d4aba Content Type Designer: Only update tab name on change (#20786)
Do not update tab name on input, as it is inappropriate when having name conflicts
2025-11-20 09:37:22 +00:00
75dd9fab2b Redirect tracking: Ensure redirects with domains are stored with the domain node id prefix (closes #20894) (#20900)
* Ensure redirects with domains are stored with the domain node id prefix.

* Handle removal of self-referencing redirect when domains are used.

* Use entity path to save further queries for retrieving ancestor IDs.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Applied refactoring suggested in code review.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-20 16:30:38 +09:00
Jacob Overgaard 1e64c8d5af bump version to 17.0.0-rc4 2025-11-20 08:11:41 +01:00
2f6fb7e395 Block-grid, Block-list: Fix issue translation in clipboard is missing (#20756) (#20903)
Fix issue translation in clipboard

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
2025-11-20 07:14:51 +01:00
Lars-Erik AabechandGitHub 43bdad59b6 Integration Tests: Use empty temp folder for legacy lang config when using integration tests outside core (closes #20888) (#20889)
* Use empty folder under temp as localized text source folder in non umbraco core integration tests.

* Added clarifying comment to the GetLocalizedTextService override for tests
2025-11-20 06:54:35 +01:00
Niels Lyngsø 61a5d26a93 Merge branch 'release/17.0' 2025-11-19 17:10:34 +01:00
Niels Lyngsø e45f232f44 update workspace context example readme 2025-11-19 17:10:04 +01:00
Andy Butland 745d74104e Migrations: Handles rich text blocks created with TinyMCE in convert local links migration and refreshes internal datatype cache following migration requiring cache rebuild (closes #20885) (#20887)
Handles rich text blocks created with TinyMCE in convert local links migration.
Refreshes internal datatype cache following migration requiring cache rebuild.
2025-11-19 15:22:57 +01:00
Andy ButlandandGitHub a488d77ce7 Migrations: Handles rich text blocks created with TinyMCE in convert local links migration and refreshes internal datatype cache following migration requiring cache rebuild (closes #20885) (#20887)
Handles rich text blocks created with TinyMCE in convert local links migration.
Refreshes internal datatype cache following migration requiring cache rebuild.
2025-11-19 14:54:12 +01:00
386611bc70 TextBox, TextArea: Message max length validation (close #20710) (#20886)
* fix bug max length messsage

* change localization

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
2025-11-19 14:24:32 +01:00
Niels Lyngsø cc8d90be91 Cherry picked 20733 2025-11-19 10:43:09 +01:00
Niels Lyngsø e2a8bea579 TODOs 2025-11-19 09:55:59 +01:00
Engiber LozadaandGitHub e46e65ef22 Content types: Apply text selection when unlocking alias (#20882)
Added requestAnimationFrame for alias text selection.
2025-11-18 16:17:35 +01:00
Kenn JacobsenandGitHub 95c9d48b6e Rendering: Don't cache RTE value conversion (closes #20867) (#20880)
Don't cache RTE rendering.
2025-11-18 15:36:18 +01:00
Nhu DinhandGitHub a59476a043 E2E: QA Updated tests with the .skip tag (#20739)
* Removed skips for tests whose related issues have been fixed.

* Remove skip tags and update tests for content with list view

* Removed skip tags

* Added comment and change to fixme for tests that need to implement later

* Removed skip tag

* Bumped version
2025-11-18 11:37:53 +00:00
c1b3f41f7c TextBox: Data-type validation for max chars (fixes #18817) (#20843)
* configure max chars for textbox

* min 1

* Adds server-side check for text box min and max character validation.

* Applied suggestion from code review.

* Bumped version of test helper

* Fixed test that was creating a text string data type with too large a maximum characters setting.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Nhu Dinh <hnd@umbraco.dk>
2025-11-18 08:16:59 +01:00
Jacob OvergaardandGitHub 92b5d11c95 Backoffice Preview: Adds notice that the site is not real (#20864)
feat: adds notice to the static backoffice site explaining to the reader that the content is not real
2025-11-17 18:31:55 +01:00
Jacob Overgaard b5ffe8930b chore: fixes merge conflict 2025-11-17 17:20:14 +01:00
Jacob Overgaard a89437e309 test: uses a real non-date value for testing 2025-11-17 17:17:59 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b8c31350fb Bump the npm_and_yarn group across 2 directories with 1 update (#20863)
Bumps the npm_and_yarn group with 1 update in the /src/Umbraco.Web.UI.Client directory: [js-yaml](https://github.com/nodeca/js-yaml).
Bumps the npm_and_yarn group with 1 update in the /src/Umbraco.Web.UI.Login directory: [js-yaml](https://github.com/nodeca/js-yaml).


Updates `js-yaml` from 4.1.0 to 4.1.1
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1)

Updates `js-yaml` from 4.1.0 to 4.1.1
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.1.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: js-yaml
  dependency-version: 4.1.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-17 16:01:59 +00:00
Jacob Overgaard bf35477399 chore: fixes merge conflict 2025-11-17 16:59:25 +01:00
Jacob Overgaard 45c8be4bb7 Merge remote-tracking branch 'origin/v16/dev' 2025-11-17 16:40:11 +01:00
Jacob Overgaard 9035f160fe Merge remote-tracking branch 'origin/release/17.0' 2025-11-17 16:37:58 +01:00
MoleandGitHub 57c66faf40 Update microsoft packages to non rc version (#20858)
* Update RC2 packages to 10.0.0

* Also update hybrid cache project
2025-11-17 15:26:30 +00:00
Niels LyngsøandGitHub 08990b9147 Document input: Find variant name (#20853)
* ability to override get name

* implement document name method
2025-11-17 14:41:47 +00:00
Engiber LozadaandGitHub eae35a27ab Block Entry Context: Update the settingsPropertyValueByAlias to observe settings. (#20861)
Replaced content for settings in the settingsPropertyValueByAlias.
2025-11-17 13:54:30 +00:00
Laura NetoandGitHub 6d44b42400 Use dependency track devops task (#20854)
* Replace dependency track bom script with devops task

* Introduce new url variable in order to fix new task uri

The initial variable contained the api path (/api) in the URL.
2025-11-17 14:54:03 +01:00
23fc355e61 Redact back-office PKCE codes from the server (#20847)
* Redact back-office PKCE codes from the server

* Update src/Umbraco.Cms.Api.Common/DependencyInjection/HideBackOfficeTokensHandler.cs

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-11-17 09:50:31 +01:00
Niels LyngsøandGitHub b868a349de Block list: ensure block items stay top-aligned when sorting (#20842)
ensure block items stay top-aligned when sorting
2025-11-15 08:32:18 +01:00
Warren BuckleyandGitHub 1fedaa3d8f Removes npm commands from MSBuild of the CSPROJ for umbraco-extension dotnet new template (#20839)
* Removes npm commands from the MSBuild of the CSPROJ of the umbraco-extension dotnet new template

Was agreed by the community package team to remove this, as this DX can cause more issues than actually help users in our opinion

* Removed the unused value - good catch by Copilot
2025-11-14 21:25:19 +01:00
43230dfac8 Redirects: Fixes self referencing redirects (closes #20139) (#20767)
* Adding fix for self-referncing redirects for 17

* Using umbraco context on failing tests

* Tests to see if self referencing redirects gets deleted

* Refactoring and adding correct tests.

* Expanding tests for RedirectTrackerTests.cs

* Optimize by only retrieving th list of existing URLs for a content item if we have a valid route to create a redirect for.

* Extract method refactoring, added explanatory comment, fixed warnings and formatting.

* Resolved warnings in RedirectService.

* Minor naming and formatting refactor in tests.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-11-14 16:37:59 +00:00
ba7d550a74 Move access/refresh tokens to secure cookies (V17) (#20820)
* Move access/refresh tokens to secure cookies (#20779)

* feat: adds the `credentials: include` header to all manual requests

* feat: adds `credentials: include` as a configurable option to xhr requests (and sets it by default to true)

* feat: configures the auto-generated fetch client from hey-api to include credentials by default

* Add OpenIddict handler to hide tokens from the back-office client

* Make back-office token redaction optional (default false)

* Clear back-office token cookies on logout

* Add configuration for backoffice cookie settings

* Make cookies forcefully secure + move cookie handler enabling to the BackOfficeTokenCookieSettings

* Use the "__Host-" prefix for cookie names

* docs: adds documentation on cookie settings

* build: sets up launch profile for vscode with new cookie recommended settings

* docs: adds extra note around SameSite settings

* docs: adds extra note around SameSite settings

* Respect sites that do not use HTTPS

* Explicitly invalidate potentially valid, old refresh tokens that should no longer be used

* Removed obsolete const

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>

* Remove configuration option

* Invalidate all existing access tokens on upgrade

* docs: updates recommended settings for development

* build: removes non-existing variable

* Skip flaky test

* Bumped version of our test helpers to fix failing tests

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
Co-authored-by: Andreas Zerbst <andr317c@live.dk>
2025-11-14 17:10:57 +01:00
Ronald BarendseandGitHub 0600df4211 Fix EntityContainer.GetUdi() for member type containers (#20840)
Fix EntityContainer.GetUdi() for member type containers
2025-11-14 16:09:46 +00:00
01b300336b Property Editors: Added form control and mandatory support to editors in picker group(Color, Content, Date, Document, Eye dropper, Multi URL). (#20684)
* Added form control support to color picker.

* Avoid submit when readonly is true.

* Added mandatory support.

* Added form control support to date picker.

* Removed an unused import.

* Added form control and mandatory support to document picker.

* Added form control support to Eye dropper.

* Added. mandatory support for multi url picker also bind inner input in the eye dropper.

* Removed unused import.

* fix update of value

* fixing not needed override of get and set methods

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2025-11-14 14:04:20 +00:00
EricandGitHub 6b4503cc7b Content Sorting: increase modal size (#20835)
Modal size increase for sorting content
2025-11-14 14:42:30 +01:00
Nicklas KramerandGitHub 06c566c074 News Dashboard: Replacing old hostname, with new custom one. (#20837)
Changing hostname of dashboard to a new custom one.
2025-11-14 13:33:35 +00:00
2a609e1eca Load Balancing: Clear request cache in cache version accessor on cache version update to prevent unnecessary cache roll forwards (#20831)
* Clear request cache on version update

* Update src/Umbraco.Core/Cache/IRepositoryCacheVersionAccessor.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 14:14:28 +01:00
NillasKA 342cb171eb Revert "Changing news dashboard URL to a new custom hostname"
This reverts commit e850c131e5.
2025-11-14 13:36:26 +01:00
NillasKA e850c131e5 Changing news dashboard URL to a new custom hostname 2025-11-14 13:31:06 +01:00
Andy ButlandandGitHub a4438e5b21 Decimal property editor: Flexibly parse decimal value with different separators (closes #20823) (#20828)
* Flexibly parse decimal value with different separators.

* Applied suggestions from code review.
2025-11-14 10:20:12 +09:00
73847d1eff Property Editors: Added form control and mandatory support to editors in rich content group(Code editor, Markdown, Block grid) (#20693)
* Added mandatory support for block grid property editor.

* Added form control and mandatory support to code editor.

* Added form control and mandatory support to markdown editor.

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2025-11-13 20:57:27 +00:00
Engiber LozadaandGitHub e549217e66 Content Type Designer: Use input-with-alias and implement regex validation for Alias. (#20755)
* Implemented input-with-alias in the content-type-design-editor.

* Added auto-generate-alias property to the input and revert deletion of checkAliasAutoGenerate method.

* Added form-validation-message.

* Added validation to the input-with-alias element to avoid special characters.
2025-11-13 20:42:48 +00:00
Engiber LozadaandGitHub 8b076597b3 Entity Sign: Improve Firefox visibility and add focus support. (#20733)
* Chenged right and left position of the infobox.

* Added focus support to open the modal.

* Moved tabindex out the constructor and added support for enter and space keys.
2025-11-13 20:37:54 +00:00
bbd30363a2 Media Picker: Remove duplicate loaders in media cards. (#20793)
* Removed isLoding condition from the rich media input and let the thumbnail handle the loader.

* Removed unused import.

* change loader and adjust lit property configuration

* update reflect configuration

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2025-11-13 20:38:32 +01:00
617d301479 Hides the content files that come from the Microsoft.CodeAnalysis.Workspaces.Common package in the web.ui project in 17 (#20825)
* Hide content files

* Update src/Umbraco.Web.UI/Umbraco.Web.UI.csproj

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 14:39:51 +00:00
Jacob Overgaard c48103cd30 bump lockfile 2025-11-13 15:16:23 +01:00
Jacob Overgaard 931041c635 Merge remote-tracking branch 'origin/v16/dev' 2025-11-13 14:19:10 +01:00
Mathias HelsengrenandGitHub 714fbf3119 Keyboard navigation: Return to opening element after modal close (#20782)
Removed the detroy from the modelContext.
It being destroyed prevented the uui-button getting into focus again after closing the modal.
2025-11-13 12:55:45 +01:00
Jacob OvergaardandGitHub eeda55c06f Preview: Add validation support to Save and Preview button (closes #20616) (#20805)
* chore(mock): adds missing try/catch around document lookup

* fix: lets the 'save and preview' button extend the 'save' button to follow the same logic in terms of when it enables/disabled - it did not have much logic before

* fix: runs validation from the server when save and previewing to ensure the UI shows what is missing
2025-11-13 11:25:17 +00:00
Mads RasmussenandGitHub 20180c3180 Collection Item Picker: support icon colors (#20787)
Update icon usage in collection menu and example data

Replaces <uui-icon> with <umb-icon> in the default collection menu item element to support colors. Also updates example picker data source items to showcase color support.
2025-11-13 12:16:59 +01:00
Mads RasmussenandGitHub 89d487e449 Picker Data Source: update getConfigValue with alias-based type safety (#20802)
* Improve type safety in getConfigValue function

* Refactor config typing in example document picker

* Update index.ts

* add unit tests
2025-11-13 10:57:18 +01:00
Andy Butland 597eb58063 Merge branch 'release/17.0'
# Conflicts:
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
2025-11-13 10:48:51 +01:00
b65d2b0ec7 Collection view test: update changes for v17 (#20812)
update extension for collection view test

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
2025-11-13 16:00:21 +07:00
Kenn JacobsenandGitHub 640f9f2615 Use HTTPS by default (#20813) 2025-11-13 09:47:08 +01:00
f075223412 Relations: Exclude the relate parent on delete relation type from checks for related documents and media on delete, when disable delete with references is enabled (closes #20803) (#20811)
* Exclude the relate parent on delete relation type from checks for related documents and media on delete, when disable delete with references is enabled.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Applied suggestions from code review.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 09:32:18 +01:00
Engiber LozadaandGitHub d4d4b8a50a Content Type Designer: Always register root route to support drag-and-drop into empty Generic tab. (#20809)
Always register root route to enable drag-drop on empty Generic tab.
2025-11-13 09:19:33 +01:00
Andy Butland 14f4c54312 Bumped version to 17.0.0-rc3. 2025-11-13 07:11:35 +01:00
139b528bda Database migrations: Support DateOnly and TimeOnly in syntax providers (#20784)
* sql column type map include dateonly and timeonly

* Split Mapper and add check null value

* Minor code tidy resolving a few warnings.

* add spaces

* clean code

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-11-13 11:10:51 +07:00
ca15aadf0e Fix for partial view caches not being cleared when content is publish… (#20794)
* Fix for partial view caches not being cleared when content is published/unpublished

* Update src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

* Change logic for clearing partial view cache

* Changed logic to only clear partial cache when content is published/unpublished or trashed

---------

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
2025-11-13 09:21:26 +09:00
Andreas ZerbstandGitHub c60cf90ffe E2E: QA added entity picker acceptance tests (#20776)
* Added entity picker settings

* Updated tests

* Updated nightly pipeline

* Updated tests

* Bumped versions

* Fixed indentation

* Added comments

* Cleaned up

* Removed duplicate

* Bumped version

* Updated naming
2025-11-12 10:18:53 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Nhu Dinh
c9fc2f2a19 Bump playwright and @playwright/test in /tests/Umbraco.Tests.AcceptanceTest (#20579)
* Bump playwright and @playwright/test

Bumps [playwright](https://github.com/microsoft/playwright) to 1.56.1 and updates ancestor dependency [@playwright/test](https://github.com/microsoft/playwright). These dependencies need to be updated together.


Updates `playwright` from 1.50.0 to 1.56.1
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.50.0...v1.56.1)

Updates `@playwright/test` from 1.50.0 to 1.56.1
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.50.0...v1.56.1)

---
updated-dependencies:
- dependency-name: playwright
  dependency-version: 1.56.1
  dependency-type: indirect
- dependency-name: "@playwright/test"
  dependency-version: 1.56.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bumped version of test helper

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Nhu Dinh <hnd@umbraco.dk>
2025-11-12 13:33:21 +07:00
Andy ButlandandGitHub cbdb1d567f Dependencies: Update to .NET 10 (#20796)
Update to .NET 10.
2025-11-11 19:18:17 +00:00
Niels LyngsøandGitHub 31072bab51 Stop using promise.reject to avoid the promise to resolve. (#20790) 2025-11-11 13:43:28 +00:00
9ad4a7eeba Adds Clear Clipboard button & logic (#20757)
* Adds new dictionary/localization item for the clipboard dialog clear all prompt

* Removes the wrapping uui-box and moved inside the component itself

* Adds Clear Clipboard button and logic

* Adds uui-box from outer components consuimg this into this component
* Adds a header to uui-box
* Adds a conditional uui-button when we have items in clipboard
* Adds confirm dialog/prompt to ask if user wants to clear all items

* Adds in general_clipboard item to use in the UUI-box header

* Removes extra space & moves the requestItems outside the for loop

* Be a better citizen

Make sure the promise for the modal is caught and we return out early if user explictiy cancels modal or presses ESC

* Cleanup my noisy comments for a re-review

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-11-11 11:50:23 +00:00
0858d02172 Single block migration (#20663)
* WiP blocklist migration

* Mostly working migration

* [WIP] deconstructed the migration to prefetch and process all data that requires the old definitions

* Working singleblock migration

* Abstracted some logic and applied it to settings elements too.

* Align class and file name.

* Minor code warning resolution.

* More and better comments + made classes internal where it made sense

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-11-11 11:43:03 +00:00
Andy Butland 524912a893 Fix accidental update to global.json. 2025-11-11 11:37:00 +01:00
d687c4365a Member types: Prepare container support (#20715)
* Add MemberType/MemberTypeContainer to supported EntityContainer object types

* Implement MemberTypeContainerRepository

* Prepare base controller for MemberTypeTreeControllerBase.

* Revert "Prepare base controller for MemberTypeTreeControllerBase."

This reverts commit ad213a23ad.

* Added foldersOnly flag in readiness for support in 17.1.

* Added foldersOnly flag in readiness for support in 17.1 (2).

---------

Co-authored-by: Ronald Barendse <ronald@barend.se>
2025-11-11 19:18:52 +09:00
Jacob Overgaard 55769b1747 Merge remote-tracking branch 'origin/release/17.0' 2025-11-11 09:51:49 +01:00
Jacob Overgaard 0797a3fa59 Merge remote-tracking branch 'origin/release/16.4' 2025-11-11 09:51:13 +01:00
41582de9d1 Collection view: add tests for create and using collection view (#20667)
* write test for custom collection view test

* add clean

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/CollectionView.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>
2025-11-11 14:46:54 +07:00
Andy ButlandandGitHub cfa32b265a Integration Tests: Avoid asserting on errors for permission tests (#20643)
* Added integration tests for PropertyTypeUsageService and adjusted assert in management API permissions test.

* Commented or fixed management API integration tests verifying permissions where we were asserting on an error response.
2025-11-11 05:59:25 +00:00
Mathias HelsengrenandGitHub d8198d2f5c Accessibility: Adding a label attribute for <uui-button> in news dashboard (#20780)
Added 'label attribute to the uui-button in the umb-news.card.element + Removing the redundant text for uui-button since label attribute is now present
2025-11-11 06:33:31 +01:00
Niels Lyngsø 43688148ae Cherry pick #20745 2025-11-10 17:40:58 +01:00
9fa382e84d Fix block list inline mode (#20745)
* Fix block list inline mode

https://github.com/umbraco/Umbraco-CMS/issues/20618

* Fixed potential runtime errors

* Code cleanup

* Fixed Code Health Review

* Revert some changes

Commented out unused state properties and related code.

* Remove commented-out state property in block workspace view

* fix localization

* no need for question mark after ids, they should be presented as required

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2025-11-10 16:17:30 +00:00
ab51aac5c6 Backoffice Item Pickers: Show error for missing items in 10 picker types (closes #19329, #20270, #20367) (#20762)
* Add errorDetail property to umb-entity-item-ref

Add optional errorDetail property to display additional context
(such as file paths or IDs) in error states. This enhances the
error display to show both the error message and relevant details.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Make _removeItem protected in UmbPickerInputContext

Change #removeItem from private to protected to allow subclasses
to reuse the removal logic while customizing the confirmation dialog.
This enables better extensibility for specialized picker contexts.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix static file picker to show error state for missing files

Update umb-input-static-file to observe statuses and render based
on item state (loading, error, success). When a static file is
missing (API returns empty array), displays error state with alert
icon and file path detail using umb-entity-item-ref.

Also adds standalone property support for proper single-item styling.

Fixes #19329

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Show file path in static file remove confirmation dialog

Override requestRemoveItem in UmbStaticFilePickerInputContext to
display the file path instead of "Not found" in the confirmation
dialog when removing missing static files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Show GUID in document picker error state

Display the document GUID as errorDetail when a document is
not found (deleted/gone). This provides useful context for
editors to identify which document was referenced.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Show GUID in document picker remove confirmation dialog

Display the document GUID instead of "Not found" in the remove
confirmation dialog when the document no longer exists. This
provides useful context for editors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: apply the temp model which the context uses

* Refactor: Move requestRemoveItem logic to base UmbPickerInputContext

Eliminated duplicate code across three picker contexts by:
- Adding protected getItemDisplayName() method to base class
- Moving requestRemoveItem implementation to base class
- Removing duplicate implementations from document, member, and static file pickers
- Static file picker overrides getItemDisplayName() to show file path

Net reduction: 19 lines of code (69 removed, 50 added)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Document Type Picker: Show error state for missing items (fixes #20367)

Apply the same error state handling to the document type picker that was
implemented for static files, documents, and members. When a referenced
document type is missing or deleted:

- Show error state with the GUID as errorDetail
- Allow removal with proper confirmation dialog
- Use umb-entity-item-ref for error display
- Use uui-ref-node-document-type for successful items

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Additional pickers: Show error states for missing items in user, language, media-type, member-type, member-group, and user-group pickers

Apply the same error state handling pattern to six additional picker types:
- user-input: Users
- input-language: Languages
- input-media-type: Media types
- input-member-type: Member types
- input-member-group: Member groups
- user-group-input: User groups

All pickers now:
- Observe statuses from UmbRepositoryItemsManager
- Show error state with GUID when referenced item is missing/deleted
- Use umb-entity-item-ref for error display
- Use specialized components (uui-ref-node, umb-user-group-ref, etc.) for successful items
- Allow removal with proper confirmation dialog showing GUID

Maintains code reusability by using the base class requestRemoveItem method
with getItemDisplayName() for consistent error handling across all pickers.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Lint: Remove unused 'when' imports from input-media-type and user-group-input

* Refactor: Add #renderItem helper method to all pickers for consistency

- Add #renderItem to user-input (extracted from inline repeat callback)
- Change _renderItem to #renderItem in user-group-input for consistency
- Change _renderItem to #renderItem in input-static-file for consistency

All 10 pickers now use consistent #renderItem helper method pattern,
improving code readability and maintainability as suggested by @nielslyngsoe

* `import` sorting

* Corrected (old) JSDoc typos

* Markup tidy-up

* exported `UmbPropertyEditorUIStaticFilePickerElement` as `element`

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-11-10 12:57:24 +00:00
Niels Lyngsø afec900204 Merge branch 'release/17.0'
# Conflicts:
#	src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs
2025-11-10 12:46:47 +01:00
89989d60ce Templates: Fix "Discard changes?" dialog after creating template with master template (fixes #20262) (#20749)
Moves the _data.updateCurrent() call inside the updateLayoutBlock conditional
in setMasterTemplate(). This prevents spurious change detection when loading
templates from the server, while maintaining proper change tracking when users
actually modify the master template via the UI.

This completes the fix started in PR #20529 which added the updateLayoutBlock
parameter but inadvertently left the data model update outside the conditional.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-10 11:00:43 +00:00
73fd52aeea Login: Added custom validation for missing password and user/email on the login form (#20233)
* Added custom validation for missing password and user/email

* Changed some of the logic behind custom validation, so it now uses aria-errormessage

* fix: imports from src folder instead

* build(deps-dev): bump vite to 7.2.0

* formatting

* fix: moves the form into the login.page.element.ts component to better control submission

* fix: creates elements globally

* fix: adds id back to form

* fix: no need to store references to all form elements

* fix: errormessage should show with password field in a span as well

* fix: checks validity of form

* fix: constructs form in auth.element.ts anyway and append localization to validation and add oninput and onblur

* chore: fixes import paths

* fix: fixes special case where ?status was not reset

* fix: changes wording in english

* fix: removes duplicate en-us keys

* feat: adds ariaLive and role attributes

* fix: always clears the text

* fix: username required validation should switch between username and email

* package-lock.json updated on (re)install

* Renamed SVG eye icon filenames

to be conventional and kebab-cased.

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-11-10 10:21:33 +00:00
Niels LyngsøandGitHub bce85e1e88 Package section: use command icon for migrations, remove prop (#20775)
change icon and remove ability to customize
2025-11-10 09:38:53 +00:00
Niels LyngsøandGitHub e39b05f44d Property type: Vary in the same way as the owner Document Type (#20751)
set new property type preset to vary if document type varies
2025-11-10 10:28:40 +01:00
Niels LyngsøandGitHub 2067db1c3c Content Workspace: not displaying varying composition values in a not varying context (Fixes #20707) (#20758)
* only vary if context varies

* add controller aliases to these observations
2025-11-10 10:05:56 +01:00
Andy ButlandandGitHub fcfaff9daa Querying: Restore ability to retrieve all children published in any culture (closes #20760) (#20766)
* Restore ability to retrieve all children published in any culture.

* Fixed typo in test name.
2025-11-10 11:02:10 +09:00
04918ec3d2 Slider property editor: Fix for preset value handling of enableRange (#20772)
Fix config value access in UmbSliderPropertyValuePreset

Updated the `UmbSliderPropertyValuePreset` class to ensure the `.value` property is accessed for configuration items. This change improves the accuracy of retrieving `enableRange`, `min`, `max`, and `step` values, addressing potential bugs in value processing.

Co-authored-by: Luuk Peters <Luuk.Peters@proudnerds.com>
2025-11-09 22:38:16 +01:00
Warren BuckleyandGitHub aae316e17e Localization: Supply the display name to the localization key for the alt and title attributes of the 2FA QR code image (#20770)
Simple fix to supply the display name to the localization key for the 2FA QR Code Image
2025-11-09 13:41:41 +01:00
Andy ButlandandGitHub ca08652a60 Installer: Fix issues with newsletter signup (#20705)
* Add setter to allow handling of requests to subscribe to newsletter on install.

* Correct serialization of newsletter subscription request.

* Fix serialization and use the Umbraco.EmailMarketing service for newsletter signup.

* Remove logging of user when setting telemetry level.

* Applied suggestions from code review.
2025-11-07 14:34:26 +01:00
b866c31105 Property action: Add tests for create and using Property Action UI Extension (#20291)
* add property action tests

* add extension property action code

* remove extension registry config

* update helper version

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/PropertyAction.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/PropertyAction.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/my-property-action/write-property-action.api.js

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/my-property-action/read-property-action.api.js

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* change tab space size

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>
2025-11-07 15:02:21 +07:00
Andy ButlandandGitHub 6f2b7ab1a0 Flags: Complete renaming of SignProviders to FlagProviders. (#20752)
Complete renaming of SignProviders to FlagProviders.
2025-11-06 22:45:41 +01:00
Andy Butland ea142d51b7 Fix memory leak with IOptionsMonitor.OnChange and non-singleton registered components (closes #20709 for 16/17) (#20723)
* Fix memory leak with IOptionsMonitor.OnChange and non-singleton registered components.

* Dispose disposable data editors in ValueEditorCache.

* Removed unnecessary refactoring and clarified code comments.
# Conflicts:
#	src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs
2025-11-06 17:22:43 +01:00
Andy ButlandandGitHub 7162c458b1 Fix memory leak with IOptionsMonitor.OnChange and non-singleton registered components (closes #20709 for 16/17) (#20723)
* Fix memory leak with IOptionsMonitor.OnChange and non-singleton registered components.

* Dispose disposable data editors in ValueEditorCache.

* Removed unnecessary refactoring and clarified code comments.
2025-11-06 17:16:01 +01:00
Niels LyngsøandGitHub f4a7a2d9be Reset password localization + format (#20750)
localizations + format
2025-11-06 10:43:46 +00:00
Nicklas KramerandAndy Butland e3c394b109 Migrations: Fixes migrations from 13 to 17. Media Folder without Collection & Last Synced Table not existing. (#20743)
* Creating and adding new migration. And fixing another small bug.

* Adding XML Header and renaming to a more clearly defined name
2025-11-06 11:41:45 +01:00
Nicklas KramerandGitHub 3ab12e9b59 Migrations: Fixes migrations from 13 to 17. Media Folder without Collection & Last Synced Table not existing. (#20743)
* Creating and adding new migration. And fixing another small bug.

* Adding XML Header and renaming to a more clearly defined name
2025-11-06 09:40:29 +00:00
Niels LyngsøandGitHub f11b8ffae9 User Workspace: localize password mismatch feedback (#20747)
localize
2025-11-06 09:01:51 +00:00
e155fdff58 Block Custom View: Add tests for create and using block custom view (#20472)
* add test for block custom view

* update format code

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/BlockCustomView.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/BlockCustomView.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/BlockCustomView.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/BlockCustomView.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* fix some name test

* add new test for block custom view

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>
2025-11-06 15:57:55 +07:00
Andy ButlandandGitHub 2e85f1bbf8 Dependencies: Update node development dependency to latest secure version of current major (17) (#20735)
Update node development dependency to latest secure version of current major.
2025-11-05 20:57:55 +01:00
Jacob OvergaardandGitHub 594c3f4eac Rich Text Editor: The media picker skips the "edit media" dialog when editing an image (closes #20066) (#20740)
* fix: Tiptap Media Picker: Skip media picker modal when editing existing images

Fixes the media picker workflow to match v13 behavior where clicking
an existing image directly opens the alt text/caption editor instead
of forcing users to re-select the same image from the media library.

Also fixes caption text extraction to properly read from the figcaption
node using Tiptap's NodeSelection API instead of unreliable attribute-based
approach.

Changes:
- Skip media picker when currentMediaUdi exists (lines 77-92)
- Extract caption from NodeSelection.node using descendants() (lines 55-73)
- Add NodeSelection export to tiptap externals for proper typing

* Refactor: Extract nested logic from media picker execute method

Reduces cyclomatic complexity from 15 to 1 by extracting conditional
logic into focused private helper methods. Addresses CodeScene warnings
for complex method and nested conditionals (bumpy road smell).

Created helper methods:
- #extractMediaUdi, #extractCaption, #findFigcaptionText
- #getMediaGuid, #updateImageWithMetadata

No functional changes - improves maintainability and testability.
2025-11-05 14:51:16 +00:00
72d7ed438f Property Editors: Hide "add button" when maximum configuration is 1 (fixes #20407) (#20738)
Hide add button when max 1

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
2025-11-05 12:12:04 +00:00
Mathias HelsengrenandGitHub 297c5d3824 Header: Adjusted button focus border color contrast (#20562)
Made use of the uui-button outline overwrite to change the focus border color to be the contrast color of the header.
2025-11-05 11:04:59 +00:00
Nhu DinhandGitHub e8f2bb33d7 E2E: QA Fixed the failing tests due to the recent UI changes (#20576)
* Added skip tag for the failing tests due to the issues and added waits for the flaky tests

* Commentted code as the reference items displays randomly

* Bumped version

* Added more waits to avoid the flaky tests

* Updated tests for setting culture and hostnames since the first content already has the default domain

* Fixed flaky tests

* Updated tests since the reload step is flaky

* Added more waits for the flaky tests in Windows

* Need to publish first document before set domain for second document

* Make permission tests run in the pipeline

* Added step to ensure the rollback action is completed

* Reverted npm command

* Added skip tag for the permission tests

* Fixed test for the culture and hostname permission

* Removed waits as it is includes in test helper

* Fixed test for adding a media in RTE Tiptap property editor

* Updated test helper function to avoid the flaky tests releated to block

* Added more waits to ensure image uploaded

* Bumped version

* Bumped version

* Reverted

* Reverted code

* Bumped version of test helper

* Bumped version

* Reverted code

* Added more waits to avoid flaky tests

* Added more waits

* Updated nightly pipeline: remove v17/dev, run different app setting tests by default and not run Relation Type in Linux as they are too flaky

* Added more waits

* Added npm command for testWindows

* Added more waits after creating a folder
2025-11-05 15:54:09 +07:00
Jacob Overgaard b22d3ddc00 Auth: Cleans up stale or completed auth details from storage (#20725)
* fix: cleans up stale PKCE keys after auth regardless of success or error

* fix: cleans up stale PKCE data on logout
2025-11-04 14:10:04 +01:00
Jacob Overgaard 5739049f90 Merge remote-tracking branch 'origin/v16/dev' 2025-11-04 14:09:18 +01:00
2b8146f72d Media: Add protection to restrict access to media in recycle bin (closes #2931) (#20378)
* Add MoveFile it IFileSystem and implement on file systems.

* Rename media file on move to recycle bin.

* Rename file on restore from recycle bin.

* Add configuration to enabled recycle bin media protection.

* Expose backoffice authentication as cookie for non-backoffice usage.
Protected requests for media in recycle bin.

* Display protected image when viewing image cropper in the backoffice media recycle bin.

* Code tidy and comments.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Introduced helper class to DRY up repeated code between image cropper and file upload notification handlers.

* Reverted client-side and management API updates.

* Moved update of path to media file in recycle bin with deleted suffix to the server.

* Separate integration tests for add and remove.

* Use interpolated strings.

* Renamed variable.

* Move EnableMediaRecycleBinProtection to ContentSettings.

* Tidied up comments.

* Added TODO for 18.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-04 07:39:44 +00:00
Jacob Overgaard b502e29d51 Merge remote-tracking branch 'origin/release/17.0' 2025-11-04 08:25:04 +01:00
Niels LyngsøandGitHub b68a6a9502 Data Type: use Property Editor UI label instead over name (#20716)
Prioritize using the property editor ui label and localize it.
2025-11-03 16:09:31 +00:00
a4d893a7b4 Rich text editor: Treat an "empty" value as a non-value (closes #20454) (#20719)
* Make the RTE treat an "empty" value as a non-value

* Additional tests

* Add tests for invariant and variant content.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-11-03 15:24:55 +00:00
313b60aca5 Load Balancing: Move temporary files and make them configurable to allow for media upload when load balancing the backoffice (#20717)
* make file upload location configurable

* Update src/Umbraco.Core/Configuration/Models/HostingSettings.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix default implementation

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-03 15:22:16 +01:00
Lee KelleherandGitHub c1a8500f12 Tiptap RTE: Localizes property editor UI label (removes "[Tiptap]" from label) (closes #20439) (#20713)
* Localized RTE property-editor UI label, removing "[Tiptap]"

* Updated acceptance test

* Localized the button label in the data-type and property-editor picker modals

* Based on @copilot suggestion, localized the property-editor UI label in the other places
2025-11-03 11:49:55 +00:00
cfa530487b Property Editors: Add mandatory support to Number Range (Refactor). (#20570)
* Added mandatory property to number range property editor and bind it to the inner input.

* Added mandatory message support.

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
2025-11-03 10:56:37 +01:00
Kenn JacobsenandGitHub 4ee1d7b13e Performance: Cache published content instances at cache service level (#20681)
Cache published content instances at cache service level
2025-11-03 10:55:23 +01:00
95cc6cc67b Performance: Request cache referenced entities when saving documents with block editors (#20590)
* Added request cache to content and media lookups in mult URL picker.

* Allow property editors to cache referenced entities from block data.

* Update src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Add obsoletions.

* Minor spellcheck

* Ensure request cache is available before relying on it.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: kjac <kja@umbraco.dk>
2025-11-03 10:39:58 +01:00
Rick ButterfieldandGitHub 4fc79ad1f8 Preview: Add allow-forms to iframe sandbox attributes (#20701)
Add 'allow-forms' to iframe sandbox attributes
2025-10-31 13:34:36 +01:00
Laura Neto 417335a5c6 Task: Dependency track (#20670)
* Generate BOM files on build

* Upload BOM to Dependency Track

* Move Backoffice BOM generation to right after install

The build and/or pack steps are deleting files that are needed for the BOM to be generated properly.

* Split the BOM uploads into different jobs

* Fix wrong usage of parameters

* Move order of dependency track stage

* Fix wrong umbracoVersion value

* Small fixes

* Log curl response headers

* Correct version sent to dependency track

* Adjusted curl flags

* Fix bom file path

* Fix dotnet bom file name

* Add Login UI to dependency track

* Generate BOM for E2E Tests

* Move dependency track stage

* Move acceptance test .env generation to e2e install template

Needed as the post install script is expecting this to exist.

* Use major version if public release

* Missing ')'

* Reverted npm install command changes in static assets project
2025-10-31 11:13:45 +01:00
Niels LyngsøandNiels Lyngsø b4220a4d80 Collection children: A slim navigation of collection children + higher take above target (#20641)
* enforce update of children when collection

* only load one above and below collection children

* take 50 above a target for default experience

* revert reset target

* remove old impl
2025-10-31 10:54:37 +01:00
Laura NetoandGitHub 973a9573cc Task: Dependency track (#20670)
* Generate BOM files on build

* Upload BOM to Dependency Track

* Move Backoffice BOM generation to right after install

The build and/or pack steps are deleting files that are needed for the BOM to be generated properly.

* Split the BOM uploads into different jobs

* Fix wrong usage of parameters

* Move order of dependency track stage

* Fix wrong umbracoVersion value

* Small fixes

* Log curl response headers

* Correct version sent to dependency track

* Adjusted curl flags

* Fix bom file path

* Fix dotnet bom file name

* Add Login UI to dependency track

* Generate BOM for E2E Tests

* Move dependency track stage

* Move acceptance test .env generation to e2e install template

Needed as the post install script is expecting this to exist.

* Use major version if public release

* Missing ')'

* Reverted npm install command changes in static assets project
2025-10-31 10:53:57 +01:00
Niels LyngsøandGitHub bd94522237 Collection children: A slim navigation of collection children + higher take above target (#20641)
* enforce update of children when collection

* only load one above and below collection children

* take 50 above a target for default experience

* revert reset target

* remove old impl
2025-10-31 10:52:10 +01:00
66409b9ebd Performance: Request cache referenced entities when saving documents with block editors (#20590)
* Added request cache to content and media lookups in mult URL picker.

* Allow property editors to cache referenced entities from block data.

* Update src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Add obsoletions.

* Minor spellcheck

* Ensure request cache is available before relying on it.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: kjac <kja@umbraco.dk>
2025-10-31 10:49:26 +01:00
Nathan WoulfeandGitHub f27fb58e13 box-sizing to ensure height is correct (#20694) 2025-10-31 06:42:45 +01:00
Laura Neto aadf5e7643 Bump the version to 17.0.0-rc2 2025-10-30 16:14:08 +01:00
Mads RasmussenandGitHub b8cb198a1a Document Recycle Bin: Remove non-relevant entity bulk actions (closes #20677) (#20685)
Add 'not trashed' condition to document bulk actions

Introduces the UMB_ENTITY_IS_NOT_TRASHED_CONDITION_ALIAS to various document-related bulk action manifests, ensuring actions like duplicate, move, publish, unpublish, and trash are only available for entities that are not already in the recycle bin.
2025-10-30 13:41:20 +01:00
5032b25e3c Icon picker: Better title for icon colors (#20649)
* Better title for icon colors

* Add name for legacy colors

* Translations of colors

* Fixed import, adding missing colour, added Italian translations.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-10-30 13:34:58 +01:00
3d24f0a51e Implementing an inline toggle button to show/hide password. (#20611)
* Implimented an inline toggle button to show/hide your password, also changed the css to accommodate these changes

* Cleaned the css

Added the svg's to their own const for easy reuse

Added localization for the arialabel on the button

Seperated the createFormLayoutItem so there is a seperate for the password input

Moved all the conditional logic in the onclick event to fit inside one if/else statement

* Removed old logic that added a 100ms timeout that would sometimes be enough for localization to load, and replaced it with a function.
The function will try and resolve the promise by checking if the localize.terms methods returns a changed value, if not then it retries every 50ms or untill it hits a max retry of 40/2 seconds.

* Re adding the hide for -ms-reveal to support Microsoft Edge browsers

* Removed a console.log

* Alligned the button behavior so it fits better with what we have in the uui libary.
Now the button is always visible instead of appearing  on hover or when in focus

* Update src/Umbraco.Web.UI.Login/src/auth.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Login/src/auth.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @iOvergaard

* Apply suggestion from @iOvergaard

* Adding the requested changes via my own fork (#20664)

Changed the logic for waitForLocallization Added the svg's as files that are imported instead of having the raw svg in the code

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-30 11:56:15 +01:00
Jacob Overgaard 1c6d4f360d bumps package.json version to 17.1.0-rc 2025-10-30 11:50:19 +01:00
Jacob Overgaard cdf0e5b3b3 Merge remote-tracking branch 'origin/v17/dev' 2025-10-30 11:49:08 +01:00
Niels Lyngsø 837a56652f Merge branch 'release/17.0' into v17/dev
# Conflicts:
#	version.json
2025-10-29 20:04:40 +01:00
Niels Lyngsø e315ae3ae1 Cherry picked Prevent Section URL Retention fix 2025-10-29 20:03:18 +01:00
Jacob Overgaard 393179d8a1 build: updates lockfile 2025-10-29 11:40:54 +01:00
Jacob Overgaard b7105c88d8 build: adds back the ^ missing from openapi-ts to allow newer versions to be used 2025-10-29 11:40:46 +01:00
Jacob Overgaard f1b7a33e86 bumps package lock file 2025-10-29 11:34:57 +01:00
Andy ButlandandGitHub e7ccfaaaac Routing: Added method to IDocumentUrlService for retrieving document key from URI (closes #20666) (#20673)
Added method to IDocumentUrlService for retrieving document key from URI.
2025-10-29 09:47:17 +01:00
Andy Butland a61bb8dbef Bumped version to 17.0.0-rc1. 2025-10-29 07:33:27 +01:00
8733230762 Property Editors: Added form control and mandatory support to editors in common group(Number, Tags, Slider). (#20659)
* Added mandatory support to property-editor-ui-number.

* Added form control to property-editor-ui-tags

* Added validator to the slider when value is missing and support for mandatory and mandatory message.

* Removed unnecessary ternary.

* Removed white space lit error.

* Fix tags input to handle undefined items array

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
2025-10-28 12:33:29 +00:00
da0bdbf8c7 Caching: Resolves publish and install issues related to stale cached data retrieval (closes #20539 and #20630) (#20640)
* Request cache published content creation with version.

* Reload memory cache after install with package migrations.

* Improve message on install for database cache rebuild.

* Update src/Umbraco.Infrastructure/Install/MigrationPlansExecutedNotificationHandler.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Relocated memory cache refresh after package install from notification handler to unattended upgrader.

* Fix construtor breaking change

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: mole <nikolajlauridsen@protonmail.ch>
2025-10-28 13:32:55 +01:00
Andy Butland f726a7d7c5 Merge branch 'release/17.0' of https://github.com/umbraco/Umbraco-CMS into release/17.0 2025-10-28 13:32:49 +01:00
Andy Butland 498bb5ee24 Merge branch 'main' into v17/dev 2025-10-28 13:32:23 +01:00
Niels Lyngsø cee730517c Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/packages/content/property-type/workspace/views/settings/property-workspace-view-settings.element.ts
2025-10-28 11:20:03 +01:00
Nhu DinhandGitHub f6b566c65f E2E: Updated user group and data type acceptance tests to match UI changes (#20658)
* Updated user group tests to match the recent UI changes

* Updated data type collection tests due to UI changes

* Bumped version of test helper
2025-10-28 13:14:52 +07:00
a3d6b4f844 Entity Data Picker: Data editor and value converter (#20661)
* change property value to an object

* add const for picker data source type

* Add value editor and converter server-side

* register schema for property editor + move settings ui

---------

Co-authored-by: kjac <kja@umbraco.dk>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2025-10-27 15:40:16 +00:00
Andy Butlandandmole 1615740bdf Media types: Handle null configured file extensions when populating allowed media types (closes #20620) (#20635)
* Handle null configured file extensions when populating allowed media types.

* Added clarifying comment.

(cherry picked from commit f33eb3f678)
2025-10-27 13:43:39 +01:00
Andy ButlandandGitHub d3b9256b4e Webhooks: Reverts nullability update to continue to allow derived classes to create null payloads (#20660)
* Trees: Restore backward compatibility for file system based tree controllers (closes #20602) (#20608)

* Restore backward compatibility for file system based tree controllers.

* Aligned obsoletion messages.

* Reverts nullability update on ConvertNotificationToRequestPayload.
2025-10-27 13:11:51 +01:00
399431b02d Remove references to Our Umbraco - moved to the new forum (#20644)
* Remove unused help controller

* Correct documentation links

* Link to the new release site for compares

* Remove unused translation key with reference to Our

* Update NoNodes / NotFound to point to the forum instead of Our

* Change dashboards form Our to Forum and de-emphasize Discord as a support channel

* Removes Help controller reference

* Forgot to rename the css Id

* Update src/Umbraco.Web.UI.Client/src/assets/lang/ar.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix typo in Community Forum help menu item name

* Refer to releases instead of a download page

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Update the default dashboard with better content and clearer headings

* Obsolete the HelpController instead

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-10-27 11:12:19 +00:00
Andy Butland 33c2838d20 Merge branch 'release/17.0' of https://github.com/umbraco/Umbraco-CMS into release/17.0 2025-10-27 11:43:34 +01:00
981c9a7db2 Load balancing backoffice: Add missing id to distributed jobs when updating (#20642)
* Add ID when updating background job

* Reduce default period to 5 seconds

* Apply suggestions from code review

Co-authored-by: Andy Butland <abutland73@gmail.com>

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-10-27 08:49:25 +01:00
Andy Butland de9cbf9ef5 Merge branch 'release/17.0' of https://github.com/umbraco/Umbraco-CMS into release/17.0 2025-10-24 14:22:42 +02:00
d9cdf03442 Preview: Allows changing the preview environment inside the preview app, and other UX changes that enhance the experience (#20598)
* Preview Device: refactored config

Fixed "flip" icon style.

Removed "shadow" as unnecessary.

Renamed "className" to "wrapperClass" to be descriptive.

* Preview element CSS refinement

* Preview element: load in private extensions

* Added "Preview Environments" preview-app

Made `unique`, `culture` and `segment` observable in the context.

* Aligned preview-app design

with `hidden` attribute and design consistency.

* Created "Preview" package

* Relocated "Preview Apps" and Context to the new package

* Deprecated `UmbDocumentPreviewRepository` (for v19)

as the methods have moved to `UmbPreviewRepository`.

* Removed Preview Sessions event listeners

* Changed localization from "End" to "Exit"

* chore: consumes context only when needed

* feat: uses the UmbPreviewRepository instead

* feat: adds localization to errors and ensures the function does not randomly throw

* feat: prevents creating a new repository for every click

* feat: prevents potential memory leak by adding a signal to the events added to each iframe update

* feat: adds a custom interface to prevent typescript errors

* feat: ensures new string states are checked properly

* docs: adds comment to avoid confusion

* feat: sets up scaling once per iframe load rather than on each update

* fix: ensures that you can go back to the default segment again

* feat: closes popovers when clicking on the iframe (losing blur) and if selecting an item (expect for devices)

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-10-24 08:29:22 +00:00
Niels Lyngsø 67d1ac3b94 Merge branch 'main' into v17/dev 2025-10-23 14:44:24 +02:00
Mads RasmussenandGitHub e482976a9d User And User Group Workspace: Make views extendable (#20548) (#20617)
* implement user details as a workspace view

* register user group details as a workspace view
2025-10-23 14:43:49 +02:00
Niels Lyngsø 1d8cadbeee Merge branch 'main' into v17/dev 2025-10-23 13:42:30 +02:00
Niels Lyngsø 602ad420bd Merge branch 'release/17.0' into v17/dev
# Conflicts:
#	version.json
2025-10-23 13:40:35 +02:00
bd6ab83712 Collection: prevent multiple load calls within a very short time (#20528)
* debounce loadCollection calls

* update document collection context

* update media collection context

* remove duplicate request to reload

* Update src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add fadeIn animation to empty state to prevent flicker

* Update collection-default.element.ts

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2025-10-23 13:15:54 +02:00
Niels LyngsøandGitHub 4252cbc64b Login photo: Update with v.17 edition (#20625)
updated login photo
2025-10-23 11:02:13 +00:00
Lee KelleherandGitHub 9cc2df7acd Preview: Removes sessions (#20561)
Removes preview sessions concept

Fixes #19443 and #19471.

The implementation of exiting sessions was a design flawed.
The v13 feature worked due to an implementation bug.

Exiting preview mode should be a deliberate action by the user.
2025-10-23 11:47:50 +02:00
Laura Neto b3145a01eb Bump version to 17.0.0-rc 2025-10-22 17:01:02 +02:00
Andy ButlandandLaura Neto aeb1437f0e Trees: Restore backward compatibility for file system based tree controllers (closes #20602) (#20608)
* Restore backward compatibility for file system based tree controllers.

* Aligned obsoletion messages.
2025-10-22 16:44:57 +02:00
Andy Butland 04df2652f5 Trees: Restore backward compatibility for file system based tree controllers (closes #20602) (#20608)
* Restore backward compatibility for file system based tree controllers.

* Aligned obsoletion messages.
2025-10-22 16:27:37 +02:00
Andy Butland 7dcf329d2c Merge branch 'main' into v17/dev 2025-10-22 16:27:09 +02:00
Andy Butland c422a9ea6a Merge branch 'main' into v17/dev 2025-10-22 15:15:18 +02:00
Nicklas KramerandAndy Butland 1bf53f3554 Filesystem: Prevent tree showing other filetypes than the supported ones (#20567)
* Added check to only find .css files in FileSystemTreeServiceBase.cs

* Marking GetFiles as virtual and overriding it in StyleSheetTreeService.cs to only find .css files

* Redone tests to fit new format

* Fix tests to use file extensions

* Adding file extensions to all other relevant tests

* Adding file filter to remaining trees

* Adding tests to ensure invalid filetypes wont show

* Encapulation and resolved minor warnings in tests.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-10-22 15:13:59 +02:00
leekelleher 924af4b565 Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs
#	src/Umbraco.Web.UI.Client/src/apps/preview/preview.context.ts
#	src/Umbraco.Web.UI.Client/src/packages/core/repository/repository-items.manager.ts
2025-10-22 13:33:45 +01:00
Mads RasmussenandGitHub a5adcbdeef Detail Request Manager: Use tryExecute for delete API call to enable error notifications (#20607)
Use tryExecute for delete API call

Replaces direct await of #delete with tryExecute to improve error handling in the delete method of UmbManagementApiDetailDataRequestManager.
2025-10-22 14:30:46 +02:00
Sven GeusensandAndy Butland 286e727de8 Webhooks: Register OutputExpansionStrategy for webhooks if Delivery API is not enabled (#20559)
* Register slimmed down OutputExpansionStrategy for webhooks if deliveryapi is not enabled

* PR review comment resolution
2025-10-22 13:51:08 +02:00
Andy Butland 201906f662 Populate IncludeDescendants on ContentPublishedNotification when publishing branch (forward port of #20578). 2025-10-22 13:37:55 +02:00
Andy Butland a4c0da3465 Migrations: Use reliable GUID to check for existence of data type when creating (#20604)
* Use reliable GUID to check for existence of data type in migration.

* Retrieve just a single field in existence check.
2025-10-22 13:27:43 +02:00
Mads RasmussenandGitHub 28c01d2819 Dictionary: Fix shortcut Ctrl + S not saving dictionary items (#20605)
* switched event listener from 'change' to 'input'

* Update workspace-view-dictionary-editor.element.ts
2025-10-22 10:29:08 +00:00
Niels Lyngsø 298db76cb1 Merge branch 'release/17.0' into v17/dev 2025-10-22 11:55:14 +02:00
Niels Lyngsø 2248eeb03e update import 2025-10-22 11:54:50 +02:00
Niels LyngsøandGitHub f6df0c1828 Item Repository: Sort statuses by order of unique (#20603)
* utility

* ability to replace

* deprecate removeStatus

* no need to call this any longer

* Sort statuses and ensure not appending statuses, only updating them
2025-10-22 09:42:13 +00:00
Jacob Overgaard 44d52392b5 Merge branch 'release/17.0' into v17/dev 2025-10-22 09:50:45 +02:00
63ae29a1aa Preview: Fixes a potential issue where the preview URL could be a different backoffice host (#20591)
* hotfix: ensures that local urls stay relative so we land up on the correct backoffice host that the user initiated the preview session from originally

* feat: since ensureAbsoluteUrl is never supplied anymore, we can remove the parameter altogether

* Remove unused dependency

* Expose IsExternal for URLs

* feat: adds localize controller

* chore: generates api models

* feat: marks the internal preview default url as relative, so that the `<base>` tag is taken into consideration - that way the URL will open on whatever host is active

* Remove IsExternal from the API again

* regenerate types

---------

Co-authored-by: kjac <kja@umbraco.dk>
2025-10-22 07:48:55 +00:00
Mads RasmussenandGitHub 6effe32136 Document Recycle Bin: Fix missing item name when restoring (#20599)
allow to pass in a item data resolver
2025-10-21 20:46:16 +02:00
leekelleher 4717264e10 Merge branch 'release/17.0' into v17/dev
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-action/restore-from-recycle-bin/restore-from-recycle-bin.action.ts
#	tests/Umbraco.Tests.AcceptanceTest/package-lock.json
#	tests/Umbraco.Tests.AcceptanceTest/package.json
2025-10-21 16:11:22 +01:00
f0ac7b4a50 Document/Media Recycle Bin: Add 'Trashed' state to info workspace view (#20581)
* Add 'Trashed' state to document workspace view

Introduces a new 'Trashed' label and tag for documents in the workspace view. Updates localization to include the 'Trashed' term for improved clarity when displaying trashed documents.

* Show trashed state in media workspace info view

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-10-21 15:51:28 +02:00
5f0122e18b Make Create Actions open as dialogs (part 2) (#20523)
* open script create options as a dialog instead of sidebar

* align and simplify the blueprint dialog options

* debounce loadCollection calls

* update document collection context

* update media collection context

* Updated tests

* Bumped test helpers

* Revert "debounce loadCollection calls"

This reverts commit 1c15dda08d.

* Revert "update document collection context"

This reverts commit 47d74a8f5d.

* Revert "update media collection context"

This reverts commit f2eb1f22c1.

* align create dialog texts

* fix indention

* Bumped version of test helpers

---------

Co-authored-by: Andreas Zerbst <andr317c@live.dk>
Co-authored-by: Andreas Zerbst <73799582+andr317c@users.noreply.github.com>
2025-10-21 15:24:52 +02:00
Jacob OvergaardandGitHub de5a9ca5af Preview: Replaces WebSocket with the SignalR library to improve connectivity in the preview window (#20585)
* feat: replaces manual WebSocket with the actual SignalR library on the preview context

* feat: informs the developer what went wrong in preview mode

* feat: awaits the stop connection before proceeding

* feat: ensures no existing connection exists
2025-10-21 13:20:17 +00:00
Kenn JacobsenandGitHub 58068d1aa7 Rendering: Explicitly contextualize variation context for language fallback (closes #20350) (#20587)
Expliticly contextualize variation context for language fallback rendering
2025-10-21 14:41:29 +02:00
5f1c65e7ea User group: permissions grouping (#20584)
* clean up

* localizations

* group user permission by entity type

* adjustments

* fix lint errors

* Support granular permissions without entity type

Updated granular permission handling to allow permissions that are not tied to a specific entity type. Adjusted rendering logic and manifest interface to support undefined or empty forEntityTypes, and added UI for displaying ungrouped granular permissions.

* revert for now

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-10-21 12:10:41 +00:00
Laura NetoandGitHub 20eedda4c7 Template: Update dotnet project template to remove previous LTS checks (#20573)
* Remove now unnecessary LTS checks from project dotnet template

* Update the starter kit versions
2025-10-21 13:12:25 +02:00
1099332edb Fixes broken integration test for v17 release (#20582)
Fixes broken integration test

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-10-21 10:42:22 +00:00
Andy ButlandandLaura Neto 3a83e4a494 Publishing: Resolve exceptions on publish branch (#20464)
* Reduce log level of image cropper converter to avoid flooding logs with expected exceptions.

* Don't run publish branch long running operation on a background thread such that UmbracoContext is available.

* Revert to background thread and use EnsureUmbracoContext to ensure we can get an IUmbracoContext in the URL providers.

* Updated tests.

* Applied suggestion from code review.

* Clarified comment.
2025-10-21 12:22:50 +02:00
Andy ButlandandLaura Neto af3b17243b Media: Fixes SQL error to ensure database relation between user group media start folder and deleted media item is removed (closes #20555) (#20572)
Fixes SQL error to ensure database relation between user group media start folder and deleted media item is removed.
# Conflicts:
#	src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs
2025-10-21 11:42:19 +02:00
d05add4e1f Tiptap RTE: Allow removal of unregistered extensions (#20571)
* Tiptap toolbar config: enable removal of unregistered extensions

* Tiptap statusbar config: enable removal of unregistered extensions

* Tiptap toolbar config: Typescript tidy-up

* Tiptap toolbar sorting amend

Removed the need for the `tiptap-toolbar-alias` attribute,
we can reuse the `data-mark`.

* Tiptap extension config UI amend

If the extension doesn't have a `description`,
then add the `alias` to the title/tooltip, to give a DX hint.

* Tiptap toolbar: adds `title` to placeholder skeleton

* Added missing `forExtensions` for Style Select and Horizontal Rule toolbar extensions

* Update src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/toolbar-configuration/property-editor-ui-tiptap-toolbar-configuration.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/statusbar-configuration/property-editor-ui-tiptap-statusbar-configuration.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-21 10:43:52 +02:00
Andy Butlandandmole 68d1b9481a Hybrid Cache: Resolve start-up errors with mis-matched types (#20554)
* Be consistent in use of GetOrCreateAsync overload in exists and retrieval.
Ensure nullability of ContentCacheNode is consistent in exists and retrieval.

* Applied suggestion from code review.

* Move seeding to Umbraco application starting rather than started, ensuring an initial request is served.

* Tighten up hybrid cache exists check with locking around check and remove, and use of cancellation token.

(cherry picked from commit 81a8a0c191)
2025-10-21 09:59:49 +02:00
Laura NetoandGitHub e6f48799a1 Property Editors: DateTimeWithTimeZone - Changing timezone mode to Local shows invalid time zone error (#20526)
* Store local time zone as UTC and do not throw validation error when stored time zone is different

* Additional fixes when switching between date time editors with and without time zone

* Additional fixes

* Ensure that an update is triggered when the expected value does not match the stored value

This will happen when switching between editors (with and without time zone) or switching between a specific time zone to the editor's local time zone.

* Fix inconsistencies with null and undefined

* Fix inconsistencies between date/time provided to the client and returned in the value converter (when switching between editors)

* Fix unit tests and small bug

* Adjust integration test

* Small improvement

* Update test data

* Adjust logic so that time zone offsets are updated every time the date value changes

* Do not pre-select time zone when switching between unspecified and time zone editors
2025-10-21 09:29:46 +02:00
dependabot[bot]andJacob Overgaard 1efdde2473 Bump vite from 7.1.9 to 7.1.11 in /src/Umbraco.Web.UI.Client
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.9 to 7.1.11.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.11/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.11
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-21 09:14:05 +02:00
Mads RasmussenandGitHub c666c00ac5 Document/Media Recycle Bin: Add missing root workspace views (#20494) (#20569)
* add document recycle bin root workspace

* add media recycle bin root workspace

* export consts
2025-10-20 16:42:52 +02:00
Mads RasmussenandGitHub 94692cccb7 Document/Media Recycle Bin: Show full breadcrumb (#20547)
* register structure context for recycle bin

* Update manifests.ts

* export consts

* move href construction to context + override for document and media
2025-10-20 13:43:10 +02:00
Lee Kelleherandleekelleher ab4be79da0 Preview: Redirect to published URL on exit (#20556)
* Preview Exit: Gets the page's published URL on exit for redirect

* Preview Open Website: Uses the page's published URL

* Tweaked the published URL logic

* Code amends based on @copilot's suggestions

(cherry picked from commit d5a2f0572e)
2025-10-20 10:55:08 +01:00
Andy ButlandandGitHub a808e3ab46 Added direct dependency to secure version of Microsoft.Build.Tasks.Core in Umbraco.Web.UI (#20546)
Added direct dependency to secure version of Microsoft.Build.Tasks.Core in Umbraco.Web.UI.
2025-10-20 11:54:26 +02:00
Mads RasmussenandGitHub 92dbaf7162 Document/Media: Readonly when in recycle bin (#20541)
* make document and media readonly when trashed + reload the entity

* introduce restore event + remove readonly

* handle media audit log todos

* disable content type picker when trashed

* disable template picker when trashed
2025-10-17 17:49:05 +00:00
Kenn JacobsenandGitHub ae2c59b703 Make the indexing batch size configurable (#20543)
* Introduce configurable batch size for indexing

* Stop using Examine indexing events for reporting index rebuild operation completeness (it is volatile)
2025-10-17 15:45:03 +02:00
Andy Butland 72d44decd4 Update OpenApi.json and client-side models. 2025-10-17 15:30:43 +02:00
Anders ReusandAndy Butland 7e7d9da144 Added trashed state so when requesting content from the recycle bin via the management api it will return trashed instead of published state (#20542)
Added trashed state so when requesting content from the recycle bin via the management api, the state will be trashed instead of published.
2025-10-17 15:22:11 +02:00
Andy Butland b142dcc84f Merge branch 'main' into v17/dev 2025-10-17 15:20:55 +02:00
Andy Butland 8b1f18699d Update OpenApi.json and client-side models. 2025-10-17 15:20:01 +02:00
Lee KelleherandJacob Overgaard f82c0a90e7 UFM: Adds $index support to Block editors (fixes #20470) (#20488)
* Block List: adds `$index` support for UFM labels

* Block Grid: adds `$index` support for UFM labels

* Block RTE: adds `$index` support for UFM labels

Which is always zero `0`.
But has been wired up if we do implement the index order in future.
2025-10-17 15:03:21 +02:00
Andy Butland 5278b67f60 Merge branch 'main' into v17/dev 2025-10-17 14:41:06 +02:00
Andy ButlandandGitHub 65798b5722 Templates: Retain layout from file when loading template (closes #20524) (#20529)
Retain layout from file when loading template.
2025-10-17 10:46:54 +02:00
Jacob Overgaard 7d0adc4755 build(deps): bumps @umbraco-ui/uui from 1.16.0-rc.0 to 1.16.0 2025-10-17 10:36:38 +02:00
Andreas ZerbstandGitHub 11bf60a67c E2E: Updated exisiting acceptance tests to match updates from front end (#20525)
* Updated tests

* E2E: Updated acceptance tests to match changes (#20493)

* Updated tests to match changes

* More updates

* Bumped version

* Reverted change
2025-10-17 09:37:44 +02:00
Andy Butland de0503d90c Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs
2025-10-16 21:12:57 +02:00
4a504e8c95 Extensions: Adds @provideContext and @consumeContext decorators for a better developer experience (#20510)
* feat: adds first draft of a context consume decorator

* feat: uses an options pattern

* feat: changes approach to use `addInitializer` and `queueMicroTask` instead

* feat: adds extra warning if context is consumed on disconnected controllers

* feat: example implementation of consume decorator

* feat: adds support for 'subscribe'

* feat: initial work on provide decorator

* docs: adds license to consume decorator

* feat: adds support for umbraco controllers with `hostConnected`

* feat: uses asPromise to handle one-time subscription instead

* test: adds unit tests for consume decorator

* feat: adds support for controllers through hostConnected injection

* feat: adds support for controllers through hostConnected injection

* test: adds unit tests for provide decorator

* docs: adds more documentation around usage and adds a few warnings in console when it detects wrong usage

* feat: removes unused controllerMap

* docs: adds wording on standard vs legacy decorators

* docs: clarifies usage around internal state

* feat: adds proper return types for decorators

* docs: adds more types

* feat: makes element optional

* feat: makes element optional

* feat: uses @consume in the log viewer to showcase

* chore: cleans up debug info

* feat: renames to `consumeContext` and `provideContext` to stay inline with our own methods

* chore: removes unneeded typings

* chore: removes not needed check

* chore: removes not needed check

* test: adds test for rendered value

* feat: splits up code into several smaller functions

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* docs: augments code example for creating a context

* Update src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/search/components/log-viewer-search-input.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-16 15:38:26 +01:00
Engiber LozadaandGitHub 271edb5214 News Dashboard: split into card + container, parent handles the data from the repo (#20503)
* Made card element it is own reusable component and passing the data as property.

* Created the umb-news-container element to handle all the priority grouping.

* Added hover styles to normal-priority cards.

* Removed unused variable.
2025-10-16 12:55:43 +00:00
Mads RasmussenandGitHub 8beb7f2acc Collection menu item extension point (#20506)
* add extension option for collection menu item

* Add collection menu module export

* remove unused css

* register user collection menu item

* register user collection menu

* use collection modal for user picker

* Delete user-picker-modal.element.ts

* Update manifests.ts

* explicit exports to avoid name collision

* hack to avoid circular dependency

* fix lint errors

* fix missing const export

* Update collection-menu-item.element.ts
2025-10-16 12:57:15 +01:00
498754e170 Explicitly flush isolated caches by key for content updates (#20519)
* Explicitly flush isolated caches by key for content updates

* Apply suggestions from code review

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-10-16 10:56:32 +00:00
Andreas ZerbstandGitHub ae73fb3431 E2E: Updated acceptance tests to match changes (#20493)
* Updated tests to match changes

* More updates

* Bumped version

* Reverted change
2025-10-16 09:24:56 +00:00
Andy ButlandandGitHub 3830d75413 Obsolete GetAtRoot on DocumentCache (#20514)
Obsolete GetAtRoot on DocumentCache.
2025-10-16 08:23:20 +02:00
Andy Butland f1f2f702c0 Restored and obsoleted removed extension method AsEnumerableOfOne. 2025-10-15 20:22:58 +02:00
Andy Butland c5c417d08e Merge branch 'release/17.0' into v17/dev 2025-10-15 20:13:08 +02:00
2503cabbce Dependencies: Update to .NET 10 RC 2 (#20508)
* Update to .NET 10 RC 2.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-15 20:12:11 +02:00
Niels Lyngsø cee163ae1f Merge branch 'release/17.0' into v17/dev 2025-10-15 14:13:47 +02:00
Niels Lyngsø 7e8a7f0b40 Cherry picked 4c05a114c5 2025-10-15 14:11:25 +02:00
Niels LyngsøandGitHub 126c8a56d5 variant selector: hint badge should render in append slotfor the variant selector (#20504)
ensure variant-selector badge gets rendered at the right spot
2025-10-15 09:29:49 +00:00
Niels Lyngsø 2412c662df Merge branch 'release/17.0' into v17/dev
# Conflicts:
#	version.json
2025-10-15 10:23:39 +02:00
Andy Butland 99ea8b9087 Back Office: Fixes link to workspace root from breadcrumb trail (closes: #20455) (#20459)
Fixes link to workspace root from breadcrumb trail.
2025-10-15 10:12:58 +02:00
Andy Butland b25feac011 Merge branch 'main' into v17/dev 2025-10-15 10:12:28 +02:00
Andy Butland 86dda9f9b3 Content Types: Prevent creation of document type with an alias that case insensitively matches an existing alias (closes #20467) (#20471)
Prevent creation of document type with an alias that case insensitively matches an existing alias.
2025-10-15 09:49:32 +02:00
Andy Butland a95fd9f340 Merge branch 'main' into v17/dev 2025-10-15 09:48:05 +02:00
Nikolaj Geisle 9c082c613e bump version to beta2 2025-10-15 08:55:15 +02:00
Copilotandleekelleher 5af9df1871 UFM: Add camelCase aliases for UFM filters to support UFMJS expressions (closes #20500) (#20501)
* Initial plan

* Add camelCase aliases for UFM filters with hyphens (stripHtml, titleCase, wordLimit)

Co-authored-by: iOvergaard <752371+iOvergaard@users.noreply.github.com>

* Add manifest tests for camelCase filter aliases

Co-authored-by: iOvergaard <752371+iOvergaard@users.noreply.github.com>

* discards tests that are not useful

* test: updates imports for stripHtml api

* Exports `UmbUfmStripHtmlFilterApi` class

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: iOvergaard <752371+iOvergaard@users.noreply.github.com>
Co-authored-by: leekelleher <leekelleher@gmail.com>
(cherry picked from commit a19b9fb5fe)
2025-10-14 17:42:22 +01:00
Mads RasmussenandGitHub 12297ea617 Picker data source: Add support for pickable filters (#20491)
* add pickable to vs code dictionary

* set up types for pickable filters in data sources

* pass search pickable filter to search result

* apply filter config in document data source example

* add pickable filters to custom tree example

* Update input-entity-data.context.ts

* remove unused

* Update types.ts
2025-10-14 17:11:34 +01:00
Anders ReusandAndy Butland 3892670d92 Added culture to the ApiContentRouteBuilder to include variant languages. (#20366) (#20499)
Added culture to the ApiContentRouteBuilder to include variant languages.
2025-10-14 16:14:02 +02:00
Andy Butland 07d0d7d2ee Merge branch 'main' into v17/dev 2025-10-14 16:10:59 +02:00
Mads Rasmussen d9de69c40f fix import in storybook of moved file 2025-10-14 12:27:21 +02:00
Mads Rasmussen ac56bffef2 fix import in storybook of moved file 2025-10-14 12:26:25 +02:00
Sven GeusensandSven Geusens 49f3fc9993 Delivery API: Fix not reindexing branch descendants when branch root already published but unchanged (closes #20370) (#20462)
* Fix deliveryApi not reindexing branch descendants when branch root already published and unchanged

* Commit update and name improvement
2025-10-14 12:18:26 +02:00
Sven Geusens 068183ac35 Merge branch 'main' into v17/dev 2025-10-14 12:17:06 +02:00
7f1cdf8ef5 Auhorization: Cherrypicked integration tests from V15 (#20492)
* V15 QA Added the authorization integration tests (#18419)

* Added authorization integration tests

* Removed unnecessary tests and update tests for preview controller

* Updated to use the newest changes from v15/dev and added an override for the AuthenticateClientAsync to use the userGroupKey

* Updated CompatibilitySuppressions to include changes from integration tests

* Updated pipelines

* Skips managementApi tests

* Only run necessary tests

* Added new schema per fixture to reduce test setup time

* Fixed failing tests

* Updated test setup

* Updated test

* Added suppression

* Fixed failing tests

* Updated addOnTeardown methods to protected

* Added method for clearing the host

* Added teardown

* Updated model usage

* Added a lot of cleanup for memory leak issues when running tests

* Added CompatibilitySuppressions.xml

* Updated tests

* Cleaned up

* Adjusted base classes

* Updated pipeline

* Updated CompatibilitySuppressions.xml

* Updated test logging

* Fixed reponse

* Updated condition to skip tests

* Updated tests, not done

* Reworked test to expect correct responses with correct setup

* Updated tests

* More updates to tests

* Updated tests

* Cleaned up tests

* Updated setup

* Cleaned up tests to match setup

* Cleaned up setup

* Removed suppression

* Fixed tests

* Move order of checks

* Fix naming

* Formatting

* Dispose of host

* Keep track of if we're disposed

* Compat suppression

* Dont dispose

* Fix failing tests

* removed unused virtual

* Updated CompatibilitySuppressions.xml

---------

Co-authored-by: Andreas Zerbst <andr317c@live.dk>
Co-authored-by: Zeegaan <skrivdetud@gmail.com>
Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
# Conflicts:
#	tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml
#	tests/Umbraco.Tests.Integration/ManagementApi/ManagementApiTest.cs
#	tests/Umbraco.Tests.Integration/ManagementApi/Policies/AllCultureControllerTests.cs
#	tests/Umbraco.Tests.Integration/ManagementApi/Policies/CreateDocumentTests.cs
#	tests/Umbraco.Tests.Integration/ManagementApi/Policies/UpdateDocumentTests.cs
#	tests/Umbraco.Tests.Integration/ManagementApi/Preview/EndPreviewTests.cs
#	tests/Umbraco.Tests.Integration/ManagementApi/Preview/EnterPreviewTests.cs
#	tests/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs

* Updated test

* Updates

* Removed unnessecary test

---------

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>
Co-authored-by: Zeegaan <skrivdetud@gmail.com>
Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
2025-10-14 10:04:10 +00:00
Andy ButlandandZeegaan 37b239b8ca Performance: Reduce number of database calls in save and publish operations (#20485)
* Added request caching to media picker media retrieval, to improve performance in save operations.

* WIP: Update or insert in bulk when updating property data.

* Add tests verifying UpdateBatch.

* Fixed issue with UpdateBatch and SQL Server.

* Removed stopwatch.

* Fix test on SQLite (failing on SQLServer).

* Added temporary test for direct call to NPoco UpdateBatch.

* Fixed test on SQLServer.

* Add integration test verifying the same property data is persisted as before the performance refactor.

* Log expected warning in DocumentUrlService as debug.

(cherry picked from commit 12adfd52bd)
2025-10-14 11:29:24 +02:00
Laura Neto ea44850804 Dependencies: Remove Microsoft.CodeAnalysis.CSharp dependency from Umbraco.Infrastructure (#20481)
* Remove Microsoft.CodeAnalysis.CSharp from Infrastructure project

This was only needed for runtime compilation and thus is no longer needed in Infrastructure.
It also caused dependency problems with EF Core Design in previous versions.

* Disable CPM for UI project to better reflect consumers

This will ensure that we face any potential dependency issues consumers are also likely to run into.

* Add `Microsoft.CodeAnalysis.CSharp` reference to `Umbraco.Cms.DevelopmentMode.Backoffice`
2025-10-14 09:49:46 +02:00
Laura Neto 4dbb4eb48b Merge branch 'main' into v17/dev
# Conflicts:
#	Directory.Packages.props
#	tests/Umbraco.Tests.AcceptanceTest/package-lock.json
#	tests/Umbraco.Tests.AcceptanceTest/package.json
2025-10-14 09:48:16 +02:00
Laura NetoandGitHub b5662d9c89 Dependencies: Remove Microsoft.CodeAnalysis.CSharp dependency from Umbraco.Infrastructure (#20481)
* Remove Microsoft.CodeAnalysis.CSharp from Infrastructure project

This was only needed for runtime compilation and thus is no longer needed in Infrastructure.
It also caused dependency problems with EF Core Design in previous versions.

* Disable CPM for UI project to better reflect consumers

This will ensure that we face any potential dependency issues consumers are also likely to run into.

* Add `Microsoft.CodeAnalysis.CSharp` reference to `Umbraco.Cms.DevelopmentMode.Backoffice`
2025-10-14 09:39:53 +02:00
Niels Lyngsø 4c42175ffc Merge branch 'release/17.0' into v17/dev
# Conflicts:
#	version.json
2025-10-14 08:54:54 +02:00
Mads RasmussenandGitHub 8be2d77006 Only register the Entity Data Picker when we have some registered Data Sources (#20484)
Add conditional registration for Entity Data Picker

Introduces an entry point for the Entity Data Picker property editor that registers its manifests only if picker data sources are present, preventing an unusable UI from appearing by default.
2025-10-13 20:56:07 +02:00
Niels LyngsøandGitHub a6eb2a1527 Modals: Make Create Actions open a Dialogs (#20489)
* generic create dialog

* document create dialog

* media create dialog

* member create

* partial view
2025-10-13 20:53:29 +02:00
11fcfba091 News dashboard: API and rendering of news stories on dashboard (#20416)
* Adding controller

* Lower case route to match other endpoints

* Adding service and typed output

* Renaming to NewsDashboard

* Moving more stuff to service

* Removing unused code

* Some refactoring in accordance with better architecture

* Created repository and mock data source for the news dashboard also display some data in the UI.

* Minor refactoring: naming, aligning with existing controller patterns.

* Update OpenApi.json.

* Update typed client sdk and types.

* Provide language to API endpoint, just in case we want to localize news in the future.

* Obsoleted configuration

* Moved mock data to mocks folder and updated repository to use the actual response model and service from the Api

* Prepared news repository with server data source.

* Rendered news items according to required group structure.
Added TODOs for remaining tasks.

* Fixed FE build issues.

* Update src/Umbraco.Core/Constants-Configuration.cs

* Fixed grid spacing, sanitize code and make the styles closer to the v13.

* Added container query and padding to the card body.

* Fix padding

* Fixed title according to priority.

* Relocated/renamed the news server data-source file

* Simplified the news repo/data-source classes

by extending `UmbControllerBase`, the host constructor is handled for us.

* Added `types.ts` export type files

* Refactored interface name + typing

* Added `uui-loader` component

* Tweaked styles, added box-shadow to cards

Added flexbox gap to the card body.

* Sorted import order

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: engjlr <enl@umbraco.dk>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-10-13 20:27:07 +02:00
3393febdca News dashboard: API and rendering of news stories on dashboard (#20416)
* Adding controller

* Lower case route to match other endpoints

* Adding service and typed output

* Renaming to NewsDashboard

* Moving more stuff to service

* Removing unused code

* Some refactoring in accordance with better architecture

* Created repository and mock data source for the news dashboard also display some data in the UI.

* Minor refactoring: naming, aligning with existing controller patterns.

* Update OpenApi.json.

* Update typed client sdk and types.

* Provide language to API endpoint, just in case we want to localize news in the future.

* Obsoleted configuration

* Moved mock data to mocks folder and updated repository to use the actual response model and service from the Api

* Prepared news repository with server data source.

* Rendered news items according to required group structure.
Added TODOs for remaining tasks.

* Fixed FE build issues.

* Update src/Umbraco.Core/Constants-Configuration.cs

* Fixed grid spacing, sanitize code and make the styles closer to the v13.

* Added container query and padding to the card body.

* Fix padding

* Fixed title according to priority.

* Relocated/renamed the news server data-source file

* Simplified the news repo/data-source classes

by extending `UmbControllerBase`, the host constructor is handled for us.

* Added `types.ts` export type files

* Refactored interface name + typing

* Added `uui-loader` component

* Tweaked styles, added box-shadow to cards

Added flexbox gap to the card body.

* Sorted import order

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: engjlr <enl@umbraco.dk>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-10-13 18:20:01 +00:00
Niels Lyngsø 28e4caaad3 fix merge gone wrong 2025-10-13 18:42:06 +02:00
Mads RasmussenandGitHub d6b9eb61b2 Entity data picker: Hide tree root (#20473)
Change tree root visibility in entity data picker
2025-10-13 06:44:11 +00:00
Bjarne FyrstenborgandAndy Butland 0367e56db6 UX: Center align log type in media history view (#20469)
Center align log type in media
2025-10-13 06:35:24 +02:00
Andy Butland e28153f004 Merge branch 'main' into v17/dev 2025-10-13 06:34:47 +02:00
Mads RasmussenandGitHub 2ce6c8f8c8 Make data source field mandatory, when supported, in data type details (#20463)
Make data source field mandatory in data type details
2025-10-12 17:52:35 +00:00
fd1c9f1673 Refactoring: Fixed spelling mistake in method name (#20460)
* Fixed spelling mistake in method name.

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-10 15:06:49 +02:00
Andy Butland 2c284a702a Merge branch 'main' into v17/dev 2025-10-10 15:06:11 +02:00
Mads RasmussenandGitHub c68a26cb5f Input Entity Data Picker: wire up client validation (#20456)
trigger client validation if the amount configuration is not met
2025-10-10 14:47:39 +02:00
c037323910 PropertyType workspace: layout & labeling adjustments (#20131)
* Property workspace update

* Fixed error with updating the properties

* Unused variable

* Added data-mark to description textarea

* make select 100% width

* tiny appearance-option style adjustments

* Make placeholder property inside the input-with-alias optional

* Moving variations and member type option to their own boxes

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2025-10-10 12:37:26 +02:00
f60436270f Property Editor Data Source (#20375)
* Add property editor data source extension types

Introduces types and extension interfaces for property editor data sources, including manifest and API definitions. Updates the main property-editor types export to include the new data source types.

* add test data sources

* wip collection and item repos

* export consts

* fix picker modal token

* make global components file

* render picker in data type

* wire up repositories

* append editor data source alias to data type detail model

* fix global manifest declaration

* make optional

* fix types

* register collection item picker modal element + wip collection menu extension

* register collection menu for property editor data source

* wire up modal tokens

* fix circular

* register as global element

* register default kind for collection menu

* wip fleshing out collection menu

* pass props + listen for selection events

* fix imports

* accept icon in manifest

* extend base type

* use correct data to calculate length

* export types

* add load more button

* wire up load more

* remove debugger

* add search for property editor data sources

* only select one data source

* rename file

* add entity type

* add manifest for search result item

* fix imports/exports

* fix manifest imports

* wire up data source value with workspace

* remove debugger

* wip property editor + input

* move data-source files

* more specific extension types

* remove copy from file name

* allow settings in manifests

* export types

* merge settings

* fix ui alias

* remerge if data source is removed

* Update data-type-details-workspace-view.element.ts

* reset data

* Update data-type-workspace.context.ts

* update merging + move mapping to data source

* Fix mutation of data.values in data type detail mapping

Refactored #mapServerResponseModelToEntityDetailModel to avoid mutating the original data.values array when removing the editorDataSourceAlias. This ensures the original server response remains unchanged and improves data integrity.

* add  forDataSourceTypes to manifest

* update interfaces

* test data source implementations

* only show data source select if property editor supports it

* remove custom context

* remove unused token

* use generic collection item picker modal

* remove custom modal

* export types

* render data source alias on data type into view

* pass data source alias

* allow data source alias

* allow data source alias

* pass data source alias

* add prop for data source alias

* Add property editor data source alias support

* Add editor data source alias to property context

Introduces support for storing and retrieving the editor data source alias in UmbPropertyContext. Updates UmbPropertyElement to use the context for managing the data source alias and ensures the alias is set on the property editor element.

* pass data source alias to input

* pass data source alias to context

* update js docs

* split types from token file

* fix import

* update error message

* add more test sources

* Refactor repository manager initialization logic

Changed the initialization flow in UmbRepositoryItemsManager to support optional repository alias and deferred repository setup. Added setItemRepository and getItemRepository methods for explicit repository management, and moved repository initialization logic to a dedicated private method.

* remove support for passing a filter

* wip wire up input with modal

* add constant

* test user data source

* add todo

* require entityType on webhook items

* add entityType

* use id as unique

* add default icon

* wire up search

* add search to media

* pass config

* support configuration in data sources + temp test cases

* remove temp text

* change to one generic extension type with a data type sub type

* search in label

* pass filter args to collection item picker

* clean up

* aligning interfaces

* iterate status instead of item

* simplify examples

* add types for config

* move to examples

* add custom data examples for collection and tree

* update imports

* add manifests for collection and tree custom data examples

* add type guards

* add type guards

* Update types.ts

* add return type

* remove debuggers

* make observables optional

* add null checks for observables

* use statuses

* extend picker input context

* map config

* use data to set value when there is no observable

* store as string array

* Add getDefaultApiConstructor to tree item element

* make it optional

* fix search types

* add fallback icon and name

* remove unused imports

* pass stored value to input

* rename file

* remove unused config value

* make api observable

* add search to custom collection example

* render fallback item

* fix import order

* add fallback render to tree item element

* Update tree-item.element.ts

* Revert "Update tree-item.element.ts"

This reverts commit 3458877de9.

* Revert "add fallback render to tree item element"

This reverts commit b30219d3ed.

* move from data type to property editor

* align file names

* introduce picker-property-editor module

* remove custom types

* use basic types

* use tree item type

* Update input-entity-data.context.ts

* update types

* add interface for item model

* force unique on collection item model

* require an item model in picker context

* allow icon to be null

* extend item model from user group item model

* add entity type to mapped data

* Update user-group-item.server.data-source.ts

* align static file models

* correct types for user picker

* extend item model

* fix types

* more type fixing

* align models

* align models

* fix types

* add utils for fallback name and icon

* add todo

* use fallback name and icon functions

* Update default-picker-search-result-item.element.ts

* add fallback tree item if none is registered

* add search to example

* extract data source config and pass to api

* align naming

* temp type cast

* move search module into core

* fix illegal imports

* add missing const exports

* make property-editor-data-source module

* register property editor data source ref item + render description

* remove console log

* remove indention

* simplify data source type

* Update src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/input/input-entity-data.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Client/src/packages/core/property-editor-data-source/input/input-property-editor-data-source.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Client/src/packages/core/collection/menu/default/default-collection-menu.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add todo

* hide add button when readonly

* check correct amount config

* Update input-entity-data.element.ts

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-10 09:46:48 +00:00
Bjarne FyrstenborgandAndy Butland 9b0b60e6cb Icon Picker: Fit icons scroll container to modal height (#20438)
* Fill height and align icons to top

* Auto scrollbar instead

* Auto height of grid rows

* Enforce scroll again
2025-10-10 10:33:21 +02:00
Andy Butland 0996891c5c Merge branch 'main' into v17/dev 2025-10-10 10:32:45 +02:00
Andy Butland 7a48c11ddd Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Infrastructure/Security/IdentityMapDefinition.cs
2025-10-10 09:54:19 +02:00
leekelleher de449079dd Fixed build error 2025-10-10 08:43:32 +01:00
leekelleher 10bcf5ba72 Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/tree-item-element-base.ts
2025-10-10 08:20:55 +01:00
Andy Butland 5f14365470 Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Infrastructure/Mail/EmailSender.cs
2025-10-09 15:41:26 +02:00
Laura NetoandAndy Butland f539a27a19 Migrations: Adjust the JsonBlockValueConverter to handle conflicts with 'values' property (#20429)
* Adjust the `JsonBlockValueConverter` to handle conflicts with 'values' property (due to old data schema)

* Simplify code

* Add unit test to verify change.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-10-09 10:17:20 +02:00
Laura Neto 296858c1ca Merge branch 'main' into v17/dev
# Conflicts:
#	templates/UmbracoProject/.template.config/template.json
2025-10-09 09:51:31 +02:00
Nikolaj Geisle 832bd7eb61 bump version to beta 2025-10-09 08:58:24 +02:00
Andy ButlandandGitHub 64836e0b7a Update version from 17.0.0-rc to 17.1.0-rc 2025-10-09 08:54:36 +02:00
d6ce8d91a9 PropertyType workspace: layout & labeling adjustments (#20131)
* Property workspace update

* Fixed error with updating the properties

* Unused variable

* Added data-mark to description textarea

* make select 100% width

* tiny appearance-option style adjustments

* Make placeholder property inside the input-with-alias optional

* Moving variations and member type option to their own boxes

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2025-10-08 18:24:58 +00:00
Nikolaj Geisle fb91ade164 Merge branch 'main' into v17/dev 2025-10-08 19:18:04 +02:00
Niels Lyngsø 38a9a79c75 Merge branch 'main' into v17/dev 2025-10-08 17:23:48 +02:00
Niels Lyngsø 924b1446e3 Merge branch 'main' into v17/dev 2025-10-08 16:37:50 +02:00
Niels LyngsøandGitHub 2e0d006a6c Single Block property editor (#20414)
* single block property editor

* clean up

* auto create a block as part of rendering
2025-10-08 13:02:15 +00:00
d9f8faf509 Load balancing: Load balance isolated caches to allow the backoffice to be load balanced (#20417)
* V16:  Cache Version Mechanism (#19747)

* Add RepositoryCacheVersion table

* Add repository

* Add Cache version lock

* Add GetAll method to repository

* Add RepositoryCacheVersionService

* Remember to add lock in data creator

* Work my way out of constructor hell

This is why we use DI folks. 🤦

* Add checks to specific cache policies

* Fix migration

* Add to schema creator

* Fix database access

* Initialize the cache version on in memory miss

* Make cache version service internal

* Add tests

* Apply suggestions from code review

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Add missing obsoletions

* Prefer full name

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>

* fixed merge

* V16/feature/move last synced id to db (#19884)

* Foundation work for moving last synced id

* register manager and repo in dependency injection

* Fixing to make tests work

* Replacing the use of the old LastSyncedFileManager.cs with the new LastSyncedManager.cs

* Testing to delete out of sync id and old entries

* changing some stuff to please the reviewer.

* Inverted saving methods id check and fixed documentation mishaps

* Loadbalancing: Add Cache Sync service to allow us to roll forward isolated caches when backoffice is load balanced. (#20398)

* Split cache refreshers into internal and external caches

* Add obsolete constructor for CacheInstructionsPruningJob

* Add xml docs

* Move lastID management into CacheInstructionService

* Cache last synced ids in memory

* Lock when processing instructions

* Sync caches when out of sync

* Fix constructors for ICacheSyncService

* Cache version on request

* Register caches as synced when instructions are processed

* Rename CacheVersionAccessor to IRepositoryCacheVersionAccessor

* Set caches as synced before actually syncing the caches

* Set caches as synced before syncing, within scope, this should also lock the cache version from being written to whilst updating caches

* Only check version for backoffice requests

* Clear request cache when caches are syned

* Default to using NOOP cache version service

* Don't generate local identity in database server messenger anymore

* Fix ambiguous constructor

* Add helper method to switch to load balanced isolated caches

* Fix LastSyncedManagerTests

* Fix RepositoryCacheVersionServiceTests

* Fix DefaultCachePolicyTests

* Use correct constructor in FullDataSetRepositoryCachePolicy

* Minor cleanup

* Add XML docs

* Add more xml docs

* Apply suggestions from code review

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

---------

Co-authored-by: Zeegaan <skrivdetud@gmail.com>
Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

* Fix migration plan

* fix tests

* Fix integration tests

* Fix changes from github review

* Move premigrations to v17

* Make lock constantws sequential

* Fix comment

* Make IRepositoryCacheVersionService and ICacheSyncService protected on EntityRepositoryBase

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Nicklas Kramer <nik@umbraco.dk>
Co-authored-by: NillasKA <kramernicklas@gmail.com>
Co-authored-by: Zeegaan <skrivdetud@gmail.com>
Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
2025-10-08 14:55:50 +02:00
Mads Rasmussen 5eaf7fdb90 Update document-tree.server.data-source.ts 2025-10-08 13:27:58 +02:00
Mads Rasmussen 0dcb10ad41 Merge branch 'main' into v17/dev 2025-10-08 13:27:52 +02:00
leekelleher 15089b28bf Fixed failing UmbTreeItemActiveManager unit-test 2025-10-08 11:42:28 +01:00
ebe056df05 Collection: hide children of collection until active child (#20392)
* remove use of modals in collections

* add parent path to support absolute path generation

* add note

* make tree load more minimalistic

* set type and expand inherited styles

* also set title

* Update src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/views/table/document-table-collection-view.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* create actions should not open as modal

* remove unused import

* fix router getActivePath

* make expand open the collection

* setTargetTakeSize to low when Collection parent

* expose typeUnique

* fix opening collection

* remove log

* active manager

* export

* impl active manager

* prepare for search param redirects

* fixed collapse feature

* set routes to undefined

* preserveQuery

* ensureSlash

* make a hard redirect for collections

* only if anscenstors are present in data.

* not full match anyway

* only forceShow on hasCollection

* remove umb-section-sidebar-context-menu

* rename to isMenu

* Update src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace-split-view.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/tree-item/document-tree-item.context.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-08 10:11:46 +00:00
Jacob Overgaard 54407a0687 add missing import (merge failure) 2025-10-08 11:27:04 +02:00
mole 787a5ccc2d Merge remote-tracking branch 'origin/main' into v17/dev 2025-10-08 10:45:16 +02:00
17a5477242 Serverside generated preview URLs (#20021)
* Serverside generated preview URLs

* Add URL provider notation to UrlInfo

* Change preview URL generation to happen at preview time based on provider alias

* Update XML docs

* Always add culture (if available) to preview URL

* Do not log user input (security vulnerability)

* Fix typo

* Re-generate TypeScript client

from Management API

* Deprecated `UmbDocumentPreviewRepository.enter()` (for v19)

Fixed TS errors

Added temp stub for `getPreviewUrl`

* Adds `previewOption` extension-type

* Adds "default" `previewOption` kind

* Relocated "Save and Preview" workspace action

reworked using the "default" `previewOption` kind.

* Added stub for "urlProvider" `previewOption` kind

* Renamed "workspace-action-default-kind.element.ts"

to a more suitable filename.

Exported element so can be reused in other packages,
e.g. documents, for the new "save and preview" feature.

* Refactored "Save and Preview" button

to work with first action's manifest/API.

* Reverted `previewOption` extension-type

Re-engineered to make a "urlProvider" kind for `workspaceActionMenuItem`.
This is to simplify the extension point and surrounding logic.

* Modified `saveAndPreview` Document Workspace Context

to accept a URL Provider Alias.

* Refactored "Save and Preview" button

to extend `UmbWorkspaceActionElement`.

This did mean exposing certain methods/properties to be overridable.

* Used `umbPeekError` to surface any errors to the user

* Renamed `urlProvider` kind to `previewOption`

* Relocated `urlProviderAlias` inside the `meta` property

* also throw an error

* Added missing `await`

* Fix build errors after forward merge

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
Co-authored-by: Laura Neto <12862535+lauraneto@users.noreply.github.com>
2025-10-08 08:27:01 +02:00
Jacob Overgaard 74328e9496 Merge remote-tracking branch 'origin/main' into v17/dev 2025-10-08 08:19:56 +02:00
Andy Butland cd9dda2aad Merge branch 'main' into v17/dev 2025-10-08 06:29:30 +02:00
Niels Lyngsø 62efc2631e remove not existing umb-section-sidebar-context-menu 2025-10-07 22:56:46 +02:00
20de48a496 Load Balancing: Implement distributed background jobs (#20397)
* Start work

* Introduce dto

* Start making repository

* Add migrations

* Implement fetchable first job

* Fix up to also finish tasks

* Refactor jobs to distributed background jobs

* Filter jobs correctly on LastRun

* Hardcode delay

* Add settings to configure delay and period

* Fix formatting

* Add default data

* Add update on startup, which will update periods on startup

* Refactor service to return job directly

* Update src/Umbraco.Infrastructure/Services/Implement/DistributedJobService.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Infrastructure/BackgroundJobs/DistributedBackgroundJobHostedService.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Infrastructure/BackgroundJobs/DistributedBackgroundJobHostedService.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Remove unused

* Move jobs and make internal

* make OpenIddictCleanupJob.cs public, as it is used elsewhere

* Minor docstring changes

* Update src/Umbraco.Core/Persistence/Constants-Locks.cs

Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>

* ´Throw correct exceptions

* Update xml doc

* Remove business logic from repository

* Remove more business logic from repository into service

* Remove adding jobs from migration

* fix creation

* Rename to ExecuteAsync

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: mole <nikolajlauridsen@protonmail.ch>
2025-10-07 18:49:21 +02:00
f0cf4703fa Member type container in management API (#16914)
* Member type container in management API

* Fix naming

* Update service

* Fix services

* Register IMemberTypeContainerService in DI container

Added a new service registration for `IMemberTypeContainerService`
in the `AddCoreServices` method of `UmbracoBuilder.cs`.

* Replace auditRepository with auditService in constructor

* Add MemberTypeContainer to UdiEntityType mapping

---------

Co-authored-by: georgebid <91198628+georgebid@users.noreply.github.com>
Co-authored-by: Sebastiaan Janssen <sebastiaan@umbraco.com>
2025-10-07 15:31:35 +02:00
Jacob Overgaard 0cf8279e65 Merge branch 'main' into v17/dev 2025-10-07 14:52:10 +02:00
Lee KelleherandGitHub 2f712cab08 Refactors backoffice sections with default element and context API (#20305)
Refactors backoffice sections

using the extensions registry to set a default/fallback
element and API for the section.
2025-10-07 14:40:00 +02:00
Niels LyngsøandGitHub 034cf2894c Tree: Minimalistic styles for load more (#20376)
* make tree load more minimalistic

* set type and expand inherited styles

* also set title
2025-10-07 13:21:41 +01:00
Lee KelleherandGitHub 1498f3bdc6 Removed umb-media-picker-create-item component (#20403)
Remove `umb-media-picker-create-item` component

it was not being used internally.

There was previously an issue due to a routing issue,
(in that the Media Picker modal wasn't routed),
so the Media create workspace wouldn't work.
This could be resolved in future and see this feature return.
2025-10-07 14:20:02 +02:00
Mads RasmussenandGitHub 324e5bcb0a Fix: undefined document name when trashing an item (#20401)
* add interface for item data resolver

* export interface

* add interface to Document item data resolver implementation

* allow to pass a item data resolver to trash action

* pipe resolver to modal

* pass resolver to document trash manifest

* use resolver in modal when available
2025-10-07 11:26:44 +00:00
Kenn JacobsenandGitHub ba4a874fb4 Models Builder: Make Models Builder better at not performing "rude edits" (#20394)
Make Models Builder better at not performing rude edits
2025-10-07 12:47:26 +02:00
1f351244d4 Dependencies: Update server dependencies (#20385)
* Bump Azure.Identity from 1.13.2 to 1.16.0

* Bump BenchmarkDotNet from 0.14.0 to 0.15.4

* Bump Bogus from 35.6.3 to 35.6.4

* Bump HtmlAgilityPack from 1.12.1 to 1.12.4

* Bump MailKit from 4.11.0 to 4.14.0

* Bump MessagePack from 3.1.3 to 3.1.4

* Bump Microsoft.AspNetCore.Mvc.Testing from 9.0.4 to 9.0.9

* Bump Microsoft.Data.SqlClient from 6.0.1 to 6.1.1

* Bump Microsoft.Extensions.Caching.Hybrid from 9.8.0 to 9.9.0

* Bump Microsoft.Extensions.Logging.Debug from 9.0.4 to 9.0.9

* Bump Microsoft.NET.Test.Sdk from 17.13.0 to 18.0.0

* Bump ncrontab from 3.3.3 to 3.4.0

* Bump Nerdbank.GitVersioning from 3.7.115 to 3.8.118

* Bump OpenIddict packages from 6.2.1 to 7.1.0

* Bump Serilog from 4.2.0 to 4.3.0

* Bump Serilog.Sinks.File from 6.0.0 to 7.0.0

* Bump Swashbuckle.AspNetCore from 8.1.1 to 9.0.6

* Bump System.Data.Odbc from 9.0.4 to 9.0.9

* Bump System.Data.OleDb from 9.0.4 to 9.0.9

* Bump Microsoft.IdentityModel.JsonWebTokens from 8.8.0 to 8.14.0

* Bump SixLabors.ImageSharp.Web from 3.1.5 to 3.2.0

- Implicit global usings were made opt-in (https://github.com/SixLabors/ImageSharp.Web/pull/391)

* Bump NJsonSchema from 11.0.2 to 11.5.1

* Bump Microsoft packages from 10.0.0-preview.7.25380.108 to 10.0.0-rc.1.25451.107

* Remove Azure.Identity package reference as implicitly referenced versions are no longer vulnerable

* Remove System.Runtime.Caching package reference as it is not used

* Remove System.Net.Http package reference as it is not used

* Set 'allowPrerelease' to true

Global.json was showing as invalid due to a pre-release version being referenced while 'allowPrerelease' was set to 'false'. This can be set to 'false' again later on.

* Remove System.Security.Cryptography.Xml package reference as implicitly referenced versions are no longer vulnerable

* Remove System.Text.RegularExpressions package reference as implicitly referenced versions are no longer vulnerable

* Remove Microsoft.IdentityModel.JsonWebTokens package reference as implicitly referenced versions are no longer vulnerable

* Remove System.Text.Encodings.Web package reference as it is not used

* Remove Microsoft.Data.SqlClient package reference as implicitly referenced versions are no longer vulnerable

* Remove Lucene.Net.Replicator package reference as implicitly referenced versions are no longer vulnerable

* Remove Microsoft.Extensions.Caching.Memory package reference where not used

* Add EFCore migration for OpenIddict v7 update

* Apply suggestion from @kjac

Cosmetic update: Removed blank line as suggested by Copilot

---------

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-10-07 12:13:14 +02:00
Mads RasmussenandGitHub e3d2001477 SectionSidebarContextMenu: Delete element and methods for interaction (#20399)
Delete sction-sidebar-context-menu element + context methods
2025-10-07 11:08:35 +01:00
7c0b5b7dd4 Collections: Stop opening items in modals (#20384)
* remove use of modals in collections

* add parent path to support absolute path generation

* add note

* Update src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/views/table/document-table-collection-view.element.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-07 10:48:09 +01:00
Nhu DinhandGitHub 7bcef5fde5 Nightly E2E test pipeline: QA Run E2E tests on v17/dev and enable the different setting app tests by default (#20379)
Update yaml file to run the nightly E2E tests against the v17/dev branch and run different app settings tests by default
2025-10-07 15:36:54 +07:00
Niels Lyngsø 173887d0a1 Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts
#	src/Umbraco.Web.UI.Client/src/packages/property-editors/slider/types.ts
2025-10-06 16:38:59 +02:00
Jacob OvergaardandGitHub c67478874e Segmentation: The Backoffice should use the enhanced endpoint to fetch segment options for documents based on GUID (#20340)
* feat: adds new repository for document by id segment options

* chore: mocks up the new endpoint

* feat: all 'null' segments should appear on all languages

* feat: uses new endpoint in content detail workspace base

* feat: maps up the name of the segment

* chore: mock segment data

* feat: adds filter on available segments

* feat: do not alter behavior depending on "undefined" and "null"

* chore: updates mock handler

* feat: ensures that the segments are loaded based on an override method (because they only work for documents) and that they use a generic type (to avoid circular imports)

* feat: refines the segment filter

* chore: updates deprecated model

* feat: treats all culture-less segments as applying to everything

* docs: updates console warn for developers
2025-10-06 09:09:17 +02:00
Andy Butland 0daf5ea506 Merge branch 'main' into v17/dev 2025-10-03 17:19:03 +02:00
Andreas ZerbstandGitHub 29bdd3cac0 E2E Pipeline: QA Remove continue on error condition (#20371)
Remove
2025-10-03 14:50:23 +02:00
c5b4571cd5 Shared status: improve for segmented scenario (#20354)
* improve shared status messages

* Update src/Umbraco.Web.UI.Client/src/packages/core/property/components/property/property.context.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-03 09:59:07 +02:00
Niels LyngsøandGitHub d45a8f2049 Segments: The experience of working with Segments and Segmented Blocks (#20358)
* initial notes

* flat mapper impl

* first tests passed

* return incoming value to ensure it does not result in an error from an extension

* define the manifest type on UmbPropertyValueResolver

* finish property value flat-mapper

* make sure also to map values with no extension

* clean up test

* export controller

* fix block editor property resolver

* fix mapper types

* ensureVariantsData method

* ensure Block List only updates if it has an update

* ensure varians across for shared across segment and shared across cultures

* fix variant selector hints for segments

* fix hints in variant selector for segmented variants
2025-10-03 09:58:09 +02:00
mole 0ded9a74fb Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Core/Services/DocumentUrlService.cs
2025-10-02 15:08:48 +02:00
99c51a54f1 Tiptap RTE: Upgrade to Tiptap v3 (#20318)
* Upgrade to Tiptap v3

* Uses `@ts-expect-error` to ignore the TS complication errors

These can be removed once Tiptap has resolved the TypeScript definitions.

* Off-topic: corrected `flags` property in the mock data

Added in PR #19915

* Update src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/link/link.tiptap-extension.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-10-02 07:44:00 +00:00
3da8f41292 Deprecations: Client-side removal of v17 deprecated code (#20294)
* Webhooks: Removal of client-side deprecations for v17

* User: Removal of client-side deprecations for v17

* UFM: Removal of client-side deprecations for v17

* Tiptap: Removal of client-side deprecations for v17

* Templating: Removal of client-side deprecations for v17

* RTE: Removal of client-side deprecations for v17

* Relations: Removal of client-side deprecations for v17

* Search: Removal of client-side deprecations for v17

* Property Editors: Removal of client-side deprecations for v17

* URL Picker: Removal of client-side deprecations for v17

* Members: Removal of client-side deprecations for v17

* Media: Removal of client-side deprecations for v17

* Extension Insights: Removal of client-side deprecations for v17

* Documents: Removal of client-side deprecations for v17

* Media: Removal of client-side deprecations for v17

(part 2)

* Data Types: Removal of client-side deprecations for v17

* Core: Removal of client-side deprecations for v17

* Content: Removal of client-side deprecations for v17

* Clipboard: Removal of client-side deprecations for v17

* Blocks: Removal of client-side deprecations for v17

* Mocks: Removal of client-side deprecations for v17

* Libs: Removal of client-side deprecations for v17

* Apps: Removal of client-side deprecations for v17

* DevOps: Removal of client-side deprecations for v17

* Document Publishing Workspace: Removal of client-side deprecations for v17

Refactored to use `UmbDocumentPublishingWorkspaceContext`

* Reverted/modified some of my TODO comments

* Updated TODO comment

* Code cleanup sweep of TODO comments and tweaks

* Updated OpenApi.json, re-gen TS client

Tried to fix up mock data.

* Refactored the document variant name/fields

* Implemented co-pilot suggestions

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-10-02 09:14:55 +02:00
Jacob Overgaard f14c4d5968 Merge remote-tracking branch 'origin/main' into v17/dev 2025-10-02 07:42:32 +02:00
Jacob Overgaard 5d986e44f6 Merge remote-tracking branch 'origin/main' into v17/dev 2025-10-01 16:03:06 +02:00
51575e5e36 Property Editors: New Date Time property editors (#19915)
* Started the implementation of the new date time property editor

* Display picked time in local and UTC

* Adjustments to the way the timezones are displayed and the picker is configured

* Filter out `Etc/` (offset) timezones from the list

* Additional adjustments

* Introduced date format and time zone options (all, local or custom)

* Adjustments to the property editor configuration and value converter

* Use UUICombobox instead of UUISelect for displaying time zone options. Display UTC offset instead of short offset name in label.

* Allow searching by offset

* Ignore case when searching for time zone

* Store dates consistently (always same format)

* Add custom PropertyIndexValueFactory for the new property editor

* Adjustments when switching between time zone modes

* Small fixes and cleanup

* Started improving time zone config selection

* Small adjustments

* Remove selected time zones from the list + display label instead of value

* Localizing labels

* Remove unwanted character

* Fix incorrect order of custom time zones list

* Small fixes (mostly validation)

* Rename input time zone component

* Small adjustments

* Using model for stored value

* Save examine value as ISO format

* Adjusting class names for consistency

* Small fixes

* Add default data type configuration

* Rename `TimeZone` to `UmbTimeZone`

* Fix failing tests

* Started adding unit tests for DateWithTimeZonePropertyEditor

* Additional tests

* Additional tests

* Additional tests

* Fixed searches with regex special characters throwing errors

* Remove offset from generic UmbTimeZone type and added new type specific for the property editor

* Adjust property editor to show error when selected time zone is no longer available, instead of pre-selecting another one

* Do not preselect a time zone if a date is stored without time zone

This most likely means that the configuration of the editor changed to add time zone support. In this case we want to force the editor to select the applicable time zone.

* Fix failing backoffice build

* Added tests for DateTimeWithTimeZonePropertyIndexValueFactory

* Improved picker validation

* Remove unused code

* Move models to their corresponding places

* Renaming `DateTimeWithTimeZone` to `DateTime2`

* Fix data type count tests

* Simplifying code + adjusting value converter to support old picker value

* Adjustments to property editor unit tests

* Fix validation issue

* Fix default configuration for 'Date Time (Unspecified)'

* Rename validator

* Fix comment

* Adjust database creator default DateTime2 data types

* Update tests after adjusting default data types

* Add integration test for DateTime2 returned value type

* Apply suggestions from code review

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Aligning DateTime2Validator with other JSON validators. Added new model for API.

* Removed unused code and updated tests

* Fix validation error message

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Splitting the new date time editor into multiple (per output type)

* Adjust tests in DateTime2PropertyIndexValueFactoryTest

* Update value converter tests

* Group the new date time tests

* Adjust new property editor tests

* Adjust property editor integration tests

* Update data editor count tests

* Naming adjustments

* Small fixes

* Cleanup

- Remove unused files
- Remove 'None' option from configuration and update all the tests

* Update luxon depedencies

* Move GetValueFromSource to the value converter

* Add new property editor examples to mock data

* Re-organizing the code

* Adjustments from code review

* Place the date time property index value factories in their own files

* Small adjustments for code consistency

* Small adjustments

* Minor adjustment

* Small fix from copilot review

* Completed the set of XML header comments.

* use already existing query property

* fail is form control element is null or undefined

* using lit ref for querying and form control registration

* state for timeZonePickerValue and remove _disableAddButton

* Adjustments to form control registration

* Remove unused declaration

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2025-09-30 13:21:09 +00:00
Kenn JacobsenandGitHub 07203b7270 Segments: Property level default segment fallback (#20309)
Property level default segment fallback
2025-09-30 08:45:20 +02:00
Andy Butland cdb2be64d4 Merge branch 'main' into v17/dev
# Conflicts:
#	tests/Umbraco.Tests.UnitTests/Umbraco.Tests.Common/Builders/PropertyTypeBuilderTests.cs
2025-09-30 07:54:26 +02:00
ac6b205e7f Unattended install: Added the ability to set the telemetry level (#20249)
* Added the ability to set the telemetry level for an unattended install

Added 'UnattendedTelemetryLevel' to 'UnattendedSettings'

Renamed 'CreateUnattendedUserNotificationHandler' to 'PostUnattendedInstallNotificationHandler'

Set the telemetry level in the unattended install notification handler

* Add DefaultValue attribute to 'UnattendedTelemetryLevel'

* Added UnattendedTelemetryLevel to template.

* Updated cli and ide hosts.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-29 17:07:30 +02:00
leekelleher 81f29be53b Corrected Tiptap import namespaces
for newer heading extensions, introduced in v16.3.0.
2025-09-25 14:19:22 +01:00
Lee KelleherandGitHub ac23ff5f38 Tiptap: Relocation of external extensions/package (#20256)
* Relocated "Bubble Menu" extension

* Relocated "Table" extension

* Relocated "Anchor" extension

* Relocated "HTML Tag: Div" extension

* Relocated "HTML Tag: Span" extension

* Refactored "HTML Attribute" extensions

* Relocated "Figure" extension

* Relocated "Text Direction" extension

* Relocated "Text Indent" extension

* Relocated "Trailing Node" extension

* Relocated "Embedded Media" extension

* Relocated "Link" extension

* Relocated "Image" extension

* Removed "@umbraco-backoffice/external-tiptap" package

relocated Tiptap exports to the "@umbraco-cms/backoffice/tiptap" package.

* `import` fixes

* Naming of Tiptap extension option interfaces
2025-09-25 14:09:00 +02:00
099e7d7d3a Adding SourceWidth and SourceHeight to ImageUrlGenerationOptions (#14499)
* Update Readme to signpost the Forum (#20268)

Update README.md with information about the forum

Making a small change to the Readme to signpost the Forum now that it's the place to go for help/questions

* Adding SourceWidth and SourceHeight to ImageUrlGenerationOptions

* Update src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* QA Skip the known failing smoke test to avoid blocking other PRs (#20269)

Added skip for the failing smoke test

---------

Co-authored-by: Owain Williams <owaingdwilliams@gmail.com>
Co-authored-by: Jason Elkin <jasonelkin86@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>
2025-09-25 13:38:49 +02:00
Bjarne FyrstenborgandGitHub b1956dea3e Localization/UX: Add a new general translation for "Fields" (#20259)
* Add a new general translation for "Fields"

* 100% height of scroll container
2025-09-25 05:19:36 +00:00
Andy Butland 8d5117fec1 Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Core/Services/PropertyValidationService.cs
#	src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs
#	src/Umbraco.Infrastructure/PublishedContentQuery.cs
#	src/Umbraco.Web.UI.Client/package-lock.json
#	src/Umbraco.Web.UI.Client/package.json
#	src/Umbraco.Web.UI.Client/src/packages/core/tree/default/default-tree.context.ts
#	src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/tree-item-element-base.ts
#	templates/UmbracoProject/.template.config/template.json
#	version.json
2025-09-24 23:31:14 +02:00
Andy Butland d8e8e32f75 Merge branch 'main' of https://github.com/umbraco/Umbraco-CMS 2025-09-24 22:30:10 +02:00
Andy Butland 765e5b0ba9 Merge branch 'main' of https://github.com/umbraco/Umbraco-CMS 2025-09-24 20:41:02 +02:00
Andy Butland 099fd2aa7c Merge branch 'main' of https://github.com/umbraco/Umbraco-CMS 2025-09-24 08:09:06 +02:00
Andy Butland cea2184d47 Merge branch 'v16/bugfix/sorting-by-updateDate-isnt-working-correctly' of https://github.com/umbraco/Umbraco-CMS 2025-09-24 07:09:00 +02:00
Laura NetoandGitHub f5cfbb110e Merge branch 'main' into v16/bugfix/sorting-by-updateDate-isnt-working-correctly 2025-09-23 17:51:29 +02:00
Laura Neto aa2a85328e Display the latest update date in document collection view 2025-09-23 17:12:57 +02:00
Nicklas KramerandGitHub 7db7346d4a Test Data: Fixing endpoint not working #20044 (#20227)
* It worked before i must have broken it somehow. Commit as checkpoint

* Adding a reference from Web.UI.csproj to TestData to allow composers to be composed

* Changing readme and removing project reference
2025-09-23 11:22:29 +00:00
859505e751 Models builder: Move InMemoryAuto models builder and razor runtime compilation into its own package to enable hot reload (#20187)
* Move in memory models builder out of core

* Move runtime validations into backoffice development project

* Obsolete ModelsMode enum

* Move the InMemoryModelsbuilder/RRC novel into the Backoffice development umbraco builder extension

* Add runtime validator to warn if InMemoryAuto is selected but the package isn't installed

* Add backoffice development to template

* Remove propertyGroup

* Remove oopsie

* Check for modelsbuilder in notification handler instead of runtime validator

* Update src/Umbraco.Cms.Api.Management/Controllers/ModelsBuilder/BuildModelsBuilderController.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Umbraco.Infrastructure/Runtime/RuntimeModeValidators/ModelsBuilderModeValidator.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Remove ModelsMode enum and ModelsModeExtensions

* Apply suggestions from code review

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>

* Move project to source folder

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-09-23 11:58:09 +02:00
5d17920a73 UTC data migration doesn't work in Linux (closes #20002) (#20112)
* Adjusted the UTC SQL Server migration to convert time zone ids to the correct format

* Apply suggestions from code review

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Small rename

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-22 13:16:13 +02:00
815f28e3b9 Section Sidebar Menu: Auto expand to paginated tree items (closes #17074) (#19883)
* align naming

* mute updates

* lower threshold

* add expansion model with target

* add function to link entries

* fix self import

* export constants

* update js docs for entity expansion manager

* link entries

* fix import

* do not export from menu here

* fix import

* fix import

* align how we register manifests

* add specific managers for section sidebar menu

* use structure items

* dot not expand current item

* Refactor section sidebar menu to use programmatic extension slot

Replaces the template-based <umb-extension-slot> with a programmatically created UmbExtensionSlotElement for improved performance and UX.

* add section context extension

* register menu as section context instead of hardcoding

* rename folder

* align naming

* export extension slot elements

* fix typings

* destroy extension slot element when host is disconnected

* Added user start node restrictions to sibling endpoints.

* use entry model

* move and rename

* register global context to hold menu state across sections

* temp observe section specific expansions

* temp observe section specific expansions

* add method to collapse multiple items

* Further integration tests.

* Tidy up.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* bind expansion to section

* make entity expansion manager generic

* Revert previous update.

* add helper method

* remove temp test data

* Retrieves item counts before and after the target for sibling endpoints and returns in API response.

* Applied previous update correctly.

* Removed blank line.

* Fix build and test asserts following merge.

* add getItem method

* Update OpenApi.json.

* generate new server types

* include last item in target

* add target pagination type

* return totalBefore and totalAfter

* call siblings endpoint for documents

* add method to load children with target

* rename to item

* wip target pagination manager

* add button to load prev tree items

* render prev and nexts buttons for tree items

* Update tree-load-prev-button.element.ts

* add util to append to unique array

* add state method to prepend data

* implement methods to load next and prev items

* add methods to interface

* Update tree-item-element-base.ts

* Update tree-item-context-base.ts

* remove unused

* align methods

* update types

* add jsdocs

* add deprecation notice

* fix jsdocs

* fix import

* Update tree-data-source.interface.ts

* remove duplicate type

* clean up

* fix page calculations

* remove unused

* clean up

* pass full entry to event

* add type for menu item expansion

* export types

* add menuItem alias

* Update types.ts

* support menu item expansion entry

* add const for menu item alias + use for breadcrumb and menu item

* add data type menu item alias const + apply to breadcrumb

* move to correct manifest

* add menu item alias to expand entries

* Update manifests.ts

* add menu structure kind types

* add kind to manifests

* add menu item context

* filter menu items

* handle menu item expansion

* clean up

* fix order

* add example dashboard and entity action

* import types

* align model type names

* align naming

* use ui component

* add guard for menu item entry

* use correct type

* Update section-sidebar-menu.element.ts

* Update entity-expansion.manager.ts

* export constants

* add menuItemAlias to manifest

* add menuItemAlias to manifest

* add menuItemAlias to manifest

* add menuItemAlias to manifest

* add menu item alias

* add menuItemAlias to manifest

* add menuItemAlias to manifest

* add menuItemAlias to manifest

* add alias

* add kind

* fix import path

* do not expand menu from modal

* collect all menu-item files in one folder

* fix lint errors

* Update content-detail-workspace-base.ts

* clean up

* rename to example

* add button to collapse everything within a section

* return correct data from base

* recalculate after

* fix breadcrumb for non-variant structure

* reload entity

* destroy

* remove self

* Updated acceptance tests to check if a caret button is open before clicking

* Bumped version of test helpers

* use const

* add target paging for tree root items

* more specific field names

* update field name

* add model for offset pagination

* add request to model name

* correct

* using Event Contsants for event map

* comment

* clean event listeners before adding new ones

* use createObservablePart

* add paging type guards

* add types

* add check for unique

* add comment

* pass data type id

* wip reload tree logic

* move start + end target logic to target pagination manager

* use target pagination manager in tree item context

* remove local references to start, end and base targets

* calculate before and after when reloading

* clean up

* support children in tree item context

* add methods to observe an expansion entry

* reload structure when item is created

* UX adjustments

* add controller alias to observer

* Update default-tree.context.ts

* Update default-tree.context.ts

* Update default-tree.context.ts

* test targets in document type tree data source

* when reloading only send the target if its part of the current items

* wip tree request manager

* make data source base a controller

* add tree request helper for document types

* use request helper in data source

* clear more data when clear is called

* when reopening a tree item - reuse previous state

* add tree item children manager

* split to manager

* clean up

* only return an entity model when getting target

* allow entity model as target

* add null checks

* add method for getSiblingsFrom

* implement target for tree data request manager

* Update default-tree.context.ts

* set parent for tree root

* reload if target is new

* add types for tree data request manager

* implement request manager for document tree

* use request manager for media tree

* add request manager for data type tree

* move into folder

* move into folder

* move into folder

* add target support for document blueprint

* add request manager for template tree

* add request manager for media type tree

* add hasChildren flag for root

* make start node its own thing

* move hasChildren logic to children manager

* Create tree-item-expansion.manager.ts

* use expansion manager

* align tree item managers

* Update tree-item-context-base.ts

* support take 0

* add methods to get new targets

* add retries

* add button loading states

* fix next start and end

* reset baset target

* use clear when restting children

* throw error if parent doesn't match request

* show notifcation when children is reset

* only render menu context for non trashed document and media items

* use correct import

* fix types

* update interfaces and imports to fix circular dependencies

* move into tree-item folder

* rename file

* Update tree-item-context-base.ts

* move token out of context file to remove circular dependency

* set take size to 50

* remove unused

* export const

* correct default value

* check on both sides after a new base target

* `import type` sort ordering

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Andreas Zerbst <andr317c@live.dk>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-09-22 07:22:54 +00:00
a7bb1fc20d Repositories: Quote table and columns and use NPoco extensions over raw SQL (#20034)
* fix sql syntax issues

* unify all dtos, fix autoIncrement for NPoco.Insert and .BulkInsert

* fix Copilot review comments

* fix sql syntax in TrackedReferencesRepository.GetPagedDescendantsInReferences()

* remove changes in TemplateServiceTests

* Tweaks and fixes from first review.

* Reverted changes outside scope of PR.

* Use FirstOrDefault over SelectTop.

* Fix delete member issue.

* Fixed issue with create of webhooks.

* Reverted changes to default data install.

* Removed unused method.

* Rationalised use of quoting helpers.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix nullability issue.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-20 11:40:16 +02:00
dca70a0bd0 Dependencies: Updates to .NET 10 RC and NPoco 6.1 (#20184)
* Update to .NET 10 RC 1.

* Update NPoco to 6.1.0 and resolve binary breaking changes.

* Resolved behavioural breaking changes (need to use List<string> over string[] for Contains).

* Update dependency on NPoco.SqlServer.

* Further fixes from manual testing.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fixed comment typos.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fixed nullability issue.

* Fix database creation issue

* Fix missing to list

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: nikolajlauridsen <nikolajlauridsen@protonmail.ch>
2025-09-19 08:59:03 +00:00
Andy Butland 07641981a4 Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Cms.Api.Management/Controllers/Content/ContentCollectionControllerBase.cs
#	src/Umbraco.Core/Services/IMemberService.cs
#	src/Umbraco.Core/Services/MemberService.cs
2025-09-17 15:42:23 +02:00
Andy Butland b593d9dadf Merge branch 'v17/dev' of https://github.com/umbraco/Umbraco-CMS into v17/dev 2025-09-17 12:48:12 +02:00
dd01a56d2a Feature: single block property editor (#20098)
* First Go at the single block property editor based on blocklistpropertyeditor

* Add simalar tests to the blocklist editor

Also check whether either block of configured blocks can be picked and used from a data perspective

* WIP singleblock Valiation tests

* Finished first full pass off SingleBlock validation testing

* Typos, Future test function

* Restore accidently removed file

* Introduce propertyValueConverter

* Comment updates

* Add singleBlock renderer

* Textual improvements

Comment improvements, remove licensing in file

* Update DataEditorCount by 1 as we introduced a new one

* Align test naming

* Add ignored singleblock default renderer

* Enable SingleBlock Property Indexing

* Enable Partial value merging

* Fix indentation

---------

Co-authored-by: kjac <kja@umbraco.dk>
2025-09-17 07:20:09 +02:00
Andy Butland 8a342be4f4 Merge branch 'v17/dev' of https://github.com/umbraco/Umbraco-CMS into v17/dev 2025-09-16 14:11:49 +02:00
Laura Neto eea970a648 Fix merge issues in DataTypeService 2025-09-16 13:01:48 +02:00
Andy Butland 20a2f90441 Merge branch 'main' into v17/dev 2025-09-16 10:33:09 +02:00
Jacob OvergaardandGitHub 3d7ac5776c V17: Moves current-user related entities from 'user' to 'current-user' package (#20125)
* fix: moves current user config repository and related dependencies to the 'current-user' package

previously, it was not exported, so is not a breaking change

* chore: moves current-user-allow-mfa condition to the 'current-user' package to avoid circular dependencies (and because it naturally belongs there)

* feat: exports all current-user config-related items

* chore: move to 'current-user'

* chore: make sure to export all constants
2025-09-15 15:14:48 +01:00
Andy Butland c3eabda10e Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Core/Services/DataTypeService.cs
2025-09-15 13:36:37 +02:00
d3e6508952 Repositories: Introduce and use NPoco and custom extension methods for SQL syntax in ContentTypeRepositoryBase (#19986)
* fix Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
     .ContentTypeRepositoryBase.DeletePropertyType method.

* fix more sql syntax

* fix review comments

* Fixed issues raised in code review.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-15 10:25:11 +00:00
Laura Neto 6dc9191ad3 Fix wrong migration order 2025-09-09 11:04:10 +02:00
Laura Neto f4c0264e61 Merge branch 'main' into v17/dev 2025-09-09 10:17:47 +02:00
Mads Rasmussen 60a03c75e4 Merge branch 'main' into v17/dev 2025-09-05 10:03:04 +02:00
MoleandGitHub f050488b92 V17: Update to dotnet 10 (#20007)
* Update dotnet version

* Update packages

* Fix System.Linq.Async breaking change

* Fix #19867

* Update template.json

* Set LTS version to 17

* Update dockerfile
2025-09-04 08:08:35 +02:00
kjac 5a0a2bf77e Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.PublishedCache.HybridCache/Factories/PublishedContentFactory.cs
2025-09-02 10:22:39 +02:00
Nicklas KramerandGitHub da8c036719 V17 - Removing obsoleted code from Umbraco.Core (#20009)
* Removing obsoleted code from ApiMediaQueryService.cs

* Removing obsoleted code from ApiRichTextMarkupParserTests.cs

* Removing obsoleted code from ContentCacheRefresher.cs

* Removing obsoleted code from ContentFinderByUrlAlias.cs and adjusting its tests to use the new logic

* Removing obsoleted code from ContentFinderByUrl.cs & its dependencies

* Removing obsoleted code from ApiRichTextMarkupParserTests.cs

* Removing obsoleted code from DocumentCache.cs & its dependencies

* Removing obsoleted code from MediaCache.cs & its dependencies

* Removing obsoleted code from PublishedCacheBase.cs & its dependencies

* Removing obsoleted code from RenderNoContentController.cs and its tests

* Removing obsoleted code from UmbracoRouteValueTransformer.cs

* Removing obsoleted constructors from DefaultUrlProvider.cs

* Removing accidental bookmark

* Introducing a helper method to get the root keys in ApiMediaQueryService.cs

* Removing obsoleted code from Cache classes

* Removing unused imports

* Refactoring to meet the CR

* Added attribute to controller

* Fixing missing using statement

* Removing obsoleted constructor from ExternalLoginService.cs and making usages fit

* Removing obsoleted method from IContentTypeFilter.cs

* Removing obsoleted methods from IContentEditingService.cs

* Removing obosoleted code from DocumentUrlService.cs

* Removed obsoleted code from DataTypeService.cs

* Removed obsoleted code from PublishStatusService.cs

* Removing obsoleted code from the IContentPublishingService.cs and its dependencies. Also implementing a TODO in the service implementation

* Removing obsoleted code from IRelationService.cs

* Removing obsoleted code from ContentPublishingService.cs

* Removing obsoleted code from ContentEditingService.cs

* Removing obsoleted code from Constants-DataTypes.cs

* Removing obsoleted code from IAction.cs and its implementations

* Removing obsoleted code from IContentService.cs

* Removing obsoleted code from DomainUtilities.cs

* Removing obsoleted code from IIndexedEntitySearchService.cs and dependencies

* Removing obsoleted code from UrlProvider.cs

* Removing obsoleted code from AliasUrlProvider.cs

* Removing obsoleted code from ApiContentRouteBuilder.cs

* Removing obsoleted code from ApiPublishedContentCache.cs

* Removing obsoleted class TemplateQueryResult.cs

* Removing obsoleted code from ApiContentBuilder.cs

* Removing obsoleted code from HealthCheck.cs

* Removing obsoleted code from ContentTypeEditingService.cs

* Removing obsoleted code from NewDefaultUrlProvider.cs

* Removing obsoleted code from PublishedElementPropertyBase.cs

* Removing obsoleted code from WebhookRequestService.cs

* Bumping to obsolete in V18, due to usage in class that will be removed in V18

* Removing obsoleted code from PropertyValidationService.cs

* Removing obsoleted code from AddUnroutableContentWarningsWhenPublishingNotificationHandler.cs

* Removing obsoleted code from IMemberService.cs

* Removing obsoleted code from DocumentCache.cs
2025-08-28 12:09:59 +02:00
Nicklas KramerandGitHub 20c29f8091 V17 - Removed obsoleted code from Umbraco.Cms.Core.Cache & .Routing (#19959)
* Removing obsoleted code from ApiMediaQueryService.cs

* Removing obsoleted code from ApiRichTextMarkupParserTests.cs

* Removing obsoleted code from ContentCacheRefresher.cs

* Removing obsoleted code from ContentFinderByUrlAlias.cs and adjusting its tests to use the new logic

* Removing obsoleted code from ContentFinderByUrl.cs & its dependencies

* Removing obsoleted code from ApiRichTextMarkupParserTests.cs

* Removing obsoleted code from DocumentCache.cs & its dependencies

* Removing obsoleted code from MediaCache.cs & its dependencies

* Removing obsoleted code from PublishedCacheBase.cs & its dependencies

* Removing obsoleted code from RenderNoContentController.cs and its tests

* Removing obsoleted code from UmbracoRouteValueTransformer.cs

* Removing obsoleted constructors from DefaultUrlProvider.cs

* Removing accidental bookmark

* Introducing a helper method to get the root keys in ApiMediaQueryService.cs

* Removing obsoleted code from Cache classes

* Removing unused imports

* Refactoring to meet the CR

* Added attribute to controller

* Fixing missing using statement
2025-08-27 11:02:21 +02:00
Nicklas KramerandGitHub b8ca11edd6 V17 - Content Finder unit tests cleanup (#19989)
* Removing ContentFinderByUrlAndTemplateTests.cs and dependencies

* Removing ContentFinderByAliasTests.cs

* Removing ContentFinderByAliasWithDomainsTests.cs

* Removing ContentFinderByIdentifierTestsBase.cs

* Removing ContentFinderByIdTests.cs

* Fixing ContentFinderByKeyTests.cs & ContentFinderByPageIdQueryTests.cs to work with new code

* Removing ContentFinderByUrlTests.cs & ContentFinderByUrlWithDomainsTests.cs

* Fixing ContentFinderByPageIdQueryTests.cs to actually test the result rather than force the result

* Removing comment and adding test scenario
2025-08-27 07:39:03 +00:00
2a6bb64c78 V17 - Properties and validators, removing obsoleted code (#19961)
* Removing obsoleted code from ApiMediaQueryService.cs

* Removing obsoleted code from ApiRichTextMarkupParserTests.cs

* Removing obsoleted code from ContentCacheRefresher.cs

* Removing obsoleted code from ContentFinderByUrlAlias.cs and adjusting its tests to use the new logic

* Removing obsoleted code from ContentFinderByUrl.cs & its dependencies

* Removing obsoleted code from ApiRichTextMarkupParserTests.cs

* Removing obsoleted code from DocumentCache.cs & its dependencies

* Removing obsoleted code from MediaCache.cs & its dependencies

* Removing obsoleted code from PublishedCacheBase.cs & its dependencies

* Removing obsoleted code from RenderNoContentController.cs and its tests

* Removing obsoleted code from UmbracoRouteValueTransformer.cs

* Removing obsoleted constructors from DefaultUrlProvider.cs

* Removing the RadioValueEditor.cs & RadioValueValidator.cs obsoleted classes.

* Removing obsolete constructor from MultipleValueValidator.cs

* Removing obsolete constructor from EmailValidator.cs

* Removing obsoleted code from DataValueReferenceFactoryCollection.cs

* Removing obsoleted code from ApiContentBuilderBase.cs

* Fixing constructor missing attribute

* Making use of the TryGet result

* Fixing use of obsoleted constructor

* Removing silly bookmark comment

* Fixing deleted code and restructuring to use new cache

* Making use of TryGetRootKeys bool, to return null if false.

* Extending code to use new constructor

* Updated PublishedContentQuery.cs to return empty array

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

---------

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
2025-08-26 11:31:27 +00:00
Nicklas KramerandGitHub 156bcdc98a V17 - Removing obsoleted code from Umbraco.Web (#19983)
* Removing obsoleted code from AspNetCoreCookieManager.cs & Dependencies

* Removing obosoleted code from AspNetCoreBackOfficeInfo.cs

* Removing obosoleted code from PublishedContentExtensions.cs

* Removing obosoleted code from UmbracoMvcConfigureOptions.cs

* Removing obosoleted code from UmbracoVirtualPageRoute.cs

* Removing obsoleted constructor from BasicAuthenticationMiddleware.cs

* Removing obsoleted constructors from UmbLoginController.cs

* Removing unused constructor
2025-08-26 10:54:03 +00:00
Nicklas KramerandGitHub 07abec239d V17 - Removed obsoleted code from Cms.Api (#19963)
* Removing obsoleted constructor from SubTypesSelector.cs

* V17 - Removed obsoleted code from Cms.Api.Delivery (#19964)

* Removing obsoleted constructors from AncestorsSelector.cs

* Removing obsoleted constructors from ChildrenSelector.cs

* Removing obsoleted code from DescendantsSelector.cs

* Removing obsoleted code from RequestCultureService.cs

* Removing obsoleted code from QueryOptionBase.cs

* Removing obsoleted code from BackOfficeApplicationManager.cs & Dependencies

* Removing obsoleted code from ByKeyDocumentController.cs

* Removing obsoleted code from ContentCollectionControllerBase.cs & dependencies

* Removing obsoleted code from DocumentPresentationFactory.cs

* Removing obsoleted code from HealthCheckGroupPresentationFactory.cs & dependencies & IBackOfficeApplicationManager.cs

* Removing obsoleted code from DocumentPermissionMapper.cs

* Removing obsoleted code from DocumentResponseModel.cs

* Removing obsoleted code from ExecuteTemplateQueryController.cs

* Removing obsoleted code from MediaCollectionControllerBase.cs & Dependencies

* Removing the class ReferencesDataTypeController.cs

* Removing obsoleted code from RelationTypePresentationFactory.cs

* Removing obsoleted code from SearchDocumentTypeItemController.cs

* Removing obsoleted code from  UserPresentationFactory.cs & Dependencies

* Removing obsoleted code from ValidateCreateDocumentController.cs & Dependencies

* Removing unused imports and redundant attribute

* Removing unused constructor attribute
2025-08-25 07:37:18 +00:00
Nicklas KramerandGitHub 50ae48b0a2 V17 - Removed obsoleted code from Umbraco.Infrastructure (#19977)
* Removing obsoleted code from MigrationPlanExecutor.cs & Interface

* Removing obsoleted code from EmailAddressPropertyEditor.cs

* Removing obsoleted class CacheRebuilder.cs

* Removing obsoleted code from TextBuilder.cs

* Removing obsoleted class ICacheRebuilder.cs

* Removing obsoleted code from SerilogLogger.cs

* Removing the use of Infrastructure IBackgroundTaskQueue.cs and replacing usage with the Core replacement

* Removing obsoleted code from the FileUploadPropertyEditor.cs

* Removing obsoleted code from BlockValuePropertyValueEditorBase.cs

* Removing obsoleted constructors and methods from MultiNodeTreePickerPropertyEditor.cs and TextHeaderWriter.cs

* Removing obsoleted code from CacheInstructionService.cs

* Bumping obsoleted code from MigrationBase.cs to V18

* Removing obsoleted code from EmailSender.cs

* Removing obsoleted code from BlockEditorVarianceHandler.cs

* Removing obsoleted code from IBackOfficeApplicationManager.cs

* Removing obsoleted code from RedirectTracker.cs & RichTextEditorPastedImages.cs
2025-08-22 14:38:27 +02:00
Andy Butland 0b2a247614 Merge branch 'v17/dev' of https://github.com/umbraco/Umbraco-CMS into v17/dev 2025-08-22 13:58:51 +02:00
Andy Butland f2b5061414 Merge branch 'main' into v17/dev 2025-08-22 13:58:39 +02:00
d623476902 Use UTC for system dates in Umbraco (#19822)
* Persist and expose Umbraco system dates as UTC (#19705)

* Updated persistence DTOs defining default dates to use UTC.

* Remove ForceToUtc = false from all persistence DTO attributes (default when not specified is true).

* Removed use of SpecifyKind setting dates to local.

* Removed unnecessary Utc suffixes on properties.

* Persist current date time with UtcNow.

* Removed further necessary Utc suffixes and fixed failing unit tests.

* Added migration for SQL server to update database date default constraints.

* Added comment justifying not providing a migration for SQLite default date constraints.

* Ensure UTC for datetimes created from persistence DTOs.

* Ensure UTC when creating dates for published content rendering in Razor and outputting in delivery API.

* Fixed migration SQL syntax.

* Introduced AuditItemFactory for creating entries for the backoffice document history, so we can control the UTC setting on the retrieved persisted dates.

* Ensured UTC dates are retrieved for document versions.

* Ensured UTC is returned for backoffice display of last edited and published for variant content.

* Fixed SQLite syntax for default current datetime.

* Apply suggestions from code review

Co-authored-by: Laura Neto <12862535+lauraneto@users.noreply.github.com>

* Further updates from code review.

---------

Co-authored-by: Laura Neto <12862535+lauraneto@users.noreply.github.com>

* Migrate system dates from local server time to UTC (#19798)

* Add settings for the migration.

* Add migration and implement for SQL server.

* Implement for SQLite.

* Fixes from testing with SQL Server.

* Fixes from testing with SQLite.

* Code tidy.

* Cleaned up usings.

* Removed audit log date from conversion.

* Removed webhook log date from conversion.

* Updated update date initialization on saving dictionary items.

* Updated filter on log queries.

* Use timezone ID instead of system name to work cross-culture.

---------

Co-authored-by: Laura Neto <12862535+lauraneto@users.noreply.github.com>
2025-08-22 11:59:23 +02:00
Andy Butland b427a8cbdd Merge branch 'main' into v17/dev 2025-08-22 09:58:14 +02:00
Nicklas KramerandGitHub bd8ad5f6f9 V17 - Removed obsoleted code from Umbraco.Examine (#19966)
Removing obsoleted code from BackOfficeExamineSearcher.cs and dependencies
2025-08-22 07:58:17 +02:00
Nicklas KramerandGitHub 596389d961 V17 - Removed obsoleted code from Umbraco.Extension (#19937)
* Removing obsoleted class GlobalSettingsExtensions.cs

* Removing obsoleted methods and usage from ObjectExtensions.cs

* Removing a ton of obsoleted methods from PublishedContentExtensions.cs

* Removing obsoleted constructors

* Removing obsoleted tag on private method that's still in use.
2025-08-22 07:32:18 +02:00
Andy Butland cd00dfaa34 Merge branch 'main' into v17/dev 2025-08-20 11:57:01 +01:00
Andy Butland 6d5271fb80 Merge branch 'main' into v17/dev 2025-08-18 13:46:04 +01:00
Andy Butland f8ec2dadfe Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Web.UI.Client/package-lock.json
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
2025-08-18 07:16:06 +01:00
Laura Neto e57f66537c Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Web.UI.Client/package-lock.json
2025-08-11 11:32:52 +02:00
DevAndersenandGitHub ab0af2a291 Changed methods in ServiceCollectionExtensions to return IServiceCollection instead of void (#19785) 2025-08-07 12:16:36 +02:00
5fc12ec36e Support persistence of unrestricted selections from the check box list (#19856)
* Use unrestricted text field when creating data types based on the CheckboxList property editor.
Initialize default checkbox list data type with the unrestricted text field for storage on new installs.
Migrate existing data type and property data.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Correctly use constant.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-05 13:19:25 +02:00
Andy Butland 73d2f207d0 Merge branch 'main' into v17/dev 2025-08-04 12:41:50 +02:00
c3e93f143c Fix nullability of return types that can be non-null (#15927)
* Fix nullability of Children extension

* Fix nullability of methods throughout the CMS

* Fix return types of some methods that cannot return null

* Revert nullable changes to result of ConvertSourceToIntermediate for property editors (whilst some property editors we know won't return null, it seems more consistent to adhere to the base class and interface nullability definition).

* Updated new webhook events to align with new nullability definitions.

* Reverted content editing service updates to align with base classes.

* Applied collection nullability updates on content repository to interface.

* Reverted value converter updates to match interface.

* Applied further collection updates to interface.

* Aligned media service interface with implementation for nullability.

* Update from code review.

---------

Co-authored-by: Ivo van der Bruggen <ivo@dutchbreeze.com>
Co-authored-by: Ivo van der Bruggen <ivo@vdbruggensoftware.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-07-30 14:19:20 +02:00
Andy Butland c6bc4ef49a Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Web.UI.Client/package.json
2025-07-30 11:30:10 +02:00
Laura NetoandGitHub c1ac80653b Use audit service instead of repository directly in services (#19357)
* Introduce new AuditEntryService

- Moved logic related to the IAuditEntryRepository from the AuditService to the new service
- Introduced new Async methods
  - Using ids (for easier transition from the previous Write method)
  - Using keys
- Moved and updated integration tests related to the audit entries to a new test class `AuditEntryServiceTests`
- Added unit tests class `AuditEntryServiceTests` and added a few unit tests
- Added migration to add columns for `performingUserKey` and `affectedUserKey` and convert existing user ids
- Adjusted usages of the old AuditService.Write method to use the new one (mostly notification handlers)

* Audit service rework

- Added new async and paged methods
- Marked (now) redundant methods as obsolete
- Updated all of the usages to use the non-obsolete methods
- Added unit tests class `AuditServiceTests` and some unit tests
- Updated existing integration test

* Use the audit service instead of the repository directly in services

* Apply suggestions from code review

* Small improvement

* Update src/Umbraco.Core/Services/AuditService.cs

* Some minor adjustments following the merge

* Delete unnecessary file

* Small cleanup on the tests

* Remove changing user id to 0 (on audit) if user id is admin in media bulk save

* Remove reference to unused IUserIdKeyResolver in TemplateService

* Remove references to unused IShortStringHelper and GlobalSettings in FileService
2025-07-24 14:52:17 +02:00
a50ad893a8 Use new submit and poll solution for examine index rebuild (#19707)
* Started implementing new LongRunningOperationService and adjusting tasks to use this service

This service will manage operations that require status to be synced between servers (load balanced setup).

* Missing migration to add new lock. Other simplifications.

* Add job to cleanup the LongRunningOperations entries

* Add new DatabaseCacheRebuilder.RebuildAsync method

This is both async and returns an attempt, which will fail if a rebuild operation is already running.

* Missing LongRunningOperation database table creation on clean install

* Store expire date in the long running operation. Better handling of non-background operations.

Storing an expiration date allows setting different expiration times depending on the type of operation, and whether it is running in the background or not.

* Added integration tests for LongRunningOperationRepository

* Added unit tests for LongRunningOperationService

* Add type as a parameter to more repository calls. Distinguish between expiration and deletion in `LongRunningOperationRepository.CleanOperations`.

* Fix failing unit test

* Fixed `PerformPublishBranchAsync` result not being deserialized correctly

* Remove unnecessary DatabaseCacheRebuildResult value

* Add status to `LongRunningOperationService.GetResult` attempt to inform on why a result could not be retrieved

* General improvements

* Missing rename

* Improve the handling of long running operations that are not in background and stale operations

* Fix failing unit tests

* Fixed small mismatch between interface and implementation

* Use the new submit and poll functionality for the Examine index rebuild

* Use a fire and forget task instead of the background queue

* Apply suggestions from code review

Co-authored-by: Andy Butland <abutland73@gmail.com>

* Make sure exceptions are caught when running in the background

* Alignment with other repositories (async + pagination)

* Fix build after merge

* Missing obsoletion messages

* Additional fixes

* Add Async suffix to service methods

* Missing adjustment

* Moved hardcoded settings to IOptions

* Fix issue in SQL Server where 0 is not accepted as requested number of rows

* Fix issue in SQL Server where query provided to count cannot contain orderby

* Additional SQL Server fixes

* Update method names

* Adjustments from code review

* Ignoring result of index rebuild in `IndexingNotificationHandler.Language.cs` (same behavior as before)

* Missed some obsoletion messages

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-07-24 14:30:14 +02:00
Andy Butland a6c311977c Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
2025-07-23 17:03:30 +02:00
Laura Neto 04ac8d3ea9 Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
2025-07-22 15:39:26 +02:00
Laura NetoandGitHub 3fcc09169a Update the existing date/time property editor to provide a kind of unspecified (#19727)
* Update the existing date/time property editor to provide a Kind of Unspecified

* Added unit tests
2025-07-22 11:55:52 +02:00
Laura Neto 2ce647738f Merge branch 'main' into v17/dev
# Conflicts:
#	version.json
2025-07-14 15:41:34 +02:00
Andy Butland 4a438ad6e9 Disabled package validation. 2025-07-08 14:37:45 +02:00
Andy Butland 0b1bd16b9d Merge branch 'main' into v17/dev 2025-07-08 09:26:54 +02:00
Laura NetoandGitHub 447cb881bd Audit service rework (#19346)
* Introduce new AuditEntryService

- Moved logic related to the IAuditEntryRepository from the AuditService to the new service
- Introduced new Async methods
  - Using ids (for easier transition from the previous Write method)
  - Using keys
- Moved and updated integration tests related to the audit entries to a new test class `AuditEntryServiceTests`
- Added unit tests class `AuditEntryServiceTests` and added a few unit tests
- Added migration to add columns for `performingUserKey` and `affectedUserKey` and convert existing user ids
- Adjusted usages of the old AuditService.Write method to use the new one (mostly notification handlers)

* Audit service rework

- Added new async and paged methods
- Marked (now) redundant methods as obsolete
- Updated all of the usages to use the non-obsolete methods
- Added unit tests class `AuditServiceTests` and some unit tests
- Updated existing integration test

* Apply suggestions from code review

* Small improvement

* Update src/Umbraco.Core/Services/AuditService.cs

* Some minor adjustments following the merge

* Delete unnecessary file

* Small cleanup on the tests
2025-07-01 09:12:37 +02:00
Andy Butland ceb745a7bd Merge branch 'main' into v17/dev
# Conflicts:
#	src/Umbraco.Core/Services/AuditService.cs
#	src/Umbraco.Core/Services/IAuditService.cs
2025-06-09 06:49:12 +02:00
Laura NetoandGitHub 7fc2bc84de Audit entries rework (#19345)
* Introduce new AuditEntryService

- Moved logic related to the IAuditEntryRepository from the AuditService to the new service
- Introduced new Async methods
  - Using ids (for easier transition from the previous Write method)
  - Using keys
- Moved and updated integration tests related to the audit entries to a new test class `AuditEntryServiceTests`
- Added unit tests class `AuditEntryServiceTests` and added a few unit tests
- Added migration to add columns for `performingUserKey` and `affectedUserKey` and convert existing user ids
- Adjusted usages of the old AuditService.Write method to use the new one (mostly notification handlers)

* Apply suggestions from code review

* Small improvement

* Some adjustments following code review. Removed UnknownUserKey and used null instead.

* Small adjustments

* Better handle audits performed during the migration state

* Update TODO comment
2025-06-06 13:12:35 +02:00
Dirk SeefeldandMatthew-Wise 321087c96e update data type descriptions for color picker according to PR 19430 2025-05-30 15:38:45 +01:00
Dirk SeefeldandMatthew-Wise f0e37e04fa fix issue 19341 2025-05-30 11:18:45 +01:00
Jacob Overgaard 135f0f63de Merge branch 'main' into v17/dev 2025-05-16 16:58:11 +02:00
2dced37117 Clean up extension methods (#17051)
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-05-05 14:53:26 +02:00
Jacob Overgaard 44d9f5ac2e build: bump version to 17.0.0-rc 2025-05-05 14:52:52 +02:00
6660 changed files with 171103 additions and 48172 deletions
+35
View File
@@ -0,0 +1,35 @@
{
"permissions": {
"allow": [
"Bash(dir:*)",
"Bash(do)",
"Bash(done)",
"Bash(echo:*)",
"Bash(find:*)",
"Bash(for:*)",
"Bash(gh pr diff:*)",
"Bash(gh pr view:*)",
"Bash(git log:*)",
"Bash(grep:*)",
"Bash(npm run build:*)",
"Bash(npm run check:*)",
"Bash(npm run compile:*)",
"Bash(npm run lint:*)",
"Bash(npm run:*)",
"Bash(npx eslint:*)",
"Bash(npx tsc:*)",
"Bash(tree:*)",
"Bash(gh issue view:*)",
"Bash(npm test:*)",
"mcp__umbraco__create*",
"mcp__umbraco__get*",
"mcp__playwright__browser_click",
"mcp__playwright__browser_type",
"mcp__playwright__browser_wait_for"
]
},
"enableAllProjectMcpServers": true,
"enabledMcpjsonServers": [
"umbraco-cms"
]
}
+1 -1
View File
@@ -102,7 +102,7 @@ dotnet_style_predefined_type_for_locals_parameters_members = true:warning
dotnet_style_predefined_type_for_member_access = true:warning
# Modifier preferences
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#normalize-modifiers
dotnet_style_require_accessibility_modifiers = always:warning
dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:warning
dotnet_style_readonly_field = true:warning
+5
View File
@@ -0,0 +1,5 @@
UMBRACO_CLIENT_ID=umbraco-back-office-mcp
UMBRACO_CLIENT_SECRET=1234567890
UMBRACO_BASE_URL=https://localhost:44339
NODE_TLS_REJECT_UNAUTHORIZED=0
UMBRACO_INCLUDE_TOOL_COLLECTIONS=data-type,document-type,document,media-type,media
+5
View File
@@ -55,3 +55,8 @@
*.sln text=auto eol=crlf merge=union
*.gitattributes text=auto
# Generated files - hidden by default in GitHub diffs
src/Umbraco.Web.UI.Client/src/packages/core/backend-api/** linguist-generated
src/Umbraco.Web.UI.Login/src/api/** linguist-generated
src/Umbraco.Cms.Api.Management/OpenApi.json linguist-generated
+2 -3
View File
@@ -9,7 +9,7 @@ In order to use Umbraco as a CMS and build your website with it, you should not
- Are you about to [create a pull request for Umbraco][contribution guidelines]?
- Are you trying to get to the bottom of a problem in your existing Umbraco installation?
If the answer is yes, please read on. Otherwise, make sure to head on over [to the download page](https://our.umbraco.com/download) and start using Umbraco CMS as intended.
If the answer is yes, please read on. Otherwise, make sure to head on over [to the releases page](https://releases.umbraco.com) and start using Umbraco CMS as intended.
## Table of contents
@@ -79,13 +79,12 @@ Conversely, if you are working on front-end only, you want to build the back-end
"AuthorizeCallbackLogoutPathName": "/logout",
"AuthorizeCallbackErrorPathName": "/error",
"BackOfficeTokenCookie": {
"Enabled": true,
"SameSite": "None"
}
```
> [!NOTE]
> If you get stuck in a login loop, try clearing your browser cookies for localhost, and make sure that the `BackOfficeTokenCookie` settings are correct. Namely, that `SameSite` should be set to `None` when running the front-end server separately.
> If you get stuck in a login loop, try clearing your browser cookies for localhost, and make sure that the `Umbraco:Cms:Security:BackOfficeTokenCookie:SameSite` setting is set to `None`.
Then run Umbraco from the command line.
+1 -1
View File
@@ -30,7 +30,7 @@ This guide describes each step to make your first contribution:
Create a new branch based on `main` and name it after the issue you're fixing. For example: `v15/bugfix/18132-rte-tinymce-onchange-value-check`.
Please follow this format for branches: `v{major}/{feature|bugfix|task}/{issue}-{description}`.
Please follow this format for branches: `v{major}/{feature|bugfix|task|qa|improvement}/{issue}-{description}`.
This is a development branch for the particular issue you're working on, in this case, a bug-fix for issue number `18132` that affects Umbraco v.15.
+7 -7
View File
@@ -24,7 +24,7 @@ Great question! The short version goes like this:
1. **Switch to the correct branch**
Switch to the `contrib` branch
Switch to the `main` branch
1. **Build**
@@ -32,7 +32,7 @@ Great question! The short version goes like this:
1. **Branch**
Create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case issue number `12345`. Don't commit to `contrib`, create a new branch first.
Create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case issue number `12345`. Don't commit to `main`, create a new branch first.
1. **Change**
@@ -42,7 +42,7 @@ Great question! The short version goes like this:
Done? Yay! 🎉
Remember to commit to your new `temp` branch, and don't commit to `contrib`. Then you can push the changes up to your fork on GitHub.
Remember to commit to your new `temp` branch, and don't commit to `main`. Then you can push the changes up to your fork on GitHub.
#### Keeping your Umbraco fork in sync with the main repository
[sync fork]: #keeping-your-umbraco-fork-in-sync-with-the-main-repository
@@ -59,10 +59,10 @@ Then when you want to get the changes from the main repository:
```
git fetch upstream
git rebase upstream/contrib
git rebase upstream/main
```
In this command we're syncing with the `contrib` branch, but you can of course choose another one if needed.
In this command we're syncing with the `main` branch, but you can of course choose another one if needed.
[More information on how this works can be found on the thoughtbot blog.][sync fork ext]
@@ -79,7 +79,7 @@ You can get in touch with [the core contributors team][core collabs] in multiple
- If there's an existing issue on the issue tracker then that's a good place to leave questions and discuss how to start or move forward.
- If you want to ask questions on some code you've already written you can create a draft pull request, [detailed in a GitHub blog post][draft prs].
- Unsure where to start? Did something not work as expected? Try leaving a note in the ["Contributing to Umbraco"][contrib forum] forum. The team monitors that one closely, so one of us will be on hand and ready to point you in the right direction.
- Unsure where to start? Did something not work as expected? Try leaving a note in the [forum][forum]. The team monitors that one closely, so one of us will be on hand and ready to point you in the right direction.
<!-- Local -->
@@ -90,7 +90,7 @@ You can get in touch with [the core contributors team][core collabs] in multiple
[sync fork ext]: http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated "Details on keeping a git fork updated"
[draft prs]: https://github.blog/2019-02-14-introducing-draft-pull-requests/ "Github's blog post providing details on draft pull requests"
[contrib forum]: https://our.umbraco.com/forum/contributing-to-umbraco-cms/
[forum]: https://forum.umbraco.com/
[Umbraco CMS repo]: https://github.com/umbraco/Umbraco-CMS
[up for grabs issues]: https://github.com/umbraco/Umbraco-CMS/issues?q=is%3Aissue+is%3Aopen+label%3Acommunity%2Fup-for-grabs
[issue tracker]: https://github.com/umbraco/Umbraco-CMS/issues
+1 -1
View File
@@ -115,7 +115,7 @@ Save the changes and return to the Backoffice to see the update.
1. Commit your changes to a new temporary branch (avoid committing directly to `contrib`).
1. Commit your changes to a new temporary branch (avoid committing directly to `main`).
2. Push the changes to your fork on GitHub.
+118 -98
View File
@@ -6,91 +6,101 @@ Always reference these instructions first and fallback to search or bash command
Bootstrap, build, and test the repository:
- Install .NET SDK (version specified in global.json):
- `curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version $(jq -r '.sdk.version' global.json)`
- `export PATH="/home/runner/.dotnet:$PATH"`
- Install Node.js (version specified in src/Umbraco.Web.UI.Client/.nvmrc):
- `curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash`
- `export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"`
- `nvm install $(cat src/Umbraco.Web.UI.Client/.nvmrc) && nvm use $(cat src/Umbraco.Web.UI.Client/.nvmrc)`
- Fix shallow clone issue (required for GitVersioning):
- `git fetch --unshallow`
- Restore packages:
- `dotnet restore` -- takes 50 seconds. NEVER CANCEL. Set timeout to 90+ seconds.
- Build the solution:
- `dotnet build` -- takes 4.5 minutes. NEVER CANCEL. Set timeout to 10+ minutes.
- Install and build frontend:
- `cd src/Umbraco.Web.UI.Client`
- `npm ci --no-fund --no-audit --prefer-offline` -- takes 11 seconds.
- `npm run build:for:cms` -- takes 1.25 minutes. NEVER CANCEL. Set timeout to 5+ minutes.
- Install and build Login
- `cd src/Umbraco.Web.UI.Login`
- `npm ci --no-fund --no-audit --prefer-offline`
- `npm run build`
- Run the application:
- `cd src/Umbraco.Web.UI`
- `dotnet run --no-build` -- Application runs on https://localhost:44339 and http://localhost:11000
- Install .NET SDK (version specified in global.json):
- `curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version $(jq -r '.sdk.version' global.json)`
- `export PATH="/home/runner/.dotnet:$PATH"`
- Install Node.js (version specified in src/Umbraco.Web.UI.Client/.nvmrc):
- `curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash`
- `export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"`
- `nvm install $(cat src/Umbraco.Web.UI.Client/.nvmrc) && nvm use $(cat src/Umbraco.Web.UI.Client/.nvmrc)`
- Fix shallow clone issue (required for GitVersioning):
- `git fetch --unshallow`
- Restore packages:
- `dotnet restore` -- takes 50 seconds. NEVER CANCEL. Set timeout to 90+ seconds.
- Build the solution:
- `dotnet build` -- takes 4.5 minutes. NEVER CANCEL. Set timeout to 10+ minutes.
- Install and build frontend:
- `cd src/Umbraco.Web.UI.Client`
- `npm ci --no-fund --no-audit --prefer-offline` -- takes 11 seconds.
- `npm run build:for:cms` -- takes 1.25 minutes. NEVER CANCEL. Set timeout to 5+ minutes.
- Install and build Login
- `cd src/Umbraco.Web.UI.Login`
- `npm ci --no-fund --no-audit --prefer-offline`
- `npm run build`
- Run the application:
- `cd src/Umbraco.Web.UI`
- `dotnet run --no-build` -- Application runs on https://localhost:44339 and http://localhost:11000
Check out [BUILD.md](./BUILD.md) for more detailed instructions.
## Validation
- ALWAYS run through at least one complete end-to-end scenario after making changes.
- Build and unit tests must pass before committing changes.
- Frontend build produces output in src/Umbraco.Web.UI.Client/dist-cms/ which gets copied to src/Umbraco.Web.UI/wwwroot/umbraco/backoffice/
- Always run `dotnet build` and `npm run build:for:cms` before running the application to see your changes.
- For login-only changes, you can run `npm run build` from src/Umbraco.Web.UI.Login and then `dotnet run --no-build` from src/Umbraco.Web.UI.
- For frontend-only changes, you can run `npm run dev:server` from src/Umbraco.Web.UI.Client for hot reloading.
- Frontend changes should be linted using `npm run lint:fix` which uses Eslint.
- ALWAYS run through at least one complete end-to-end scenario after making changes.
- Build and unit tests must pass before committing changes.
- Frontend build produces output in src/Umbraco.Web.UI.Client/dist-cms/ which gets copied to src/Umbraco.Web.UI/wwwroot/umbraco/backoffice/
- Always run `dotnet build` and `npm run build:for:cms` before running the application to see your changes.
- For login-only changes, you can run `npm run build` from src/Umbraco.Web.UI.Login and then `dotnet run --no-build` from src/Umbraco.Web.UI.
- For frontend-only changes, you can run `npm run dev:server` from src/Umbraco.Web.UI.Client for hot reloading.
- Frontend changes should be linted using `npm run lint:fix` which uses Eslint.
## Testing
### Unit Tests (.NET)
- Location: tests/Umbraco.Tests.UnitTests/
- Run: `dotnet test tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj --configuration Release --verbosity minimal`
- Duration: ~1 minute with 3,343 tests
- NEVER CANCEL: Set timeout to 5+ minutes
- Location: tests/Umbraco.Tests.UnitTests/
- Run: `dotnet test tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj --configuration Release --verbosity minimal`
- Duration: ~1 minute with 3,343 tests
- NEVER CANCEL: Set timeout to 5+ minutes
### Integration Tests (.NET)
- Location: tests/Umbraco.Tests.Integration/
- Run: `dotnet test tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj --configuration Release --verbosity minimal`
- NEVER CANCEL: Set timeout to 10+ minutes
- Location: tests/Umbraco.Tests.Integration/
- Run: `dotnet test tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj --configuration Release --verbosity minimal`
- NEVER CANCEL: Set timeout to 10+ minutes
### Frontend Tests
- Location: src/Umbraco.Web.UI.Client/
- Run: `npm test` (requires `npx playwright install` first)
- Frontend tests use Web Test Runner with Playwright
- Location: src/Umbraco.Web.UI.Client/
- Run: `npm test` (requires `npx playwright install` first)
- Frontend tests use Web Test Runner with Playwright
### Acceptance Tests (E2E)
- Location: tests/Umbraco.Tests.AcceptanceTest/
- Requires running Umbraco application and configuration
- See tests/Umbraco.Tests.AcceptanceTest/README.md for detailed setup (requires `npx playwright install` first)
- Location: tests/Umbraco.Tests.AcceptanceTest/
- Requires running Umbraco application and configuration
- See tests/Umbraco.Tests.AcceptanceTest/README.md for detailed setup (requires `npx playwright install` first)
## Project Structure
The solution contains 30 C# projects organized as follows:
### Main Application Projects
- **Umbraco.Web.UI**: Main web application project (startup project)
- **Umbraco.Web.UI.Client**: TypeScript frontend (backoffice)
- **Umbraco.Web.UI.Login**: Separate login screen frontend
- **Umbraco.Core**: Core domain models and interfaces
- **Umbraco.Infrastructure**: Data access and infrastructure
- **Umbraco.Cms**: Main CMS package
- **Umbraco.Web.UI**: Main web application project (startup project)
- **Umbraco.Web.UI.Client**: TypeScript frontend (backoffice)
- **Umbraco.Web.UI.Login**: Separate login screen frontend
- **Umbraco.Core**: Core domain models and interfaces
- **Umbraco.Infrastructure**: Data access and infrastructure
- **Umbraco.Cms**: Main CMS package
### API Projects
- **Umbraco.Cms.Api.Management**: Management API
- **Umbraco.Cms.Api.Delivery**: Content Delivery API
- **Umbraco.Cms.Api.Common**: Shared API components
- **Umbraco.Cms.Api.Management**: Management API
- **Umbraco.Cms.Api.Delivery**: Content Delivery API
- **Umbraco.Cms.Api.Common**: Shared API components
### Persistence Projects
- **Umbraco.Cms.Persistence.SqlServer**: SQL Server support
- **Umbraco.Cms.Persistence.Sqlite**: SQLite support
- **Umbraco.Cms.Persistence.EFCore**: Entity Framework Core abstractions
- **Umbraco.Cms.Persistence.SqlServer**: SQL Server support
- **Umbraco.Cms.Persistence.Sqlite**: SQLite support
- **Umbraco.Cms.Persistence.EFCore**: Entity Framework Core abstractions
### Test Projects
- **Umbraco.Tests.UnitTests**: Unit tests
- **Umbraco.Tests.Integration**: Integration tests
- **Umbraco.Tests.AcceptanceTest**: End-to-end tests with Playwright
- **Umbraco.Tests.Common**: Shared test utilities
- **Umbraco.Tests.UnitTests**: Unit tests
- **Umbraco.Tests.Integration**: Integration tests
- **Umbraco.Tests.AcceptanceTest**: End-to-end tests with Playwright
- **Umbraco.Tests.Common**: Shared test utilities
## Common Tasks
@@ -98,6 +108,7 @@ The solution contains 30 C# projects organized as follows:
**Production Mode (Standard Development)**
Use this for backend development, testing full builds, or when you don't need hot reloading:
1. Build frontend assets: `cd src/Umbraco.Web.UI.Client && npm run build:for:cms`
2. Run backend: `cd src/Umbraco.Web.UI && dotnet run --no-build`
3. Access backoffice: `https://localhost:44339/umbraco`
@@ -105,17 +116,17 @@ Use this for backend development, testing full builds, or when you don't need ho
**Vite Dev Server Mode (Frontend Development with Hot Reload)**
Use this for frontend-only development with hot module reloading:
1. Configure backend for frontend development - Add to `src/Umbraco.Web.UI/appsettings.json` under `Umbraco:CMS:Security`:
```json
"BackOfficeHost": "http://localhost:5173",
"AuthorizeCallbackPathName": "/oauth_complete",
"AuthorizeCallbackLogoutPathName": "/logout",
"AuthorizeCallbackErrorPathName": "/error",
"BackOfficeTokenCookie": {
"Enabled": true,
"SameSite": "None"
}
```
```json
"BackOfficeHost": "http://localhost:5173",
"AuthorizeCallbackPathName": "/oauth_complete",
"AuthorizeCallbackLogoutPathName": "/logout",
"AuthorizeCallbackErrorPathName": "/error",
"BackOfficeTokenCookie": {
"SameSite": "None"
}
```
2. Run backend: `cd src/Umbraco.Web.UI && dotnet run --no-build`
3. Run frontend dev server: `cd src/Umbraco.Web.UI.Client && npm run dev:server`
4. Access backoffice: `http://localhost:5173/` (no `/umbraco` prefix)
@@ -124,39 +135,48 @@ Use this for frontend-only development with hot module reloading:
**Important:** Remove the `BackOfficeHost` configuration before committing or switching back to production mode.
### Backend-Only Development
For backend-only changes, disable frontend builds:
- Comment out the target named "BuildStaticAssetsPreconditions" in src/Umbraco.Cms.StaticAssets.csproj:
```
<!--<Target Name="BuildStaticAssetsPreconditions" BeforeTargets="AssignTargetPaths">
[...]
</Target>-->
```
- Remember to uncomment before committing
- Comment out the target named "BuildStaticAssetsPreconditions" in src/Umbraco.Cms.StaticAssets.csproj:
```
<!--<Target Name="BuildStaticAssetsPreconditions" BeforeTargets="AssignTargetPaths">
[...]
</Target>-->
```
- Remember to uncomment before committing
### Building NuGet Packages
To build custom NuGet packages for testing:
```bash
dotnet pack -c Release -o Build.Out
dotnet nuget add source [Path to Build.Out folder] -n MyLocalFeed
```
### Regenerating Frontend API Types
When changing Management API:
```bash
cd src/Umbraco.Web.UI.Client
npm run generate:server-api-dev
```
Also update OpenApi.json from /umbraco/swagger/management/swagger.json
## Database Setup
Default configuration supports SQLite for development. For production-like testing:
- Use SQL Server/LocalDb for better performance
- Configure connection string in src/Umbraco.Web.UI/appsettings.json
- Use SQL Server/LocalDb for better performance
- Configure connection string in src/Umbraco.Web.UI/appsettings.json
## Clean Up / Reset
To reset development environment:
```bash
# Remove configuration and database
rm src/Umbraco.Web.UI/appsettings.json
@@ -168,31 +188,31 @@ git clean -xdf .
## Version Information
- Target Framework: .NET (version specified in global.json)
- Current Version: (specified in version.json)
- Node.js Requirement: (specified in src/Umbraco.Web.UI.Client/.nvmrc)
- npm Requirement: Latest compatible version
- Target Framework: .NET (version specified in global.json)
- Current Version: (specified in version.json)
- Node.js Requirement: (specified in src/Umbraco.Web.UI.Client/.nvmrc)
- npm Requirement: Latest compatible version
## Known Issues
- Build requires full git history (not shallow clone) due to GitVersioning
- Some NuGet package security warnings are expected (SixLabors.ImageSharp vulnerabilities)
- Frontend tests require Playwright browser installation: `npx playwright install`
- Older Node.js versions may show engine compatibility warnings (check .nvmrc for current requirement)
- Build requires full git history (not shallow clone) due to GitVersioning
- Some NuGet package security warnings are expected (SixLabors.ImageSharp vulnerabilities)
- Frontend tests require Playwright browser installation: `npx playwright install`
- Older Node.js versions may show engine compatibility warnings (check .nvmrc for current requirement)
## Timing Expectations
**NEVER CANCEL** these operations - they are expected to take time:
| Operation | Expected Time | Timeout Setting |
|-----------|--------------|-----------------|
| `dotnet restore` | 50 seconds | 90+ seconds |
| `dotnet build` | 4.5 minutes | 10+ minutes |
| `npm ci` | 11 seconds | 30+ seconds |
| `npm run build:for:cms` | 1.25 minutes | 5+ minutes |
| `npm test` | 2 minutes | 5+ minutes |
| `npm run lint` | 1 minute | 5+ minutes |
| Unit tests | 1 minute | 5+ minutes |
| Integration tests | Variable | 10+ minutes |
| Operation | Expected Time | Timeout Setting |
| ----------------------- | ------------- | --------------- |
| `dotnet restore` | 50 seconds | 90+ seconds |
| `dotnet build` | 4.5 minutes | 10+ minutes |
| `npm ci` | 11 seconds | 30+ seconds |
| `npm run build:for:cms` | 1.25 minutes | 5+ minutes |
| `npm test` | 2 minutes | 5+ minutes |
| `npm run lint` | 1 minute | 5+ minutes |
| Unit tests | 1 minute | 5+ minutes |
| Integration tests | Variable | 10+ minutes |
Always wait for commands to complete rather than canceling and retrying.
Always wait for commands to complete rather than canceling and retrying.
+2
View File
@@ -4,6 +4,7 @@ on:
push:
branches:
- main
- release/*
- v*/dev
- v*/main
paths:
@@ -15,6 +16,7 @@ on:
types: [opened, synchronize, reopened, closed]
branches:
- main
- release/*
- v*/dev
- v*/main
workflow_dispatch:
@@ -12,6 +12,7 @@ permissions:
jobs:
reconcile:
if: github.repository == 'umbraco/Umbraco-CMS'
runs-on: ubuntu-latest
steps:
- name: Reconcile release/* labels → discussions
+9 -2
View File
@@ -51,6 +51,10 @@ tools/docfx/
/build/csharp-docs/api/
/build/csharp-docs/_site/
# Local config
.claude/settings.local.json
.env.local
# Build
/build.out/
/build.tmp/
@@ -95,6 +99,10 @@ tools/docfx/
/tests/Umbraco.Tests.Integration/[Uu]mbraco/[Ll]ogs/
/tests/Umbraco.Tests.Integration/Views/
/tests/Umbraco.Tests.UnitTests/[Uu]mbraco/[Dd]ata/TEMP/
/BenchmarkDotNet.Artifacts/
playwright-report
trace.zip
/tests/Umbraco.Tests.AcceptanceTest/results
# Ignore auto-generated schema
/src/Umbraco.Cms.Targets/tasks/
@@ -103,9 +111,8 @@ tools/docfx/
/src/Umbraco.Web.UI/appsettings-schema.json
/src/Umbraco.Web.UI/appsettings-schema.*.json
/src/Umbraco.Web.UI/umbraco-package-schema.json
/src/Umbraco.Web.UI.Client/umbraco-package-schema.json
/tests/Umbraco.Tests.Integration/appsettings-schema.json
/tests/Umbraco.Tests.Integration/appsettings-schema.*.json
/tests/Umbraco.Tests.Integration/umbraco-package-schema.json
/src/Umbraco.Cms/appsettings-schema.json
playwright-report
trace.zip
+14
View File
@@ -0,0 +1,14 @@
{
"mcpServers": {
"umbraco-cms": {
"command": "npx",
"args": ["@umbraco-cms/mcp-dev@17"]
},
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp@latest"
]
}
}
}
-1
View File
@@ -107,7 +107,6 @@
"UMBRACO__CMS__SECURITY__AUTHORIZECALLBACKLOGOUTPATHNAME": "/logout",
"UMBRACO__CMS__SECURITY__AUTHORIZECALLBACKERRORPATHNAME": "/error",
"UMBRACO__CMS__SECURITY__KEEPUSERLOGGEDIN": "true",
"UMBRACO__CMS__SECURITY__BACKOFFICETOKENCOOKIE__ENABLED": "true",
"UMBRACO__CMS__SECURITY__BACKOFFICETOKENCOOKIE__SAMESITE": "None"
},
"sourceFileMap": {
+4
View File
@@ -1,5 +1,9 @@
{
"cSpell.words": [
"backoffice",
"pickable",
"Pickable",
"Umbraco",
"unprovide",
"Unproviding"
],
+541
View File
@@ -0,0 +1,541 @@
# Umbraco CMS - Multi-Project Repository
Enterprise-grade CMS built on .NET 10.0. This repository contains 21 production projects organized in a layered architecture with clear separation of concerns.
**Repository**: https://github.com/umbraco/Umbraco-CMS
**License**: MIT
**Main Branch**: `main`
---
## 1. Overview
### What This Repository Contains
**21 Production Projects** organized in 3 main categories:
1. **Core Architecture** (Domain & Infrastructure)
- `Umbraco.Core` - Interface contracts, domain models, notifications
- `Umbraco.Infrastructure` - Service implementations, data access, caching
2. **Web & APIs** (Presentation Layer)
- `Umbraco.Web.UI` - Main ASP.NET Core web application
- `Umbraco.Web.Common` - Shared web functionality, controllers, middleware
- `Umbraco.Cms.Api.Management` - Backoffice Management API (REST)
- `Umbraco.Cms.Api.Delivery` - Content Delivery API (headless)
- `Umbraco.Cms.Api.Common` - Shared API infrastructure
3. **Specialized Features** (Pluggable Modules)
- Persistence: EF Core (modern), NPoco (legacy) for SQL Server & SQLite
- Caching: `PublishedCache.HybridCache` (in-memory + distributed)
- Search: `Examine.Lucene` (full-text search)
- Imaging: `Imaging.ImageSharp` v1 & v2 (image processing)
- Other: Static assets, targets, development tools
**6 Test Projects**:
- `Umbraco.Tests.Common` - Shared test utilities
- `Umbraco.Tests.UnitTests` - Unit tests
- `Umbraco.Tests.Integration` - Integration tests
- `Umbraco.Tests.Benchmarks` - Performance benchmarks
- `Umbraco.Tests.AcceptanceTest` - E2E tests
- `Umbraco.Tests.AcceptanceTest.UmbracoProject` - Test instance
### Key Technologies
- **.NET 10.0** - Target framework for all projects
- **ASP.NET Core** - Web framework
- **Entity Framework Core** - Modern ORM
- **OpenIddict** - OAuth 2.0/OpenID Connect authentication
- **Swashbuckle** - OpenAPI/Swagger documentation
- **Lucene.NET** - Full-text search via Examine
- **ImageSharp** - Image processing
---
## 2. Repository Structure
```
Umbraco-CMS/
├── src/ # 21 production projects
│ ├── Umbraco.Core/ # Domain contracts (interfaces only)
│ │ └── CLAUDE.md # ⭐ Core architecture guide
│ ├── Umbraco.Infrastructure/ # Service implementations
│ ├── Umbraco.Web.Common/ # Web utilities
│ ├── Umbraco.Web.UI/ # Main web application
│ ├── Umbraco.Cms.Api.Management/ # Management API
│ ├── Umbraco.Cms.Api.Delivery/ # Delivery API (headless)
│ ├── Umbraco.Cms.Api.Common/ # Shared API infrastructure
│ │ └── CLAUDE.md # ⭐ API patterns guide
│ ├── Umbraco.PublishedCache.HybridCache/ # Content caching
│ ├── Umbraco.Examine.Lucene/ # Search indexing
│ ├── Umbraco.Cms.Persistence.EFCore/ # EF Core data access
│ ├── Umbraco.Cms.Persistence.EFCore.Sqlite/
│ ├── Umbraco.Cms.Persistence.EFCore.SqlServer/
│ ├── Umbraco.Cms.Persistence.Sqlite/ # Legacy SQLite
│ ├── Umbraco.Cms.Persistence.SqlServer/ # Legacy SQL Server
│ ├── Umbraco.Cms.Imaging.ImageSharp/ # Image processing v1
│ ├── Umbraco.Cms.Imaging.ImageSharp2/ # Image processing v2
│ ├── Umbraco.Cms.StaticAssets/ # Embedded assets
│ ├── Umbraco.Cms.DevelopmentMode.Backoffice/
│ ├── Umbraco.Cms.Targets/ # NuGet targets
│ └── Umbraco.Cms/ # Meta-package
├── tests/ # 6 test projects
│ ├── Umbraco.Tests.Common/
│ ├── Umbraco.Tests.UnitTests/
│ ├── Umbraco.Tests.Integration/
│ ├── Umbraco.Tests.Benchmarks/
│ ├── Umbraco.Tests.AcceptanceTest/
│ └── Umbraco.Tests.AcceptanceTest.UmbracoProject/
├── templates/ # Project templates
│ └── Umbraco.Templates/
├── tools/ # Build tools
│ └── Umbraco.JsonSchema/
├── umbraco.sln # Main solution file
├── Directory.Build.props # Shared build configuration
├── Directory.Packages.props # Centralized package versions
├── .editorconfig # Code style
└── .globalconfig # Roslyn analyzers
```
### Architecture Layers
**Dependency Flow** (unidirectional, always flows inward):
```
Web.UI → Web.Common → Infrastructure → Core
Api.Management → Api.Common → Infrastructure → Core
Api.Delivery → Api.Common → Infrastructure → Core
```
**Key Principle**: Core has NO dependencies (pure contracts). Infrastructure implements Core. Web/APIs depend on Infrastructure.
### Project Dependencies
**Core Layer**:
- `Umbraco.Core` → No dependencies (only Microsoft.Extensions.*)
**Infrastructure Layer**:
- `Umbraco.Infrastructure``Umbraco.Core`
- `Umbraco.PublishedCache.*``Umbraco.Infrastructure`
- `Umbraco.Examine.Lucene``Umbraco.Infrastructure`
- `Umbraco.Cms.Persistence.*``Umbraco.Infrastructure`
**Web Layer**:
- `Umbraco.Web.Common``Umbraco.Infrastructure` + caching + search
- `Umbraco.Web.UI``Umbraco.Web.Common` + all features
**API Layer**:
- `Umbraco.Cms.Api.Common``Umbraco.Web.Common`
- `Umbraco.Cms.Api.Management``Umbraco.Cms.Api.Common`
- `Umbraco.Cms.Api.Delivery``Umbraco.Cms.Api.Common`
---
## 3. Teamwork & Collaboration
### Branching Strategy
- **Main branch**: `main` (protected)
- **Branch naming convention**: `v<version>/<type>/<description>`
**Format**: `v{major-version}/{type}/{kebab-case-description}`
**Version**: Read from `version.json` in the repository root. Use the major version number (e.g., `v17` for version 17.x.x).
**Types**:
| Type | Use Case |
|------|----------|
| `feature` | New feature being introduced to the product |
| `bugfix` | Fix to an existing issue with the product |
| `qa` | Adding or updating unit, integration, or end-to-end tests |
| `improvement` | Update to something that already exists but isn't broken (UI finessing, refactoring) |
| `task` | Update that doesn't directly impact product behavior (dependency updates, build pipeline) |
**Description**: A short, kebab-case description (a few words). This should be prefixed with the GitHub issue number if the update is related to resolving a tracked issue.
**Examples**:
```
v17/bugfix/12345-correct-display-of-pending-migrations
v17/feature/add-webhook-support
v17/improvement/optimize-content-cache
v17/qa/add-media-service-tests
v17/task/update-ef-core-dependency
```
See `.github/CONTRIBUTING.md` for full guidelines.
### Pull Request Process
- **PR Template**: `.github/pull_request_template.md`
- **Required CI Checks**:
- All tests pass
- Code formatting (dotnet format)
- No build warnings
- **Merge Strategy**: Squash and merge (via GitHub UI)
- **Reviews**: Required from code owners
#### PR Naming Convention
Use the format: `Area: Description (closes #IssueID)`
**Examples**:
| Area | Description | Issue |
|------|-------------|-------|
| Relations: | Move persistence of relations from repository into notification handlers | (closes #00000) |
| Management API: | Correct the population of the parent for sibling items when retrieved under a folder | |
| Docs: | Updated contributing guidelines to welcome contributions on bugfixes | |
**Area**: The feature or aspect affected (e.g., UFM, TipTap, Docs, Segmentation, Migrations). Helps readers quickly understand what is being changed.
**Description Best Practices**:
- Include the area of change (Relations, Management API, etc.)
- Describe the change and its impact
- Be specific, not vague (describe "a golden retriever" not just "a dog")
**Issue Linking**: Add `(closes #IssueID)` to auto-close linked issues on merge.
### Commit Messages
Follow Conventional Commits format:
```
<type>(<scope>): <description>
Types: feat, fix, docs, style, refactor, test, chore
Scope: project name (core, web, api, etc.)
Examples:
feat(core): add IContentService.GetByIds method
fix(api): resolve null reference in schema handler
docs(web): update routing documentation
```
### Code Owners
Project ownership is distributed across teams. Check individual project directories for ownership.
---
## 4. Architecture Patterns
### Core Architectural Decisions
1. **Layered Architecture with Dependency Inversion**
- Core defines contracts (interfaces)
- Infrastructure implements contracts
- Web/APIs consume implementations via DI
2. **Interface-First Design**
- All services defined as interfaces in Core
- Enables testing, polymorphism, extensibility
3. **Notification Pattern** (not C# events)
- See `/src/Umbraco.Core/CLAUDE.md` → "2. Notification System (Event Handling)"
4. **Composer Pattern** (DI registration)
- See `/src/Umbraco.Core/CLAUDE.md` → "3. Composer Pattern (DI Registration)"
5. **Scoping Pattern** (Unit of Work)
- See `/src/Umbraco.Core/CLAUDE.md` → "5. Scoping Pattern (Unit of Work)"
6. **Attempt Pattern** (operation results)
- `Attempt<TResult, TStatus>` instead of exceptions
- Strongly-typed operation status enums
### Key Design Patterns Used
- **Repository Pattern** - Data access abstraction
- **Unit of Work** - Scoping for transactions
- **Builder Pattern** - `ProblemDetailsBuilder` for API errors
- **Strategy Pattern** - OpenAPI handlers (schema ID, operation ID)
- **Options Pattern** - All configuration via `IOptions<T>`
- **Factory Pattern** - Content type factories
- **Mediator Pattern** - Notification aggregator
---
## 5. Avoiding Breaking Changes
No binary breaking changes are allowed within a major version. Three patterns are used:
### 5.1 Obsolete Constructor + StaticServiceProvider
When a public class needs new dependencies, obsolete the existing constructor and add a new one. The old constructor delegates to the new one, resolving missing deps via `StaticServiceProvider`.
```csharp
[Obsolete("Please use the constructor with all parameters. Scheduled for removal in Umbraco 19.")]
public MyService(IDependencyA depA)
: this(
depA,
StaticServiceProvider.Instance.GetRequiredService<IDependencyB>())
{
}
public MyService(IDependencyA depA, IDependencyB depB)
{
_depA = depA;
_depB = depB;
}
```
**Examples**:
- `ContentCollectionPresentationFactory` - added `FlagProviderCollection`
- `CacheInstructionService` - added `ILastSyncedManager`, `IRepositoryCacheVersionService`
- `DocumentPresentationFactory` - added `FlagProviderCollection`
**Rules**:
- Old constructor marked `[Obsolete("... Scheduled for removal in Umbraco {current-major+2}.")]`
- Old constructor calls new constructor via `: this(...)`
- Uses `StaticServiceProvider.Instance.GetRequiredService<T>()` for new params only
- DI registration must use the NEW constructor (old is for external consumers only)
### 5.2 Obsolete Method + New Overload
When a public method signature needs to change, add the new method/overload and obsolete the old. The obsolete method should call the new one with suitable defaults.
```csharp
[Obsolete("Use the overload taking all parameters. Scheduled for removal in Umbraco 19.")]
public void DoThing(string name)
=> DoThing(name, extraParam: null);
public void DoThing(string name, string? extraParam)
{
// Real implementation here
}
```
**Rules**:
- Old method marked `[Obsolete]` with removal schedule
- DRY: old method calls new method, providing defaults for new parameters
- All internal callers must be updated to use the new method
- No callers should remain on the obsolete method within the codebase
### 5.3 Default Interface Implementation
When adding methods to a public interface, provide a default implementation so existing external implementations don't break.
```csharp
public interface IMyService
{
// Existing method
void ExistingMethod();
// New method with default implementation
void NewMethod(string param)
=> ExistingMethod(); // delegate to existing if possible
}
```
**Strategies for the default** (in order of preference):
1. **Use existing interface methods** to satisfy the contract (even if not optimal)
2. **Return a sensible default** like empty collection, null, etc.
3. **Throw `NotImplementedException`** if no reasonable default exists
**Example**: `IContentService.SaveBlueprint` - new overload with `IContent? createdFromContent` has a default impl that calls the old method (ignoring the new param).
**Example**: `IDocumentPresentationFactory.CreateCulturePublishScheduleModels` - full default implementation with logic, uses `StaticServiceProvider` for dependency resolution within the interface.
**Rules**:
- Add `// TODO (V{next-major}): Remove the default implementation when {obsolete method} is removed.` comment
- Default impl should be functionally correct even if not optimal
- If using `StaticServiceProvider` in a default impl, note this is temporary
### 5.4 General Rules
- **Removal policy**: Obsoleted members must remain for at least one full major version before removal. If obsoleted in version N, the earliest removal is version N+2. For example, something obsoleted in v17 is scheduled for removal in v19 (giving the whole of v18 as a deprecation period).
- All `[Obsolete]` attributes must include **"Scheduled for removal in Umbraco {current+2}"**
- Read `version.json` to determine the current major version
- Suppress `CS0618` warnings where obsolete members must call each other:
```csharp
#pragma warning disable CS0618 // Type or member is obsolete
=> OldMethod(param);
#pragma warning restore CS0618 // Type or member is obsolete
```
- Update ALL internal callers to use the new API - no internal code should use obsolete members
---
## 6. Project-Specific Notes
### Centralized Package Management
**All NuGet package versions** are centralized in `Directory.Packages.props`. Individual projects do NOT specify versions.
```xml
<!-- Individual projects reference WITHOUT version -->
<PackageReference Include="Swashbuckle.AspNetCore" />
<!-- Versions defined in Directory.Packages.props -->
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />
```
### Build Configuration
- `Directory.Build.props` - Shared properties (target framework, company, copyright)
- `.editorconfig` - Code style rules
- `.globalconfig` - Roslyn analyzer rules
### Persistence Layer - NPoco and EF Core
The repository contains BOTH (actively supported):
- **Current**: NPoco-based persistence (`Umbraco.Cms.Persistence.Sqlite`, `Umbraco.Cms.Persistence.SqlServer`) - widely used and fully supported
- **Future**: EF Core-based persistence (`Umbraco.Cms.Persistence.EFCore.*`) - migration in progress
**Note**: The codebase is actively migrating to EF Core, but NPoco remains the primary persistence layer and is not deprecated. Both are fully supported.
### Authentication: OpenIddict
All APIs use **OpenIddict** (OAuth 2.0/OpenID Connect):
- Reference tokens (not JWT) for better security
- **Secure cookie-based token storage** (v17+) - tokens stored in HTTP-only cookies with `__Host-` prefix
- Tokens are redacted from client-side responses and passed via secure cookies only
- ASP.NET Core Data Protection for token encryption
- Configured in `Umbraco.Cms.Api.Common`
- API requests must include credentials (`credentials: include` for fetch)
**Load Balancing Requirement**: All servers must share the same Data Protection key ring.
### Content Caching Strategy
**HybridCache** (`Umbraco.PublishedCache.HybridCache`):
- In-memory cache + distributed cache support
- Published content only (not draft)
- Invalidated via notifications and cache refreshers
### API Versioning
APIs use `Asp.Versioning.Mvc`:
- Management API: `/umbraco/management/api/v{version}/*`
- Delivery API: `/umbraco/delivery/api/v{version}/*`
- OpenAPI/Swagger docs per version
### Backoffice npm Package Structure
The backoffice (`Umbraco.Web.UI.Client`) is published to npm as **`@umbraco-cms/backoffice`** with a plugin architecture:
#### Architecture Overview
- **Multi-workspace structure**: Subprojects in `src/libs/*`, `src/packages/*`, `src/external/*`
- **Export model**: All exports defined in root `package.json` → `./exports` field
- **Importmap-driven runtime**: Dependencies provided at runtime via importmap (single source of truth)
- **Build-time types**: TypeScript types come from npm peerDependencies
- **Plugin model**: Developers create plugins that import from `@umbraco-cms/backoffice/*` exports
#### Dependency Hoisting Strategy
When building for npm (`npm pack`), the `cleanse-pkg.js` script hoists subproject dependencies to root `peerDependencies` with intelligent version range conversion:
**Version Range Logic** (uses `semver` package):
1. **Pre-release (0.x.y)**: Convert to explicit range
- Input: `^0.85.0` or `0.85.0`
- Output: `>=0.85.0 <1.0.0`
- Rationale: Pre-release caret only allows patch updates, explicit range allows minor upgrades within 0.x.x
- Example: Plugin can use `@hey-api/openapi-ts@0.91.1` while backoffice uses `0.85.0`
2. **Stable with caret (^X.Y.Z where X ≥ 1)**: Keep as-is
- Input: `^3.3.1`
- Output: `^3.3.1` (unchanged)
- Rationale: Caret already implements correct semantics for stable versions
3. **Stable exact versions (X.Y.Z where X ≥ 1)**: Add caret
- Input: `3.16.0`
- Output: `^3.16.0`
- Rationale: Normalizes to conventional semver format
#### Key Dependencies
**Runtime via importmap** (types available from peerDependencies):
- `lit`, `rxjs`, `@umbraco-ui/uui` - Core framework
- `monaco-editor`, `@tiptap/*` - Feature-specific editors
- `@hey-api/openapi-ts` - HTTP client type generation
**Build-time only** (not hoisted):
- `vite`, `typescript`, `eslint` - Dev tooling
#### Plugin Development Implications
Plugin developers should:
- **Declare explicit dependencies** in their own `package.json` (avoid relying on transitive deps)
- **Understand the version ranges**: `>=0.85.0 <1.0.0` means they can use newer pre-release versions
- **Know that types match npm ranges**, but runtime comes from importmap (managed by backoffice)
- **When `@hey-api` hits 1.0.0**: Published constraint will automatically become `^1.0.0`
#### Implementation Details
- Script location: `src/Umbraco.Web.UI.Client/devops/publish/cleanse-pkg.js`
- Runs as `prepack` hook before npm pack
- Uses `semver.minVersion()` for robust version range parsing
- Generates single source of truth for importmap versions
### Known Limitations
1. **Circular Dependencies**: Avoided via `Lazy<T>` or event notifications
2. **Multi-Server**: Requires shared Data Protection key ring and synchronized clocks (NTP)
3. **Database Support**: SQL Server, SQLite
---
## Quick Reference
### Essential Commands
```bash
# Build solution
dotnet build
# Run all tests
dotnet test
# Run specific test category
dotnet test --filter "Category=Integration"
# Format code
dotnet format
# Pack all projects
dotnet pack -c Release
```
### Key Projects
| Project | Type | Description |
|---------|------|-------------|
| **Umbraco.Core** | Library | Interface contracts and domain models |
| **Umbraco.Infrastructure** | Library | Service implementations and data access |
| **Umbraco.Web.UI** | Application | Main web application (Razor/MVC) |
| **Umbraco.Cms.Api.Management** | Library | Management API (backoffice) |
| **Umbraco.Cms.Api.Delivery** | Library | Delivery API (headless CMS) |
| **Umbraco.Cms.Api.Common** | Library | Shared API infrastructure |
| **Umbraco.PublishedCache.HybridCache** | Library | Published content caching |
| **Umbraco.Examine.Lucene** | Library | Full-text search indexing |
### Important Files
- **Solution**: `umbraco.sln`
- **Build Config**: `Directory.Build.props`, `Directory.Packages.props`
- **Code Style**: `.editorconfig`, `.globalconfig`
- **Documentation**: `/CLAUDE.md`, `/src/Umbraco.Core/CLAUDE.md`, `/src/Umbraco.Cms.Api.Common/CLAUDE.md`
### Project-Specific Documentation
For detailed information about individual projects, see their CLAUDE.md files:
- **Core Architecture**: `/src/Umbraco.Core/CLAUDE.md` - Service contracts, notification patterns
- **API Infrastructure**: `/src/Umbraco.Cms.Api.Common/CLAUDE.md` - OpenAPI, authentication, serialization
### Getting Help
- **Official Docs**: https://docs.umbraco.com/
- **Contributing Guide**: `.github/CONTRIBUTING.md`
- **Issues**: https://github.com/umbraco/Umbraco-CMS/issues
- **Community**: https://forum.umbraco.com/
- **Releases**: https://releases.umbraco.com/
---
**This repository follows a layered architecture with strict dependency rules. The Core defines contracts, Infrastructure implements them, and Web/APIs consume them. Each layer can be understood independently, but dependencies always flow inward toward Core.**
+2 -2
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Company>Umbraco HQ</Company>
<Authors>Umbraco</Authors>
<Copyright>Copyright © Umbraco $([System.DateTime]::Today.ToString('yyyy'))</Copyright>
@@ -41,7 +41,7 @@
<PropertyGroup>
<GenerateCompatibilitySuppressionFile>false</GenerateCompatibilitySuppressionFile>
<EnablePackageValidation>true</EnablePackageValidation>
<PackageValidationBaselineVersion>16.0.0</PackageValidationBaselineVersion>
<PackageValidationBaselineVersion>17.0.0</PackageValidationBaselineVersion>
<EnableStrictModeForCompatibleFrameworksInPackage>true</EnableStrictModeForCompatibleFrameworksInPackage>
<EnableStrictModeForCompatibleTfms>true</EnableStrictModeForCompatibleTfms>
</PropertyGroup>
+47 -59
View File
@@ -2,39 +2,39 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<!-- Global packages (private, build-time packages for all projects) -->
<ItemGroup>
<GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.7.115" />
<GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.9.50" />
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<GlobalPackageReference Include="Umbraco.Code" Version="2.3.0" />
<GlobalPackageReference Include="Umbraco.Code" Version="2.4.0" />
<GlobalPackageReference Include="Umbraco.GitVersioning.Extensions" Version="0.2.0" />
</ItemGroup>
<!-- Microsoft packages -->
<ItemGroup>
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.13.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.13.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.4.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="10.0.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="10.2.0" />
<PackageVersion Include="System.Linq.Async" Version="7.0.0" />
</ItemGroup>
<!-- Umbraco packages -->
<ItemGroup>
@@ -42,26 +42,27 @@
</ItemGroup>
<!-- Third-party packages -->
<ItemGroup>
<PackageVersion Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageVersion Include="Asp.Versioning.Mvc" Version="8.1.1" />
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.1" />
<PackageVersion Include="Dazinator.Extensions.FileProviders" Version="2.0.0" />
<PackageVersion Include="Examine" Version="3.7.1" />
<PackageVersion Include="Examine.Core" Version="3.7.1" />
<PackageVersion Include="HtmlAgilityPack" Version="1.12.1" />
<PackageVersion Include="HtmlAgilityPack" Version="1.12.4" />
<PackageVersion Include="JsonPatch.Net" Version="3.3.0" />
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.8" />
<PackageVersion Include="MailKit" Version="4.11.0" />
<PackageVersion Include="MailKit" Version="4.14.1" />
<PackageVersion Include="Markdig" Version="0.44.0" />
<PackageVersion Include="Markdown" Version="2.2.1" />
<PackageVersion Include="MessagePack" Version="3.1.3" />
<PackageVersion Include="MessagePack" Version="3.1.4" />
<PackageVersion Include="MiniProfiler.AspNetCore.Mvc" Version="4.5.4" />
<PackageVersion Include="MiniProfiler.Shared" Version="4.5.4" />
<PackageVersion Include="ncrontab" Version="3.3.3" />
<PackageVersion Include="NPoco" Version="5.7.1" />
<PackageVersion Include="NPoco.SqlServer" Version="5.7.1" />
<PackageVersion Include="OpenIddict.Abstractions" Version="6.2.1" />
<PackageVersion Include="OpenIddict.AspNetCore" Version="6.2.1" />
<PackageVersion Include="OpenIddict.EntityFrameworkCore" Version="6.2.1" />
<PackageVersion Include="Serilog" Version="4.2.0" />
<PackageVersion Include="ncrontab" Version="3.4.0" />
<PackageVersion Include="NPoco" Version="6.1.0" />
<PackageVersion Include="NPoco.SqlServer" Version="6.1.0" />
<PackageVersion Include="OpenIddict.Abstractions" Version="7.2.0" />
<PackageVersion Include="OpenIddict.AspNetCore" Version="7.2.0" />
<PackageVersion Include="OpenIddict.EntityFrameworkCore" Version="7.2.0" />
<PackageVersion Include="Serilog" Version="4.3.0" />
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="Serilog.Enrichers.Process" Version="3.0.0" />
<PackageVersion Include="Serilog.Enrichers.Thread" Version="4.0.0" />
@@ -71,33 +72,20 @@
<PackageVersion Include="Serilog.Formatting.Compact.Reader" Version="4.0.0" />
<PackageVersion Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageVersion Include="Serilog.Sinks.Map" Version="2.0.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.11" />
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.1.5" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="8.1.1" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.12" />
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.2.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="10.1.0" />
</ItemGroup>
<!-- Transitive pinned versions (only required because our direct dependencies have vulnerable versions of transitive dependencies) -->
<ItemGroup>
<!-- Microsoft.EntityFrameworkCore.SqlServer and NPoco.SqlServer brings in a vulnerable version of Azure.Identity -->
<!-- Take top-level depedendency on Azure.Identity, because Microsoft.EntityFrameworkCore.SqlServer depends on a vulnerable version -->
<PackageVersion Include="Azure.Identity" Version="1.13.2" />
<!-- Microsoft.EntityFrameworkCore.SqlServer brings in a vulnerable version of System.Runtime.Caching -->
<PackageVersion Include="System.Runtime.Caching" Version="9.0.4" />
<!-- Dazinator.Extensions.FileProviders brings in a vulnerable version of System.Net.Http -->
<!-- Dazinator.Extensions.FileProviders references vulnerable versions of the following: -->
<!-- TODO (V18): Remove these pinned dependencies when the Dazinator.Extensions.FileProviders dependency is removed. -->
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<!-- Examine brings in a vulnerable version of System.Security.Cryptography.Xml -->
<PackageVersion Include="System.Security.Cryptography.Xml" Version="9.0.4" />
<!-- Dazinator.Extensions.FileProviders and MiniProfiler.AspNetCore.Mvc brings in a vulnerable version of System.Text.RegularExpressions -->
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
<!-- Markdown references vulnerable version of the following: -->
<!-- TODO (V19): Remove these pinned dependencies when the Markdown dependency is removed. -->
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
<!-- OpenIddict.AspNetCore, Npoco.SqlServer and Microsoft.EntityFrameworkCore.SqlServer brings in a vulnerable version of Microsoft.IdentityModel.JsonWebTokens -->
<!-- Take top-level depedendency on Microsoft.IdentityModel.JsonWebTokens, because OpenIddict.AspNetCore, Npoco.SqlServer and Microsoft.EntityFrameworkCore.SqlServer depends on a vulnerable version -->
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.8.0" />
<!-- Azure.Identity, Microsoft.EntityFrameworkCore.SqlServer and Dazinator.Extensions.FileProviders brings in a legacy version of System.Text.Encodings.Web -->
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.4" />
<!-- NPoco.SqlServer brings in a vulnerable version of Microsoft.Data.SqlClient -->
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.0.1" />
<!-- Examine.Lucene brings in a vulnerable version of Lucene.Net.Replicator -->
<PackageVersion Include="Lucene.Net.Replicator" Version="4.8.0-beta00017" />
</ItemGroup>
</Project>
+144
View File
@@ -0,0 +1,144 @@
# MCP (Model Context Protocol) Setup
This repository includes configuration for [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers, enabling AI tooling integration for Umbraco CMS development workflows.
## Overview
MCP allows AI assistants (like Claude) to interact with external tools and services. This repository configures two MCP servers:
| Server | Purpose | Package |
|--------|---------|---------|
| **umbraco-cms** | Manage Umbraco content types, documents, and media | `@umbraco-cms/mcp-dev@17` |
| **playwright** | Browser automation for testing and debugging | `@playwright/mcp@latest` |
## Quick Start
### 1. Start Umbraco Locally
Ensure your local Umbraco instance is running at `https://localhost:44339` (or update the URL in your `.env.local`).
### 2. Configure Environment Variables
Copy the example environment file and customize it:
```bash
cp .env.example .env.local
```
Edit `.env.local` with your local settings:
```env
UMBRACO_CLIENT_ID=umbraco-back-office-mcp
UMBRACO_CLIENT_SECRET=<your-client-secret>
UMBRACO_BASE_URL=https://localhost:44339
NODE_TLS_REJECT_UNAUTHORIZED=0
UMBRACO_INCLUDE_TOOL_COLLECTIONS=data-type,document-type,document,media-type,media
```
### 3. Configure the OAuth Client in Umbraco
Create an OAuth client in your Umbraco instance with:
- **Client ID**: `umbraco-back-office-mcp`
- **Client Secret**: The value you set in `.env.local`
- **Grant Type**: Client Credentials
## Environment Variables Reference
| Variable | Description | Example |
|----------|-------------|---------|
| `UMBRACO_CLIENT_ID` | OAuth client ID configured in Umbraco | `umbraco-back-office-mcp` |
| `UMBRACO_CLIENT_SECRET` | OAuth client secret (keep secure!) | `your-secure-secret` |
| `UMBRACO_BASE_URL` | URL of your local Umbraco instance | `https://localhost:44339` |
| `NODE_TLS_REJECT_UNAUTHORIZED` | Set to `0` for self-signed certificates (local dev only) | `0` |
| `UMBRACO_INCLUDE_TOOL_COLLECTIONS` | Comma-separated list of tool collections to enable | `data-type,document-type,document` |
### Tool Collections
The `UMBRACO_INCLUDE_TOOL_COLLECTIONS` variable controls which Umbraco MCP tools are available:
- `data-type` - Manage data types (property editors)
- `document-type` - Manage document types (content types)
- `document` - Manage content/documents
- `media-type` - Manage media types
- `media` - Manage media items
## Security Considerations
> **Warning**: This configuration is for **local development only**.
### Self-Signed Certificates
`NODE_TLS_REJECT_UNAUTHORIZED=0` disables SSL certificate validation. This is necessary for self-signed certificates in local development but:
- **Never use in production**
- Affects all HTTPS connections made by Node.js processes
- Consider trusting your local development certificate instead
### Client Secrets
- Never commit real secrets to source control
- The `.env.local` file is gitignored for this reason
- Use strong, unique secrets even in development
- The example value `1234567890` in `.env.example` is a placeholder only
## File Structure
```
Umbraco-CMS/
├── .mcp.json # MCP server configuration
├── .env.example # Example environment variables (committed)
├── .env.local # Your local environment variables (gitignored)
├── .claude/
│ ├── settings.json # Shared Claude AI permissions (committed)
│ └── settings.local.json # Local Claude overrides (gitignored)
├── .gitignore # Ignores .env.local and settings.local.json
└── MCP.md # This documentation (you are here)
```
## Claude AI Permissions
The `.claude/settings.json` file configures which MCP tools Claude can use automatically without prompting. This is shared across the team for consistent developer experience.
### Customizing Permissions Locally
Create `.claude/settings.local.json` to override permissions for your environment:
```json
{
"permissions": {
"allow": [
"mcp__umbraco__get-all-document-types"
]
}
}
```
## Troubleshooting
### "Connection refused" errors
- Ensure Umbraco is running at the configured `UMBRACO_BASE_URL`
- Check that the port matches your local setup
### "Unauthorized" errors
- Verify the OAuth client is configured in Umbraco
- Check that `UMBRACO_CLIENT_ID` and `UMBRACO_CLIENT_SECRET` match
- Ensure the client has appropriate permissions
### "Certificate" errors
- For local development, set `NODE_TLS_REJECT_UNAUTHORIZED=0` in `.env.local`
- Alternatively, trust your local development certificate
### MCP server not starting
- Ensure Node.js is installed (v22+ recommended, matching .nvmrc)
- Run `npx @umbraco-cms/mcp-dev@17 --help` to verify the package works
## Further Reading
- [Model Context Protocol Documentation](https://modelcontextprotocol.io/)
- [Umbraco MCP Package](https://www.npmjs.com/package/@umbraco-cms/mcp-dev)
- [Playwright MCP](https://www.npmjs.com/package/@playwright/mcp)
- [Claude Code Documentation](https://docs.anthropic.com/claude-code)
+8
View File
@@ -196,6 +196,14 @@ Copyright: 2013-2024 .NET Foundation and Contributors
---
Markdig: A fast, powerful, CommonMark compliant, extensible Markdown processor for .NET
URL: https://github.com/xoofx/markdig
License: BSD-2-Clause license
Copyright: 2018+, Alexandre Mutel. All rights reserved.
---
Markdown: A library for parsing and compiling Markdown
URL: https://github.com/hey-red/Markdown
+30 -13
View File
@@ -107,6 +107,14 @@ stages:
command: build
projects: $(solution)
arguments: "--configuration $(buildConfiguration) --no-restore --property:ContinuousIntegrationBuild=true --property:GeneratePackageOnBuild=true --property:PackageOutputPath=$(Build.ArtifactStagingDirectory)/nupkg"
# Publish compiled DLLs for C# API documentation generation
# Separate artifact to avoid increasing build_output size for all builds
- task: PublishPipelineArtifact@1
displayName: Publish DocFX DLLs
condition: and(succeeded(), or(eq(variables['build.NBGV_PublicRelease'], 'True'), eq('${{ parameters.buildApiDocs }}', 'True')))
inputs:
targetPath: $(Build.SourcesDirectory)/src/Umbraco.Cms/bin/Release
artifactName: csharp-docs-dlls
- powershell: |
dotnet tool install --global CycloneDX
dotnet-CycloneDX $(solution) --output $(Build.ArtifactStagingDirectory)/bom --filename bom-dotnet.xml
@@ -200,12 +208,22 @@ stages:
variables:
umbracoMajorVersion: $[ stageDependencies.Build.A.outputs['build.NBGV_VersionMajor'] ]
jobs:
# C# API Reference
# C# API Reference - uses pre-compiled DLLs for faster generation (csproj approach caused timeouts)
- job:
displayName: Build C# API Reference
pool:
vmImage: "windows-latest"
steps:
- checkout: self
submodules: false
lfs: false
fetchDepth: 1
fetchFilter: tree:0
- task: DownloadPipelineArtifact@2
displayName: Download DocFX DLLs
inputs:
artifact: csharp-docs-dlls
path: $(Build.SourcesDirectory)/src/Umbraco.Cms/bin/Release
- task: UseDotNet@2
displayName: Use .NET SDK from global.json
inputs:
@@ -215,7 +233,7 @@ stages:
inputs:
targetType: inline
script: |
choco install docfx --version=2.59.4 -y
dotnet tool install -g docfx --version 2.78.4
if ($lastexitcode -ne 0){
throw ("Error installing DocFX")
}
@@ -365,8 +383,8 @@ stages:
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
LinuxPart3Of3:
vmImage: "ubuntu-latest"
# Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
macOSPart1Of3:
vmImage: "macOS-latest"
# Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace
@@ -377,8 +395,8 @@ stages:
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
macOSPart3Of3:
vmImage: "macOS-latest"
# Filter tests that are not part of the Umbraco.Infrastructure namespace.
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure and the ManagementApi namespace
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
pool:
vmImage: $(vmImage)
variables:
@@ -441,8 +459,8 @@ stages:
vmImage: "windows-latest"
Tests__Database__DatabaseType: LocalDb
Tests__Database__SQLServerMasterConnectionString: N/A
# Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
LinuxPart1Of3:
vmImage: "ubuntu-latest"
SA_PASSWORD: UmbracoIntegration123!
@@ -462,8 +480,8 @@ stages:
SA_PASSWORD: UmbracoIntegration123!
Tests__Database__DatabaseType: SqlServer
Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
# Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
pool:
vmImage: $(vmImage)
steps:
@@ -817,9 +835,7 @@ stages:
- stage: Deploy_NuGet
displayName: NuGet release
dependsOn:
- Deploy_MyGet
- Build_Docs
dependsOn: Deploy_MyGet
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.nuGetDeploy}}))
jobs:
- job:
@@ -879,6 +895,7 @@ stages:
displayName: Upload API Documentation
dependsOn:
- Build
- Build_Docs
- Deploy_NuGet
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.uploadApiDocs}}))
jobs:
+4 -8
View File
@@ -3,17 +3,13 @@
{
"src": [
{
"src": "../../src",
"src": "../../src/Umbraco.Cms/bin/Release",
"files": [
"**/*.csproj"
"**/Umbraco.*.dll"
],
"exclude": [
"**/obj/**",
"**/bin/**",
"**/Umbraco.Web.csproj",
"**/Umbraco.Web.UI.csproj",
"**/Umbraco.Cms.StaticAssets.csproj",
"**/JsonSchema.csproj"
"**/Umbraco.Cms.StaticAssets.dll",
"**/Umbraco.Cms.Targets.dll"
]
}
],
@@ -9,7 +9,7 @@
<meta name="generator" content="docfx {{_docfxVersion}}">
{{#_description}}<meta name="description" content="{{_description}}">{{/_description}}
<link rel="icon" type="image/png" href="https://our.umbraco.com/assets/images/app-icons/favicon.png">
<link rel="stylesheet" href="{{_rel}}styles/docfx.vendor.css">
<link rel="stylesheet" href="{{_rel}}styles/docfx.vendor.min.css">
<link rel="stylesheet" href="{{_rel}}styles/docfx.css">
<link rel="stylesheet" href="{{_rel}}styles/main.css">
<meta property="docfx:navrel" content="{{_navRel}}">
+9 -3
View File
@@ -54,11 +54,17 @@ steps:
- pwsh: |
$sourcePath = "$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/tests/${{ parameters.testFolder }}/AdditionalSetup"
$destinationPath = "UmbracoProject"
$csharpFiles = Get-ChildItem -Path $sourcePath -Filter "*.cs"
$csharpFiles = Get-ChildItem -Path $sourcePath -Filter "*.cs" -Recurse
if ($csharpFiles) {
$csharpFiles | ForEach-Object {
Write-Host "Copying: $($_.FullName)"
Copy-Item -Path $_.FullName -Destination $destinationPath -Force
$relativePath = $_.FullName.Substring($sourcePath.Length + 1)
$targetPath = Join-Path -Path $destinationPath -ChildPath $relativePath
$targetDir = Split-Path -Path $targetPath -Parent
if (-not (Test-Path -Path $targetDir)) {
New-Item -ItemType Directory -Path $targetDir -Force | Out-Null
}
Write-Host "Copying: $($_.FullName) -> $targetPath"
Copy-Item -Path $_.FullName -Destination $targetPath -Force
}
} else {
Write-Host "No C# files found."
+2 -2
View File
@@ -44,7 +44,7 @@ steps:
$cmsVersion = "$(Build.BuildNumber)" -replace "\+",".g"
dotnet new nugetconfig
dotnet nuget add source ./nupkg --name Local
dotnet new install Umbraco.Templates::$cmsVersion
dotnet new umbraco --name UmbracoProject --version $cmsVersion --exclude-gitignore --no-restore --no-update-check
dotnet new install Umbraco.Templates@$cmsVersion
dotnet new umbraco --name UmbracoProject --exclude-gitignore --no-restore --no-update-check
displayName: Install Template
workingDirectory: $(Agent.BuildDirectory)/app
+123 -29
View File
@@ -18,8 +18,8 @@ parameters:
type: boolean
default: false
- name: differentAppSettingsAcceptanceTests
displayName: Run acceptance tests with different app settings
- name: skipDifferentAppSettingsAcceptanceTests
displayName: Skip acceptance tests with different app settings
type: boolean
default: false
@@ -28,6 +28,12 @@ parameters:
type: boolean
default: false
# Can we slow our tests down when running on SQLite? That way we might be able to avoid DB locks
- name: skipSqliteAcceptanceTests
displayName: Skip SQLite acceptance tests
type: boolean
default: true
variables:
nodeVersion: 20
solution: umbraco.sln
@@ -123,31 +129,39 @@ stages:
matrix:
# Windows:
# vmImage: 'windows-latest'
# We split the tests into 3 parts for each OS to reduce the time it takes to run them on the pipeline
LinuxPart1Of3:
# We split the tests into 4 parts for each OS to reduce the time it takes to run them on the pipeline
LinuxPart1Of4:
vmImage: "ubuntu-latest"
# Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)"
LinuxPart2Of3:
LinuxPart2Of4:
vmImage: "ubuntu-latest"
# Filter tests that are part of the Umbraco.Infrastructure.Service namespace
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
LinuxPart3Of3:
LinuxPart3Of4:
vmImage: "ubuntu-latest"
# Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
macOSPart1Of3:
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
LinuxPart4Of4:
vmImage: "ubuntu-latest"
# Filter tests that are part of the ManagementApi namespace
testFilter: "(FullyQualifiedName~ManagementApi)"
macOSPart1Of4:
vmImage: "macOS-latest"
# Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)"
macOSPart2Of3:
macOSPart2Of4:
vmImage: "macOS-latest"
# Filter tests that are part of the Umbraco.Infrastructure.Service namespace
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
macOSPart3Of3:
macOSPart3Of4:
vmImage: "macOS-latest"
# Filter tests that are not part of the Umbraco.Infrastructure namespace.
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace.
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
macOSPart4Of4:
vmImage: "macOS-latest"
# Filter tests that are part of the ManagementApi namespace.
testFilter: "(FullyQualifiedName~ManagementApi)"
pool:
vmImage: $(vmImage)
variables:
@@ -187,43 +201,55 @@ stages:
SA_PASSWORD: UmbracoAcceptance123!
strategy:
matrix:
# We split the tests into 3 parts for each OS to reduce the time it takes to run them on the pipeline
WindowsPart1Of3:
# We split the tests into 4 parts for each OS to reduce the time it takes to run them on the pipeline
WindowsPart1Of4:
vmImage: "windows-latest"
Tests__Database__DatabaseType: LocalDb
Tests__Database__SQLServerMasterConnectionString: N/A
# Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)"
WindowsPart2Of3:
WindowsPart2Of4:
vmImage: "windows-latest"
Tests__Database__DatabaseType: LocalDb
Tests__Database__SQLServerMasterConnectionString: N/A
# Filter tests that are part of the Umbraco.Infrastructure.Service namespace
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
WindowsPart3Of3:
WindowsPart3Of4:
vmImage: "windows-latest"
Tests__Database__DatabaseType: LocalDb
Tests__Database__SQLServerMasterConnectionString: N/A
# Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
LinuxPart1Of3:
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace.
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
WindowsPart4Of4:
vmImage: "windows-latest"
Tests__Database__DatabaseType: LocalDb
Tests__Database__SQLServerMasterConnectionString: N/A
# Filter tests that are part of the ManagementApi namespace.
testFilter: "(FullyQualifiedName~ManagementApi)"
LinuxPart1Of4:
vmImage: "ubuntu-latest"
Tests__Database__DatabaseType: SqlServer
Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
# Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)"
LinuxPart2Of3:
LinuxPart2Of4:
vmImage: "ubuntu-latest"
Tests__Database__DatabaseType: SqlServer
Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
# Filter tests that are part of the Umbraco.Infrastructure.Service namespace
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
LinuxPart3Of3:
LinuxPart3Of4:
vmImage: "ubuntu-latest"
Tests__Database__DatabaseType: SqlServer
Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
# Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace.
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
LinuxPart4Of4:
vmImage: "ubuntu-latest"
Tests__Database__DatabaseType: SqlServer
Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
# Filter tests that are part of the ManagementApi namespace.
testFilter: "(FullyQualifiedName~ManagementApi)"
pool:
vmImage: $(vmImage)
steps:
@@ -322,7 +348,7 @@ stages:
- job:
displayName: E2E Tests (SQLite)
timeoutInMinutes: 180
condition: ${{ eq(parameters.skipDefaultConfigAcceptanceTests, false) }}
condition: ${{ and(eq(parameters.skipDefaultConfigAcceptanceTests, false), eq(parameters.skipSqliteAcceptanceTests, false)) }}
variables:
# Connection string
CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=Umbraco;Mode=Memory;Cache=Shared;Foreign Keys=True;Pooling=True
@@ -423,15 +449,15 @@ stages:
vmImage: "ubuntu-latest"
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
WindowsPart1Of3:
testCommand: "npm run test -- --shard=1/3"
testCommand: "npm run testWindows -- --shard=1/3"
testFolder: "DefaultConfig"
vmImage: "windows-latest"
WindowsPart2Of3:
testCommand: "npm run test -- --shard=2/3"
testCommand: "npm run testWindows -- --shard=2/3"
testFolder: "DefaultConfig"
vmImage: "windows-latest"
WindowsPart3Of3:
testCommand: "npm run test -- --shard=3/3"
testCommand: "npm run testWindows -- --shard=3/3"
testFolder: "DefaultConfig"
vmImage: "windows-latest"
pool:
@@ -485,7 +511,7 @@ stages:
jobs:
- job:
displayName: E2E Tests with Different App settings (SQL Server)
condition: ${{ or(eq(parameters.differentAppSettingsAcceptanceTests, true), eq(parameters.skipDefaultConfigAcceptanceTests, true)) }}
condition: ${{ eq(parameters.skipDifferentAppSettingsAcceptanceTests, false) }}
timeoutInMinutes: 180
variables:
SA_PASSWORD: UmbracoAcceptance123!
@@ -544,6 +570,49 @@ stages:
CONNECTIONSTRINGS__UMBRACODBDSN: Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
additionalEnvironmentVariables: false
# EntityDataPicker
WindowsEntityDataPicker:
vmImage: "windows-latest"
testFolder: "EntityDataPicker"
port: ''
testCommand: "npx playwright test --project=entityDataPicker"
CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
additionalEnvironmentVariables: false
LinuxEntityDataPicker:
vmImage: "ubuntu-latest"
testFolder: "EntityDataPicker"
port: ''
testCommand: "npx playwright test --project=entityDataPicker"
CONNECTIONSTRINGS__UMBRACODBDSN: Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
additionalEnvironmentVariables: false
# ContentSettingConfig
WindowsContentSettingsConfig:
vmImage: "windows-latest"
testFolder: "ContentSettingConfig"
port: ''
testCommand: "npx playwright test --project=contentSettingConfig"
CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
additionalEnvironmentVariables: false
LinuxContentSettingsConfig:
vmImage: "ubuntu-latest"
testFolder: "ContentSettingConfig"
port: ''
testCommand: "npx playwright test --project=contentSettingConfig"
CONNECTIONSTRINGS__UMBRACODBDSN: Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
additionalEnvironmentVariables: false
# SMTP
LinuxSMTP:
vmImage: "ubuntu-latest"
testFolder: "SMTP"
port: ''
testCommand: "npx playwright test --project=smtp"
CONNECTIONSTRINGS__UMBRACODBDSN: Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
additionalEnvironmentVariables: false
pool:
vmImage: $(vmImage)
steps:
@@ -621,6 +690,23 @@ stages:
AZUREADB2CCLIENTID: $(AZUREB2CCLIENTID)
AZUREADB2CCLIENTSECRET: $(AZUREB2CCLIENTSECRET)
# Start SMTP4dev via Docker for SMTP tests
- bash: |
echo "Starting SMTP4dev container..."
docker run -d --name smtp4dev -p 5000:80 -p 25:25 rnwood/smtp4dev
echo "Waiting for SMTP4dev to be ready..."
for i in {1..30}; do
if curl -s http://localhost:5000/api/messages > /dev/null; then
echo "SMTP4dev is ready"
break
fi
echo "Attempt $i: Waiting for SMTP4dev..."
sleep 2
done
displayName: Start SMTP4dev Docker container (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), contains(variables['testFolder'], 'SMTP'))
# Run tests Template
- template: nightly-E2E-run-tests-template.yml
parameters:
@@ -631,6 +717,14 @@ stages:
AZUREB2CTESTUSERPASSWORD: $(AZUREB2CTESTUSERPASSWORD)
DatabaseType: ${{ variables.DatabaseType }}
# Stop SMTP4dev container
- bash: |
echo "Stopping SMTP4dev container..."
docker stop smtp4dev
docker rm smtp4dev
displayName: Stop SMTP4dev Docker container
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), contains(variables['testFolder'], 'SMTP'))
- stage: NotifySlackBot
displayName: Notify Slack on Failure
dependsOn: DefaultConfigE2E
+1 -1
View File
@@ -9,7 +9,7 @@ schedules:
branches:
include:
- v13/dev
- v17/dev
- v16/dev
- main
steps:
+12 -13
View File
@@ -13,12 +13,12 @@ jobs:
- checkout: none
- bash: |
project_id=$(curl --no-progress-meter -H "X-Api-Key: $(DT_API_KEY)" "$(DT_API_URL)/v1/project/lookup?name=${{ parameters.projectName }}&version=${{ parameters.umbracoVersion }}" | jq -r '.uuid')
project_id=$(curl --no-progress-meter -H "X-Api-Key: $(DT_API_KEY)" "$(DT_API_URI)/api/v1/project/lookup?name=${{ parameters.projectName }}&version=${{ parameters.umbracoVersion }}" | jq -r '.uuid')
if [ "$project_id" != "null" ] && [ -n "$project_id" ]; then
echo "Project '${{ parameters.projectName }}' with version '${{ parameters.umbracoVersion }}' already exists (ID: $project_id)."
else
project_id=$(curl --no-progress-meter \
-X PUT "$(DT_API_URL)/v1/project" \
-X PUT "$(DT_API_URI)/api/v1/project" \
-H "X-Api-Key: $(DT_API_KEY)" \
-H "Content-Type: application/json" \
-d '{"name": "${{ parameters.projectName }}", "version": "${{ parameters.umbracoVersion }}", "collectionLogic": "AGGREGATE_DIRECT_CHILDREN"}' \
@@ -42,15 +42,14 @@ jobs:
artifact: ${{ project.artifact }}
displayName: Download ${{ project.artifact }} artifact
- script: |
curl --no-progress-meter --fail-with-body \
-X POST "$(DT_API_URL)/v1/bom" \
-H "X-Api-Key: $(DT_API_KEY)" \
-H "Content-Type: multipart/form-data" \
-F "autoCreate=true" \
-F "projectName=${{ parameters.projectName }}-${{ project.name }}" \
-F "projectVersion=${{ parameters.umbracoVersion }}" \
-F "parentName=${{ parameters.projectName }}" \
-F "parentVersion=${{ parameters.umbracoVersion }}" \
-F "bom=@$(Pipeline.Workspace)/${{ project.artifact }}/${{ project.bomFilePath }}"
- task: upload-bom-dtrack@1
inputs:
dtrackURI: $(DT_API_URI)
dtrackAPIKey: $(DT_API_KEY)
dtrackProjAutoCreate: true
dtrackProjName: '${{ parameters.projectName }}-${{ project.name }}'
dtrackProjVersion: ${{ parameters.umbracoVersion }}
dtrackParentProjName: ${{ parameters.projectName }}
dtrackParentProjVersion: ${{ parameters.umbracoVersion }}
bomFilePath: '$(Pipeline.Workspace)/${{ project.artifact }}/${{ project.bomFilePath }}'
displayName: Upload ${{ project.name }} BOM to Dependency Track
+4
View File
@@ -0,0 +1,4 @@
{
"url": "https://context7.com/umbraco/umbraco-cms",
"public_key": "pk_GTIgsrGAQiHNxCirZBDIM"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "9.0.306",
"version": "10.0.100",
"rollForward": "latestFeature",
"allowPrerelease": false
}
@@ -1,10 +1,17 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using Umbraco.Cms.Core.DeliveryApi;
namespace Umbraco.Cms.Api.Common.Accessors;
/// <summary>
/// Provides access to the <see cref="IOutputExpansionStrategy"/> for the current HTTP request context.
/// </summary>
public sealed class RequestContextOutputExpansionStrategyAccessor : RequestContextServiceAccessorBase<IOutputExpansionStrategy>, IOutputExpansionStrategyAccessor
{
/// <summary>
/// Initializes a new instance of the <see cref="RequestContextOutputExpansionStrategyAccessor"/> class.
/// </summary>
/// <param name="httpContextAccessor">The HTTP context accessor.</param>
public RequestContextOutputExpansionStrategyAccessor(IHttpContextAccessor httpContextAccessor)
: base(httpContextAccessor)
{
@@ -1,17 +1,30 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace Umbraco.Cms.Api.Common.Accessors;
/// <summary>
/// Base class for accessing request-scoped services from the current HTTP context.
/// </summary>
/// <typeparam name="T">The type of service to access.</typeparam>
public abstract class RequestContextServiceAccessorBase<T>
where T : class
{
private readonly IHttpContextAccessor _httpContextAccessor;
/// <summary>
/// Initializes a new instance of the <see cref="RequestContextServiceAccessorBase{T}"/> class.
/// </summary>
/// <param name="httpContextAccessor">The HTTP context accessor.</param>
protected RequestContextServiceAccessorBase(IHttpContextAccessor httpContextAccessor)
=> _httpContextAccessor = httpContextAccessor;
/// <summary>
/// Attempts to retrieve the service from the current HTTP context's request services.
/// </summary>
/// <param name="requestStartNodeService">When this method returns, contains the service instance if found; otherwise, <c>null</c>.</param>
/// <returns><c>true</c> if the service was found; otherwise, <c>false</c>.</returns>
public bool TryGetValue([NotNullWhen(true)] out T? requestStartNodeService)
{
requestStartNodeService = _httpContextAccessor.HttpContext?.RequestServices.GetService<T>();
@@ -1,9 +1,19 @@
namespace Umbraco.Cms.Api.Common.Attributes;
/// <summary>
/// Attribute used to map a class to a specific API for OpenAPI documentation generation.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class MapToApiAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="MapToApiAttribute"/> class.
/// </summary>
/// <param name="apiName">The name of the API to map to.</param>
public MapToApiAttribute(string apiName) => ApiName = apiName;
/// <summary>
/// Gets the name of the API this class is mapped to.
/// </summary>
public string ApiName { get; }
}
@@ -1,9 +1,12 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Common.Builders;
/// <summary>
/// A fluent builder for creating RFC 7807 <see cref="ProblemDetails"/> responses.
/// </summary>
public class ProblemDetailsBuilder
{
private string? _title;
@@ -12,24 +15,45 @@ public class ProblemDetailsBuilder
private string? _operationStatus;
private IDictionary<string, object>? _extensions;
/// <summary>
/// Sets the title of the problem details.
/// </summary>
/// <param name="title">A short, human-readable summary of the problem type.</param>
/// <returns>The current builder instance for method chaining.</returns>
public ProblemDetailsBuilder WithTitle(string title)
{
_title = title;
return this;
}
/// <summary>
/// Sets the detail of the problem details.
/// </summary>
/// <param name="detail">A human-readable explanation specific to this occurrence of the problem.</param>
/// <returns>The current builder instance for method chaining.</returns>
public ProblemDetailsBuilder WithDetail(string detail)
{
_detail = detail;
return this;
}
/// <summary>
/// Sets the type of the problem details.
/// </summary>
/// <param name="type">A URI reference that identifies the problem type.</param>
/// <returns>The current builder instance for method chaining.</returns>
public ProblemDetailsBuilder WithType(string type)
{
_type = type;
return this;
}
/// <summary>
/// Sets the operation status from an enum value.
/// </summary>
/// <typeparam name="TEnum">The enum type representing operation statuses.</typeparam>
/// <param name="operationStatus">The operation status enum value.</param>
/// <returns>The current builder instance for method chaining.</returns>
public ProblemDetailsBuilder WithOperationStatus<TEnum>(TEnum operationStatus)
where TEnum : Enum
{
@@ -37,9 +61,20 @@ public class ProblemDetailsBuilder
return this;
}
/// <summary>
/// Adds request model validation errors to the problem details.
/// </summary>
/// <param name="errors">A dictionary of field names to error messages.</param>
/// <returns>The current builder instance for method chaining.</returns>
public ProblemDetailsBuilder WithRequestModelErrors(IDictionary<string, string[]> errors)
=> WithExtension(nameof(HttpValidationProblemDetails.Errors).ToFirstLowerInvariant(), errors);
/// <summary>
/// Adds a custom extension to the problem details.
/// </summary>
/// <param name="key">The extension key.</param>
/// <param name="value">The extension value.</param>
/// <returns>The current builder instance for method chaining.</returns>
public ProblemDetailsBuilder WithExtension(string key, object value)
{
_extensions ??= new Dictionary<string, object>();
@@ -47,6 +82,10 @@ public class ProblemDetailsBuilder
return this;
}
/// <summary>
/// Builds the <see cref="ProblemDetails"/> instance with all configured values.
/// </summary>
/// <returns>A new <see cref="ProblemDetails"/> instance.</returns>
public ProblemDetails Build()
{
var problemDetails = new ProblemDetails
+371
View File
@@ -0,0 +1,371 @@
# Umbraco.Cms.Api.Common
Shared infrastructure for Umbraco CMS REST APIs (Management and Delivery).
---
## 1. Architecture
**Type**: Class Library (NuGet Package)
**Target Framework**: .NET 10.0
**Purpose**: Common API infrastructure - OpenAPI/Swagger, JSON serialization, OpenIddict authentication, problem details
### Key Technologies
- **ASP.NET Core** - Web framework
- **Swashbuckle** - OpenAPI/Swagger documentation generation
- **OpenIddict** - OAuth 2.0/OpenID Connect authentication
- **Asp.Versioning** - API versioning
- **System.Text.Json** - Polymorphic JSON serialization
### Dependencies
- `Umbraco.Core` - Domain models and service contracts
- `Umbraco.Web.Common` - Web functionality
### Project Structure (46 files)
```
Umbraco.Cms.Api.Common/
├── OpenApi/ # Schema/Operation ID handlers for Swagger
│ ├── SchemaIdHandler.cs # Generates schema IDs (e.g., "PagedUserModel")
│ ├── OperationIdHandler.cs # Generates operation IDs
│ └── SubTypesHandler.cs # Polymorphism support
├── Serialization/ # JSON type resolution
│ └── UmbracoJsonTypeInfoResolver.cs
├── Configuration/ # Options configuration
│ ├── ConfigureUmbracoSwaggerGenOptions.cs
│ └── ConfigureOpenIddict.cs
├── DependencyInjection/ # Service registration
│ ├── UmbracoBuilderApiExtensions.cs
│ └── UmbracoBuilderAuthExtensions.cs
├── Builders/ # RFC 7807 problem details
│ └── ProblemDetailsBuilder.cs
├── ViewModels/Pagination/ # Common DTOs
└── Security/ # Auth paths and handlers
```
### Design Patterns
1. **Strategy Pattern** - `ISchemaIdHandler`, `IOperationIdHandler` (extensible via inheritance)
2. **Builder Pattern** - `ProblemDetailsBuilder` for fluent error responses
3. **Options Pattern** - All configuration via `IConfigureOptions<T>`
---
## 2. Commands
See "Quick Reference" section at bottom for common commands.
---
## 3. Key Patterns
### Virtual Handlers for Extensibility
Handlers are intentionally virtual to allow consuming APIs to override:
```csharp
// NOTE: Left unsealed on purpose, so it is extendable.
public class SchemaIdHandler : ISchemaIdHandler
{
public virtual bool CanHandle(Type type) { }
public virtual string Handle(Type type) { }
}
```
**Why**: Management and Delivery APIs can customize schema/operation ID generation.
### Schema ID Sanitization (OpenApi/SchemaIdHandler.cs:24-29, 32)
```csharp
// Add "Model" suffix to avoid TypeScript name clashes (lines 24-29)
if (name.EndsWith("Model") == false)
{
// because some models names clash with common classes in TypeScript (i.e. Document),
// we need to add a "Model" postfix to all models
name = $"{name}Model";
}
// Remove invalid characters to prevent OpenAPI generation errors (line 32)
return Regex.Replace(name, @"[^\w]", string.Empty);
```
### Polymorphic Deserialization (Serialization/UmbracoJsonTypeInfoResolver.cs:29-35)
```csharp
// IMPORTANT: do NOT return an empty enumerable here. it will cause nullability to fail on reference
// properties, because "$ref" does not mix and match well with "nullable" in OpenAPI.
if (type.IsInterface is false)
{
return new[] { type };
}
```
**Why**: Interfaces must return concrete types to avoid OpenAPI schema conflicts.
---
## 4. Testing
**Location**: No direct tests - tested via integration tests in consuming APIs
**How to test changes**:
```bash
# Run integration tests that exercise this library
dotnet test tests/Umbraco.Tests.Integration/
# Verify OpenAPI generation
# 1. Run Management API
# 2. Navigate to /umbraco/swagger/
# 3. Check schema IDs and operation IDs
```
**Focus areas when testing**:
- OpenAPI document generation (schema IDs, operation IDs)
- Polymorphic JSON serialization/deserialization
- OpenIddict authentication flow
- Problem details formatting
---
## 5. OpenIddict Authentication
### Key Configuration (DependencyInjection/UmbracoBuilderAuthExtensions.cs)
**Reference Tokens over JWT** (line 76-80):
```csharp
// Enable reference tokens
// - see https://documentation.openiddict.com/configuration/token-storage.html
options
.UseReferenceAccessTokens()
.UseReferenceRefreshTokens();
```
**Why**: More secure (revocable), better for load balancing, uses ASP.NET Core Data Protection.
**Token Lifetime** (line 88-91):
```csharp
// Make the access token lifetime 25% of the refresh token lifetime
options.SetAccessTokenLifetime(new TimeSpan(timeOut.Ticks / 4));
options.SetRefreshTokenLifetime(timeOut);
```
**PKCE Required** (line 59-63):
```csharp
// Enable authorization code flow with PKCE
options
.AllowAuthorizationCodeFlow()
.RequireProofKeyForCodeExchange()
.AllowRefreshTokenFlow();
```
**Endpoints**: Backoffice `/umbraco/management/api/v1/security/*`, Member `/umbraco/member/api/v1/security/*`
### Secure Cookie-Based Token Storage (v17+)
**Implementation** (DependencyInjection/HideBackOfficeTokensHandler.cs):
Back-office tokens are hidden from client-side JavaScript via HTTP-only cookies:
```csharp
private const string AccessTokenCookieKey = "__Host-umbAccessToken";
private const string RefreshTokenCookieKey = "__Host-umbRefreshToken";
// Tokens are encrypted via Data Protection and stored in cookies
SetCookie(httpContext, AccessTokenCookieKey, context.Response.AccessToken);
context.Response.AccessToken = "[redacted]"; // Client sees redacted value
```
**Key Security Features** (lines 143-165): `HttpOnly`, `IsEssential`, `Path="/"`, `Secure` (HTTPS), `__Host-` prefix
**Configuration**: `BackOfficeTokenCookieSettings.Enabled` (default: true in v17+)
**Implications**: Client-side cannot access tokens; encrypted with Data Protection; load balancing needs shared key ring; API requests need `credentials: include`
---
## 6. Common Issues & Edge Cases
### Polymorphic Deserialization Requires `$type`
**Issue**: Deserializing to an interface without `$type` discriminator fails.
**Handled in** (Json/NamedSystemTextJsonInputFormatter.cs:24-29):
```csharp
catch (NotSupportedException exception)
{
// This happens when trying to deserialize to an interface, without sending the $type as part of the request
context.ModelState.TryAddModelException(string.Empty, new InputFormatterException(exception.Message, exception));
return await InputFormatterResult.FailureAsync();
}
```
**Solution**: Clients must include `$type` property for interface types, or use concrete types.
### Schema ID Collisions with TypeScript
**Issue**: Type names like `Document` clash with TypeScript built-ins.
**Solution**: Add "Model" suffix (OpenApi/SchemaIdHandler.cs:24-29)
### Generic Type Handling
**Issue**: `PagedViewModel<T>` needs flattened schema name.
**Solution** (OpenApi/SchemaIdHandler.cs:41-50):
```csharp
private string HandleGenerics(string name, Type type)
{
if (!type.IsGenericType)
return name;
// use attribute custom name or append the generic type names
// turns "PagedViewModel<RelationItemViewModel>" into "PagedRelationItem"
return $"{name}{string.Join(string.Empty, type.GenericTypeArguments.Select(SanitizedTypeName))}";
}
```
---
## 7. Extending This Library
### Adding a Custom OpenAPI Handler
1. **Implement interface**:
```csharp
public class MySchemaIdHandler : SchemaIdHandler
{
public override bool CanHandle(Type type)
=> type.Namespace?.StartsWith("MyProject") is true;
public override string Handle(Type type)
=> $"My{base.Handle(type)}";
}
```
2. **Register in consuming API**:
```csharp
builder.Services.AddSingleton<ISchemaIdHandler, MySchemaIdHandler>();
```
**Note**: Handlers registered later take precedence in the selector.
### Customizing Problem Details
```csharp
var problemDetails = new ProblemDetailsBuilder()
.WithTitle("Validation Failed")
.WithDetail("The request contains errors")
.WithType("ValidationError")
.WithOperationStatus(MyOperationStatus.ValidationFailed)
.WithRequestModelErrors(errors)
.Build();
return BadRequest(problemDetails);
```
---
## 8. Project-Specific Notes
### Why Virtual Handlers?
**Decision**: Make `SchemaIdHandler`, `OperationIdHandler`, etc. virtual.
**Why**: Management API and Delivery API have different schema ID requirements. Virtual methods allow override without rewriting the entire handler.
**Example**: Management API might prefix all schemas with "Management", Delivery API with "Delivery".
### Performance: Subtype Caching
**Why**: Cache discovered subtypes (UmbracoJsonTypeInfoResolver.cs:14) to avoid expensive reflection calls
### Known Limitations
1. **Polymorphic Deserialization**:
- Requires `$type` discriminator in JSON for interfaces
- Only discovers types in Umbraco namespaces
- Not all .NET types are discoverable
2. **OpenAPI Schema Generation**:
- Generic types are flattened (e.g., `PagedViewModel<T>` → `PagedTModel`)
- Type names may need "Model" suffix to avoid clashes
3. **OpenIddict Multi-Server**:
- Requires shared Data Protection key ring
- All servers must have synchronized clocks (NTP)
- Reference tokens require database storage
### External Dependencies
**OpenIddict**:
- OAuth 2.0 / OpenID Connect provider
- Version: See `Directory.Packages.props`
- Uses ASP.NET Core Data Protection for token encryption
**Swashbuckle**:
- OpenAPI 3.0 document generation
- Custom filters: `EnumSchemaFilter`, `MimeTypeDocumentFilter`, `RemoveSecuritySchemesDocumentFilter`
**Asp.Versioning**:
- API versioning via `ApiVersion` attribute
- API explorer integration for multi-version Swagger docs
### Configuration
**HTTPS**: `DisableTransportSecurityRequirement` for local dev only (ConfigureOpenIddict.cs:14). **Warning**: Never disable in production.
### Usage Pattern
Consuming APIs call `builder.AddUmbracoApiOpenApiUI().AddUmbracoOpenIddict()`
---
## Quick Reference
### Essential Commands
```bash
# Build project
dotnet build src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj
# Pack for NuGet
dotnet pack src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj -c Release
# Test via integration tests
dotnet test tests/Umbraco.Tests.Integration/
# Check packages
dotnet list src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj package --outdated
dotnet list src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj package --vulnerable
```
### Key Classes
| Class | Purpose | File |
|-------|---------|------|
| `ProblemDetailsBuilder` | Build RFC 7807 error responses | Builders/ProblemDetailsBuilder.cs |
| `SchemaIdHandler` | Generate OpenAPI schema IDs | OpenApi/SchemaIdHandler.cs |
| `UmbracoJsonTypeInfoResolver` | Polymorphic JSON serialization | Serialization/UmbracoJsonTypeInfoResolver.cs |
| `UmbracoBuilderAuthExtensions` | Configure OpenIddict | DependencyInjection/UmbracoBuilderAuthExtensions.cs |
| `HideBackOfficeTokensHandler` | Secure cookie-based token storage | DependencyInjection/HideBackOfficeTokensHandler.cs |
| `PagedViewModel<T>` | Generic pagination model | ViewModels/Pagination/PagedViewModel.cs |
### Important Files
- `Umbraco.Cms.Api.Common.csproj` - Project dependencies
- `DependencyInjection/UmbracoBuilderApiExtensions.cs` - OpenAPI registration (line 12-31)
- `DependencyInjection/UmbracoBuilderAuthExtensions.cs` - OpenIddict setup (line 20-183)
- `Security/Paths.cs` - API endpoint path constants
### Getting Help
- **Root documentation**: `/CLAUDE.md` - Repository overview
- **Core patterns**: `/src/Umbraco.Core/CLAUDE.md` - Core contracts and patterns
- **Official docs**: https://docs.umbraco.com/
- **OpenIddict docs**: https://documentation.openiddict.com/
---
**This library is the foundation for all Umbraco CMS REST APIs. Focus on OpenAPI customization, authentication configuration, and polymorphic serialization when working here.**
@@ -1,10 +1,14 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
namespace Umbraco.Cms.Api.Common.Configuration;
/// <summary>
/// Configures <see cref="ApiBehaviorOptions"/> for Umbraco APIs.
/// </summary>
public class ConfigureApiBehaviorOptions : IConfigureOptions<ApiBehaviorOptions>
{
/// <inheritdoc/>
public void Configure(ApiBehaviorOptions options) =>
// disable ProblemDetails as default result type for every non-success response (i.e. 404)
// - see https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.apibehavioroptions.suppressmapclienterrors
@@ -5,12 +5,21 @@ using Umbraco.Cms.Api.Common.Json;
namespace Umbraco.Cms.Api.Common.Configuration;
/// <summary>
/// Configures <see cref="MvcOptions"/> with named JSON input and output formatters for Umbraco APIs.
/// </summary>
public class ConfigureMvcJsonOptions : IConfigureOptions<MvcOptions>
{
private readonly string _jsonOptionsName;
private readonly IOptionsMonitor<JsonOptions> _jsonOptions;
private readonly ILoggerFactory _loggerFactory;
/// <summary>
/// Initializes a new instance of the <see cref="ConfigureMvcJsonOptions"/> class.
/// </summary>
/// <param name="jsonOptionsName">The name of the JSON options configuration to use.</param>
/// <param name="jsonOptions">The JSON options monitor.</param>
/// <param name="loggerFactory">The logger factory.</param>
public ConfigureMvcJsonOptions(
string jsonOptionsName,
IOptionsMonitor<JsonOptions> jsonOptions,
@@ -21,6 +30,7 @@ public class ConfigureMvcJsonOptions : IConfigureOptions<MvcOptions>
_loggerFactory = loggerFactory;
}
/// <inheritdoc/>
public void Configure(MvcOptions options)
{
JsonOptions jsonOptions = _jsonOptions.Get(_jsonOptionsName);
@@ -4,12 +4,24 @@ using Umbraco.Cms.Core.Configuration.Models;
namespace Umbraco.Cms.Api.Common.Configuration;
/// <summary>
/// Configures OpenIddict server options for Umbraco authentication.
/// </summary>
/// <remarks>
/// Disables transport security requirement when HTTPS is not configured in global settings.
/// Warning: This should only be used in development environments.
/// </remarks>
internal sealed class ConfigureOpenIddict : IConfigureOptions<OpenIddictServerAspNetCoreOptions>
{
private readonly IOptions<GlobalSettings> _globalSettings;
/// <summary>
/// Initializes a new instance of the <see cref="ConfigureOpenIddict"/> class.
/// </summary>
/// <param name="globalSettings">The global settings options.</param>
public ConfigureOpenIddict(IOptions<GlobalSettings> globalSettings) => _globalSettings = globalSettings;
/// <inheritdoc/>
public void Configure(OpenIddictServerAspNetCoreOptions options)
=> options.DisableTransportSecurityRequirement = _globalSettings.Value.UseHttps is false;
}
@@ -1,32 +1,62 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;
using Umbraco.Cms.Api.Common.OpenApi;
using Umbraco.Extensions;
using Umbraco.Cms.Core.DependencyInjection;
namespace Umbraco.Cms.Api.Common.Configuration;
/// <summary>
/// Configures Swagger/OpenAPI generation options for Umbraco APIs.
/// </summary>
public class ConfigureUmbracoSwaggerGenOptions : IConfigureOptions<SwaggerGenOptions>
{
private readonly IOperationIdSelector _operationIdSelector;
private readonly ISchemaIdSelector _schemaIdSelector;
private readonly ISubTypesSelector _subTypesSelector;
private readonly IDocumentInclusionSelector _documentInclusionSelector;
/// <summary>
/// Initializes a new instance of the <see cref="ConfigureUmbracoSwaggerGenOptions"/> class.
/// </summary>
/// <param name="operationIdSelector">The operation ID selector.</param>
/// <param name="schemaIdSelector">The schema ID selector.</param>
/// <param name="subTypesSelector">The sub-types selector for polymorphism support.</param>
/// <param name="documentInclusionSelector">The document inclusion selector.</param>
public ConfigureUmbracoSwaggerGenOptions(
IOperationIdSelector operationIdSelector,
ISchemaIdSelector schemaIdSelector,
ISubTypesSelector subTypesSelector)
ISubTypesSelector subTypesSelector,
IDocumentInclusionSelector documentInclusionSelector)
{
_operationIdSelector = operationIdSelector;
_schemaIdSelector = schemaIdSelector;
_subTypesSelector = subTypesSelector;
_documentInclusionSelector = documentInclusionSelector;
}
/// <summary>
/// Initializes a new instance of the <see cref="ConfigureUmbracoSwaggerGenOptions"/> class.
/// </summary>
/// <param name="operationIdSelector">The operation ID selector.</param>
/// <param name="schemaIdSelector">The schema ID selector.</param>
/// <param name="subTypesSelector">The sub-types selector for polymorphism support.</param>
[Obsolete("Please use the constructor with all parameters. Scheduled for removal in Umbraco 19.")]
public ConfigureUmbracoSwaggerGenOptions(
IOperationIdSelector operationIdSelector,
ISchemaIdSelector schemaIdSelector,
ISubTypesSelector subTypesSelector)
: this(
operationIdSelector,
schemaIdSelector,
subTypesSelector,
StaticServiceProvider.Instance.GetRequiredService<IDocumentInclusionSelector>())
{
}
/// <inheritdoc/>
public void Configure(SwaggerGenOptions swaggerGenOptions)
{
swaggerGenOptions.SwaggerDoc(
@@ -39,19 +69,11 @@ public class ConfigureUmbracoSwaggerGenOptions : IConfigureOptions<SwaggerGenOpt
});
swaggerGenOptions.CustomOperationIds(description => _operationIdSelector.OperationId(description));
swaggerGenOptions.DocInclusionPredicate((name, api) =>
{
if (api.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor
&& controllerActionDescriptor.HasMapToApiAttribute(name))
{
return true;
}
ApiVersionMetadata apiVersionMetadata = api.ActionDescriptor.GetApiVersionMetadata();
return apiVersionMetadata.Name == name
|| (string.IsNullOrEmpty(apiVersionMetadata.Name) && name == DefaultApiConfiguration.ApiName);
});
swaggerGenOptions.TagActionsBy(api => new[] { api.GroupName });
swaggerGenOptions.DocInclusionPredicate(_documentInclusionSelector.Include);
swaggerGenOptions.TagActionsBy(api =>
api.GroupName is null
? []
: new[] { api.GroupName });
swaggerGenOptions.OrderActionsBy(ActionOrderBy);
swaggerGenOptions.SchemaFilter<EnumSchemaFilter>();
swaggerGenOptions.CustomSchemaIds(_schemaIdSelector.SchemaId);
@@ -59,7 +81,14 @@ public class ConfigureUmbracoSwaggerGenOptions : IConfigureOptions<SwaggerGenOpt
swaggerGenOptions.SupportNonNullableReferenceTypes();
}
// see https://github.com/domaindrivendev/Swashbuckle.AspNetCore#change-operation-sort-order-eg-for-ui-sorting
/// <summary>
/// Generates a sort key for API actions.
/// </summary>
/// <param name="apiDesc">The API description.</param>
/// <returns>A string used to sort API operations in the documentation.</returns>
/// <remarks>
/// See https://github.com/domaindrivendev/Swashbuckle.AspNetCore#change-operation-sort-order-eg-for-ui-sorting.
/// </remarks>
private static string ActionOrderBy(ApiDescription apiDesc)
=> $"{apiDesc.GroupName}_{apiDesc.ActionDescriptor.AttributeRouteInfo?.Template ?? apiDesc.ActionDescriptor.RouteValues["controller"]}_{(apiDesc.ActionDescriptor.RouteValues.TryGetValue("action", out var action) ? action : null)}_{apiDesc.HttpMethod}";
}
@@ -1,6 +1,12 @@
namespace Umbraco.Cms.Api.Common.Configuration;
/// <summary>
/// Contains default configuration values for the API.
/// </summary>
internal static class DefaultApiConfiguration
{
/// <summary>
/// The default API name used for endpoints not assigned to a specific API.
/// </summary>
public const string ApiName = "default";
}
@@ -13,6 +13,14 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Common.DependencyInjection;
/// <summary>
/// Handles secure storage of back-office authentication tokens in HTTP-only cookies.
/// </summary>
/// <remarks>
/// This handler intercepts OpenIddict token responses for the back-office client and stores
/// access tokens, refresh tokens, and PKCE codes in encrypted HTTP-only cookies. The tokens
/// are redacted from the response to prevent client-side JavaScript access.
/// </remarks>
internal sealed class HideBackOfficeTokensHandler
: IOpenIddictServerHandler<OpenIddictServerEvents.ApplyTokenResponseContext>,
IOpenIddictServerHandler<OpenIddictServerEvents.ApplyAuthorizationResponseContext>,
@@ -21,15 +29,26 @@ internal sealed class HideBackOfficeTokensHandler
INotificationHandler<UserLogoutSuccessNotification>
{
private const string RedactedTokenValue = "[redacted]";
private const string AccessTokenCookieKey = "__Host-umbAccessToken";
private const string RefreshTokenCookieKey = "__Host-umbRefreshToken";
private const string PkceCodeCookieKey = "__Host-umbPkceCode";
// The __Host- prefix enforces secure cookies at browser level (requires Secure, Path=/, no Domain).
// For local development over HTTP, we use a simpler prefix to avoid browser rejection.
private const string SecureCookiePrefix = "__Host-";
private const string AccessTokenCookieName = "umbAccessToken";
private const string RefreshTokenCookieName = "umbRefreshToken";
private const string PkceCodeCookieName = "umbPkceCode";
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IDataProtectionProvider _dataProtectionProvider;
private readonly BackOfficeTokenCookieSettings _backOfficeTokenCookieSettings;
private readonly GlobalSettings _globalSettings;
/// <summary>
/// Initializes a new instance of the <see cref="HideBackOfficeTokensHandler"/> class.
/// </summary>
/// <param name="httpContextAccessor">The HTTP context accessor.</param>
/// <param name="dataProtectionProvider">The data protection provider for encrypting cookie values.</param>
/// <param name="backOfficeTokenCookieSettings">The back-office token cookie settings.</param>
/// <param name="globalSettings">The global settings.</param>
public HideBackOfficeTokensHandler(
IHttpContextAccessor httpContextAccessor,
IDataProtectionProvider dataProtectionProvider,
@@ -59,13 +78,13 @@ internal sealed class HideBackOfficeTokensHandler
if (context.Response.AccessToken is not null)
{
SetCookie(httpContext, AccessTokenCookieKey, context.Response.AccessToken);
SetCookie(httpContext, AccessTokenCookieName, context.Response.AccessToken);
context.Response.AccessToken = RedactedTokenValue;
}
if (context.Response.RefreshToken is not null)
{
SetCookie(httpContext, RefreshTokenCookieKey, context.Response.RefreshToken);
SetCookie(httpContext, RefreshTokenCookieName, context.Response.RefreshToken);
context.Response.RefreshToken = RedactedTokenValue;
}
@@ -87,7 +106,7 @@ internal sealed class HideBackOfficeTokensHandler
if (context.Response.Code is not null)
{
SetCookie(GetHttpContext(), PkceCodeCookieKey, context.Response.Code);
SetCookie(GetHttpContext(), PkceCodeCookieName, context.Response.Code);
context.Response.Code = RedactedTokenValue;
}
@@ -105,14 +124,16 @@ internal sealed class HideBackOfficeTokensHandler
return ValueTask.CompletedTask;
}
HttpContext httpContext = GetHttpContext();
// Handle when the PKCE code is being exchanged for an access token.
if (context.Request.Code == RedactedTokenValue
&& TryGetCookie(PkceCodeCookieKey, out var code))
&& TryGetCookie(httpContext, PkceCodeCookieName, out var code))
{
context.Request.Code = code;
// We won't need the PKCE cookie after this, let's remove it.
RemoveCookie(GetHttpContext(), PkceCodeCookieKey);
RemoveCookie(httpContext, PkceCodeCookieName);
}
else
{
@@ -123,7 +144,7 @@ internal sealed class HideBackOfficeTokensHandler
// Handle when a refresh token is being exchanged for a new access token.
if (context.Request.RefreshToken == RedactedTokenValue
&& TryGetCookie(RefreshTokenCookieKey, out var refreshToken))
&& TryGetCookie(httpContext, RefreshTokenCookieName, out var refreshToken))
{
context.Request.RefreshToken = refreshToken;
}
@@ -149,7 +170,7 @@ internal sealed class HideBackOfficeTokensHandler
return ValueTask.CompletedTask;
}
if (TryGetCookie(AccessTokenCookieKey, out var accessToken))
if (TryGetCookie(GetHttpContext(), AccessTokenCookieName, out var accessToken))
{
context.AccessToken = accessToken;
}
@@ -157,10 +178,11 @@ internal sealed class HideBackOfficeTokensHandler
return ValueTask.CompletedTask;
}
/// <inheritdoc/>
public void Handle(UserLogoutSuccessNotification notification)
{
HttpContext? context = _httpContextAccessor.HttpContext;
if (context is null)
HttpContext? httpContext = _httpContextAccessor.HttpContext;
if (httpContext is null)
{
// For some reason there is no ambient HTTP context, so we can't clean up the cookies.
// This is OK, because the tokens in the cookies have already been revoked at user sign-out,
@@ -168,23 +190,32 @@ internal sealed class HideBackOfficeTokensHandler
return;
}
context.Response.Cookies.Delete(AccessTokenCookieKey);
context.Response.Cookies.Delete(RefreshTokenCookieKey);
RemoveCookie(httpContext, AccessTokenCookieName);
RemoveCookie(httpContext, RefreshTokenCookieName);
}
private HttpContext GetHttpContext()
=> _httpContextAccessor.GetRequiredHttpContext();
private void SetCookie(HttpContext httpContext, string key, string value)
private string GetCookieKey(HttpContext httpContext, string cookieName)
=> _globalSettings.UseHttps || httpContext.Request.IsHttps
? $"{SecureCookiePrefix}{cookieName}"
: cookieName;
private void SetCookie(HttpContext httpContext, string cookieName, string value)
{
var key = GetCookieKey(httpContext, cookieName);
var cookieValue = EncryptionHelper.Encrypt(value, _dataProtectionProvider);
RemoveCookie(httpContext, key);
RemoveCookie(httpContext, cookieName);
httpContext.Response.Cookies.Append(key, cookieValue, GetCookieOptions(httpContext));
}
private void RemoveCookie(HttpContext httpContext, string key)
=> httpContext.Response.Cookies.Delete(key, GetCookieOptions(httpContext));
private void RemoveCookie(HttpContext httpContext, string cookieName)
{
var key = GetCookieKey(httpContext, cookieName);
httpContext.Response.Cookies.Delete(key, GetCookieOptions(httpContext));
}
private CookieOptions GetCookieOptions(HttpContext httpContext) =>
new()
@@ -211,9 +242,10 @@ internal sealed class HideBackOfficeTokensHandler
SameSite = ParseSameSiteMode(_backOfficeTokenCookieSettings.SameSite),
};
private bool TryGetCookie(string key, [NotNullWhen(true)] out string? value)
private bool TryGetCookie(HttpContext httpContext, string cookieName, [NotNullWhen(true)] out string? value)
{
if (GetHttpContext().Request.Cookies.TryGetValue(key, out var cookieValue))
var key = GetCookieKey(httpContext, cookieName);
if (httpContext.Request.Cookies.TryGetValue(key, out var cookieValue))
{
value = EncryptionHelper.Decrypt(cookieValue, _dataProtectionProvider);
return true;
@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -6,8 +6,18 @@ using Umbraco.Cms.Api.Common.Configuration;
namespace Umbraco.Cms.Api.Common.DependencyInjection;
/// <summary>
/// Extension methods for <see cref="IMvcBuilder"/>.
/// </summary>
public static class MvcBuilderExtensions
{
/// <summary>
/// Adds named JSON serialization options to the MVC builder.
/// </summary>
/// <param name="builder">The MVC builder.</param>
/// <param name="settingsName">The name for the JSON options configuration.</param>
/// <param name="configure">The action to configure the JSON options.</param>
/// <returns>The MVC builder for method chaining.</returns>
public static IMvcBuilder AddJsonOptions(this IMvcBuilder builder, string settingsName, Action<JsonOptions> configure)
{
builder.Services.Configure(settingsName, configure);
@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using OpenIddict.Server;
using OpenIddict.Validation;
using Umbraco.Cms.Core;
@@ -6,12 +6,23 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Common.DependencyInjection;
/// <summary>
/// Handles OpenIddict request processing to skip handling for non-authentication requests.
/// </summary>
/// <remarks>
/// This handler prevents OpenIddict from processing every request to the server,
/// limiting its scope to back-office and well-known OpenID Connect endpoints.
/// </remarks>
public class ProcessRequestContextHandler
: IOpenIddictServerHandler<OpenIddictServerEvents.ProcessRequestContext>, IOpenIddictValidationHandler<OpenIddictValidationEvents.ProcessRequestContext>
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly string[] _pathsToHandle;
/// <summary>
/// Initializes a new instance of the <see cref="ProcessRequestContextHandler"/> class.
/// </summary>
/// <param name="httpContextAccessor">The HTTP context accessor.</param>
public ProcessRequestContextHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
@@ -21,6 +32,11 @@ public class ProcessRequestContextHandler
_pathsToHandle = [backOfficePathSegment, "/.well-known/openid-configuration", "/.well-known/jwks"];
}
/// <summary>
/// Handles the server process request context event.
/// </summary>
/// <param name="context">The process request context.</param>
/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
public ValueTask HandleAsync(OpenIddictServerEvents.ProcessRequestContext context)
{
if (SkipOpenIddictHandlingForRequest())
@@ -31,6 +47,11 @@ public class ProcessRequestContextHandler
return ValueTask.CompletedTask;
}
/// <summary>
/// Handles the validation process request context event.
/// </summary>
/// <param name="context">The process request context.</param>
/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
public ValueTask HandleAsync(OpenIddictValidationEvents.ProcessRequestContext context)
{
if (SkipOpenIddictHandlingForRequest())
@@ -7,8 +7,16 @@ using Umbraco.Cms.Web.Common.ApplicationBuilder;
namespace Umbraco.Cms.Api.Common.DependencyInjection;
/// <summary>
/// Extension methods for <see cref="IUmbracoBuilder"/> to configure API services.
/// </summary>
public static class UmbracoBuilderApiExtensions
{
/// <summary>
/// Adds Umbraco API OpenAPI/Swagger UI services to the builder.
/// </summary>
/// <param name="builder">The Umbraco builder.</param>
/// <returns>The Umbraco builder for method chaining.</returns>
public static IUmbracoBuilder AddUmbracoApiOpenApiUI(this IUmbracoBuilder builder)
{
if (builder.Services.Any(x => !x.IsKeyedService && x.ImplementationType == typeof(OperationIdSelector)))
@@ -25,6 +33,7 @@ public static class UmbracoBuilderApiExtensions
builder.Services.AddSingleton<ISchemaIdHandler, SchemaIdHandler>();
builder.Services.AddSingleton<ISubTypesSelector, SubTypesSelector>();
builder.Services.AddSingleton<ISubTypesHandler, SubTypesHandler>();
builder.Services.AddSingleton<IDocumentInclusionSelector, DocumentInclusionSelector>();
builder.Services.Configure<UmbracoPipelineOptions>(options => options.AddFilter(new SwaggerRouteTemplatePipelineFilter("UmbracoApiCommon")));
return builder;
@@ -10,13 +10,25 @@ using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
using Umbraco.Extensions;
using Umbraco.Cms.Infrastructure.BackgroundJobs;
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
namespace Umbraco.Cms.Api.Common.DependencyInjection;
/// <summary>
/// Extension methods for <see cref="IUmbracoBuilder"/> to configure authentication services.
/// </summary>
public static class UmbracoBuilderAuthExtensions
{
/// <summary>
/// Adds OpenIddict authentication services for Umbraco APIs.
/// </summary>
/// <param name="builder">The Umbraco builder.</param>
/// <returns>The Umbraco builder for method chaining.</returns>
/// <remarks>
/// Configures OpenIddict with authorization code flow (with PKCE), client credentials flow,
/// reference tokens, and ASP.NET Core Data Protection for token encryption.
/// </remarks>
public static IUmbracoBuilder AddUmbracoOpenIddict(this IUmbracoBuilder builder)
{
if (builder.Services.Any(x => !x.IsKeyedService && x.ImplementationType == typeof(OpenIddictCleanupJob)) is false)
@@ -29,11 +41,6 @@ public static class UmbracoBuilderAuthExtensions
private static void ConfigureOpenIddict(IUmbracoBuilder builder)
{
// Optionally hide tokens from the back-office.
var hideBackOfficeTokens = (builder.Config
.GetSection(Constants.Configuration.ConfigBackOfficeTokenCookie)
.Get<BackOfficeTokenCookieSettings>() ?? new BackOfficeTokenCookieSettings()).Enabled;
builder.Services.AddOpenIddict()
// Register the OpenIddict server components.
.AddServer(options =>
@@ -120,27 +127,24 @@ public static class UmbracoBuilderAuthExtensions
configuration.UseSingletonHandler<ProcessRequestContextHandler>().SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.ResolveRequestUri.Descriptor.Order - 1);
});
if (hideBackOfficeTokens)
options.AddEventHandler<OpenIddictServerEvents.ApplyTokenResponseContext>(configuration =>
{
options.AddEventHandler<OpenIddictServerEvents.ApplyTokenResponseContext>(configuration =>
{
configuration
.UseSingletonHandler<HideBackOfficeTokensHandler>()
.SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.ProcessJsonResponse<OpenIddictServerEvents.ApplyTokenResponseContext>.Descriptor.Order - 1);
});
options.AddEventHandler<OpenIddictServerEvents.ApplyAuthorizationResponseContext>(configuration =>
{
configuration
.UseSingletonHandler<HideBackOfficeTokensHandler>()
.SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.Authentication.ProcessQueryResponse.Descriptor.Order - 1);
});
options.AddEventHandler<OpenIddictServerEvents.ExtractTokenRequestContext>(configuration =>
{
configuration
.UseSingletonHandler<HideBackOfficeTokensHandler>()
.SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.ExtractPostRequest<OpenIddictServerEvents.ExtractTokenRequestContext>.Descriptor.Order + 1);
});
}
configuration
.UseSingletonHandler<HideBackOfficeTokensHandler>()
.SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.ProcessJsonResponse<OpenIddictServerEvents.ApplyTokenResponseContext>.Descriptor.Order - 1);
});
options.AddEventHandler<OpenIddictServerEvents.ApplyAuthorizationResponseContext>(configuration =>
{
configuration
.UseSingletonHandler<HideBackOfficeTokensHandler>()
.SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.Authentication.ProcessQueryResponse.Descriptor.Order - 1);
});
options.AddEventHandler<OpenIddictServerEvents.ExtractTokenRequestContext>(configuration =>
{
configuration
.UseSingletonHandler<HideBackOfficeTokensHandler>()
.SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.ExtractPostRequest<OpenIddictServerEvents.ExtractTokenRequestContext>.Descriptor.Order + 1);
});
})
// Register the OpenIddict validation components.
@@ -166,24 +170,18 @@ public static class UmbracoBuilderAuthExtensions
configuration.UseSingletonHandler<ProcessRequestContextHandler>().SetOrder(OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers.ResolveRequestUri.Descriptor.Order - 1);
});
if (hideBackOfficeTokens)
options.AddEventHandler<OpenIddictValidationEvents.ProcessAuthenticationContext>(configuration =>
{
options.AddEventHandler<OpenIddictValidationEvents.ProcessAuthenticationContext>(configuration =>
{
configuration
.UseSingletonHandler<HideBackOfficeTokensHandler>()
// IMPORTANT: the handler must be AFTER the built-in query string handler, because the client-side SignalR library sometimes appends access tokens to the query string.
.SetOrder(OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers.ExtractAccessTokenFromQueryString.Descriptor.Order + 1);
});
}
configuration
.UseSingletonHandler<HideBackOfficeTokensHandler>()
// IMPORTANT: the handler must be AFTER the built-in query string handler, because the client-side SignalR library sometimes appends access tokens to the query string.
.SetOrder(OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers.ExtractAccessTokenFromQueryString.Descriptor.Order + 1);
});
});
builder.Services.AddRecurringBackgroundJob<OpenIddictCleanupJob>();
builder.Services.AddSingleton<IDistributedBackgroundJob, OpenIddictCleanupJob>();
builder.Services.ConfigureOptions<ConfigureOpenIddict>();
if (hideBackOfficeTokens)
{
builder.AddNotificationHandler<UserLogoutSuccessNotification, HideBackOfficeTokensHandler>();
}
builder.AddNotificationHandler<UserLogoutSuccessNotification, HideBackOfficeTokensHandler>();
}
}
@@ -33,4 +33,4 @@ public static class ActionDescriptorApiCommonExtensions
return mapToApiAttributes.SingleOrDefault()?.ApiName;
}
}
}
@@ -5,16 +5,28 @@ using Umbraco.Cms.Api.Common.Configuration;
namespace Umbraco.Extensions;
/// <summary>
/// Extension methods for <see cref="MethodInfo"/> to work with API-related attributes.
/// </summary>
public static class MethodInfoApiCommonExtensions
{
public static string? GetMapToApiVersionAttributeValue(this MethodInfo methodInfo)
/// <summary>
/// Gets the API version values from <see cref="MapToApiVersionAttribute"/> applied to the method.
/// </summary>
/// <param name="methodInfo">The method info to inspect.</param>
/// <returns>A pipe-separated string of API version values.</returns>
public static string GetMapToApiVersionAttributeValue(this MethodInfo methodInfo)
{
MapToApiVersionAttribute[] mapToApis = methodInfo.GetCustomAttributes(typeof(MapToApiVersionAttribute), inherit: true).Cast<MapToApiVersionAttribute>().ToArray();
return string.Join("|", mapToApis.SelectMany(x=>x.Versions));
return string.Join("|", mapToApis.SelectMany(x => x.Versions));
}
/// <summary>
/// Gets the API name from <see cref="MapToApiAttribute"/> applied to the method's declaring type.
/// </summary>
/// <param name="methodInfo">The method info to inspect.</param>
/// <returns>The API name if the attribute is present; otherwise, <c>null</c>.</returns>
public static string? GetMapToApiAttributeValue(this MethodInfo methodInfo)
{
MapToApiAttribute[] mapToApis = (methodInfo.DeclaringType?.GetCustomAttributes(typeof(MapToApiAttribute), inherit: true) ?? Array.Empty<object>()).Cast<MapToApiAttribute>().ToArray();
@@ -22,6 +34,15 @@ public static class MethodInfoApiCommonExtensions
return mapToApis.SingleOrDefault()?.ApiName;
}
/// <summary>
/// Determines whether the method's declaring type has a <see cref="MapToApiAttribute"/> with the specified API name.
/// </summary>
/// <param name="methodInfo">The method info to inspect.</param>
/// <param name="apiName">The API name to check for.</param>
/// <returns>
/// <c>true</c> if the attribute is present and matches the specified API name,
/// or if the attribute is not present and the API name matches the default API name; otherwise, <c>false</c>.
/// </returns>
public static bool HasMapToApiAttribute(this MethodInfo methodInfo, string apiName)
{
var value = methodInfo.GetMapToApiAttributeValue();
@@ -1,9 +1,19 @@
namespace Umbraco.Cms.Api.Common.Filters;
namespace Umbraco.Cms.Api.Common.Filters;
/// <summary>
/// Attribute used to specify the named JSON serialization options for a controller.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class JsonOptionsNameAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="JsonOptionsNameAttribute"/> class.
/// </summary>
/// <param name="jsonOptionsName">The name of the JSON options configuration to use.</param>
public JsonOptionsNameAttribute(string jsonOptionsName) => JsonOptionsName = jsonOptionsName;
/// <summary>
/// Gets the name of the JSON options configuration.
/// </summary>
public string JsonOptionsName { get; }
}
@@ -1,10 +1,18 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using Umbraco.Cms.Api.Common.Filters;
namespace Umbraco.Cms.Api.Common.Json;
/// <summary>
/// Extension methods for <see cref="HttpContext"/> related to JSON serialization.
/// </summary>
public static class HttpContextJsonExtensions
{
/// <summary>
/// Gets the named JSON options configuration for the current endpoint.
/// </summary>
/// <param name="context">The HTTP context.</param>
/// <returns>The JSON options name if specified via <see cref="JsonOptionsNameAttribute"/>; otherwise, <c>null</c>.</returns>
public static string? CurrentJsonOptionsName(this HttpContext context)
=> context.GetEndpoint()?.Metadata.GetMetadata<JsonOptionsNameAttribute>()?.JsonOptionsName;
}
@@ -1,20 +1,31 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Logging;
namespace Umbraco.Cms.Api.Common.Json;
/// <summary>
/// A JSON input formatter that only processes requests for endpoints with matching named JSON options.
/// </summary>
internal sealed class NamedSystemTextJsonInputFormatter : SystemTextJsonInputFormatter
{
private readonly string _jsonOptionsName;
/// <summary>
/// Initializes a new instance of the <see cref="NamedSystemTextJsonInputFormatter"/> class.
/// </summary>
/// <param name="jsonOptionsName">The name of the JSON options configuration this formatter handles.</param>
/// <param name="options">The JSON options.</param>
/// <param name="logger">The logger.</param>
public NamedSystemTextJsonInputFormatter(string jsonOptionsName, JsonOptions options, ILogger<NamedSystemTextJsonInputFormatter> logger)
: base(options, logger) =>
_jsonOptionsName = jsonOptionsName;
/// <inheritdoc/>
public override bool CanRead(InputFormatterContext context)
=> context.HttpContext.CurrentJsonOptionsName() == _jsonOptionsName && base.CanRead(context);
/// <inheritdoc/>
public override async Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
{
try
@@ -1,17 +1,26 @@
using System.Text.Json;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc.Formatters;
namespace Umbraco.Cms.Api.Common.Json;
/// <summary>
/// A JSON output formatter that only processes responses for endpoints with matching named JSON options.
/// </summary>
internal sealed class NamedSystemTextJsonOutputFormatter : SystemTextJsonOutputFormatter
{
private readonly string _jsonOptionsName;
/// <summary>
/// Initializes a new instance of the <see cref="NamedSystemTextJsonOutputFormatter"/> class.
/// </summary>
/// <param name="jsonOptionsName">The name of the JSON options configuration this formatter handles.</param>
/// <param name="jsonSerializerOptions">The JSON serializer options.</param>
public NamedSystemTextJsonOutputFormatter(string jsonOptionsName, JsonSerializerOptions jsonSerializerOptions) : base(jsonSerializerOptions)
{
_jsonOptionsName = jsonOptionsName;
}
/// <inheritdoc/>
public override bool CanWriteResult(OutputFormatterCanWriteContext context)
=> context.HttpContext.CurrentJsonOptionsName() == _jsonOptionsName && base.CanWriteResult(context);
}
@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.DependencyInjection;
@@ -16,6 +16,13 @@ public sealed class EmptyCreatedAtActionResult : ActionResult
private readonly object _routeValues;
private readonly string _resourceIdentifier;
/// <summary>
/// Initializes a new instance of the <see cref="EmptyCreatedAtActionResult"/> class.
/// </summary>
/// <param name="actionName">The name of the action to generate the URL for.</param>
/// <param name="controllerName">The name of the controller to generate the URL for.</param>
/// <param name="routeValues">The route values to use for URL generation.</param>
/// <param name="resourceIdentifier">The identifier of the created resource.</param>
public EmptyCreatedAtActionResult(string actionName, string controllerName, object routeValues, string resourceIdentifier)
{
_actionName = actionName;
@@ -24,6 +31,7 @@ public sealed class EmptyCreatedAtActionResult : ActionResult
_resourceIdentifier = resourceIdentifier;
}
/// <inheritdoc/>
public override void ExecuteResult(ActionContext context)
{
ArgumentNullException.ThrowIfNull(context);
@@ -0,0 +1,30 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Umbraco.Cms.Api.Common.Configuration;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// Determines whether an API description should be included in a specific documentation set based on the document name
/// and API metadata.
/// </summary>
public class DocumentInclusionSelector : IDocumentInclusionSelector
{
/// <inheritdoc/>
public bool Include(string documentName, ApiDescription apiDescription)
{
if (apiDescription.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor
&& controllerActionDescriptor.HasMapToApiAttribute(documentName))
{
return true;
}
ApiVersionMetadata apiVersionMetadata = apiDescription.ActionDescriptor.GetApiVersionMetadata();
return apiVersionMetadata.Name == documentName
|| (string.IsNullOrEmpty(apiVersionMetadata.Name) && documentName == DefaultApiConfiguration.ApiName);
}
}
@@ -1,25 +1,35 @@
using System.Reflection;
using System.Reflection;
using System.Runtime.Serialization;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using System.Text.Json.Nodes;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// A schema filter that converts enum schemas to string type with enum member names.
/// </summary>
/// <remarks>
/// This filter ensures enums are represented as strings in the OpenAPI schema,
/// using <see cref="EnumMemberAttribute"/> values when available.
/// </remarks>
public class EnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema model, SchemaFilterContext context)
/// <inheritdoc/>
public void Apply(IOpenApiSchema model, SchemaFilterContext context)
{
if (context.Type.IsEnum)
if (model is not OpenApiSchema schema || context.Type.IsEnum is false)
{
model.Type = "string";
model.Format = null;
model.Enum.Clear();
foreach (var name in Enum.GetNames(context.Type))
{
var actualName = context.Type.GetField(name)?.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? name;
model.Enum.Add(new OpenApiString(actualName));
}
return;
}
schema.Type = JsonSchemaType.String;
schema.Format = null;
schema.Enum = new List<JsonNode>();
foreach (var name in Enum.GetNames(context.Type))
{
var actualName = context.Type.GetField(name)?.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? name;
schema.Enum.Add(actualName);
}
}
}
@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Mvc.ApiExplorer;
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// Defines a method that determines whether a given API description should be included in a specific documentation
/// document.
/// </summary>
public interface IDocumentInclusionSelector
{
/// <summary>
/// Determines whether the specified API description should be included in the generated documentation for the given
/// document name.
/// </summary>
/// <param name="documentName">The name of the documentation document being generated.</param>
/// <param name="apiDescription">The API description to evaluate for inclusion.</param>
/// <returns>true if the API description should be included in the documentation; otherwise, false.</returns>
bool Include(string documentName, ApiDescription apiDescription);
}
@@ -2,9 +2,22 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer;
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// Defines a handler for generating OpenAPI operation IDs.
/// </summary>
public interface IOperationIdHandler
{
bool CanHandle(ApiDescription apiDescription);
/// <summary>
/// Determines whether this handler can generate an operation ID for the specified API description.
/// </summary>
/// <param name="apiDescription">The API description to check.</param>
/// <returns><c>true</c> if this handler can handle the API description; otherwise, <c>false</c>.</returns>
bool CanHandle(ApiDescription apiDescription);
string Handle(ApiDescription apiDescription);
/// <summary>
/// Generates an operation ID for the specified API description.
/// </summary>
/// <param name="apiDescription">The API description to generate an operation ID for.</param>
/// <returns>The generated operation ID.</returns>
string Handle(ApiDescription apiDescription);
}
@@ -3,7 +3,15 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer;
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// Defines a selector for choosing operation IDs from registered handlers.
/// </summary>
public interface IOperationIdSelector
{
/// <summary>
/// Selects an operation ID for the specified API description.
/// </summary>
/// <param name="apiDescription">The API description to generate an operation ID for.</param>
/// <returns>The operation ID, or <c>null</c> if none could be determined.</returns>
string? OperationId(ApiDescription apiDescription);
}
@@ -1,8 +1,21 @@
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// Defines a handler for generating OpenAPI schema IDs.
/// </summary>
public interface ISchemaIdHandler
{
/// <summary>
/// Determines whether this handler can generate a schema ID for the specified type.
/// </summary>
/// <param name="type">The type to check.</param>
/// <returns><c>true</c> if this handler can handle the type; otherwise, <c>false</c>.</returns>
bool CanHandle(Type type);
/// <summary>
/// Generates a schema ID for the specified type.
/// </summary>
/// <param name="type">The type to generate a schema ID for.</param>
/// <returns>The generated schema ID.</returns>
string Handle(Type type);
}
@@ -1,6 +1,14 @@
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// Defines a selector for choosing schema IDs from registered handlers.
/// </summary>
public interface ISchemaIdSelector
{
/// <summary>
/// Selects a schema ID for the specified type.
/// </summary>
/// <param name="type">The type to generate a schema ID for.</param>
/// <returns>The schema ID.</returns>
string SchemaId(Type type);
}
@@ -1,8 +1,22 @@
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// Defines a handler for discovering sub-types for polymorphic OpenAPI schemas.
/// </summary>
public interface ISubTypesHandler
{
/// <summary>
/// Determines whether this handler can discover sub-types for the specified type and document.
/// </summary>
/// <param name="type">The type to check.</param>
/// <param name="documentName">The OpenAPI document name.</param>
/// <returns><c>true</c> if this handler can handle the type; otherwise, <c>false</c>.</returns>
bool CanHandle(Type type, string documentName);
/// <summary>
/// Discovers sub-types for the specified type.
/// </summary>
/// <param name="type">The type to discover sub-types for.</param>
/// <returns>An enumerable of discovered sub-types.</returns>
IEnumerable<Type> Handle(Type type);
}
@@ -1,6 +1,14 @@
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// Defines a selector for choosing sub-types from registered handlers.
/// </summary>
public interface ISubTypesSelector
{
/// <summary>
/// Selects sub-types for the specified type for polymorphic OpenAPI schema generation.
/// </summary>
/// <param name="type">The type to find sub-types for.</param>
/// <returns>An enumerable of sub-types.</returns>
IEnumerable<Type> SubTypes(Type type);
}
@@ -1,4 +1,4 @@
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;
using Umbraco.Extensions;
@@ -11,8 +11,13 @@ public class MimeTypeDocumentFilter : IDocumentFilter
{
private readonly string _documentName;
/// <summary>
/// Initializes a new instance of the <see cref="MimeTypeDocumentFilter"/> class.
/// </summary>
/// <param name="documentName">The name of the OpenAPI document to filter.</param>
public MimeTypeDocumentFilter(string documentName) => _documentName = documentName;
/// <inheritdoc/>
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
if (context.DocumentName != _documentName)
@@ -21,25 +26,32 @@ public class MimeTypeDocumentFilter : IDocumentFilter
}
OpenApiOperation[] operations = swaggerDoc.Paths
.SelectMany(path => path.Value.Operations.Values)
.SelectMany(path => path.Value.Operations?.Values ?? Enumerable.Empty<OpenApiOperation>())
.ToArray();
void RemoveUnwantedMimeTypes(IDictionary<string, OpenApiMediaType> content)
static void RemoveUnwantedMimeTypes(IDictionary<string, OpenApiMediaType>? content)
{
if (content.ContainsKey("application/json"))
if (content is null || content.ContainsKey("application/json") is false)
{
content.RemoveAll(r => r.Key != "application/json");
return;
}
content.RemoveAll(r => r.Key != "application/json");
}
OpenApiRequestBody[] requestBodies = operations.Select(operation => operation.RequestBody).WhereNotNull().ToArray();
OpenApiRequestBody[] requestBodies = operations
.Select(operation => operation.RequestBody)
.OfType<OpenApiRequestBody>()
.ToArray();
foreach (OpenApiRequestBody requestBody in requestBodies)
{
RemoveUnwantedMimeTypes(requestBody.Content);
}
OpenApiResponse[] responses = operations.SelectMany(operation => operation.Responses.Values).WhereNotNull().ToArray();
OpenApiResponse[] responses = operations
.SelectMany(operation => operation.Responses?.Values ?? Enumerable.Empty<IOpenApiResponse>())
.OfType<OpenApiResponse>()
.ToArray();
foreach (OpenApiResponse response in responses)
{
RemoveUnwantedMimeTypes(response.Content);
@@ -6,14 +6,24 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Common.OpenApi;
// NOTE: Left unsealed on purpose, so it is extendable.
/// <summary>
/// Default handler for generating OpenAPI operation IDs for Umbraco API controllers.
/// </summary>
/// <remarks>
/// Left unsealed on purpose, so it is extendable by consuming APIs.
/// </remarks>
public class OperationIdHandler : IOperationIdHandler
{
private readonly ApiVersioningOptions _apiVersioningOptions;
/// <summary>
/// Initializes a new instance of the <see cref="OperationIdHandler"/> class.
/// </summary>
/// <param name="apiVersioningOptions">The API versioning options.</param>
public OperationIdHandler(IOptions<ApiVersioningOptions> apiVersioningOptions)
=> _apiVersioningOptions = apiVersioningOptions.Value;
/// <inheritdoc/>
public bool CanHandle(ApiDescription apiDescription)
{
if (apiDescription.ActionDescriptor is not ControllerActionDescriptor controllerActionDescriptor)
@@ -24,9 +34,16 @@ public class OperationIdHandler : IOperationIdHandler
return CanHandle(apiDescription, controllerActionDescriptor);
}
/// <summary>
/// Determines whether this handler can process the API description based on the controller namespace.
/// </summary>
/// <param name="apiDescription">The API description.</param>
/// <param name="controllerActionDescriptor">The controller action descriptor.</param>
/// <returns><c>true</c> if the controller is in an Umbraco.Cms.Api namespace; otherwise, <c>false</c>.</returns>
protected virtual bool CanHandle(ApiDescription apiDescription, ControllerActionDescriptor controllerActionDescriptor)
=> controllerActionDescriptor.ControllerTypeInfo.Namespace?.StartsWith("Umbraco.Cms.Api") is true;
/// <inheritdoc/>
public virtual string Handle(ApiDescription apiDescription)
=> UmbracoOperationId(apiDescription);
@@ -1,4 +1,4 @@
using System.Text.RegularExpressions;
using System.Text.RegularExpressions;
namespace Umbraco.Cms.Api.Common.OpenApi;
@@ -3,19 +3,30 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer;
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// Selects an operation ID for an API description using registered handlers.
/// </summary>
public class OperationIdSelector : IOperationIdSelector
{
private readonly IEnumerable<IOperationIdHandler> _operationIdHandlers;
[Obsolete("Use non-obsolete constructor. This will be removed in Umbraco 15.")]
/// <summary>
/// Initializes a new instance of the <see cref="OperationIdSelector"/> class.
/// </summary>
[Obsolete("Use non-obsolete constructor. Scheduled for removal in Umbraco 18.")]
public OperationIdSelector()
: this(Enumerable.Empty<IOperationIdHandler>())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="OperationIdSelector"/> class.
/// </summary>
/// <param name="operationIdHandlers">The registered operation ID handlers.</param>
public OperationIdSelector(IEnumerable<IOperationIdHandler> operationIdHandlers)
=> _operationIdHandlers = operationIdHandlers;
/// <inheritdoc/>
public virtual string? OperationId(ApiDescription apiDescription)
{
IOperationIdHandler? handler = _operationIdHandlers.FirstOrDefault(h => h.CanHandle(apiDescription));
@@ -1,4 +1,4 @@
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace Umbraco.Cms.Api.Common.OpenApi;
@@ -10,9 +10,14 @@ public class RemoveSecuritySchemesDocumentFilter : IDocumentFilter
{
private readonly string _documentName;
/// <summary>
/// Initializes a new instance of the <see cref="RemoveSecuritySchemesDocumentFilter"/> class.
/// </summary>
/// <param name="documentName">The name of the OpenAPI document to filter.</param>
public RemoveSecuritySchemesDocumentFilter(string documentName)
=> _documentName = documentName;
/// <inheritdoc/>
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
if (context.DocumentName != _documentName)
@@ -20,6 +25,6 @@ public class RemoveSecuritySchemesDocumentFilter : IDocumentFilter
return;
}
swaggerDoc.Components.SecuritySchemes.Clear();
swaggerDoc.Components?.SecuritySchemes?.Clear();
}
}
@@ -3,12 +3,20 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Common.OpenApi;
// NOTE: Left unsealed on purpose, so it is extendable.
/// <summary>
/// Default handler for generating OpenAPI schema IDs for Umbraco types.
/// </summary>
/// <remarks>
/// Left unsealed on purpose, so it is extendable by consuming APIs.
/// Adds "Model" suffix to avoid TypeScript name clashes and removes invalid characters.
/// </remarks>
public class SchemaIdHandler : ISchemaIdHandler
{
/// <inheritdoc/>
public virtual bool CanHandle(Type type)
=> type.Namespace?.StartsWith("Umbraco.Cms") is true;
/// <inheritdoc/>
public virtual string Handle(Type type)
=> UmbracoSchemaId(type);
@@ -1,12 +1,20 @@
namespace Umbraco.Cms.Api.Common.OpenApi;
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// Selects a schema ID for a type using registered handlers.
/// </summary>
public class SchemaIdSelector : ISchemaIdSelector
{
private readonly IEnumerable<ISchemaIdHandler> _schemaIdHandlers;
/// <summary>
/// Initializes a new instance of the <see cref="SchemaIdSelector"/> class.
/// </summary>
/// <param name="schemaIdHandlers">The registered schema ID handlers.</param>
public SchemaIdSelector(IEnumerable<ISchemaIdHandler> schemaIdHandlers)
=> _schemaIdHandlers = schemaIdHandlers;
/// <inheritdoc/>
public virtual string SchemaId(Type type)
{
ISchemaIdHandler? handler = _schemaIdHandlers.FirstOrDefault(h => h.CanHandle(type));
@@ -2,19 +2,33 @@ using Umbraco.Cms.Api.Common.Serialization;
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// Default handler for discovering sub-types for polymorphic OpenAPI schemas.
/// </summary>
public class SubTypesHandler : ISubTypesHandler
{
private readonly IUmbracoJsonTypeInfoResolver _umbracoJsonTypeInfoResolver;
/// <summary>
/// Initializes a new instance of the <see cref="SubTypesHandler"/> class.
/// </summary>
/// <param name="umbracoJsonTypeInfoResolver">The JSON type info resolver for finding sub-types.</param>
public SubTypesHandler(IUmbracoJsonTypeInfoResolver umbracoJsonTypeInfoResolver)
=> _umbracoJsonTypeInfoResolver = umbracoJsonTypeInfoResolver;
/// <summary>
/// Determines whether this handler can process the specified type based on namespace.
/// </summary>
/// <param name="type">The type to check.</param>
/// <returns><c>true</c> if the type is in an Umbraco.Cms namespace; otherwise, <c>false</c>.</returns>
protected virtual bool CanHandle(Type type)
=> type.Namespace?.StartsWith("Umbraco.Cms") is true;
/// <inheritdoc/>
public virtual bool CanHandle(Type type, string documentName)
=> CanHandle(type);
/// <inheritdoc/>
public virtual IEnumerable<Type> Handle(Type type)
=> _umbracoJsonTypeInfoResolver.FindSubTypes(type);
}
@@ -8,6 +8,9 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// Selects sub-types for polymorphic OpenAPI schemas using registered handlers.
/// </summary>
public class SubTypesSelector : ISubTypesSelector
{
private readonly IHostingEnvironment _hostingEnvironment;
@@ -15,20 +18,13 @@ public class SubTypesSelector : ISubTypesSelector
private readonly IEnumerable<ISubTypesHandler> _subTypeHandlers;
private readonly IUmbracoJsonTypeInfoResolver _umbracoJsonTypeInfoResolver;
[Obsolete("The settings parameter is not required anymore, use the other constructor instead. Scheduled for removal in Umbraco 17.")]
public SubTypesSelector(
IOptions<GlobalSettings> settings,
IHostingEnvironment hostingEnvironment,
IHttpContextAccessor httpContextAccessor,
IEnumerable<ISubTypesHandler> subTypeHandlers,
IUmbracoJsonTypeInfoResolver umbracoJsonTypeInfoResolver)
{
_hostingEnvironment = hostingEnvironment;
_httpContextAccessor = httpContextAccessor;
_subTypeHandlers = subTypeHandlers;
_umbracoJsonTypeInfoResolver = umbracoJsonTypeInfoResolver;
}
/// <summary>
/// Initializes a new instance of the <see cref="SubTypesSelector"/> class.
/// </summary>
/// <param name="hostingEnvironment">The hosting environment.</param>
/// <param name="httpContextAccessor">The HTTP context accessor.</param>
/// <param name="subTypeHandlers">The registered sub-type handlers.</param>
/// <param name="umbracoJsonTypeInfoResolver">The JSON type info resolver for finding sub-types.</param>
public SubTypesSelector(
IHostingEnvironment hostingEnvironment,
IHttpContextAccessor httpContextAccessor,
@@ -41,6 +37,7 @@ public class SubTypesSelector : ISubTypesSelector
_umbracoJsonTypeInfoResolver = umbracoJsonTypeInfoResolver;
}
/// <inheritdoc/>
public IEnumerable<Type> SubTypes(Type type)
{
var backOfficePath = _hostingEnvironment.GetBackOfficePath();
@@ -49,8 +46,7 @@ public class SubTypesSelector : ISubTypesSelector
if (_httpContextAccessor.HttpContext?.Request.Path.StartsWithSegments(swaggerPath) ?? false)
{
// Split the path into segments
var segments = _httpContextAccessor.HttpContext.Request.Path.Value!
.Substring(swaggerPath.Length)
var segments = _httpContextAccessor.HttpContext.Request.Path.Value![swaggerPath.Length..]
.TrimStart(Constants.CharArrays.ForwardSlash)
.Split(Constants.CharArrays.ForwardSlash);
@@ -3,19 +3,25 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.SwaggerUI;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Web.Common.ApplicationBuilder;
using Umbraco.Extensions;
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;
namespace Umbraco.Cms.Api.Common.OpenApi;
/// <summary>
/// Pipeline filter that configures Swagger/OpenAPI endpoints for Umbraco APIs.
/// </summary>
public class SwaggerRouteTemplatePipelineFilter : UmbracoPipelineFilter
{
/// <summary>
/// Initializes a new instance of the <see cref="SwaggerRouteTemplatePipelineFilter"/> class.
/// </summary>
/// <param name="name">The name of the pipeline filter.</param>
public SwaggerRouteTemplatePipelineFilter(string name)
: base(name)
=> PostPipeline = PostPipelineAction;
@@ -37,15 +43,36 @@ public class SwaggerRouteTemplatePipelineFilter : UmbracoPipelineFilter
applicationBuilder.UseSwaggerUI(swaggerUiOptions => SwaggerUiConfiguration(swaggerUiOptions, swaggerGenOptions.Value, applicationBuilder));
}
/// <summary>
/// Determines whether Swagger is enabled for the application.
/// </summary>
/// <param name="applicationBuilder">The application builder.</param>
/// <returns><c>true</c> if Swagger is enabled; otherwise, <c>false</c>.</returns>
protected virtual bool SwaggerIsEnabled(IApplicationBuilder applicationBuilder)
=> applicationBuilder.ApplicationServices.GetRequiredService<IWebHostEnvironment>().IsProduction() is false;
/// <summary>
/// Gets the route template for Swagger JSON endpoints.
/// </summary>
/// <param name="applicationBuilder">The application builder.</param>
/// <returns>The Swagger route template.</returns>
protected virtual string SwaggerRouteTemplate(IApplicationBuilder applicationBuilder)
=> $"{GetBackOfficePath(applicationBuilder).TrimStart(Constants.CharArrays.ForwardSlash)}/swagger/{{documentName}}/swagger.json";
/// <summary>
/// Gets the route prefix for the Swagger UI.
/// </summary>
/// <param name="applicationBuilder">The application builder.</param>
/// <returns>The Swagger UI route prefix.</returns>
protected virtual string SwaggerUiRoutePrefix(IApplicationBuilder applicationBuilder)
=> $"{GetBackOfficePath(applicationBuilder).TrimStart(Constants.CharArrays.ForwardSlash)}/swagger";
/// <summary>
/// Configures the Swagger UI options.
/// </summary>
/// <param name="swaggerUiOptions">The Swagger UI options to configure.</param>
/// <param name="swaggerGenOptions">The Swagger generation options.</param>
/// <param name="applicationBuilder">The application builder.</param>
protected virtual void SwaggerUiConfiguration(
SwaggerUIOptions swaggerUiOptions,
SwaggerGenOptions swaggerGenOptions,
@@ -4,30 +4,64 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Common.Rendering;
/// <summary>
/// Implements output expansion strategy for element-only rendering in the Delivery API.
/// </summary>
/// <remarks>
/// This strategy handles the expansion and filtering of properties when rendering content
/// through the Delivery API based on expand and fields query parameters.
/// </remarks>
public class ElementOnlyOutputExpansionStrategy : IOutputExpansionStrategy
{
/// <summary>
/// The parameter value indicating all properties should be included.
/// </summary>
protected const string All = "$all";
/// <summary>
/// The parameter value indicating no properties should be included.
/// </summary>
protected const string None = "";
/// <summary>
/// The name of the expand query parameter.
/// </summary>
protected const string ExpandParameterName = "expand";
/// <summary>
/// The name of the fields query parameter.
/// </summary>
protected const string FieldsParameterName = "fields";
private readonly IApiPropertyRenderer _propertyRenderer;
/// <summary>
/// Gets the stack of expand property nodes for tracking nested expansions.
/// </summary>
protected Stack<Node?> ExpandProperties { get; } = new();
/// <summary>
/// Gets the stack of include property nodes for tracking nested field selections.
/// </summary>
protected Stack<Node?> IncludeProperties { get; } = new();
/// <summary>
/// Initializes a new instance of the <see cref="ElementOnlyOutputExpansionStrategy"/> class.
/// </summary>
/// <param name="propertyRenderer">The property renderer for converting property values.</param>
public ElementOnlyOutputExpansionStrategy(
IApiPropertyRenderer propertyRenderer)
{
_propertyRenderer = propertyRenderer;
}
/// <inheritdoc/>
public virtual IDictionary<string, object?> MapContentProperties(IPublishedContent content)
=> content.ItemType == PublishedItemType.Content
? MapProperties(content.Properties)
: throw new ArgumentException($"Invalid item type. This method can only be used with item type {nameof(PublishedItemType.Content)}, got: {content.ItemType}");
/// <inheritdoc/>
public virtual IDictionary<string, object?> MapMediaProperties(IPublishedContent media, bool skipUmbracoProperties = true)
{
if (media.ItemType != PublishedItemType.Media)
@@ -45,6 +79,7 @@ public class ElementOnlyOutputExpansionStrategy : IOutputExpansionStrategy
: new Dictionary<string, object?>();
}
/// <inheritdoc/>
public virtual IDictionary<string, object?> MapElementProperties(IPublishedElement element)
=> MapProperties(element.Properties, true);
@@ -87,12 +122,27 @@ public class ElementOnlyOutputExpansionStrategy : IOutputExpansionStrategy
private object? GetPropertyValue(IPublishedProperty property)
=> _propertyRenderer.GetPropertyValue(property, ExpandProperties.Peek() is not null);
/// <summary>
/// Represents a node in the parsed expand/fields parameter tree structure.
/// </summary>
protected sealed class Node
{
/// <summary>
/// Gets the key of this node.
/// </summary>
public string Key { get; private set; } = string.Empty;
/// <summary>
/// Gets the child nodes of this node.
/// </summary>
public List<Node> Items { get; } = new();
/// <summary>
/// Parses an expand/fields parameter value into a node tree structure.
/// </summary>
/// <param name="value">The parameter value to parse.</param>
/// <returns>The root node of the parsed tree.</returns>
/// <exception cref="ArgumentException">Thrown when the value has invalid syntax.</exception>
public static Node Parse(string value)
{
// verify that there are as many start brackets as there are end brackets
@@ -0,0 +1,84 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using OpenIddict.Server;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Security;
using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Security;
/// <summary>
/// Provides OpenIddict server event handlers to expose the backoffice authentication token via a custom authentication scheme.
/// </summary>
public class ExposeBackOfficeAuthenticationOpenIddictServerEventsHandler : IOpenIddictServerHandler<OpenIddictServerEvents.GenerateTokenContext>,
IOpenIddictServerHandler<OpenIddictServerEvents.ApplyRevocationResponseContext>
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly string[] _claimTypes;
private readonly TimeSpan _timeOut;
/// <summary>
/// Initializes a new instance of the <see cref="ExposeBackOfficeAuthenticationOpenIddictServerEventsHandler"/> class.
/// </summary>
public ExposeBackOfficeAuthenticationOpenIddictServerEventsHandler(
IHttpContextAccessor httpContextAccessor,
IOptions<GlobalSettings> globalSettings,
IOptions<BackOfficeIdentityOptions> backOfficeIdentityOptions)
{
_httpContextAccessor = httpContextAccessor;
_timeOut = globalSettings.Value.TimeOut;
// These are the type identifiers for the claims required by the principal
// for the custom authentication scheme.
// We make available the ID, user name and allowed applications (sections) claims.
_claimTypes =
[
backOfficeIdentityOptions.Value.ClaimsIdentity.UserIdClaimType,
backOfficeIdentityOptions.Value.ClaimsIdentity.UserNameClaimType,
Core.Constants.Security.AllowedApplicationsClaimType,
];
}
/// <inheritdoc/>
/// <remarks>
/// Event handler for when access tokens are generated (created or refreshed).
/// </remarks>
public async ValueTask HandleAsync(OpenIddictServerEvents.GenerateTokenContext context)
{
// Only proceed if this is a back-office sign-in.
if (context.Principal.Identity?.AuthenticationType != Core.Constants.Security.BackOfficeAuthenticationType)
{
return;
}
// Create a new principal with the claims from the authenticated principal.
var principal = new ClaimsPrincipal(
new ClaimsIdentity(
context.Principal.Claims.Where(claim => _claimTypes.Contains(claim.Type)),
Core.Constants.Security.BackOfficeExposedAuthenticationType));
// Sign-in the new principal for the custom authentication scheme.
await _httpContextAccessor
.GetRequiredHttpContext()
.SignInAsync(Core.Constants.Security.BackOfficeExposedAuthenticationType, principal, GetAuthenticationProperties());
}
/// <inheritdoc/>
/// <remarks>
/// Event handler for when access tokens are revoked.
/// </remarks>
public async ValueTask HandleAsync(OpenIddictServerEvents.ApplyRevocationResponseContext context)
=> await _httpContextAccessor
.GetRequiredHttpContext()
.SignOutAsync(Core.Constants.Security.BackOfficeExposedAuthenticationType, GetAuthenticationProperties());
private AuthenticationProperties GetAuthenticationProperties()
=> new()
{
IsPersistent = true,
IssuedUtc = DateTimeOffset.UtcNow,
ExpiresUtc = DateTimeOffset.UtcNow.Add(_timeOut)
};
}
@@ -2,35 +2,77 @@ using Umbraco.Cms.Core;
namespace Umbraco.Cms.Api.Common.Security;
/// <summary>
/// Contains OAuth/OpenID Connect endpoint paths for Umbraco APIs.
/// </summary>
public static class Paths
{
/// <summary>
/// Contains endpoint paths for back-office authentication.
/// </summary>
public static class BackOfficeApi
{
/// <summary>
/// The base endpoint template for back-office security endpoints.
/// </summary>
public const string EndpointTemplate = "security/back-office";
/// <summary>
/// The authorization endpoint path.
/// </summary>
public static readonly string AuthorizationEndpoint = EndpointPath($"{EndpointTemplate}/authorize");
/// <summary>
/// The token endpoint path.
/// </summary>
public static readonly string TokenEndpoint = EndpointPath($"{EndpointTemplate}/token");
/// <summary>
/// The logout/sign-out endpoint path.
/// </summary>
public static readonly string LogoutEndpoint = EndpointPath($"{EndpointTemplate}/signout");
/// <summary>
/// The token revocation endpoint path.
/// </summary>
public static readonly string RevokeEndpoint = EndpointPath($"{EndpointTemplate}/revoke");
private static string EndpointPath(string relativePath) => $"/umbraco{Constants.Web.ManagementApiPath}v1/{relativePath}";
}
/// <summary>
/// Contains endpoint paths for member authentication.
/// </summary>
public static class MemberApi
{
/// <summary>
/// The base endpoint template for member security endpoints.
/// </summary>
public const string EndpointTemplate = "security/member";
/// <summary>
/// The authorization endpoint path.
/// </summary>
public static readonly string AuthorizationEndpoint = EndpointPath($"{EndpointTemplate}/authorize");
/// <summary>
/// The token endpoint path.
/// </summary>
public static readonly string TokenEndpoint = EndpointPath($"{EndpointTemplate}/token");
/// <summary>
/// The logout/sign-out endpoint path.
/// </summary>
public static readonly string LogoutEndpoint = EndpointPath($"{EndpointTemplate}/signout");
/// <summary>
/// The token revocation endpoint path.
/// </summary>
public static readonly string RevokeEndpoint = EndpointPath($"{EndpointTemplate}/revoke");
/// <summary>
/// The user info endpoint path.
/// </summary>
public static readonly string UserinfoEndpoint = EndpointPath($"{EndpointTemplate}/userinfo");
// NOTE: we're NOT using /api/v1.0/ here because it will clash with the Delivery API docs
@@ -2,9 +2,22 @@ using System.Text.Json.Serialization.Metadata;
namespace Umbraco.Cms.Api.Common.Serialization;
/// <summary>
/// Extends <see cref="IJsonTypeInfoResolver"/> with Umbraco-specific type resolution for polymorphic JSON serialization.
/// </summary>
public interface IUmbracoJsonTypeInfoResolver : IJsonTypeInfoResolver
{
/// <summary>
/// Finds all sub-types of the specified type for polymorphic serialization.
/// </summary>
/// <param name="type">The base type to find sub-types for.</param>
/// <returns>An enumerable of sub-types.</returns>
IEnumerable<Type> FindSubTypes(Type type);
/// <summary>
/// Gets the type discriminator value used for polymorphic serialization.
/// </summary>
/// <param name="type">The type to get the discriminator value for.</param>
/// <returns>The discriminator value, or <c>null</c> if not applicable.</returns>
string? GetTypeDiscriminatorValue(Type type);
}
@@ -8,14 +8,26 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Common.Serialization;
/// <summary>
/// Implements JSON type info resolution for Umbraco with support for polymorphic serialization.
/// </summary>
/// <remarks>
/// This resolver discovers sub-types of interfaces for polymorphic JSON serialization,
/// caching results for performance. It also handles type discriminator values for OpenAPI schema generation.
/// </remarks>
public sealed class UmbracoJsonTypeInfoResolver : DefaultJsonTypeInfoResolver, IUmbracoJsonTypeInfoResolver
{
private readonly ITypeFinder _typeFinder;
private readonly ConcurrentDictionary<Type, ISet<Type>> _subTypesCache = new ConcurrentDictionary<Type, ISet<Type>>();
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoJsonTypeInfoResolver"/> class.
/// </summary>
/// <param name="typeFinder">The type finder for discovering sub-types.</param>
public UmbracoJsonTypeInfoResolver(ITypeFinder typeFinder)
=> _typeFinder = typeFinder;
/// <inheritdoc/>
public IEnumerable<Type> FindSubTypes(Type type)
{
JsonDerivedTypeAttribute[] explicitJsonDerivedTypes = type
@@ -44,6 +56,7 @@ public sealed class UmbracoJsonTypeInfoResolver : DefaultJsonTypeInfoResolver, I
return result;
}
/// <inheritdoc/>
public string? GetTypeDiscriminatorValue(Type type)
{
JsonDerivedTypeAttribute? jsonDerivedTypeAttribute = type
@@ -62,6 +75,7 @@ public sealed class UmbracoJsonTypeInfoResolver : DefaultJsonTypeInfoResolver, I
return typeof(IOpenApiDiscriminator).IsAssignableFrom(type) ? type.Name : null;
}
/// <inheritdoc/>
public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
{
JsonTypeInfo result = base.GetTypeInfo(type, options);
@@ -4,6 +4,12 @@
<Description>Contains the bits and pieces that are shared between the Umbraco CMS APIs.</Description>
</PropertyGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Umbraco.Tests.UnitTests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
@@ -16,11 +22,6 @@
<PackageReference Include="OpenIddict.AspNetCore" />
</ItemGroup>
<ItemGroup>
<!-- Take top-level depedendency on OpenIddict.AspNetCore depends on a vulnerable version -->
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj" />
<ProjectReference Include="..\Umbraco.Web.Common\Umbraco.Web.Common.csproj" />
@@ -1,14 +1,28 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
namespace Umbraco.Cms.Api.Common.ViewModels.Pagination;
/// <summary>
/// Represents a paged collection of items with total count.
/// </summary>
/// <typeparam name="T">The type of items in the collection.</typeparam>
public class PagedViewModel<T>
{
/// <summary>
/// Gets or sets the total number of items available.
/// </summary>
[Required]
public long Total { get; set; }
/// <summary>
/// Gets or sets the items in the current page.
/// </summary>
[Required]
public IEnumerable<T> Items { get; set; } = Enumerable.Empty<T>();
/// <summary>
/// Creates an empty paged view model.
/// </summary>
/// <returns>An empty <see cref="PagedViewModel{T}"/> instance.</returns>
public static PagedViewModel<T> Empty() => new();
}
@@ -2,16 +2,33 @@ using System.ComponentModel.DataAnnotations;
namespace Umbraco.Cms.Api.Common.ViewModels.Pagination;
/// <summary>
/// Represents a subset of items with counts of items before and after the subset.
/// </summary>
/// <typeparam name="T">The type of items in the collection.</typeparam>
public class SubsetViewModel<T>
{
/// <summary>
/// Gets or sets the total number of items before this subset.
/// </summary>
[Required]
public long TotalBefore { get; set; }
/// <summary>
/// Gets or sets the total number of items after this subset.
/// </summary>
[Required]
public long TotalAfter { get; set; }
/// <summary>
/// Gets or sets the items in the subset.
/// </summary>
[Required]
public IEnumerable<T> Items { get; set; } = Enumerable.Empty<T>();
/// <summary>
/// Creates an empty subset view model.
/// </summary>
/// <returns>An empty <see cref="SubsetViewModel{T}"/> instance.</returns>
public static SubsetViewModel<T> Empty() => new();
}
+382
View File
@@ -0,0 +1,382 @@
# Umbraco.Cms.Api.Delivery
Headless content delivery REST API for Umbraco CMS. Enables frontend applications to fetch published content, media, and member-protected resources.
---
## 1. Architecture
**Type**: Class Library (NuGet Package)
**Target Framework**: .NET 10.0
**Purpose**: Content Delivery API for headless CMS scenarios
### Key Technologies
- **ASP.NET Core** - Web framework
- **OpenIddict** - Member authentication (OAuth 2.0)
- **Asp.Versioning** - API versioning (V1, V2)
- **Output Caching** - Configurable response caching
- **Examine/Lucene** - Content querying
### Dependencies
- `Umbraco.Cms.Api.Common` - Shared API infrastructure (OpenAPI, auth)
- `Umbraco.Web.Common` - Web functionality
### Project Structure (86 files)
```
Umbraco.Cms.Api.Delivery/
├── Controllers/
│ ├── Content/ # Content endpoints (by ID, route, query)
│ ├── Media/ # Media endpoints (by ID, path, query)
│ └── Security/ # Member auth (authorize, token, signout)
├── Querying/
│ ├── Filters/ # ContentType, Name, CreateDate, UpdateDate
│ ├── Selectors/ # Ancestors, Children, Descendants
│ └── Sorts/ # Name, CreateDate, UpdateDate, Level, SortOrder
├── Indexing/ # Lucene index field handlers
├── Services/ # Business logic and query building
├── Caching/ # Output cache policies
├── Rendering/ # Output expansion strategies
├── Configuration/ # Swagger configuration
└── Filters/ # Action filters (access, validation)
```
### Design Patterns
1. **Strategy Pattern** - Query handlers (`ISelectorHandler`, `IFilterHandler`, `ISortHandler`)
2. **Factory Pattern** - `ApiContentQueryFactory` builds Examine queries
3. **Template Method** - `ContentApiControllerBase` for shared controller logic
4. **Options Pattern** - `DeliveryApiSettings` for all configuration
---
## 2. Commands
See "Quick Reference" section at bottom for common commands.
---
## 3. Key Patterns
### API Versioning (V1 vs V2)
**V1** (legacy) and **V2** (current) coexist. Key difference is output expansion:
```csharp
// DependencyInjection/UmbracoBuilderExtensions.cs:49-52
// V1 uses RequestContextOutputExpansionStrategy
// V2+ uses RequestContextOutputExpansionStrategyV2
return apiVersion.MajorVersion == 1
? provider.GetRequiredService<RequestContextOutputExpansionStrategy>()
: provider.GetRequiredService<RequestContextOutputExpansionStrategyV2>();
```
**Why V2**: Improved `expand` and `fields` query parameter parsing (tree-based).
### Query System Architecture
Content querying flows through handlers registered in DI:
1. **Selectors** (`fetch` parameter): `ancestors:id`, `children:id`, `descendants:id`
2. **Filters** (`filter[]` parameter): `contentType:alias`, `name:value`, `createDate>2024-01-01`
3. **Sorts** (`sort[]` parameter): `name:asc`, `createDate:desc`, `level:asc`
```csharp
// Services/ApiContentQueryService.cs:91-96
ISelectorHandler? selectorHandler = _selectorHandlers.FirstOrDefault(h => h.CanHandle(fetch));
return selectorHandler?.BuildSelectorOption(fetch);
```
### Path Decoding Workaround
ASP.NET Core doesn't decode forward slashes in route parameters:
```csharp
// Controllers/DeliveryApiControllerBase.cs:21-31
// OpenAPI clients URL-encode paths, but ASP.NET Core doesn't decode "/"
// See https://github.com/dotnet/aspnetcore/issues/11544
if (path.Contains("%2F", StringComparison.OrdinalIgnoreCase))
{
path = WebUtility.UrlDecode(path);
}
```
---
## 4. Testing
**Location**: No direct tests - tested via integration tests in test projects
```bash
dotnet test tests/Umbraco.Tests.Integration/ --filter "FullyQualifiedName~Delivery"
```
**Internals exposed to** (csproj lines 28-36):
- `Umbraco.Tests.UnitTests`
- `Umbraco.Tests.Integration`
- `DynamicProxyGenAssembly2` (for mocking)
**Focus areas**:
- Query parsing (selectors, filters, sorts)
- Member authentication flows
- Output caching behavior
- Protected content access
---
## 5. Security & Access Control
### Three Access Modes
```csharp
// Services/ApiAccessService.cs:21-27
public bool HasPublicAccess() => _deliveryApiSettings.PublicAccess || HasValidApiKey();
public bool HasPreviewAccess() => HasValidApiKey();
public bool HasMediaAccess() => _deliveryApiSettings is { PublicAccess: true, Media.PublicAccess: true } || HasValidApiKey();
```
**Access levels**:
1. **Public** - No authentication required (if enabled)
2. **API Key** - Via `Api-Key` header
3. **Preview** - Always requires API key
### Member Authentication
OpenIddict-based OAuth 2.0 for member-protected content:
**Flows supported** (Controllers/Security/MemberController.cs):
- Authorization Code + PKCE (line 53)
- Client Credentials (line 112)
- Refresh Token (line 98)
**Endpoints**:
- `GET /umbraco/delivery/api/v1/security/member/authorize`
- `POST /umbraco/delivery/api/v1/security/member/token`
- `GET /umbraco/delivery/api/v1/security/member/signout`
**Scopes**: Only `openid` and `offline_access` allowed for members (line 220-222)
### Protected Content
Member access checked via `ProtectedAccess` model:
```csharp
// Controllers/Content/QueryContentApiController.cs:56-57
ProtectedAccess protectedAccess = await _requestMemberAccessService.MemberAccessAsync();
Attempt<PagedModel<Guid>, ApiContentQueryOperationStatus> queryAttempt =
_apiContentQueryService.ExecuteQuery(fetch, filter, sort, protectedAccess, skip, take);
```
---
## 6. Output Caching
### Cache Policy Configuration
```csharp
// DependencyInjection/UmbracoBuilderExtensions.cs:120-136
// Content and Media have separate cache durations
options.AddPolicy(
Constants.DeliveryApi.OutputCache.ContentCachePolicy,
new DeliveryApiOutputCachePolicy(
outputCacheSettings.ContentDuration,
new StringValues([AcceptLanguage, AcceptSegment, StartItem])));
```
**Cache invalidation conditions** (Caching/DeliveryApiOutputCachePolicy.cs:31):
```csharp
// Never cache preview or non-public access
context.EnableOutputCaching = requestPreviewService.IsPreview() is false
&& apiAccessService.HasPublicAccess();
```
**Vary by headers**: `Accept-Language`, `Accept-Segment`, `Start-Item`
---
## 7. Edge Cases & Known Issues
### Technical Debt (TODOs in codebase)
1. **V1 Removal Pending** (4 locations):
- `DependencyInjection/UmbracoBuilderExtensions.cs:98` - FIXME: remove matcher policy
- `Routing/DeliveryApiItemsEndpointsMatcherPolicy.cs:11` - FIXME: remove class
- `Filters/SwaggerDocumentationFilterBase.cs:79,83` - FIXME: remove V1 swagger docs
2. **Obsolete Reference Warnings** (csproj:9-13):
- `ASP0019` - IHeaderDictionary.Append usage
- `CS0618/CS0612` - Obsolete member references
### Empty Query Results
Query service returns empty results (not errors) for invalid options:
```csharp
// Services/ApiContentQueryService.cs:54-78
// Invalid selector/filter/sort returns fail status with empty result
return Attempt.FailWithStatus(ApiContentQueryOperationStatus.SelectorOptionNotFound, emptyResult);
```
### Start Item Fallback
When no `fetch` parameter provided, uses start item or all content:
```csharp
// Services/ApiContentQueryService.cs:99-112
if (_requestStartItemProviderAccessor.TryGetValue(out IRequestStartItemProvider? requestStartItemProvider))
{
IPublishedContent? startItem = requestStartItemProvider.GetStartItem();
// Use descendants of start item
}
return _apiContentQueryProvider.AllContentSelectorOption(); // Fallback to all
```
---
## 8. Project-Specific Notes
### V1 vs V2 Differences
| Feature | V1 | V2 |
|---------|----|----|
| Output expansion | Basic | Tree-based parsing |
| `expand` parameter | Flat list | Nested syntax |
| `fields` parameter | Limited | Full property selection |
| Default expansion strategy | `RequestContextOutputExpansionStrategy` | `RequestContextOutputExpansionStrategyV2` |
**Migration note**: V1 is deprecated; plan removal when V17+ drops V1 support.
### JSON Configuration
Delivery API has its own JSON options (distinct from Management API):
```csharp
// DependencyInjection/UmbracoBuilderExtensions.cs:82-88
.AddJsonOptions(Constants.JsonOptionsNames.DeliveryApi, options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.TypeInfoResolver = new DeliveryApiJsonTypeResolver();
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
```
### Member Token Revocation
Tokens automatically revoked on member changes:
```csharp
// DependencyInjection/UmbracoBuilderExtensions.cs:93-96
builder.AddNotificationAsyncHandler<MemberSavedNotification, RevokeMemberAuthenticationTokensNotificationHandler>();
builder.AddNotificationAsyncHandler<MemberDeletedNotification, RevokeMemberAuthenticationTokensNotificationHandler>();
builder.AddNotificationAsyncHandler<AssignedMemberRolesNotification, RevokeMemberAuthenticationTokensNotificationHandler>();
builder.AddNotificationAsyncHandler<RemovedMemberRolesNotification, RevokeMemberAuthenticationTokensNotificationHandler>();
```
### External Dependencies
**Examine/Lucene** (via Core):
- Powers content querying
- Selector/Filter/Sort handlers build Lucene queries
**OpenIddict** (via Api.Common):
- Member OAuth 2.0 authentication
- Reference tokens (not JWT)
### Configuration (appsettings.json)
```json
{
"Umbraco": {
"CMS": {
"DeliveryApi": {
"Enabled": true,
"PublicAccess": true,
"ApiKey": "your-api-key",
"Media": {
"Enabled": true,
"PublicAccess": true
},
"MemberAuthorization": {
"AuthorizationCodeFlow": { "Enabled": true },
"ClientCredentialsFlow": { "Enabled": false }
},
"OutputCache": {
"Enabled": true,
"ContentDuration": "00:01:00",
"MediaDuration": "00:01:00"
}
}
}
}
}
```
### API Endpoints Summary
**Content** (`/umbraco/delivery/api/v2/content`):
- `GET /item/{id}` - Single content by GUID
- `GET /item/{path}` - Single content by route
- `GET /items` - Multiple by IDs
- `GET /` - Query with fetch/filter/sort
**Media** (`/umbraco/delivery/api/v2/media`):
- `GET /item/{id}` - Single media by GUID
- `GET /item/{path}` - Single media by path
- `GET /items` - Multiple by IDs
- `GET /` - Query media
**Security** (`/umbraco/delivery/api/v1/security/member`):
- `GET /authorize` - Start OAuth flow
- `POST /token` - Exchange code for token
- `GET /signout` - Revoke session
---
## Quick Reference
### Essential Commands
```bash
# Build project
dotnet build src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj
# Pack for NuGet
dotnet pack src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj -c Release
# Run integration tests
dotnet test tests/Umbraco.Tests.Integration/ --filter "FullyQualifiedName~Delivery"
# Check packages
dotnet list src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj package --outdated
```
### Key Classes
| Class | Purpose | File |
|-------|---------|------|
| `DeliveryApiControllerBase` | Base controller with path decoding | Controllers/DeliveryApiControllerBase.cs |
| `ApiContentQueryService` | Query orchestration | Services/ApiContentQueryService.cs |
| `ApiAccessService` | Access control logic | Services/ApiAccessService.cs |
| `DeliveryApiOutputCachePolicy` | Cache policy implementation | Caching/DeliveryApiOutputCachePolicy.cs |
| `MemberController` | OAuth endpoints | Controllers/Security/MemberController.cs |
| `RequestContextOutputExpansionStrategyV2` | V2 output expansion | Rendering/RequestContextOutputExpansionStrategyV2.cs |
### Important Files
- `Umbraco.Cms.Api.Delivery.csproj` - Project dependencies
- `DependencyInjection/UmbracoBuilderExtensions.cs` - DI registration (lines 33-141)
- `Configuration/DeliveryApiConfiguration.cs` - API constants
- `Services/ApiContentQueryService.cs` - Query execution
### Getting Help
- **Root documentation**: `/CLAUDE.md` - Repository overview
- **API Common patterns**: `/src/Umbraco.Cms.Api.Common/CLAUDE.md`
- **Official docs**: https://docs.umbraco.com/umbraco-cms/reference/content-delivery-api
- **Media docs**: https://docs.umbraco.com/umbraco-cms/reference/content-delivery-api/media-delivery-api
---
**This library exposes Umbraco content and media via REST for headless scenarios. Focus on query handlers, access control, and member authentication when working here.**
@@ -1,6 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;
using Umbraco.Cms.Api.Common.OpenApi;
using Umbraco.Cms.Api.Delivery.Filters;
@@ -1,6 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;
using Umbraco.Cms.Api.Common.Security;
using Umbraco.Cms.Api.Delivery.Controllers.Content;
@@ -35,23 +35,9 @@ public class ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions :
return;
}
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = AuthSchemeName,
}
},
[]
}
}
};
var schemaRef = new OpenApiSecuritySchemeReference(AuthSchemeName, context.Document);
operation.Security ??= new List<OpenApiSecurityRequirement>();
operation.Security.Add(new OpenApiSecurityRequirement { [schemaRef] = [] });
}
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
@@ -61,7 +47,7 @@ public class ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions :
return;
}
swaggerDoc.Components.SecuritySchemes.Add(
swaggerDoc.AddComponent(
AuthSchemeName,
new OpenApiSecurityScheme
{
@@ -74,9 +60,9 @@ public class ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions :
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri(Paths.MemberApi.AuthorizationEndpoint, UriKind.Relative),
TokenUrl = new Uri(Paths.MemberApi.TokenEndpoint, UriKind.Relative)
}
}
TokenUrl = new Uri(Paths.MemberApi.TokenEndpoint, UriKind.Relative),
},
},
});
}
}
@@ -99,8 +99,8 @@ public class ByRouteContentApiController : ContentApiItemControllerBase
private IActionResult RedirectTo(IApiContentRoute redirectRoute)
{
Response.Headers.Add("Location-Start-Item-Path", redirectRoute.StartItem.Path);
Response.Headers.Add("Location-Start-Item-Id", redirectRoute.StartItem.Id.ToString("D"));
Response.Headers.Append("Location-Start-Item-Path", redirectRoute.StartItem.Path);
Response.Headers.Append("Location-Start-Item-Id", redirectRoute.StartItem.Id.ToString("D"));
return RedirectPermanent(redirectRoute.Path);
}
}
@@ -1,9 +1,9 @@
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using System.Text.Json.Nodes;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;
using Umbraco.Cms.Api.Delivery.Configuration;
using Umbraco.Cms.Api.Delivery.Controllers;
using Umbraco.Cms.Api.Delivery.Controllers.Content;
using Umbraco.Cms.Core;
namespace Umbraco.Cms.Api.Delivery.Filters;
@@ -13,7 +13,7 @@ internal sealed class SwaggerContentDocumentationFilter : SwaggerDocumentationFi
protected override void ApplyOperation(OpenApiOperation operation, OperationFilterContext context)
{
operation.Parameters ??= new List<OpenApiParameter>();
operation.Parameters ??= new List<IOpenApiParameter>();
AddExpand(operation, context);
@@ -21,50 +21,50 @@ internal sealed class SwaggerContentDocumentationFilter : SwaggerDocumentationFi
operation.Parameters.Add(new OpenApiParameter
{
Name = Core.Constants.DeliveryApi.HeaderNames.AcceptLanguage,
Name = Constants.DeliveryApi.HeaderNames.AcceptLanguage,
In = ParameterLocation.Header,
Required = false,
Description = "Defines the language to return. Use this when querying language variant content items.",
Schema = new OpenApiSchema { Type = "string" },
Examples = new Dictionary<string, OpenApiExample>
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
Examples = new Dictionary<string, IOpenApiExample>
{
{ "Default", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
{ "English culture", new OpenApiExample { Value = new OpenApiString("en-us") } }
}
{ "Default", new OpenApiExample { Value = string.Empty } },
{ "English culture", new OpenApiExample { Value = "en-us" } },
},
});
operation.Parameters.Add(new OpenApiParameter
{
Name = Core.Constants.DeliveryApi.HeaderNames.AcceptSegment,
Name = Constants.DeliveryApi.HeaderNames.AcceptSegment,
In = ParameterLocation.Header,
Required = false,
Description = "Defines the segment to return. Use this when querying segment variant content items.",
Schema = new OpenApiSchema { Type = "string" },
Examples = new Dictionary<string, OpenApiExample>
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
Examples = new Dictionary<string, IOpenApiExample>
{
{ "Default", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
{ "Segment One", new OpenApiExample { Value = new OpenApiString("segment-one") } }
}
{ "Default", new OpenApiExample { Value = string.Empty } },
{ "Segment One", new OpenApiExample { Value = "segment-one" } },
},
});
AddApiKey(operation);
operation.Parameters.Add(new OpenApiParameter
{
Name = Core.Constants.DeliveryApi.HeaderNames.Preview,
Name = Constants.DeliveryApi.HeaderNames.Preview,
In = ParameterLocation.Header,
Required = false,
Description = "Whether to request draft content.",
Schema = new OpenApiSchema { Type = "boolean" }
Schema = new OpenApiSchema { Type = JsonSchemaType.Boolean },
});
operation.Parameters.Add(new OpenApiParameter
{
Name = Core.Constants.DeliveryApi.HeaderNames.StartItem,
Name = Constants.DeliveryApi.HeaderNames.StartItem,
In = ParameterLocation.Header,
Required = false,
Description = "URL segment or GUID of a root content item.",
Schema = new OpenApiSchema { Type = "string" }
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
});
}
@@ -92,105 +92,36 @@ internal sealed class SwaggerContentDocumentationFilter : SwaggerDocumentationFi
}
}
private Dictionary<string, OpenApiExample> FetchQueryParameterExamples() =>
private Dictionary<string, IOpenApiExample> FetchQueryParameterExamples() =>
new()
{
{ "Select all", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
{
"Select all ancestors of a node by id",
new OpenApiExample { Value = new OpenApiString("ancestors:id") }
},
{
"Select all ancestors of a node by path",
new OpenApiExample { Value = new OpenApiString("ancestors:path") }
},
{
"Select all children of a node by id",
new OpenApiExample { Value = new OpenApiString("children:id") }
},
{
"Select all children of a node by path",
new OpenApiExample { Value = new OpenApiString("children:path") }
},
{
"Select all descendants of a node by id",
new OpenApiExample { Value = new OpenApiString("descendants:id") }
},
{
"Select all descendants of a node by path",
new OpenApiExample { Value = new OpenApiString("descendants:path") }
}
{ "Select all", new OpenApiExample { Value = string.Empty } },
{ "Select all ancestors of a node by id", new OpenApiExample { Value = "ancestors:id" } },
{ "Select all ancestors of a node by path", new OpenApiExample { Value = "ancestors:path" } },
{ "Select all children of a node by id", new OpenApiExample { Value = "children:id" } },
{ "Select all children of a node by path", new OpenApiExample { Value = "children:path" } },
{ "Select all descendants of a node by id", new OpenApiExample { Value = "descendants:id" } },
{ "Select all descendants of a node by path", new OpenApiExample { Value = "descendants:path" } },
};
private Dictionary<string, OpenApiExample> FilterQueryParameterExamples() =>
private Dictionary<string, IOpenApiExample> FilterQueryParameterExamples() =>
new()
{
{ "Default filter", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
{
"Filter by content type (equals)",
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("contentType:alias1") } }
},
{
"Filter by name (contains)",
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("name:nodeName") } }
},
{
"Filter by creation date (less than)",
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("createDate<2024-01-01") } }
},
{
"Filter by update date (greater than or equal)",
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("updateDate>:2023-01-01") } }
}
{ "Default filter", new OpenApiExample { Value = string.Empty } },
{ "Filter by content type (equals)", new OpenApiExample { Value = new JsonArray { "contentType:alias1" } } },
{ "Filter by name (contains)", new OpenApiExample { Value = new JsonArray { "name:nodeName" } } },
{ "Filter by creation date (less than)", new OpenApiExample { Value = new JsonArray { "createDate<2024-01-01" } } },
{ "Filter by update date (greater than or equal)", new OpenApiExample { Value = new JsonArray { "updateDate>:2023-01-01" } } },
};
private Dictionary<string, OpenApiExample> SortQueryParameterExamples() =>
private Dictionary<string, IOpenApiExample> SortQueryParameterExamples() =>
new()
{
{ "Default sort", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
{
"Sort by create date",
new OpenApiExample
{
Value = new OpenApiArray
{
new OpenApiString("createDate:asc"), new OpenApiString("createDate:desc")
}
}
},
{
"Sort by level",
new OpenApiExample
{
Value = new OpenApiArray { new OpenApiString("level:asc"), new OpenApiString("level:desc") }
}
},
{
"Sort by name",
new OpenApiExample
{
Value = new OpenApiArray { new OpenApiString("name:asc"), new OpenApiString("name:desc") }
}
},
{
"Sort by sort order",
new OpenApiExample
{
Value = new OpenApiArray
{
new OpenApiString("sortOrder:asc"), new OpenApiString("sortOrder:desc")
}
}
},
{
"Sort by update date",
new OpenApiExample
{
Value = new OpenApiArray
{
new OpenApiString("updateDate:asc"), new OpenApiString("updateDate:desc")
}
}
}
{ "Default sort", new OpenApiExample { Value = string.Empty } },
{ "Sort by create date", new OpenApiExample { Value = new JsonArray { "createDate:asc", "createDate:desc" } } },
{ "Sort by level", new OpenApiExample { Value = new JsonArray { "level:asc", "level:desc" } } },
{ "Sort by name", new OpenApiExample { Value = new JsonArray { "name:asc", "name:desc" } } },
{ "Sort by sort order", new OpenApiExample { Value = new JsonArray { "sortOrder:asc", "sortOrder:desc" } } },
{ "Sort by update date", new OpenApiExample { Value = new JsonArray { "updateDate:asc", "updateDate:desc" } } },
};
}
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;
using Umbraco.Cms.Core;
namespace Umbraco.Cms.Api.Delivery.Filters;
@@ -9,6 +9,8 @@ internal abstract class SwaggerDocumentationFilterBase<TBaseController>
: SwaggerFilterBase<TBaseController>, IOperationFilter, IParameterFilter
where TBaseController : Controller
{
protected abstract string DocumentationLink { get; }
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (CanApply(context))
@@ -17,21 +19,19 @@ internal abstract class SwaggerDocumentationFilterBase<TBaseController>
}
}
public void Apply(OpenApiParameter parameter, ParameterFilterContext context)
public void Apply(IOpenApiParameter parameter, ParameterFilterContext context)
{
if (CanApply(context))
if (CanApply(context) && parameter is OpenApiParameter openApiParameter)
{
ApplyParameter(parameter, context);
ApplyParameter(openApiParameter, context);
}
}
protected abstract string DocumentationLink { get; }
protected abstract void ApplyOperation(OpenApiOperation operation, OperationFilterContext context);
protected abstract void ApplyParameter(OpenApiParameter parameter, ParameterFilterContext context);
protected void AddQueryParameterDocumentation(OpenApiParameter parameter, Dictionary<string, OpenApiExample> examples, string description)
protected void AddQueryParameterDocumentation(OpenApiParameter parameter, Dictionary<string, IOpenApiExample> examples, string description)
{
parameter.Description = QueryParameterDescription(description);
parameter.Examples = examples;
@@ -60,15 +60,19 @@ internal abstract class SwaggerDocumentationFilterBase<TBaseController>
AddFields(operation);
}
protected void AddApiKey(OpenApiOperation operation) =>
operation.Parameters.Add(new OpenApiParameter
{
Name = Core.Constants.DeliveryApi.HeaderNames.ApiKey,
In = ParameterLocation.Header,
Required = false,
Description = "API key specified through configuration to authorize access to the API.",
Schema = new OpenApiSchema { Type = "string" }
});
protected void AddApiKey(OpenApiOperation operation)
{
operation.Parameters ??= new List<IOpenApiParameter>();
operation.Parameters.Add(
new OpenApiParameter
{
Name = Constants.DeliveryApi.HeaderNames.ApiKey,
In = ParameterLocation.Header,
Required = false,
Description = "API key specified through configuration to authorize access to the API.",
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
});
}
protected string PaginationDescription(bool skip, string itemType)
=> $"Specifies the number of found {itemType} items to {(skip ? "skip" : "take")}. Use this to control pagination of the response.";
@@ -82,78 +86,70 @@ internal abstract class SwaggerDocumentationFilterBase<TBaseController>
// FIXME: remove this when Delivery API V1 has been removed (expectedly in V15)
private void AddExpandV1(OpenApiOperation operation)
=> operation.Parameters.Add(new OpenApiParameter
{
Name = "expand",
In = ParameterLocation.Query,
Required = false,
Description = QueryParameterDescription("Defines the properties that should be expanded in the response"),
Schema = new OpenApiSchema { Type = "string" },
Examples = new Dictionary<string, OpenApiExample>
{
operation.Parameters ??= new List<IOpenApiParameter>();
operation.Parameters.Add(
new OpenApiParameter
{
{ "Expand none", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
{ "Expand all", new OpenApiExample { Value = new OpenApiString("all") } },
Name = "expand",
In = ParameterLocation.Query,
Required = false,
Description =
QueryParameterDescription("Defines the properties that should be expanded in the response"),
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
Examples = new Dictionary<string, IOpenApiExample>
{
"Expand specific property",
new OpenApiExample { Value = new OpenApiString("property:alias1") }
{ "Expand none", new OpenApiExample { Value = string.Empty } },
{ "Expand all", new OpenApiExample { Value = "all" } },
{ "Expand specific property", new OpenApiExample { Value = "property:alias1" } },
{ "Expand specific properties", new OpenApiExample { Value = "property:alias1,alias2" } },
},
{
"Expand specific properties",
new OpenApiExample { Value = new OpenApiString("property:alias1,alias2") }
}
}
});
});
}
private void AddExpand(OpenApiOperation operation)
=> operation.Parameters.Add(new OpenApiParameter
{
Name = "expand",
In = ParameterLocation.Query,
Required = false,
Description = QueryParameterDescription("Defines the properties that should be expanded in the response"),
Schema = new OpenApiSchema { Type = "string" },
Examples = new Dictionary<string, OpenApiExample>
{
operation.Parameters ??= new List<IOpenApiParameter>();
operation.Parameters.Add(
new OpenApiParameter
{
{ "Expand none", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
{ "Expand all properties", new OpenApiExample { Value = new OpenApiString("properties[$all]") } },
Name = "expand",
In = ParameterLocation.Query,
Required = false,
Description =
QueryParameterDescription("Defines the properties that should be expanded in the response"),
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
Examples = new Dictionary<string, IOpenApiExample>
{
"Expand specific property",
new OpenApiExample { Value = new OpenApiString("properties[alias1]") }
{ "Expand none", new OpenApiExample { Value = string.Empty } },
{ "Expand all properties", new OpenApiExample { Value = "properties[$all]" } },
{ "Expand specific property", new OpenApiExample { Value = "properties[alias1]" } },
{ "Expand specific properties", new OpenApiExample { Value = "properties[alias1,alias2]" } },
{ "Expand nested properties", new OpenApiExample { Value = "properties[alias1[properties[nestedAlias1,nestedAlias2]]]" } },
},
{
"Expand specific properties",
new OpenApiExample { Value = new OpenApiString("properties[alias1,alias2]") }
},
{
"Expand nested properties",
new OpenApiExample { Value = new OpenApiString("properties[alias1[properties[nestedAlias1,nestedAlias2]]]") }
}
}
});
});
}
private void AddFields(OpenApiOperation operation)
=> operation.Parameters.Add(new OpenApiParameter
{
Name = "fields",
In = ParameterLocation.Query,
Required = false,
Description = QueryParameterDescription("Explicitly defines which properties should be included in the response (by default all properties are included)"),
Schema = new OpenApiSchema { Type = "string" },
Examples = new Dictionary<string, OpenApiExample>
{
operation.Parameters ??= new List<IOpenApiParameter>();
operation.Parameters.Add(
new OpenApiParameter
{
{ "Include all properties", new OpenApiExample { Value = new OpenApiString("properties[$all]") } },
Name = "fields",
In = ParameterLocation.Query,
Required = false,
Description =
QueryParameterDescription(
"Explicitly defines which properties should be included in the response (by default all properties are included)"),
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
Examples = new Dictionary<string, IOpenApiExample>
{
"Include only specific property",
new OpenApiExample { Value = new OpenApiString("properties[alias1]") }
{ "Include all properties", new OpenApiExample { Value = "properties[$all]" } },
{ "Include only specific property", new OpenApiExample { Value = "properties[alias1]" } },
{ "Include only specific properties", new OpenApiExample { Value = "properties[alias1,alias2]" } },
{ "Include only specific nested properties", new OpenApiExample { Value = "properties[alias1[properties[nestedAlias1,nestedAlias2]]]" } },
},
{
"Include only specific properties",
new OpenApiExample { Value = new OpenApiString("properties[alias1,alias2]") }
},
{
"Include only specific nested properties",
new OpenApiExample { Value = new OpenApiString("properties[alias1[properties[nestedAlias1,nestedAlias2]]]") }
}
}
});
});
}
}
@@ -1,8 +1,7 @@
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using System.Text.Json.Nodes;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;
using Umbraco.Cms.Api.Delivery.Configuration;
using Umbraco.Cms.Api.Delivery.Controllers;
using Umbraco.Cms.Api.Delivery.Controllers.Media;
namespace Umbraco.Cms.Api.Delivery.Filters;
@@ -13,7 +12,7 @@ internal sealed class SwaggerMediaDocumentationFilter : SwaggerDocumentationFilt
protected override void ApplyOperation(OpenApiOperation operation, OperationFilterContext context)
{
operation.Parameters ??= new List<OpenApiParameter>();
operation.Parameters ??= new List<IOpenApiParameter>();
AddExpand(operation, context);
@@ -46,77 +45,29 @@ internal sealed class SwaggerMediaDocumentationFilter : SwaggerDocumentationFilt
}
}
private Dictionary<string, OpenApiExample> FetchQueryParameterExamples() =>
private Dictionary<string, IOpenApiExample> FetchQueryParameterExamples() =>
new()
{
{
"Select all children at root level",
new OpenApiExample { Value = new OpenApiString("children:/") }
},
{
"Select all children of a media item by id",
new OpenApiExample { Value = new OpenApiString("children:id") }
},
{
"Select all children of a media item by path",
new OpenApiExample { Value = new OpenApiString("children:path") }
}
{ "Select all children at root level", new OpenApiExample { Value = "children:/" } },
{ "Select all children of a media item by id", new OpenApiExample { Value = "children:id" } },
{ "Select all children of a media item by path", new OpenApiExample { Value = "children:path" } },
};
private Dictionary<string, OpenApiExample> FilterQueryParameterExamples() =>
private Dictionary<string, IOpenApiExample> FilterQueryParameterExamples() =>
new()
{
{ "Default filter", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
{
"Filter by media type",
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("mediaType:alias1") } }
},
{
"Filter by name",
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("name:nodeName") } }
}
{ "Default filter", new OpenApiExample { Value = string.Empty } },
{ "Filter by media type", new OpenApiExample { Value = new JsonArray { "mediaType:alias1" } } },
{ "Filter by name", new OpenApiExample { Value = new JsonArray { "name:nodeName" } } },
};
private Dictionary<string, OpenApiExample> SortQueryParameterExamples() =>
private Dictionary<string, IOpenApiExample> SortQueryParameterExamples() =>
new()
{
{ "Default sort", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
{
"Sort by create date",
new OpenApiExample
{
Value = new OpenApiArray
{
new OpenApiString("createDate:asc"), new OpenApiString("createDate:desc")
}
}
},
{
"Sort by name",
new OpenApiExample
{
Value = new OpenApiArray { new OpenApiString("name:asc"), new OpenApiString("name:desc") }
}
},
{
"Sort by sort order",
new OpenApiExample
{
Value = new OpenApiArray
{
new OpenApiString("sortOrder:asc"), new OpenApiString("sortOrder:desc")
}
}
},
{
"Sort by update date",
new OpenApiExample
{
Value = new OpenApiArray
{
new OpenApiString("updateDate:asc"), new OpenApiString("updateDate:desc")
}
}
}
{ "Default sort", new OpenApiExample { Value = string.Empty } },
{ "Sort by create date", new OpenApiExample { Value = new JsonArray { "createDate:asc", "createDate:desc" } } },
{ "Sort by name", new OpenApiExample { Value = new JsonArray { "name:asc", "name:desc" } } },
{ "Sort by sort order", new OpenApiExample { Value = new JsonArray { "sortOrder:asc", "sortOrder:desc" } } },
{ "Sort by update date", new OpenApiExample { Value = new JsonArray { "updateDate:asc", "updateDate:desc" } } },
};
}
@@ -1,8 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.DeliveryApi;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Delivery.Querying;
@@ -14,30 +11,6 @@ public abstract class QueryOptionBase
private readonly IApiDocumentUrlService _apiDocumentUrlService;
private readonly IVariationContextAccessor _variationContextAccessor;
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
public QueryOptionBase(
IPublishedContentCache publishedContentCache,
IRequestRoutingService requestRoutingService)
: this(
requestRoutingService,
StaticServiceProvider.Instance.GetRequiredService<IRequestPreviewService>(),
StaticServiceProvider.Instance.GetRequiredService<IApiDocumentUrlService>(),
StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>())
{
}
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
public QueryOptionBase(
IPublishedContentCache publishedContentCache,
IRequestRoutingService requestRoutingService,
IRequestPreviewService requestPreviewService,
IRequestCultureService requestCultureService,
IApiDocumentUrlService apiDocumentUrlService,
IVariationContextAccessor variationContextAccessor)
: this(requestRoutingService, requestPreviewService, apiDocumentUrlService, variationContextAccessor)
{
}
public QueryOptionBase(
IRequestRoutingService requestRoutingService,
IRequestPreviewService requestPreviewService,
@@ -1,9 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Delivery.Indexing.Selectors;
using Umbraco.Cms.Core.DeliveryApi;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Core.Services.Navigation;
namespace Umbraco.Cms.Api.Delivery.Querying.Selectors;
@@ -13,46 +10,6 @@ public sealed class AncestorsSelector : QueryOptionBase, ISelectorHandler
private readonly IDocumentNavigationQueryService _navigationQueryService;
private const string AncestorsSpecifier = "ancestors:";
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
public AncestorsSelector(
IPublishedContentCache publishedContentCache,
IRequestRoutingService requestRoutingService,
IDocumentNavigationQueryService navigationQueryService,
IRequestPreviewService requestPreviewService)
: this(
requestRoutingService,
requestPreviewService,
StaticServiceProvider.Instance.GetRequiredService<IApiDocumentUrlService>(),
StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>(),
navigationQueryService)
{
}
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
public AncestorsSelector(
IPublishedContentCache publishedContentCache,
IRequestRoutingService requestRoutingService,
IDocumentNavigationQueryService navigationQueryService)
: this(
requestRoutingService,
StaticServiceProvider.Instance.GetRequiredService<IRequestPreviewService>(),
StaticServiceProvider.Instance.GetRequiredService<IApiDocumentUrlService>(),
StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>(),
navigationQueryService)
{
}
[Obsolete("Use the constructor that takes all parameters. Scheduled for removal in V17.")]
public AncestorsSelector(IPublishedContentCache publishedContentCache, IRequestRoutingService requestRoutingService)
: this(
requestRoutingService,
StaticServiceProvider.Instance.GetRequiredService<IRequestPreviewService>(),
StaticServiceProvider.Instance.GetRequiredService<IApiDocumentUrlService>(),
StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>(),
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>())
{
}
public AncestorsSelector(
IRequestRoutingService requestRoutingService,
IRequestPreviewService requestPreviewService,
@@ -62,18 +19,6 @@ public sealed class AncestorsSelector : QueryOptionBase, ISelectorHandler
: base(requestRoutingService, requestPreviewService, apiDocumentUrlService, variationContextAccessor)
=> _navigationQueryService = navigationQueryService;
[Obsolete("Use the constructor that takes all parameters. Scheduled for removal in V17.")]
public AncestorsSelector(
IRequestRoutingService requestRoutingService,
IPublishedContentCache publishedContentCache,
IRequestPreviewService requestPreviewService,
IApiDocumentUrlService apiDocumentUrlService,
IVariationContextAccessor variationContextAccessor,
IDocumentNavigationQueryService navigationQueryService)
: this(requestRoutingService, requestPreviewService, apiDocumentUrlService, variationContextAccessor, navigationQueryService)
{
}
/// <inheritdoc />
public bool CanHandle(string query)
=> query.StartsWith(AncestorsSpecifier, StringComparison.OrdinalIgnoreCase);
@@ -12,27 +12,6 @@ public sealed class ChildrenSelector : QueryOptionBase, ISelectorHandler
{
private const string ChildrenSpecifier = "children:";
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
public ChildrenSelector(IPublishedContentCache publishedContentCache, IRequestRoutingService requestRoutingService)
: this(
requestRoutingService,
StaticServiceProvider.Instance.GetRequiredService<IRequestPreviewService>(),
StaticServiceProvider.Instance.GetRequiredService<IApiDocumentUrlService>(),
StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>())
{
}
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
public ChildrenSelector(
IPublishedContentCache publishedContentCache,
IRequestRoutingService requestRoutingService,
IRequestPreviewService requestPreviewService,
IApiDocumentUrlService apiDocumentUrlService,
IVariationContextAccessor variationContextAccessor)
: this(requestRoutingService, requestPreviewService, apiDocumentUrlService, variationContextAccessor)
{
}
public ChildrenSelector(
IRequestRoutingService requestRoutingService,
IRequestPreviewService requestPreviewService,
@@ -12,27 +12,6 @@ public sealed class DescendantsSelector : QueryOptionBase, ISelectorHandler
{
private const string DescendantsSpecifier = "descendants:";
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
public DescendantsSelector(IPublishedContentCache publishedContentCache, IRequestRoutingService requestRoutingService)
: this(
requestRoutingService,
StaticServiceProvider.Instance.GetRequiredService<IRequestPreviewService>(),
StaticServiceProvider.Instance.GetRequiredService<IApiDocumentUrlService>(),
StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>())
{
}
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
public DescendantsSelector(
IPublishedContentCache publishedContentCache,
IRequestRoutingService requestRoutingService,
IRequestPreviewService requestPreviewService,
IApiDocumentUrlService apiDocumentUrlService,
IVariationContextAccessor variationContextAccessor)
: this(requestRoutingService, requestPreviewService, apiDocumentUrlService, variationContextAccessor)
{
}
public DescendantsSelector(
IRequestRoutingService requestRoutingService,
IRequestPreviewService requestPreviewService,
@@ -66,9 +66,14 @@ internal sealed class DeliveryApiItemsEndpointsMatcherPolicy : MatcherPolicy, IE
{
ApiVersion[]? supportedApiVersions = endpoint.Metadata.GetMetadata<MapToApiVersionAttribute>()?.Versions.ToArray();
// if the endpoint is versioned, the requested API version must be among the API versions supported by the endpoint.
// if the endpoint is NOT versioned, it cannot be used with a requested API version
return supportedApiVersions?.Contains(requestedApiVersion) ?? requestedApiVersion is null;
// If the endpoint is versioned, the requested API version must be among the API versions supported by the endpoint.
// If the endpoint is NOT versioned, it cannot be used with a requested API version.
if (supportedApiVersions is not null && requestedApiVersion is not null)
{
return supportedApiVersions.Contains(requestedApiVersion);
}
return requestedApiVersion is null;
}
private static bool IsByIdsController(ControllerActionDescriptor? controllerActionDescriptor)
@@ -66,7 +66,7 @@ internal sealed class ApiMediaQueryService : IApiMediaQueryService
private IPublishedContent? TryGetByPath(string path, IPublishedMediaCache mediaCache)
{
var segments = path.Split(Constants.CharArrays.ForwardSlash, StringSplitOptions.RemoveEmptyEntries);
IEnumerable<IPublishedContent> currentChildren = mediaCache.GetAtRoot();
IEnumerable<IPublishedContent> currentChildren = GetRootContent(mediaCache);
IPublishedContent? resolvedMedia = null;
foreach (var segment in segments)
@@ -103,7 +103,7 @@ internal sealed class ApiMediaQueryService : IApiMediaQueryService
IPublishedMediaCache mediaCache = GetRequiredPublishedMediaCache();
if (childrenOf.Trim(Constants.CharArrays.ForwardSlash).Length == 0)
{
return mediaCache.GetAtRoot();
return GetRootContent(mediaCache);
}
IPublishedContent? parent = Guid.TryParse(childrenOf, out Guid parentKey)
@@ -196,4 +196,8 @@ internal sealed class ApiMediaQueryService : IApiMediaQueryService
return Attempt.SucceedWithStatus(ApiMediaQueryOperationStatus.Success, result);
}
private IEnumerable<IPublishedContent> GetRootContent(IPublishedMediaCache mediaCache)
=> _mediaNavigationQueryService.TryGetRootKeys(out IEnumerable<Guid> rootKeys) is false ? []
: rootKeys.Select(x => mediaCache.GetById(false, x)).WhereNotNull();
}
@@ -19,12 +19,6 @@ internal sealed partial class RequestCultureService : RequestHeaderHandler, IReq
return ValidLanguageHeaderRegex().IsMatch(acceptLanguage) ? acceptLanguage : null;
}
[Obsolete("Use IVariationContextAccessor to manipulate the variation context. Scheduled for removal in V17.")]
public void SetRequestCulture(string culture)
{
// no-op
}
// at the time of writing we're introducing this to get rid of accept-language header values like "en-GB,en-US;q=0.9,en;q=0.8",
// so we don't want to be too restrictive in this regex - keep it simple for now.
[GeneratedRegex(@"^[\w-]*$")]
@@ -3,19 +3,26 @@
<Title>Umbraco CMS - Delivery API</Title>
<Description>Contains the presentation layer for the Umbraco CMS Delivery API.</Description>
</PropertyGroup>
<PropertyGroup>
<!--
TODO: Fix and remove overrides:
[ASP0019] use IHeaderDictionary.Append or the indexer to append or set headers
[CS0618/CS0612] update obsolete references
-->
<WarningsNotAsErrors>$(WarningsNotAsErrors),ASP0019,CS0618,CS0612</WarningsNotAsErrors>
<WarningsNotAsErrors>$(WarningsNotAsErrors),CS0618,CS0612</WarningsNotAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Umbraco.Cms.Api.Common\Umbraco.Cms.Api.Common.csproj" />
<ProjectReference Include="..\Umbraco.Web.Common\Umbraco.Web.Common.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Linq.Async">
<ExcludeAssets>compile</ExcludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Umbraco.Tests.UnitTests</_Parameter1>
+621
View File
@@ -0,0 +1,621 @@
# Umbraco CMS - Management API
RESTful API for Umbraco backoffice operations. Manages content, media, users, and system configuration through OpenAPI-documented endpoints.
**Project**: `Umbraco.Cms.Api.Management`
**Type**: ASP.NET Core Web API Library
**Files**: 1,317 C# files across 54+ controller domains
---
## 1. Architecture
### Target Framework
- **.NET 10.0** (`net10.0`)
- **C# 12** with nullable reference types enabled
- **ASP.NET Core** Web API
### Application Type
**REST API Library** - Plugged into Umbraco.Web.UI, provides the Management API surface for backoffice operations.
### Key Technologies
- **Web Framework**: ASP.NET Core MVC with `Asp.Versioning.Mvc` (v1.0 currently)
- **OpenAPI**: Swashbuckle.AspNetCore with custom schema/operation filters
- **Authentication**: OpenIddict via `Umbraco.Cms.Api.Common` (reference tokens, not JWT)
- **Authorization**: Policy-based with `IAuthorizationService`
- **Validation**: FluentValidation via base controllers
- **Serialization**: System.Text.Json with custom converters
- **Mapping**: Manual presentation factories (no AutoMapper)
- **Patching**: JsonPatch.Net for PATCH operations
- **Real-time**: SignalR hubs (`BackofficeHub`, `ServerEventHub`)
- **DI**: Microsoft.Extensions.DependencyInjection via `ManagementApiComposer`
### Project Structure
```
src/Umbraco.Cms.Api.Management/
├── Controllers/ # 54+ domain-specific controller folders
│ ├── Document/ # Document (content) CRUD + publish/unpublish
│ ├── Media/ # Media CRUD + upload
│ ├── Member/ # Member management
│ ├── User/ # User management
│ ├── DataType/ # Data type configuration
│ ├── DocumentType/ # Content type schemas
│ ├── Template/ # Razor template management
│ ├── Dictionary/ # Localization dictionary
│ ├── Language/ # Language/culture config
│ ├── Security/ # Auth, login, external logins
│ ├── Install/ # Installation wizard
│ ├── Upgrade/ # Upgrade operations
│ ├── LogViewer/ # Log browsing
│ ├── HealthCheck/ # Health check dashboard
│ ├── Webhook/ # Webhook management
│ └── [48 more domains...]
├── ViewModels/ # Request/response DTOs (one folder per domain)
├── Factories/ # Domain model → ViewModel converters
├── Services/ # Business logic (thin layer over Core.Services)
├── Mapping/ # ViewModel → domain model mappers
├── Security/ # Auth providers, sign-in manager, external logins
├── OpenApi/ # Swashbuckle filters (schema, operation, security)
├── Routing/ # Route configuration, SignalR hubs
├── DependencyInjection/ # Service registration (55+ files)
├── Middleware/ # Preview, server events
├── Configuration/ # IOptions configurators
├── Filters/ # Action filters
├── Serialization/ # JSON converters
└── OpenApi.json # Embedded OpenAPI spec (1.3MB)
```
### Dependencies
- **Umbraco.Cms.Api.Common** - Shared API infrastructure (base controllers, OpenAPI config)
- **Umbraco.Infrastructure** - Service implementations, data access
- **Umbraco.PublishedCache.HybridCache** - Published content queries
- **JsonPatch.Net** - JSON Patch (RFC 6902) support
- **Swashbuckle.AspNetCore** - OpenAPI generation
### Design Patterns
1. **Controller-per-Operation** - Each endpoint is a separate controller class
- Example: `CreateDocumentController`, `UpdateDocumentController`, `DeleteDocumentController`
- Enables fine-grained authorization and operation-specific logic
- **Responsibilities**: entrypoint/routing, authorization and mapping
- **avoid**: business logic directly in controllers (there are a few known violations)
2. **Presentation Factory Pattern** - Factories convert domain models to ViewModels
- Example: `IDocumentEditingPresentationFactory` (src/Umbraco.Cms.Api.Management/Factories/)
- Separation: Controllers → Factories → ViewModels
3. **Attempt Pattern** - Operations return `Attempt<TResult, TStatus>` for status-based error handling
- Controllers map status enums to HTTP status codes via helper methods
4. **Authorization Service Pattern** - All authorization via `IAuthorizationService`, not attributes
- Checked in base controller methods (see `ManagementApiControllerBase`)
5. **Options Pattern** - All configuration via `IOptions<T>` (security, routing, OpenAPI)
6. **SignalR Event Broadcasting** - Real-time notifications via `BackofficeHub` and `ServerEventHub`
---
## 2. Commands
### Build & Run
```bash
# Build
dotnet build src/Umbraco.Cms.Api.Management
# Test (tests in ../../tests/Umbraco.Tests.Integration and Umbraco.Tests.UnitTests)
dotnet test --filter "FullyQualifiedName~Management"
# Pack (for NuGet distribution)
dotnet pack src/Umbraco.Cms.Api.Management -c Release
```
### Code Quality
```bash
# Format code
dotnet format src/Umbraco.Cms.Api.Management
# Build with warnings (note: some warnings suppressed, see .csproj line 23)
dotnet build src/Umbraco.Cms.Api.Management /p:TreatWarningsAsErrors=true
```
### OpenAPI Documentation
The project embeds a pre-generated `OpenApi.json` (1.3MB). To regenerate:
```bash
# Run Umbraco.Web.UI, access /umbraco/swagger
# Export JSON from Swagger UI
```
### Package Management
```bash
# Add package (versions centralized in Directory.Packages.props)
dotnet add src/Umbraco.Cms.Api.Management package [PackageName]
# Check for vulnerable packages
dotnet list src/Umbraco.Cms.Api.Management package --vulnerable
```
### Environment Setup
1. **Prerequisites**: .NET 10 SDK
2. **IDE**: Visual Studio 2022 or Rider (with .editorconfig support)
3. **Configuration**: Inherits from `Umbraco.Web.UI` appsettings (no app settings in this library)
---
## 3. Style Guide
### Project-Specific Patterns
**Controller Naming** (line examples from CreateDocumentController.cs:16):
```csharp
[ApiVersion("1.0")]
public class CreateDocumentController : CreateDocumentControllerBase
```
- Pattern: `{Verb}{Entity}Controller` (e.g., `CreateDocumentController`, `UpdateMediaController`)
- Base class: `{Verb}{Entity}ControllerBase` for shared logic
- **Critical**: One operation per controller (not one controller per resource)
**Async Naming** - All async methods use `Async` suffix consistently:
```csharp
await _contentEditingService.CreateAsync(model, CurrentUserKey(_backOfficeSecurityAccessor));
```
**Factory Pattern Usage** (line 44):
```csharp
ContentCreateModel model = _documentEditingPresentationFactory.MapCreateModel(requestModel);
```
- ViewModels → Domain: `Map{Operation}Model(requestModel)`
- Domain → ViewModels: Factory classes in `Factories/` folder
### Key Patterns from Codebase
**ControllerBase Helper Methods** (inherited from `ManagementApiControllerBase` in Api.Common):
- `CreatedAtId<TController>(expression, id)` - Returns 201 with Location header
- `ContentEditingOperationStatusResult(status)` - Maps status enum to ProblemDetails
- `CurrentUserKey(accessor)` - Gets current user from security context
**Authorization Pattern** (all controllers):
```csharp
private readonly IAuthorizationService _authorizationService;
// Check permissions in action, not via [Authorize] attribute
```
---
## 4. Test Bench
### Test Location
- **Unit Tests**: `tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Management/`
- **Integration Tests**: `tests/Umbraco.Tests.Integration/Umbraco.Cms.Api.Management/`
### Running Tests
```bash
# All Management API tests
dotnet test --filter "FullyQualifiedName~Management"
# Specific domain (e.g., Document controllers)
dotnet test --filter "FullyQualifiedName~Management.Controllers.Document"
```
### Testing Focus
1. **Controller logic** - Request validation, authorization checks, status code mapping
2. **Factories** - ViewModel ↔ Domain model conversion accuracy
3. **Authorization** - Policy enforcement for each operation
4. **OpenAPI schema** - Ensure Swagger generation doesn't break
### InternalsVisibleTo
Tests have access to internal types (see .csproj:44-52):
- `Umbraco.Tests.UnitTests`
- `Umbraco.Tests.Integration`
- `DynamicProxyGenAssembly2` (for Moq)
---
## 5. Error Handling
### Operation Status Pattern
Controllers use `Attempt<TResult, TStatus>` with strongly-typed status enums:
```csharp
Attempt<ContentCreateResult, ContentEditingOperationStatus> result =
await _contentEditingService.CreateAsync(model, userKey);
return result.Success
? CreatedAtId<ByKeyDocumentController>(controller => nameof(controller.ByKey), result.Result.Content!.Key)
: ContentEditingOperationStatusResult(result.Status); // Maps to ProblemDetails
```
**Status Enums** (from Core):
- `ContentEditingOperationStatus` - InvalidParent, NotFound, NotAllowed, etc.
- `UserOperationStatus` - UserNameIsNotEmail, DuplicateUserName, etc.
- Each enum value maps to specific HTTP status + ProblemDetails type
### ProblemDetails
All errors return RFC 7807 ProblemDetails via helper methods in base controllers:
- 400 Bad Request: Validation failures, invalid operations
- 403 Forbidden: Authorization failures
- 404 Not Found: Resource not found
- 409 Conflict: Duplicate operations
### Critical Logging Points
1. **Authorization failures** - Logged by AuthorizationService
2. **Service operation failures** - Logged in Infrastructure layer services
3. **External login errors** - BackOfficeSignInManager (Security/)
---
## 6. Clean Code
### Key Design Decisions
**Why Controller-per-Operation?** (not RESTful resource-based controllers)
- Fine-grained authorization per operation
- Operation-specific request/response models
- Clearer OpenAPI documentation
- Example: 20+ controllers in `Controllers/Document/` for different operations
**Why Manual Factories instead of AutoMapper?**
- Explicit control over mapping logic
- Easier debugging (no magic)
- Better performance (no reflection overhead)
- See `Factories/` directory (92 factory classes)
### Project-Specific Architectural Decisions
**Embedded OpenAPI Spec** (OpenApi.json - 1.3MB):
- Pre-generated, embedded as resource
- Served for client SDK generation
- **Why?** Deterministic output, faster startup (no runtime generation)
**SignalR for Real-time** (Routing/BackofficeHub.cs:33):
- `BackofficeHub` - User notifications, cache refreshes
- `ServerEventHub` - Background job updates, health checks
- Routes: `/umbraco/backoffice-signalr`, `/umbraco/serverevent-signalr`
### Code Smells to Watch For
1. **Large factory classes** - Some factories have 1000+ lines (e.g., `UserGroupPresentationFactory.cs`)
- Consider splitting by operation
2. **Repeated authorization checks** - Each controller duplicates auth logic
- Already abstracted to base classes, but still verbose
3. **ViewModel explosion** - 1000+ ViewModel classes across ViewModels/ folders
- Consider shared base models or composition
---
## 7. Security
### Authentication & Authorization
**Method**: OpenIddict (OAuth 2.0) via Umbraco.Cms.Api.Common
- Reference tokens (not JWT) stored in database
- Token validation via OpenIddict middleware
- ASP.NET Core Data Protection for token encryption
**Authorization**:
- Basic authorization is done trough policies and the `AuthorizeAttribute`
```csharp
// Example from DocumentTreeControllerBase.cs, the user needs at least access to a section that uses trees
[Authorize(Policy = AuthorizationPolicies.SectionAccessForContentTree)]
```
- Authorization that needs (parts of) the payload are done manually trough the IAuthorizationService
```csharp
// Example from CreateDocumentController.cs:22
private readonly IAuthorizationService _authorizationService;
// Authorization checked in base controller methods, not attributes
protected async Task<IActionResult> HandleRequest(request, Func<Task<IActionResult>> handler)
{
var authResult = await _authorizationService.AuthorizeAsync(User, request, policy);
// ...
}
```
**Policies** (defined in Security/Authorization/):
- `ContentPermissionHandler` - Document/Media CRUD permissions
- `SectionAccessHandler` - Backoffice section access
- `UserGroupPermissionHandler` - Admin operations
**Password Requirements** (Security/ConfigureBackOfficeIdentityOptions.cs:18):
- See Identity options configuration
### External Login Providers
**Location**: `Security/BackOfficeExternalLoginProviders.cs`
- Google, Microsoft, OpenID Connect providers
- Auto-linking with `ExternalSignInAutoLinkOptions`
- **Critical**: Validate external claims before auto-linking users
### Input Validation
**FluentValidation** used throughout:
- Request models validated automatically via MVC integration
- Custom validators in each domain folder (e.g., `ViewModels/Document/Validators/`)
**Parameter Validation**:
```csharp
// Controllers validate IDs, keys before service calls
if (requestModel.Parent == null)
return BadRequest(new ProblemDetailsBuilder()...);
```
### API Security
**CORS** - Configured in Umbraco.Web.UI (not this project)
**HTTPS Enforcement** - Configured in Umbraco.Web.UI
**Request Size Limits** - Configured for file uploads in Umbraco.Web.UI
**Security Headers** - Handled by Umbraco.Web.UI middleware
### Secrets Management
**No secrets in this project** - Configuration injected from parent application (Umbraco.Web.UI)
### Dependency Security
```bash
# Check vulnerable packages
dotnet list src/Umbraco.Cms.Api.Management package --vulnerable
```
### Security Anti-Patterns to Avoid
1. **Never bypass authorization checks** - All operations must authorize
2. **Never trust client validation** - Always validate on server
3. **Never expose stack traces** - ProblemDetails abstracts errors
4. **Never log sensitive data** - User passwords, tokens, API keys
---
## 8. Teamwork and Workflow
**⚠️ SKIPPED** - This is a sub-project. See root `/CLAUDE.md` for repository-wide teamwork protocols.
---
## 9. Edge Cases
### Domain-Specific Edge Cases
**Document Operations**:
1. **Publishing with descendants** - Can timeout on large trees
- Use `PublishDocumentWithDescendantsController` with result polling
- See `Controllers/Document/PublishDocumentWithDescendantsResultController.cs`
2. **Recycle bin operations** - Items in recycle bin can't be published
- Check `IsTrashed` before publish operations
3. **Public access rules** - Affects authorization and routing
- See `Controllers/Document/CreatePublicAccessDocumentController.cs`
**Media Upload**:
1. **Large file uploads** - Request size limits in parent app
- Controllers accept multipart/form-data
- Temporary files cleaned by background job
2. **Media picker** - Can reference deleted media
- Validation in `Factories/` checks for orphaned references
**User Management**:
1. **External logins** - Auto-linking can create duplicate users if email mismatches
- See `Security/ExternalSignInAutoLinkOptions.cs`
2. **User groups** - Deleting user group doesn't delete users
- Users reassigned to default group
**Webhooks**:
1. **Webhook failures** - Failed webhooks retry with exponential backoff
- See `Controllers/Webhook/` for configuration
### Known Gotchas (from TODO comments)
**StyleSheet/Script/PartialView Tree Controllers** - All have identical TODO comment:
```
TODO: [NL] This must return path segments for a query to work
// src/Umbraco.Cms.Api.Management/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs
// src/Umbraco.Cms.Api.Management/Controllers/Script/Tree/ScriptTreeControllerBase.cs
// src/Umbraco.Cms.Api.Management/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs
```
**BackOfficeController** - External login TODO:
```
// src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs
// TODO: Handle external logins properly
```
---
## 10. Agentic Workflow
### When to Add a New Endpoint
**Decision Points**:
1. Does the operation fit an existing controller domain? (Document, Media, User, etc.)
2. Is this a new CRUD operation or a custom action?
3. Does this require new authorization policies?
**Workflow**:
1. **Create Controller** in appropriate `Controllers/{Domain}/` folder
- Follow naming: `{Verb}{Entity}Controller`
- Inherit from domain-specific base controller or `ManagementApiControllerBase`
2. **Define ViewModels** in `ViewModels/{Domain}/`
- Request model (e.g., `CreateDocumentRequestModel`)
- Response model (if not reusing existing)
3. **Create or Update Factory** in `Factories/`
- Map request ViewModel → domain model
- Map domain result → response ViewModel
4. **Authorization** - Check required permissions in controller action
- Use `IAuthorizationService.AuthorizeAsync()`
5. **Service Layer** - Call Core services (from `Umbraco.Core.Services`)
- Handle `Attempt<>` results
- Map status to HTTP status codes
6. **OpenAPI Annotations**:
- `[ApiVersion("1.0")]`
- `[MapToApiVersion("1.0")]`
- `[ProducesResponseType(...)]` for all status codes
7. **Testing**:
- Unit test controller logic
- Integration test end-to-end flow
8. **Update OpenApi.json** (if needed for client generation)
### Quality Gates Before PR
1. All tests pass
2. Code formatted (`dotnet format`)
3. No new warnings (check suppressed warnings list in .csproj:23)
4. OpenAPI schema valid (run Swagger UI)
5. Authorization tested (unit + integration tests)
### Common Pitfalls
1. **Forgetting authorization checks** - Every operation must authorize
2. **Inconsistent status code mapping** - Use base controller helpers
3. **Large factory classes** - Split by operation if >500 lines
4. **Missing ProducesResponseType** - Breaks OpenAPI client generation
5. **Not handling Attempt failures** - Always check `result.Success`
---
## 11. Project-Specific Notes
### Key Design Decisions
**Why 1,317 files for one API?**
- Controller-per-operation pattern = many controllers
- 54 domains × average 10-20 operations per domain
- Tradeoff: Verbose but explicit, easier to navigate than megacontrollers
**Why manual factories instead of AutoMapper?**
- Performance: No reflection overhead
- Debuggability: Step through mapping logic
- Control: Complex mappings (e.g., security trimming) require custom logic
**Why embedded OpenApi.json?**
- Deterministic OpenAPI spec for client generation
- Faster startup (no runtime generation)
- Easier versioning (commit changes to spec)
- Added `OpenAPIContractTest` to test that all operations are exported
### External Integrations
None directly in this project. All integrations handled by:
- **Umbraco.Infrastructure** - External search, media, email providers
- **Umbraco.Core** - External data sources, webhooks
### Known Limitations
1. **API Versioning**: Currently v1.0 and v1.1
- Future versions will require new controller classes or action methods
2. **Batch Operations**: Limited batch endpoint support
- Most operations are single-entity (create one document at a time)
3. **Real-time Limits**: SignalR hubs don't scale beyond single-server without Redis backplane
- Configure Redis for multi-server setups
4. **File Upload Size**: Controlled by parent app (Umbraco.Web.UI)
- This project doesn't set limits
### Performance Considerations
**Caching**:
- Published content cached via `Umbraco.PublishedCache.HybridCache`
- Controllers query cache, not database (for published content)
**Background Jobs**:
- Long-running operations (publish with descendants, export) return job ID
- Poll result endpoint for completion
- See `Controllers/Document/PublishDocumentWithDescendantsResultController.cs`
**OpenAPI Generation**:
- Pre-generated (OpenApi.json embedded)
- Runtime generation disabled for performance
### Technical Debt (Top Issues from TODO comments)
1. **Warnings Suppressed** (Umbraco.Cms.Api.Management.csproj:9-22):
```
TODO: Fix and remove overrides:
- SA1117: params all on same line
- SA1401: make fields private
- SA1134: own line attributes
- CS0108: hidden inherited member
- CS0618/CS9042: update obsolete references
- CS1998: remove async or make method synchronous
- CS8524: switch statement exhaustiveness
- IDE0060: removed unused parameter
- SA1649: file name match type
- CS0419: ambiguous reference
- CS1573: param tag for all parameters
- CS1574: unresolveable cref
```
2. **Tree Controller Path Segments** (multiple files):
- Stylesheet/Script/PartialView tree controllers need path segment support for queries
- Files: `Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs`
- Files: `Controllers/Script/Tree/ScriptTreeControllerBase.cs`
- Files: `Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs`
3. **External Login Handling** (Controllers/Security/BackOfficeController.cs):
- TODO: Handle external logins properly
4. **Large Factory Classes** (Factories/UserGroupPresentationFactory.cs):
- Some factories exceed 1000 lines - consider splitting
5. **ViewModel Explosion** - 1000+ ViewModel classes
- Consider shared base models or composition to reduce duplication
---
## Quick Reference
### Essential Commands
```bash
# Build
dotnet build src/Umbraco.Cms.Api.Management
# Test Management API
dotnet test --filter "FullyQualifiedName~Management"
# Format code
dotnet format src/Umbraco.Cms.Api.Management
# Pack for NuGet
dotnet pack src/Umbraco.Cms.Api.Management -c Release
```
### Key Projects
- **Umbraco.Cms.Api.Management** (this) - Management API controllers and models
- **Umbraco.Cms.Api.Common** - Shared API infrastructure, base controllers
- **Umbraco.Infrastructure** - Service implementations, data access
- **Umbraco.Core** - Domain models, service interfaces
### Important Files
- **Project file**: `src/Umbraco.Cms.Api.Management/Umbraco.Cms.Api.Management.csproj`
- **Composer**: `src/Umbraco.Cms.Api.Management/ManagementApiComposer.cs` (DI entry point)
- **Routing**: `src/Umbraco.Cms.Api.Management/Routing/BackOfficeAreaRoutes.cs`
- **OpenAPI Spec**: `src/Umbraco.Cms.Api.Management/OpenApi.json` (embedded, 1.3MB)
- **Base Controllers**: See `Umbraco.Cms.Api.Common` project
### Configuration
No appsettings in this library - all configuration from parent app (Umbraco.Web.UI):
- OpenIddict settings
- CORS settings
- File upload limits
### API Endpoints
Base path: `/umbraco/management/api/v1/`
Examples:
- `POST /umbraco/management/api/v1/document` - Create document
- `GET /umbraco/management/api/v1/document/{id}` - Get document by key
- `PUT /umbraco/management/api/v1/document/{id}` - Update document
- `DELETE /umbraco/management/api/v1/document/{id}` - Delete document
Full spec: See OpenApi.json or Swagger UI at `/umbraco/swagger`
### Getting Help
- **Root Documentation**: `/CLAUDE.md` (repository overview)
- **API Common Docs**: `../Umbraco.Cms.Api.Common/CLAUDE.md` (shared API patterns)
- **Core Docs**: `../Umbraco.Core/CLAUDE.md` (domain architecture)
- **Official Docs**: https://docs.umbraco.com/umbraco-cms/reference/management-api
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using Umbraco.Cms.Api.Management.Security;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
@@ -219,10 +220,44 @@ public class ConfigureBackOfficeCookieOptions : IConfigureNamedOptions<CookieAut
}
return Task.CompletedTask;
}
},
// FIXME: We want to change this over to using an attribute on the backoffice controllers
// See this for more: https://github.com/dotnet/aspnetcore/issues/63093#issuecomment-3201530217
OnRedirectToLogin = context =>
{
if (IsXhr(context.Request))
{
context.Response.Headers.Location = context.RedirectUri;
context.Response.StatusCode = 401;
}
else
{
context.Response.Redirect(context.RedirectUri);
}
return Task.CompletedTask;
},
OnRedirectToAccessDenied = context =>
{
if (IsXhr(context.Request))
{
context.Response.Headers.Location = context.RedirectUri;
context.Response.StatusCode = 403;
}
else
{
context.Response.Redirect(context.RedirectUri);
}
return Task.CompletedTask;
},
};
}
private bool IsXhr(HttpRequest request) =>
string.Equals(request.Query[HeaderNames.XRequestedWith], "XMLHttpRequest", StringComparison.Ordinal) ||
string.Equals(request.Headers.XRequestedWith, "XMLHttpRequest", StringComparison.Ordinal);
/// <summary>
/// Ensures the ticket is renewed if the <see cref="SecuritySettings.KeepUserLoggedIn" /> is set to true
/// and the current request is for the get user seconds endpoint
@@ -1,8 +1,8 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;
using Umbraco.Cms.Api.Common.OpenApi;
using Umbraco.Cms.Api.Common.Security;
using Umbraco.Cms.Api.Common.Serialization;
using Umbraco.Cms.Api.Management.DependencyInjection;
using Umbraco.Cms.Api.Management.OpenApi;
@@ -11,7 +11,7 @@ namespace Umbraco.Cms.Api.Management.Configuration;
public class ConfigureUmbracoManagementApiSwaggerGenOptions : IConfigureOptions<SwaggerGenOptions>
{
private IUmbracoJsonTypeInfoResolver _umbracoJsonTypeInfoResolver;
private readonly IUmbracoJsonTypeInfoResolver _umbracoJsonTypeInfoResolver;
public ConfigureUmbracoManagementApiSwaggerGenOptions(IUmbracoJsonTypeInfoResolver umbracoJsonTypeInfoResolver)
{
@@ -20,7 +20,6 @@ public class ConfigureUmbracoManagementApiSwaggerGenOptions : IConfigureOptions<
public void Configure(SwaggerGenOptions swaggerGenOptions)
{
swaggerGenOptions.SwaggerDoc(
ManagementApiConfiguration.ApiName,
new OpenApiInfo
@@ -51,10 +50,10 @@ public class ConfigureUmbracoManagementApiSwaggerGenOptions : IConfigureOptions<
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl =
new Uri(Common.Security.Paths.BackOfficeApi.AuthorizationEndpoint, UriKind.Relative),
TokenUrl = new Uri(Common.Security.Paths.BackOfficeApi.TokenEndpoint, UriKind.Relative)
}
}
new Uri(Paths.BackOfficeApi.AuthorizationEndpoint, UriKind.Relative),
TokenUrl = new Uri(Paths.BackOfficeApi.TokenEndpoint, UriKind.Relative),
},
},
});
// Sets Security requirement on backoffice apis
@@ -7,23 +7,6 @@ using Umbraco.Cms.Core.Hosting;
namespace Umbraco.Cms.Api.Management;
[BindProperties]
public class BackOfficeLoginModel
{
/// <summary>
/// Gets or sets the value of the "ReturnUrl" query parameter or defaults to the configured Umbraco directory.
/// </summary>
[FromQuery(Name = "ReturnUrl")]
public string? ReturnUrl { get; set; }
/// <summary>
/// The configured Umbraco directory.
/// </summary>
public string? UmbracoUrl { get; set; }
public bool UserIsAlreadyLoggedIn { get; set; }
}
[ApiExplorerSettings(IgnoreApi = true)]
[Route(LoginPath)]
public class BackOfficeLoginController : Controller
@@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Mvc;
namespace Umbraco.Cms.Api.Management;
[BindProperties]
public class BackOfficeLoginModel
{
/// <summary>
/// Gets or sets the value of the "ReturnUrl" query parameter or defaults to the configured Umbraco directory.
/// </summary>
[FromQuery(Name = "ReturnUrl")]
public string? ReturnUrl { get; set; }
/// <summary>
/// The configured Umbraco directory.
/// </summary>
public string? UmbracoUrl { get; set; }
public bool UserIsAlreadyLoggedIn { get; set; }
}
@@ -29,39 +29,6 @@ public abstract class ContentCollectionControllerBase<TContent, TCollectionRespo
_flagProviders = flagProvider;
}
[Obsolete("Use the constructer with all parameters. To be removed in Umbraco 18")]
protected ContentCollectionControllerBase(IUmbracoMapper mapper)
: this(mapper, StaticServiceProvider.Instance.GetRequiredService<FlagProviderCollection>())
{
}
[Obsolete("This method is no longer used and will be removed in Umbraco 17.")]
protected IActionResult CollectionResult(ListViewPagedModel<TContent> result)
{
PagedModel<TContent> collectionItemsResult = result.Items;
ListViewConfiguration collectionConfiguration = result.ListViewConfiguration;
var collectionPropertyAliases = collectionConfiguration
.IncludeProperties
.Select(p => p.Alias)
.WhereNotNull()
.ToArray();
List<TCollectionResponseModel> collectionResponseModels =
_mapper.MapEnumerable<TContent, TCollectionResponseModel>(collectionItemsResult.Items, context =>
{
context.SetIncludedProperties(collectionPropertyAliases);
});
var pageViewModel = new PagedViewModel<TCollectionResponseModel>
{
Items = collectionResponseModels,
Total = collectionItemsResult.Total,
};
return Ok(pageViewModel);
}
/// <summary>
/// Creates a collection result from the provided collection response models and total number of items.
/// </summary>

Some files were not shown because too many files have changed in this diff Show More