Compare commits

...
Author SHA1 Message Date
Nhu DinhandGitHub 9505011d71 Build: Updated nightly E2E test pipeline schedule in v16 (#22805)
Updated nightly E2E test pipeline schedule
2026-05-12 15:36:39 +07:00
Nhu DinhandGitHub 2579aaf2db Build: Cherry pick #22164 for V16 (#22171)
Serialize E2E stages and stagger branch schedules to reduce agent usage
2026-03-19 21:19:45 +07:00
Andy Butland 2b7784a226 Merge branch 'release/16.5.1' into v16/dev 2026-03-10 06:36:50 +01:00
8555a97b39 Merge commit from fork
* Add authorization checks for domain operations.

* Remove duplicate 403 ProducesResponseType attributes.

BackOfficeSecurityRequirementsOperationFilterBase already adds 403
responses for endpoints whose controllers inject IAuthorizationService.

---------

Co-authored-by: Laura Neto <12862535+lauraneto@users.noreply.github.com>
2026-03-10 05:11:16 +01:00
Andy ButlandandGitHub c2dd685a4b Merge commit from fork 2026-03-10 05:10:31 +01:00
Andy ButlandandGitHub 66fc819379 Merge commit from fork
* Protect endpoint that sets user groups for a user collection to prevent elevation of permissions for users.

* Update tests from code review feedback.
2026-03-10 05:07:41 +01:00
Andy Butland 4f1f7e15c4 Bump version to 16.5.1. 2026-02-23 16:38:23 +01:00
Andy Butland a826c52e2e Merge branch 'release/16.5' into v16/dev and bumped version to 16.6.0-rc 2026-01-22 06:45:06 +01:00
Andy Butland 8b2c22aaf1 Merge branch 'release/16.5' of https://github.com/umbraco/Umbraco-CMS into release/16.5 2026-01-21 17:40:17 +01:00
Andy Butland aecfee4469 Bump version to 16.5.0. 2026-01-21 17:39:59 +01:00
Niels LyngsøandGitHub 9c785a9c5b Varying Compositions in Invariant Document Types, Cherrypick of #21267 (#21472)
* cherry picked a5a6d0645f

* correct to getVariesBySegment
2026-01-21 15:27:26 +00:00
Nhu DinhandGitHub 2fe10387ee E2E: V16 QA Update acceptance tests to use refactored UI helpers (#21271)
* Bumped version of test helper

* Bumped version

* Bumped version
2026-01-05 17:25:23 +07:00
8642b9e615 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:21:17 +01:00
Andreas ZerbstandGitHub 2a604c8719 E2E: QA Replaced unreliable Thread.Sleep(500) with a counter/gate pattern that ensures both transactions are initialized before releasing them to compete for locks (#21165)
Fix flaky test
2025-12-16 12:02:07 +00:00
9c0a0a1086 V16/drag event media (#20893)
* updates the drag event to convert types to lowercase

* clearing up

* clearing up

* Fixed style problem in safari and an unused parameter.

* Fixed lint error.

---------

Co-authored-by: engjlr <enl@umbraco.dk>
Co-authored-by: Engiber Lozada <89547469+engijlr@users.noreply.github.com>
2025-12-15 14:16:57 +00:00
Andy Butland 1a4256f997 Add option to hide colors from icon picker (#20650)
* Add option to hide colors from icon picker

* Hide colors

* Hide colors from config
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-picker-modal/icon-picker-modal.element.ts
2025-12-11 06:57:29 +01:00
Andy Butland 80ae0380a2 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:55:09 +01:00
Andy Butland fd01282798 Merge branch 'release/16.4.1' into v16/dev
# Conflicts:
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
2025-12-05 06:44:51 +01:00
f7ba2eaa62 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-12-04 16:34:10 +01:00
9485a95c0e 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-04 16:32:10 +01:00
f1ab605bb9 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 10:24:12 +01:00
Andy Butland 3472ff9ba3 Bump version to 16.4.1. 2025-12-03 15:38:10 +01:00
577dc06d55 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-12-03 15:37:03 +01:00
f4771d1495 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-12-03 12:32:30 +01:00
Andy Butland 4b3ce53acf Merge branch 'release/16.4' into v16/dev
# Conflicts:
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
2025-11-26 07:18:09 +01:00
Andy Butland ca267047d3 Bumped version to 16.4.0. 2025-11-25 12:32:28 +01:00
Jacob Overgaard 0543163817 bumps version to 16.5.0-rc 2025-11-24 16:29:11 +01:00
Jacob Overgaard 72f43a5821 Merge branch 'release/16.4' into v16/dev 2025-11-24 16:28:24 +01:00
aea9034adf Localization: Restores region-specific cultures (#20939) (#20942)
* 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: Lee Kelleher <leekelleher@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-24 15:27:35 +00:00
Jacob Overgaard 6e6f822761 bumps version to 16.4.0-rc3 2025-11-24 15:24:58 +01:00
Niels LyngsøandGitHub 137aa20a10 Block Editors: avoid discard changes for no changes (Fixes #20680) (#20941)
* ensure Block List only updates if it has an update

* ensures RTE and Grid Block Editor ony updates value if there is a change
2025-11-24 14:04:59 +00:00
Callum WhyteandZeegaan d7231c5435 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>
(cherry picked from commit 908974c6ac)
2025-11-24 12:47:08 +09:00
Andy Butland be116436d9 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.
# Conflicts:
#	src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs
2025-11-19 15:35:48 +01:00
Jacob Overgaard aed7505e4b Merge remote-tracking branch 'origin/release/16.4' into v16/dev 2025-11-17 16:39:38 +01:00
590a020303 Redact back-office PKCE codes from the server (V16) (#20851)
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 11:17:18 +01:00
Jacob Overgaard 15c6ca7628 Merge remote-tracking branch 'origin/release/16.4' into v16/dev 2025-11-13 14:15:24 +01:00
49ba89c22a 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>
2025-11-13 08:19:42 +01:00
Andy Butland c295271757 Bumped version to 16.4.0-rc2. 2025-11-13 06:39:10 +01:00
Niels LyngsøandNiels Lyngsø 12b483ff05 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 17:42:16 +01:00
Andy ButlandandGitHub 7502a38033 Dependencies: Update dotnet sdk and node development dependency to latest secure version of current major (16) (#20734)
* Update dotnet sdk and node development dependency to latest secure version of current major.

* Update package-lock.json.
2025-11-05 20:02:11 +01:00
Jacob Overgaard 4e74dbf218 Merge branch 'release/16.4' into v16/dev 2025-11-04 14:07:30 +01:00
Jacob OvergaardandGitHub fa5c53b571 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 11:31:34 +00:00
Andreas ZerbstandGitHub 76fed82e91 E2E: QA cherry picked acceptance tests updates from 17 (#20714)
* Updated tests

* Bumped version

* Added v16 to nightly e2e run
2025-11-04 08:23:04 +01:00
Rick ButterfieldandAndy Butland 43ac32282c Preview: Add allow-forms to iframe sandbox attributes (#20701)
Add 'allow-forms' to iframe sandbox attributes
2025-10-31 13:37:35 +01:00
96ecef0a92 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 12:41:46 +01:00
Laura Neto 5e87dead44 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 12:11:32 +01:00
7af67d2944 Have to control of the state store navigation for custom sections or … (#20637)
* Have to control of the state store navigation for custom sections or overrides

* revert wording

* move logic and update comment

---------

Co-authored-by: Lucas Bach Bisgaard <lucas.bisgaard@kraftvaerk.com>
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2025-10-29 20:00:36 +01:00
Jacob Overgaard ce59537006 build: updates lockfile 2025-10-29 11:40:10 +01:00
Jacob Overgaard f87e15b941 build: adds back the ^ missing from openapi-ts to allow newer versions to be used 2025-10-29 11:38:44 +01:00
Andy Butland 1f82bdde3d Merge branch 'release/16.3.4'
# Conflicts:
#	src/Umbraco.Web.UI.Client/package-lock.json
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
2025-10-29 06:45:38 +01:00
0d2393d866 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:26:26 +01:00
bea21d7b99 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 12:25:13 +00:00
Andy Butland 3dc65c48b3 Bump package-lock.json to 16.3.4. 2025-10-28 09:58:31 +01:00
Niels LyngsøandAndy Butland 18ab333afc Hotfix: Implement a specific sorting method for statuses as the existing has … (#20609)
Implement a specific sorting method for statuses as the existing has to support deprecated implementation of custom getUnique method
2025-10-28 09:15:17 +01:00
Niels LyngsøandAndy Butland fd91f88a7e 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
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/packages/core/repository/repository-items.manager.ts
2025-10-28 09:15:05 +01:00
Andy Butland 13c164d81f Bump version to 16.3.4. 2025-10-28 08:58:03 +01:00
Andy ButlandandGitHub f33eb3f678 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.
2025-10-27 13:42:48 +01:00
Jan SkovgaardandSebastiaan Janssen e893682723 Don't call generateAlias on #onAliasChange()
Currently it's not possible to use characters like "_" and "-" in aliases due to this check - At least that is was @nul800sebastiaan told me 😇

Suggested fix for #20622
2025-10-25 10:47:56 +02:00
d9c201e3d1 docs: Add backoffice preview URL to README files (#20623)
* docs: Add backoffice preview URL to README files

Added links to https://backofficepreview.umbraco.com/ in both the main repository README and the Umbraco.Web.UI.Client package README to make the live backoffice preview easily discoverable.

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

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

* docs: fix link

* Update .github/README.md

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-10-24 08:36:34 +02:00
Andy Butland a5fcfc231d Merge branch 'main' of https://github.com/umbraco/Umbraco-CMS 2025-10-24 06:44:08 +02:00
Andy Butland 6ba03a48c8 Merge branch 'release/16.3.3'
# Conflicts:
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
2025-10-24 06:43:55 +02:00
8434c7d0cb Icon Picker: Fix empty selection allowed on mandatory fields and add validation. (#20536)
* Not show the empty tile when filtering is active.

* Added mandatory property to the icon picker.

* Avoid deselecting the icon on second click when not showing the empty option.

* Extends the form control mixin to the icon picker.

* Used super.value.

* Support mandatory from settings config.

* Removed mandatoryConf.

* remove requestUpdate

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2025-10-23 13:49:14 +00:00
Bjarne FyrstenborgandGitHub 3854b2bd53 Block List: Remove bold label from inline editing (#20437)
Remove bold label from block list inline editing
2025-10-23 14:32:37 +02:00
dependabot[bot]andJacob Overgaard 08d217360e Bump vite from 7.1.9 to 7.1.11 in /src/Umbraco.Web.UI.Login
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-23 12:05:29 +02:00
Sebastiaan JanssenandGitHub b762135554 Exclude 'release/no-notes' from release labels 2025-10-23 11:21:25 +02:00
Andy Butland 644334c63b 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:43:12 +02:00
Andy Butland a09e1777c4 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 16:43:04 +02:00
Andy Butland 9cb59fe1b4 Bumped version to 16.3.3. 2025-10-22 16:42:12 +02:00
Andy ButlandandGitHub 6bc498ad41 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 14:20:20 +00:00
f88e28d642 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:12:39 +02:00
Mads RasmussenandNiels Lyngsø 194fee7c91 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:31:41 +02:00
Niels LyngsøandGitHub 4a65f56d9d Hotfix: Implement a specific sorting method for statuses as the existing has … (#20609)
Implement a specific sorting method for statuses as the existing has to support deprecated implementation of custom getUnique method
2025-10-22 11:57:09 +00:00
Sven GeusensandGitHub 62c1d44a5d 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:46:56 +02:00
Andy Butland c2eea5d6cc Populate IncludeDescendants on ContentPublishedNotification when publishing branch (forward port of #20578). 2025-10-22 13:37:19 +02:00
Niels Lyngsø 21bf23b67d 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 12:37:13 +02:00
Andy ButlandandGitHub 48759b9852 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 10:21:42 +00:00
Niels Lyngsø 79639c0571 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
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/packages/core/repository/repository-items.manager.ts
2025-10-22 11:52:09 +02:00
Andy Butland 0792e4358b Merge branch 'release/16.3.2'
# Conflicts:
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
2025-10-22 07:11:02 +02:00
Jacob Overgaard caeb3454e1 build(dev): adds umbracoapplicationurl to vscode launch params 2025-10-21 16:22:57 +02:00
Jacob Overgaard 942ccc82d9 docs: Add 'Running Umbraco in Different Modes' section to copilot-instructions 2025-10-21 16:22:36 +02:00
Andy Butland 8aa9dc8f19 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.
2025-10-21 15:27:15 +02:00
Andy Butland 5488c77e0e Bumped version to 16.3.2. 2025-10-21 15:26:29 +02:00
Andy ButlandandGitHub daace4b4a0 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:05:10 +02:00
Andy ButlandandGitHub 1ceec183a3 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.
2025-10-21 11:38:01 +02:00
Andy ButlandandGitHub 81a8a0c191 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.
2025-10-21 09:57:29 +02:00
ae41438a36 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 07:28:01 +00:00
dependabot[bot]andJacob Overgaard 5337c38f2c 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:13:34 +02:00
Nhu DinhandGitHub 7751e40ba8 E2E: QA Fixed the flaky tests related to publishing content with image cropper (#20577)
Added more waits
2025-10-21 08:50:13 +02:00
Lee KelleherandGitHub d5a2f0572e 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
2025-10-20 11:51:38 +02:00
Andy Butland 5a65eb1758 Update OpenApi.json and client-side models. 2025-10-17 15:01:09 +02:00
Anders ReusandGitHub 105cb9da41 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 14:40:18 +02:00
Andy ButlandandJacob Overgaard a3a8be4717 Templates: Retain layout from file when loading template (closes #20524) (#20529)
Retain layout from file when loading template.
2025-10-17 10:47:21 +02:00
Jacob Overgaard d17ba805b2 build(deps): bumps @umbraco-ui/uui from 1.16.0-rc.0 to 1.16.0 2025-10-17 09:56:42 +02:00
Andy Butland 96f597e440 Merge branch 'release/16.3.1'
# Conflicts:
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
2025-10-17 09:55:20 +02:00
Ben WhiteandAndy Butland 6458bb40f9 Don't use non-generic ILogger as a fallback in BlockEditorPropertyValueEditor (#20532)
Update logger service retrieval in BlockEditorPropertyValueEditor
2025-10-16 21:07:16 +02:00
Ben WhiteandGitHub 31bcbc1147 Don't use non-generic ILogger as a fallback in BlockEditorPropertyValueEditor (#20532)
Update logger service retrieval in BlockEditorPropertyValueEditor
2025-10-16 21:06:10 +02:00
Andy Butland 62edad17a1 Bumped version to 16.3.1. 2025-10-16 20:50:59 +02:00
Kenn JacobsenandAndy Butland 369b020d9d 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 14:17:04 +02:00
Laura Neto ec354cef92 Merge branch 'release/16.3'
# Conflicts:
#	src/Umbraco.Web.UI.Client/package-lock.json
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
2025-10-16 10:05:33 +02:00
Laura Neto a504fd1ef8 Bump version to 16.3.0 2025-10-16 08:22:11 +02:00
Warren BuckleyandGitHub 4c05a114c5 Fixes 20476 - Changes icon to be no entry sign (#20496) 2025-10-15 14:10:39 +02:00
Andy ButlandandGitHub e71f36d816 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:06:00 +02:00
Andy ButlandandGitHub fdf759d08d 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:41:41 +02:00
1ab13a970b Dashboard: Add tests for create and using custom dashboard (#20253)
* add tests for custom dashboard

* update test dashboard using helper

* remove extensionRegistry for playwright config

* update helper version for dashboard

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/CustomDashboard.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* fix format code

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>
2025-10-15 13:33:41 +07:00
e22b459d9c WorkspaceView: Add tests for create and using custom workspace view (#20408)
* WorkspaceView: Add tests for create and using custom workspace view

* update helper version

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* update format code

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.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-10-15 13:33:24 +07:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>iOvergaardleekelleher
a19b9fb5fe 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>
2025-10-14 16:35:42 +00:00
Anders ReusandGitHub cdf9ee4566 Added culture to the ApiContentRouteBuilder to include variant languages. (#20366) (#20499)
Added culture to the ApiContentRouteBuilder to include variant languages.
2025-10-14 16:06:48 +02:00
Sven GeusensandGitHub e53220c8f5 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:15:01 +02:00
Andy ButlandandGitHub 12adfd52bd 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.
2025-10-14 11:22:21 +02:00
494674d354 Entity Actions: More create button discernible text, extension of #20434 (#20458)
* added hovering and focus border to RTE

* fix main to OG

* fix to main again

* I'm going to cry

* Missing localiztion feature, maybe UmbLitElement?

* added localization controller to fetch localized version

* localization successful for viewActionsFor and CreateFor

* clean up button text

* Changed label for content header to display proper name

* clean up code

* Included button labels for media section

* clean code

* Relocated localization keys,

as `actions_viewActionsFor` already existed.

Also made into a function, to support a fallback label.

* Simplified the "Create for" label/localization

Removed the need for a `getCreateAriaLabel()` method.

* Removed the double-localizations (of `actions_viewActionsFor`)

as the "umb-entity-actions-bundle" component handles this now.

* imports tidy-up

* Simplified localization key condition

* switched to new localization key for other sections for new labeling

* Bumped `@umbraco/playwright-testhelpers` 16.0.55

https://github.com/umbraco/Umbraco.Playwright.Testhelpers/releases/tag/release%2F16.0.55

---------

Co-authored-by: Oskar kruger <obk@umbraco.dk>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-10-14 07:20:01 +00:00
Lee KelleherandGitHub 4ba186633c 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-14 08:46:48 +02:00
Andy Butland e336f9dfb0 Merge branch 'release/16.3'
# Conflicts:
#	version.json
2025-10-14 08:22:07 +02:00
Lee KelleherandGitHub 3ac37f3686 Recycle Bin: Trigger cache invalidation for trashed document/media items (#20483)
* Configure document/media items to listen for `Trashed` server-events for cache invalidation

* Fire reload event on restore destination tree/menu

* Removed "trashed" part of the code comment
2025-10-13 16:32:48 +01:00
0a027dd80d Dependencies: Fixed dependency conflicts when installing Microsoft.EntityFrameworkCore.Design (closes #20421) (#20474)
* Add explicit references to Microsoft.CodeAnalysis.* packages to fix conflicts when installing Microsoft.EntityFrameworkCore.Design

This allows consumers to simply install Microsoft.EntityFrameworkCore.Design without having to manually install specific versions to deal with transitive dependency problems.

* Disable CPM for UI project to better reflect consumers

* Update src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-13 13:12:45 +02:00
Bjarne FyrstenborgandGitHub 7b4684cd70 UX: Center align log type in media history view (#20469)
Center align log type in media
2025-10-13 06:34:02 +02:00
3df8b9e41a 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 13:00:47 +00:00
Bjarne FyrstenborgandGitHub fd34ce5bd7 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:32:19 +02:00
99c2aaf17a Members: Forward port of fix for member lockout issue #16988 from PR #17007 for 16 (#20441)
* Port PR #17007

* Update src/Umbraco.Infrastructure/Security/IdentityMapDefinition.cs

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-10 09:52:57 +02:00
a4c373d3b5 Entity Actions: Create button discernible text (fixes #20205) (#20434)
* added hovering and focus border to RTE

* fix main to OG

* fix to main again

* I'm going to cry

* Missing localiztion feature, maybe UmbLitElement?

* added localization controller to fetch localized version

* localization successful for viewActionsFor and CreateFor

* clean up button text

* Changed label for content header to display proper name

* clean up code

* Included button labels for media section

* clean code

* Relocated localization keys,

as `actions_viewActionsFor` already existed.

Also made into a function, to support a fallback label.

* Simplified the "Create for" label/localization

Removed the need for a `getCreateAriaLabel()` method.

* Removed the double-localizations (of `actions_viewActionsFor`)

as the "umb-entity-actions-bundle" component handles this now.

* imports tidy-up

* Simplified localization key condition

---------

Co-authored-by: Oskar kruger <obk@umbraco.dk>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-10-09 13:56:30 +00:00
bcedc8de2a Emails: Add Expires header (#20285)
* Add `Expiry` header to emails, set default expiry to 30 days and allow user config via `appsettings`

* Remove `IsSmtpExpirationConfigured` as it will always have a value

* Check for `emailExpiration` value

* Removed `EmailExpiration` default value as it should be opt-in

* Simplify SMTP email expiration condition

* Fix APICompat issue

* Add implementation to `NotImplementedEmailSender`

* Rename `emailExpiration` to `expires` to match the SMTP header

* Obsolete interfaces without `expires` parameter, delegate to an existing method.

* Set expiry TimeSpan values from user configurable settings with defaults

* Fix formating

* Handle breaking changes, add obsoletion messages and simplify interfaces.

* Fix default of invite expires timespan (was being parsed as 72 days not 72 hours).

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-10-09 14:27:53 +02:00
767894b723 Color Picker: Validate uniqueness of selected colors (#20431)
* Added unique color checker to color picker.

* Added Unittest for duplicates

* optimized for codescene

* removed the bump and simplified the function

* Fixed behaviour for duplicate checks so unit test passes.
A little refactoring.

* Adds continue so invalid colors aren't checked for duplicates.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-10-09 11:50:11 +02:00
1fe7931d07 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 09:41:41 +02:00
Andy ButlandandGitHub 16132b0075 Update Umbraco version for LTS release in template 2025-10-09 06:37:21 +02:00
Niels LyngsøandGitHub 9a18dd547d Content Type Designer: Fix moving a group to an inherited tab (#20138)
move group to inherited tab
2025-10-08 16:49:48 +00:00
Niels LyngsøandGitHub 7ecc6ece7e Block Workspace: dynamic headline & browser title (#20424)
* get content type name method

* use local virtual render for the workspace headline and browser title
2025-10-08 15:40:42 +00:00
a676dc4bad Announce newest notification using live region (#19979)
* Added screen readers notification support

* Making the sr-live div not visible for users

* Moved aria-live outside the repeat

---------

Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2025-10-08 15:27:56 +00:00
9fe8b2162b Adjust styling for inherited content type property (#20261)
* Adjust styling for inherited content type property

* Slight style adjustments inherited property

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-10-08 15:22:09 +00:00
Mads RasmussenandGitHub 429f8b0bed Permissions: Added User fallback permission condition (closes #20097) (#20224)
* add user fallback permission condition

* add example
2025-10-08 16:17:35 +01:00
Niels LyngsøandGitHub 1a462bcc7d Content Workspace: Invariant Breadcrumbs does not work (fix #20423) (#20428)
fix navigating to an invariant document via breadcrumb
2025-10-08 15:09:15 +00:00
eaac515b30 V16/bug/20035 validation context uncaught promise (#20036)
* #20035 Updated validation context example to reflect issue

* Fixes #20035 by handling returned promise

* just catch if it was rejected

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2025-10-08 12:33:04 +00:00
c6e5df40c9 Backoffice: Add Entity Signs (overlay icons) to tree items. (#20328)
* entity signs folder

* update package.json

* entity sign extension type

* implement entity sign extension

* POC document has collection sign

* implement icon kind

* rename file

* note about this being wrong

* move type

* change import

* entity sign bundle element

* implement icon kind label

* Display icon and show popover on hover

* Fix the popover logic

* Moving the sign icon to the iconContainer to handle position

* fix missing document tree icon

* revert removal of icon slot render

* remove unused styles

* document tree item - inherit styles from the base element

* correctly extend styles

* revert document tree item icon change

* move icon container html

* add method to get an icon name

* Adding delay to the popover when opens

* Add animation to popover when it opens

* Making the parent of the entity bundle trigger popover on hover

* Display 2 icons over the main icon

* Updating some styles

* Position one icon on top of the other and add css style variables

* Changing popover-container for position-anchor

* generate server types

* Using css properties to display and animate the signs

* Stacked icons using grid property

* Use translate property to move the icons around

* Added fallback styles for firefox

* formatting of state properties

* implement entity flags across content types

* lint fixes

* fix import extension mess

* await both properties for this to work

* transfer flags to entity sign bundle ext initializer

* is-protected entity sign

* Made signs infobox show downward.

* Changed px to rems

* Change the manifest for the actual signs we will display

* add icon color, remove unused label, add weight

* changes styles + animation + slotted icon inside

* Overwrite pending changes when schedule is active and added green color to schedule.

* adjust animation

* add background for sign

* avoid re-rendering when properties are being set

* Bind the flags to each sign manifest.

* increase signs offset

* fix document tree item draft style

* Removed unused exports.

* Remove duplicated hover timer logic.

* Added eslint disable line to keep the empty method for future implementation.

* rename class

* Rename interface for optional entity flags

* make alias more explicit to prevent future collisions

* include alias in field name to make it clear that we do not except all colors

* align function names with conventions

* always include flags in document items

* compose tree types

* set up entity-flag module and move related types

* change label

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
Co-authored-by: Mads Rasmussen <madsr@hey.com>
2025-10-08 10:40:27 +00:00
Engiber LozadaandGitHub 193d8af9af Icon Picker: Deselect current icon & add “No icon” tile inside the modal. (#20342)
* Show selected icon and color(if any) when open the modal.

* Add a button inside the modal that clears the value

* Deselect the value if we click the already selected icon.

* Add placeholder icon and some localization for labels

* Remove unused variable

* Added config for the placeholder icon in case no icon is selected.
2025-10-08 10:28:44 +00:00
dc92cf4b19 Add accessibility label for splitview divider (#20380)
* added hovering and focus border to RTE

* fix main to OG

* fix to main again

* I'm going to cry

* Added label for splitviewdivider

* Added localization to divider label and updated common lang files

* Removes unused import

---------

Co-authored-by: Oskar kruger <obk@umbraco.dk>
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-10-08 11:11:40 +01:00
Jacob Overgaard edf95e6fab adds deprecation notices to AllowNonExistingSegmentsCreation 2025-10-08 09:58:22 +02:00
Sebastiaan Janssen adf8708987 Move yml file to the correct location 2025-10-08 09:17:43 +02:00
Sebastiaan JanssenandGitHub de2b15d89e Add workflow to create release discussions from labels 2025-10-08 09:15:37 +02:00
Andy Butland dab9df3f10 Bumped version to 16.3.0-rc4. 2025-10-08 07:59:10 +02:00
Andy Butland b036eb3a75 Performance: Added request cache to media type retrieval in media picker validation (#20405)
* Added request cache to media type retrieval in media picker validation.

* Applied suggestions from code review.
2025-10-08 07:58:35 +02:00
Andy ButlandandGitHub 913b79d682 Performance: Added request cache to media type retrieval in media picker validation (#20405)
* Added request cache to media type retrieval in media picker validation.

* Applied suggestions from code review.
2025-10-08 07:56:49 +02:00
Jacob OvergaardandGitHub 4197ca637b build(deps): bumps @umbraco-ui/uui from 1.15.0 to 1.16.0-rc.0 (#20402) 2025-10-08 04:29:03 +00:00
Laura NetoandGitHub df81264794 Integration tests: Fix failing SQLServer integration tests (#20406)
Fix failing SQLServer integration tests

Adjusted the tests so that the created content is retrieved again after creation, instead of using the returned IContent.
This is needed because SQLServer, when using datetime, rounds to the closest .000, .003, or .007, which would cause the comparisons to fail.
We should consider moving away from datetime to datetime2, as the former should be avoided according to Microsoft.

https://learn.microsoft.com/en-us/sql/t-sql/data-types/datetime-transact-sql?view=sql-server-ver17
2025-10-07 16:55:31 +02:00
Piers TaylorandGitHub e9ae3cf5db Repositories: Retrieve users for groups in batches to fix UserService.GetAllInGroup failing on too many users in a group (#20298)
* fix: Identified everywhere the bugs happen and implemented the InGroupOf() extension to successfully batch the SQL queries

* Added helper function in case this batching functionality is in future scopes

* Accidently deleted a groupIds.Any() check while adding BatchFetch helper function

* Removed helper function and instead utilising built in FetchByGroups extension method
2025-10-07 16:09:24 +02:00
Jacob Overgaard f5da0db977 chore: generate new openapi types 2025-10-07 14:43:31 +02:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>iOvergaard
cb8c331777 UFM: Fixes the truncate filter to only add ellipsis when text is actually truncated (closes #20395) (#20396)
* Initial plan

* Fix UFM truncate filter to only add ellipsis when text is actually truncated

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

* feat: trims string before evaluating

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: iOvergaard <752371+iOvergaard@users.noreply.github.com>
2025-10-07 14:19:11 +02:00
Nhu DinhandGitHub 4af9b1e156 User Profile: QA Add acceptance tests for the regression issue #18084 (#20329)
* Added tests for changing own password

* Updated tests for current user profile

* Bumped version

* Make CurrentUserProfile tests run in the pipeline

* Added step to ensure the error notification toast not displays

* Reverted npm command
2025-10-07 09:17:58 +00:00
Jacob OvergaardandGitHub b4e97ea49f Dependencies: Updates all npm dependencies for Client, Login, and UmbracoExtension (#20386)
* build(deps): bump backoffice dependencies

* revert types

* build(deps): updates @hey-api/openapi-ts to 0.85.0 and regenerates files on client and template

* build(deps): updates all icons + regenerate

* build(deps): bumps all login dependencies + regenerate api

* chore: runs eslint --fix

* chore: runs prettier fix on files

* chore: fixes imports
2025-10-07 07:14:23 +00:00
Nhu DinhandGitHub d400ba5899 Delivery API: QA Add acceptance test for media delivery api (#20182)
* Added tests for media delivery api

* Added tests for content delivery api

* Fixed import

* Updated skip tag and issue link for the failing tests

* Bumped version

* Split delivery api tests into 2 files

* Updated tests for media delivery Api

* Cleaned up

* Fixed comments

* Fixed comments
2025-10-07 06:58:16 +00:00
Nikolaj Geisle 629e905187 Bump version 2025-10-06 21:21:14 +02:00
Andy ButlandandNikolaj Geisle bfd2594c7b Hybrid cache: Check for ContentCacheNode instead of object on exists for hybrid cache to ensure correct deserialization (closes #20352) (#20383)
Checked for ContentCacheNode instead of object on exists for hybrid cache to ensure correct deserialization.

(cherry picked from commit 184c17e2c8)
2025-10-06 21:20:56 +02:00
Andy ButlandandGitHub 184c17e2c8 Hybrid cache: Check for ContentCacheNode instead of object on exists for hybrid cache to ensure correct deserialization (closes #20352) (#20383)
Checked for ContentCacheNode instead of object on exists for hybrid cache to ensure correct deserialization.
2025-10-06 21:20:16 +02:00
Bjarne FyrstenborgandGitHub 4330a99830 Block Grid: Styling of group drag with handle (#20361)
* Adjustment of block grid group drag with handle

* Adjust input, handle and delete button
2025-10-06 18:12:43 +02:00
Bjarne FyrstenborgandGitHub fa5f6b9acc Block Grid: Configure minimum one column for areas (#20363)
Configure minimum 1 column for areas in block grid
2025-10-06 18:10:34 +02:00
7819b6dfac Value Presets: also run value presets on load (#20239)
* term example

* better localization options

* localize range

* ensure range value handling

* extract lox high from value setting

* further improvements

* stop requiring entity-type for values

* setup for parsing blueprints as values to the value preset manager

* write test for blueprint values in value preset controller

* deprecate scaffold method in order to use a new more generic name

* Avoid manipulating the incoming data

* Update src/Umbraco.Web.UI.Client/src/assets/lang/en.ts

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

* use max here

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-06 15:15:38 +02:00
Jacob Overgaard 11b62518ae Removed v10/dev, added v17/dev 2025-10-06 13:39:55 +02:00
Nhu DinhandGitHub f3d121d91f Content: QA Add acceptance tests for regression issue #17735 (#20330)
* Added tests for duplicate a content

* Bumped version

* Make all tests for duplicating a content run in the pipeline

* Fixed comments

* Reverted npm command
2025-10-06 10:54:18 +00:00
84fb1f4d6c Extension Registry: QA Added acceptance tests for readOnlyGuards rule, entity action and custom property editor (#19939)
* Added tests for readOnlyGuard rules

* Added backoffice override files for readOnlyGuard tests

* Bumped version

* Added project for ExtensionRegistry tests in playwright configs

* Updated nightly E2E test pipelines to run Extension Registry tests

* Updated nightly E2E test pipeline

* Updated nightly E2E test pipeline

* Updated playwright configs

* Updated nightly E2E test pipeline

* Add test for Entity Action Extension to retrieve entityType and unique (#20020)

* Add entity action test to get unique and entity type

* update test entity action

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>

* Added job to run the Extension Registry tests in the nightly pipeline

* Cleaned up

* Restructure AdditionSetup folder for extension registry

* Updated yaml file for nightly E2E pipeline

* Updated json file for lock action

* Skip test for content delivery API

* Updated port

* Comment out others to run only extension registry tests

* Updated port

* Remove retrieve action folder to test

* Reverted nightly E2E test pipeline

* Reverted

* Updated umbraco package json

* Reverted

* Renamed AdditionalSetup folder

* Renamed folder

* Added appsetting.json file

* Updated appsettings.json

* Updated appsettings.json

* Added debug step

* Added step to build backoffice

* Reverted

* Only spec.ts file run in the extension registry project

* Property Editor: Add tests for create and using custom property editor (#20213)

* Property Editor: tests for create and using custom property editor

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/PropertyEditorTest.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/PropertyEditorTest.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/PropertyEditorTest.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* update review from Nhu

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/CustomPropertyEditor.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/CustomPropertyEditor.spec.ts

Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* fix comment from Nhu

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>

* Format code

* Fixed

* Format code

* Format code

* Format code

* Updated indentation

* Fixed comments

* change the name of test

---------

Co-authored-by: NguyenThuyLan <116753400+NguyenThuyLan@users.noreply.github.com>
Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Andreas Zerbst <andr317c@live.dk>
2025-10-06 09:47:06 +00:00
Niels LyngsøandGitHub e7fde8c01f Entity-Action Bundle: Ensure First Action are updated in rendering (#20351)
ensure requesting rendering update
2025-10-06 11:43:10 +02:00
Niels LyngsøandGitHub a95a2b955f Loader bar: Implement loader for Content Workspaces (#20288)
* no need for a ctrl alias here

* added ctrl aliases and clean up unused css

* implement view loader
2025-10-06 11:41:50 +02:00
02b93e90cb Slider: improved value fallback handling + validation (#20228)
* term example

* better localization options

* localize range

* ensure range value handling

* extract lox high from value setting

* further improvements

* 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-10-06 11:28:38 +02:00
16c0de803b Block Grid: Add a bit more spacing and align button in block grid areas config (#20374)
* Add a bit more spacing and align button in block grid areas config

* Remove unnecessary blank line in CSS

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-10-06 05:20:25 +00:00
Bjarne FyrstenborgandGitHub e80cc1a9f0 Block Grid: Add more spacing in combobox group header (#20373)
Add a bit more spacing in combobox group header
2025-10-06 06:37:00 +02:00
963331448a APIs: Use EndpointMetadata to check for MapToApiAttribute at runtime to include Controller in Swagger document (#20144)
* Use EndpointMetadata to check for existing MapToApiAttribute at runtime

* fix api breaking change

* revert MethodInfoApiCommonExtensions.cs

* remove empty line in ActionDescriptorApiCommonExtensions.cs

* Add xml comments to ActionDescriptorApiCommonExtensions

* Revert boy scout refactoring to primary constructur

* Better xml comments in ActionDescriptorApiCommonExtensions

---------

Co-authored-by: Marcus Wilhelmson <marcus.wilhelmson@consid.se>
2025-10-03 16:42:39 +02:00
Bjarne FyrstenborgandGitHub e5c942b459 Block Grid: Add icon to allowed block types in area (#20364)
* Add icon to allowed block types in area

* Adjust type with icon property

* Use umb-icon instead

* Add styling for combo list option
2025-10-03 11:23:47 +02:00
28fa373b0c Change to layout of default Content area dashboard (#20355)
Change to layout of default content dashboard.

I have removed the max width on the wrapper and increased the padding on the small pods to match the larger pod. This improves consistency with all other default dashboards in other sections of the CMS, none of which had a max width applied and where all pods had larger padding.

Co-authored-by: Paul <paul@madebycrunch.com>
2025-10-03 11:21:52 +02:00
Andreas ZerbstandGitHub e0449d3c00 E2E Pipeline: QA Remove continue on error condition (#20365)
Cherrypicked changes
2025-10-03 09:02:10 +02:00
Bjarne FyrstenborgandGitHub 5a565b7e8d Localization: Added translations for area validation property labels (#20362)
Localization of property labels
2025-10-03 08:03:16 +02:00
Bjarne FyrstenborgandGitHub cd1cbb3b15 Color Picker: Styling for drag handle at swatch items (#20360)
Add styling for drag handle at color picker item input
2025-10-03 07:53:21 +02:00
Bjarne FyrstenborgandGitHub 36dbab2289 Color Picker: Set initial hex value for picker (#20357)
* Set intial color for hidden color input

* Expand 3-digits hex to 6-digits
2025-10-03 07:47:36 +02:00
Andy Butland d9592aa26d Bumped version to 16.3.0-rc2. 2025-10-02 21:16:25 +02:00
Andy ButlandandKenn Jacobsen 97e0c79d94 Caching: Fixes regression of the caching of null representations for missing dictionary items (closes #20336 for 16) (#20349)
* Ports fix to regression of the caching of null representations for missing dictionary items.

* Fixed error raised in code review.

---------

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-10-02 21:15:21 +02:00
Carl SargunarandMatthew-Wise 3f8ad2238f trust certificate (for local dev only) 2025-10-02 20:07:00 +01:00
Carl SargunarandMatthew-Wise 42c7b744c5 update tool path 2025-10-02 20:07:00 +01:00
CarlandMatthew-Wise 589285d081 Switch container to use SQL 2022 instead of SQL Edge 2025-10-02 20:07:00 +01:00
61277b3812 Caching: Fixes regression of the caching of null representations for missing dictionary items (closes #20336 for 16) (#20349)
* Ports fix to regression of the caching of null representations for missing dictionary items.

* Fixed error raised in code review.

---------

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-10-02 18:10:16 +00:00
e6d1d7a6b9 Data Type: Improve missing property editor and property editor UI feedback (#20339)
* Adjust data type workspace UI when opening a data type that has an editor or editor UI that could not be found

* Also display a custom UI in a document property where the editor UI could not be found

* Fix circular dependency

* Small renames

* provide data-path for property editor picker

* update console warning

* Text copy changes

* add comment to element

* Fix editor alias not updating when selecting a different property editor UI

* Remove outdated comment

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2025-10-02 17:41:11 +02:00
Niels LyngsøandGitHub 5d64f51003 Segments: Auto unfold the culture variant in non-culture-variant mode (#20189)
in segment only variant situation then auto unfold the culture variant
2025-10-02 16:57:21 +02:00
Mathias HelsengrenandGitHub 1f747d3b7f Login form: Improved accessibility by removing duplicate label and aria-label (#20232)
Removed Aria label from the username and password input, since we already have <label> tag
2025-10-02 16:49:22 +02:00
Niels LyngsøandGitHub b35db9cb5b Handle vary by segment variant-entires (#20191)
* refactor code

* display language name for empty names

* ensure all culture variants when entering a segment-shared value, shared across cultures

* Revert parts of "ensure all culture variants when entering a segment-shared value, shared across cultures"

This reverts commit 0e64f72695.
2025-10-02 14:51:34 +02:00
436be6ec3f Exception handling: Improve error messaging on invalid umbraco-package.json file (#20332)
* Improve error messaging on invalid umbraco-package.json file.

* Adjust failing unit tests

---------

Co-authored-by: Laura Neto <12862535+lauraneto@users.noreply.github.com>
2025-10-02 13:03:00 +02:00
Laura Neto cf61356b80 Merge branch 'release/16.3' 2025-10-02 12:22:29 +02:00
AlexandGitHub 0d98527c58 Media: Prevent svg image to extend outside the editor bounds (#20346)
Prevent svg image to extend outside the editor bounds
2025-10-02 07:02:56 +02:00
78f4caa2be Refactoring: Add extension method for retrieval of language ISO codes if that's all we need (#20324)
* Retrieve only ISO codes from the database rather than full language objects if that's all we need.

* Apply suggestions from code review

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

* Removed repository updates and migrated the new service method to an extension method.

* Fixed issue after merge.

* Removed left-over using

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-10-01 18:21:41 +00:00
Anders ReusandGitHub 3f0428c8ef Removed the dot "." in front of the jpg file extension example. (#20334) 2025-10-01 15:46:33 +02:00
Erik-Jan WestendorpandGitHub b261628b4b Extend logging (add delay and period) (#20333) 2025-10-01 11:54:27 +00:00
a84d67eff8 Migrations: Create missing tabs on content types when referenced by both composition and content type groups (closes #20058) (#20303)
* Add migration to create missing tabs

In v13, if a tab had groups in both a composition and the content type, the tab might not exist on the content type itself.
Newer versions require such tabs to also exist directly on the content type. This migration ensures those tabs are created.

Also fixes an issue in LeftJoin where nested sql arguments were being discarded.

* Small fixes

* WIP: Integration test.

* Added asserts to show the current issue with the integration test.

* Adjusted the integration test

* Added logging of result. Minor re-order and extraction refactoring in integration test.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-10-01 09:39:56 +02:00
Mads RasmussenandGitHub 0c3e9fb832 16.3 Hotfix: remove status entry when removing item (#20316)
* Add removeStatus method to repository manager

Introduces a removeStatus method to UmbRepositoryItemsManager, allowing removal of a status by its unique identifier.

* Remove item status on picker input removal

Calls removeStatus on the item manager when an item is removed from the picker input selection to ensure its status is updated accordingly.
2025-09-30 12:47:26 +00:00
Bjarne FyrstenborgandGitHub 1082bf17b2 Fix localized message in block group delete confirm modal (#20312)
* Fix localized message in block group delete confirm modal

* Add translation for block configuration removal

* Fix typo

* Fix a few translations

* Adjust translation
2025-09-30 07:01:28 +00:00
418043bf59 Log Viewer: Fixes continuous polling (closes #20274) (#20310)
* LogViewer: Fixes continuous polling (closes #20274)

* Update src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer-workspace.context.ts

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-30 07:31:00 +02:00
65393ff3d9 Models: PropertyType constructor sets the DataTypeKey if IDataType has identity (#20301)
* PropertyType constructor sets the DataTypeKey if passed IDataType has identity

* Updated unit tests to verify behaviour.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-30 05:18:26 +00:00
Niels LyngsøandGitHub 82fc41a459 Media Workspace Path: add parent path to support absolute path generation (#20302)
add parent path to support absolute path generation
2025-09-29 19:21:55 +01:00
Nhu DinhandGitHub 1ca7f3162f E2E nightly pipeline: QA Fix failing acceptance tests for invalid cases (#20289)
* Updated steps to verify the error validation message

* Updated default extension for Tiptap

* Removed skip tag for fixed smoke tests

* Bumped version
2025-09-29 15:12:38 +02:00
Jacob Overgaard a30f837384 Merge remote-tracking branch 'origin/release/16.3' 2025-09-29 13:17:08 +02:00
Jacob Overgaard 2758813547 Merge branch 'release/16.3' 2025-09-29 13:16:43 +02:00
Lee KelleherandGitHub 062b185edf Mocks: Replaced signs for flags (#20293) 2025-09-29 10:47:45 +00:00
03bb658e2e Dependencies: Update ImageSharp to latest patch releases (16) (#20276)
Update ImageSharp dependencies.

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-09-29 07:23:56 +00:00
HenrikandGitHub fbbbc45c24 Micro-optimization: Move compilation of some Regex generation to compile time instead of runtime, make some static ones compiled (#20287)
Move compilation of some Regex generation to compile time instead of runtime, make some static ones compiled
2025-09-28 23:40:50 +02:00
Janus HasseriisandMatthew-Wise fc60b5b5ff Add custom property for #headerColumn 2025-09-26 09:25:28 +01:00
Mads RasmussenandAndy Butland 811cccf24f AllowEditInvariantFromNonDefault : Fix property write guard to use correct variant ID (closes #20250) (#20277)
Fix property write guard to use correct variant ID

Replaces the use of propertyVariantId with _datasetVariantId in the property write guard check to ensure permissions are evaluated for the correct variant.
2025-09-26 09:34:12 +02:00
Mads RasmussenandGitHub 84811046f1 AllowEditInvariantFromNonDefault : Fix property write guard to use correct variant ID (closes #20250) (#20277)
Fix property write guard to use correct variant ID

Replaces the use of propertyVariantId with _datasetVariantId in the property write guard check to ensure permissions are evaluated for the correct variant.
2025-09-26 09:25:51 +02:00
Andy Butlandandleekelleher e752828a24 Fixes issue with service registration for file system tree service (#20273)
* Display the latest update date in document collection view

* Fix issue with service registration of physical file system tree service.

* Apply suggestions from code review

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

---------

Co-authored-by: Laura Neto <12862535+lauraneto@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
(cherry picked from commit 61f1c4abd0)
2025-09-25 14:34:36 +01:00
7572ef5fff NuGet vulnerability warnings: Warn in non-Release mode, Error in non-Release mode (#17244)
* Initial adjustment of the projects with package vulnerabilities that errored, to change to ignore the four specific Nuget vulnerability warnings in Debug mode (but not Release) as per https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu1901-nu1904 (NU1901,NU1902,NU1903,NU1904)

* Fixed formatting errors with tests

* No trailing whitespace

* Move NuGet vulnerability warnings error suppression to Directory.Build.props, combine WarningsNotAsErrors and fix minor issues

* Update Umbraco.JsonSchema.csproj

Removed unwanted change

* Update Umbraco.JsonSchema.csproj

Removed unwanted change

* Revert unecessary changes since merge

* Tweak more unecessary changes

* Small tweaks

* Remove space

* Reverted spacing changes

* Remove no longer required warning exclusions

* Reverted unwanted change

* Reversed order

* A few tweaks to reduce warnings in Umbraco.TestData

* More warnings removed as no longer an issue

---------

Co-authored-by: Ronald Barendse <ronald@barend.se>
Co-authored-by: Emma Garland <emma.garland@rocksolidknowledge.com>
Co-authored-by: Jason Elkin <jasonelkin86@gmail.com>
2025-09-25 15:31:36 +02:00
61f1c4abd0 Fixes issue with service registration for file system tree service (#20273)
* Display the latest update date in document collection view

* Fix issue with service registration of physical file system tree service.

* Apply suggestions from code review

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

---------

Co-authored-by: Laura Neto <12862535+lauraneto@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-25 13:13:02 +00:00
dependabot[bot]andJacob Overgaard cf10a59a4c Bump tar-fs from 3.1.0 to 3.1.1 in /src/Umbraco.Web.UI.Client
Bumps [tar-fs](https://github.com/mafintosh/tar-fs) from 3.1.0 to 3.1.1.
- [Commits](https://github.com/mafintosh/tar-fs/compare/v3.1.0...v3.1.1)

---
updated-dependencies:
- dependency-name: tar-fs
  dependency-version: 3.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-25 14:09:27 +02:00
Bjarne FyrstenborgandGitHub 149b191cf4 Add slightly more padding at top so user card outline is not cutoff (#20258) 2025-09-25 13:54:01 +02:00
Nhu DinhandGitHub 9e3b27cfdd QA Skip the known failing smoke test to avoid blocking other PRs (#20269)
Added skip for the failing smoke test
2025-09-25 09:31:05 +00:00
Owain WilliamsandGitHub 54b49f90bd 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
2025-09-25 10:33:53 +02:00
Jacob OvergaardandGitHub 6714050f4b Installer: Improves user accessibility by adding missing <form> elements and setting the right focus (#20265)
* fix: adds a <form> element around the consent/telemetry step to ensure proper form handling

also adds a submit action so that you can continue with click of ENTER

* fix: adds umbFocus to select inputs to allow the user to proceed with tabbing too much around, i.e. they will start within the form
2025-09-25 09:07:08 +01:00
Bjarne FyrstenborgandGitHub e93802769c Make remove button consistent with other multi value item remove button (#20257) 2025-09-24 21:19:14 +02:00
Laura Neto 1aa8bdf3aa Bump version to 16.4.0-rc 2025-09-24 14:01:34 +02:00
Andreas ZerbstandGitHub a8cdba6e70 Tiptap: QA Added acceptance tests for regression issue #19763 (#20226)
* Added test

* Finished up test

* Updated tests

* Removed comment

* Updated testCommand

* Reverted smokeTest
2025-09-24 11:05:00 +00:00
Niels LyngsøandGitHub 6001d94f50 Add more icons correct Content Type Designer Icons (#20252)
* add more icons correct Content Type Designer Icons

* add icon
2025-09-24 10:55:00 +00:00
Laura Neto 7a3c711add Bump @umbraco-cms/backoffice to 16.2.0 in Umbraco.Web.UI.Login 2025-09-24 12:01:40 +02:00
6d7c722ec3 Upload field Property Editor: Fix resetting value to undefined when empty (#20134)
* set value to undefined when empty

* fix nullable checks

* ensure promise rejection when validation fails

* avoid js error when detailStore is not present

* implement editor as form control

* remove unused

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
2025-09-24 09:29:42 +00:00
Bjarne FyrstenborgandGitHub a6c92d8a91 Tiptap RTE: Adds heading (h4-h6) toolbar buttons (#20243)
* Register more heading icons

* TipTap heading extensions

* Change icon for headings
2025-09-24 10:18:58 +01:00
Bjarne FyrstenborgandGitHub cacd5c23bd Register more heading icons (#20242) 2025-09-24 09:16:28 +00:00
3c7e05dc4a Server Validation: Ugly hack to convert server validation path to match JSON model (#20240)
* Ugly hack to convert server validation path to match JSON model

* Update src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validator.context.ts

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

* Update src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validator.context.ts

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-24 08:18:39 +00:00
Niels LyngsøandGitHub 909bc5ac33 View Context: rename setBrowserTitle to setTitle (#20248)
rename setBrowserTitle to setTitle
2025-09-24 07:58:38 +00:00
36d46624bf Dropdown: Support migrated empty values (closes #20029) (#20247)
* Display the latest update date in document collection view

* Don't consider "" as a missing option when initializing the drop down list.

* Don't flag "" as a missing option when validatng server-side.

---------

Co-authored-by: Laura Neto <12862535+lauraneto@users.noreply.github.com>
2025-09-24 08:48:47 +01:00
Niels LyngsøandGitHub 8c25295294 Workspace Base: make nonesense getter method to make Lit happy (#20238)
make nonesense getter method to make lit happy
2025-09-24 08:32:58 +01:00
Laura NetoandGitHub e96f0e58b7 Collection view: Wrong Last edited date displayed in document collection view (closes #19988) (#20235)
Display the latest update date in document collection view
2025-09-24 07:11:58 +02:00
Bjarne FyrstenborgandGitHub 8136b251f1 Adjust height of image crop input append (#20245) 2025-09-24 07:03:14 +02:00
Bjarne FyrstenborgandGitHub 9cc801a7f7 Make package item readonly instead of disabled (#20246) 2025-09-24 06:59:21 +02:00
Bjarne FyrstenborgandGitHub a02cd8ecb1 Alignment of daterange in logviewer (#20244) 2025-09-24 06:53:10 +02:00
Lee KelleherandGitHub 85cf3d835e Block List: Fixed clipboard label for nested block items (closes #19929) (#20223)
* Clipboard label for nested block item

* Falsey check
2025-09-23 14:10:46 +02:00
9f2d404ca3 Add test for sibling endpoint returning parent id even when its a folder (#20118)
* Improve GetManagementApiUrl to use the globally defined default version if not specified on the controller

* Add a test to check logic introduced in #20083

* Update tests/Umbraco.Tests.Integration/ManagementApi/Trees/DocumentTypeSiblingControllerTests.cs

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

* Update tests/Umbraco.Tests.Integration/ManagementApi/Trees/DocumentTypeSiblingControllerTests.cs

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

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-23 13:56:52 +02:00
Jacob OvergaardandGitHub 8a2f1bc233 Localization: Fixes an issue where the fallback localization was not available after changing DefaultUILanguage (fixes #20216) (#20230)
fix: always load the default/fallback language code so it is available
2025-09-23 10:55:56 +01:00
8213da1b77 Trees: Expanding sibling endpoints to include all entities with trees (#20150)
* Adding member types sibling endpoints

* Introducing sibling endpoint for Partial Views and logic.

* Introducing sibling endpoint for stylesheets

* Introducing sibling endpoint for scripts

* Introducing FileSystemTreeServiceBase.cs

* Introducing interfaces for implementation specific services

* Introducing services for specific trees

* Modifying controller bases to fit new interface and logic.

* Obsoleting old constructors related to PartialView

* Obsoleting ctors related to Stylesheets

* Obsoleting ctors related to scripts

* Adding tests for scriptsTreeService

* Adding tests for siblings

* Removing unused dependencies

* Removing signs and replacing it with flags

* Fixing breaking changes by obsoletion

* Fixing more breaking changes

* Registering missing service

* Fixing breaking changes again

* Changing name of method GetSiblingsViewModels

* Rewritten tests for less bloat and less duplicate code

* Expanding tests to include other methods from service

* Test refactoring: avoided populating file systems that weren't under test, updated encapsulation, renaming, further re-use.

* Management API: Expanding the existing sibling endpoints to support trashed entities (#20154)

* Refactoring existing logic to include trashed items

* Including tests for trashed entities

* Groundwork for trashed siblings

* Documents trashed siblings endpoint

* Controller for Media trashed items

* Expanding tests to include a test for trashed siblings

* Code review corrections

* Resolving code review

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-23 11:17:25 +02:00
Lee KelleherandGitHub f379c9bbdd Member Workspace: Fixes property validation (closes #20210) (#20222) 2025-09-23 09:05:50 +02:00
493dd1dc78 Fix DecimalPropertyEditor on non-English systems (#20215)
* Directly convert from double or float when possible. Also fixes string parsing to work on all cultures. Fixes #20214

* Added unit tests to verify behaviour.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-23 06:59:08 +00:00
Andy ButlandandGitHub a6e736849f Update Umbraco version for LTS release in UmbracoProject template 2025-09-23 06:23:00 +02:00
Niels LyngsøandGitHub 3b8d8c57c3 Content Editor: Fix shared label and localize it (#20188)
* correct shared label and localize it

* fix localization
2025-09-22 17:05:30 +01:00
Niels LyngsøandGitHub fb0f719c7d Shortcuts: Implement context and a few shortcuts of interest (#20204)
* setup files

* allow Unproviding as a valid word

* setup context

* declare new module

* clean up on destroy

* implement keydown listener

* rename to all

* Revert "rename to all"

This reverts commit 5384408d5f.

* revert shortcuts revert

* move view initialization to submittable workspace base

* comment on destroy thingy

* submit workspace shortcut

* rename to action

* observe parent activation to make sure children follows along.

* fix comment to make AI happy

* implement modal view and titles

* fix getting title from token

* rename context alias

* use controller not context here

* provide modal view at modal element

* implement view context at app level

* Refactor view inheritance logic

* reverse children to be activated loop

* note on global shortcuts

* additional note
2025-09-22 15:19:28 +00:00
Nikolaj GeisleandGitHub e29e612e46 Caching: Don't remove null values from hybrid cache to avoid broken content references repeatedly requiring a database hit (closes #18892) (#20209)
* Implement initial fix

* Revert "Implement initial fix"

This reverts commit 05e5803eba.

* Don't remove null cache values, they can always get removed when clearing cache
2025-09-22 16:13:00 +02:00
dependabot[bot]andJacob Overgaard 3c592ad2cd Bump vite from 7.1.3 to 7.1.5 in /src/Umbraco.Web.UI.Login
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.3 to 7.1.5.
- [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.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.5
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-22 13:45:13 +02:00
Andy Butland 2be39d8b9f Merge branch 'main' of https://github.com/umbraco/Umbraco-CMS 2025-09-22 13:34:55 +02:00
Andy Butland 410fc1900d Cherry-pick of #20129 to 16 (part 2). 2025-09-22 13:34:44 +02:00
d7d39658e0 Permissions: Fix removal of check on removal the final admin user (closes #19917) (#19921)
Reworks update of user groups on a user by updating in place rather than deleting and re-adding.
Ensure user groups affected by the update are invalidated in the repository cache.

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-09-22 12:51:27 +02:00
79de4e3871 Tiptap RTE: Adds hover and focus border input states (#20172)
* added hovering and focus border to RTE

* fix main to OG

* fix to main again

* I'm going to cry

* added hovering and focus border to RTE

* fix indentation

* Refactored to set `--umb-tiptap-edge-border-color` variable

so that the toolbar and statusbar can pick up the state changes.

* Applies `transition` to the toolbar/statusbar components

---------

Co-authored-by: Oskar kruger <obk@umbraco.dk>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-09-22 10:35:52 +00:00
8ff11e7c64 Link rendering: Add support for UrlMode parameter in HtmlLocalLinkParser (port to 16) (#20207)
* Add support for UrlMode parameter in HtmlLocalLinkParser (port of #20200 from 13 to 16).

* 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>
Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-09-22 09:34:08 +00:00
Andy Butland 2c3a2e2b2d Cherry-pick of #20129 to 16. 2025-09-22 11:26:25 +02:00
5dd75c0877 Update label for tree item caret to identify if open or closed (#20199)
* added hovering and focus border to RTE

* fix main to OG

* fix to main again

* I'm going to cry

* added dynamic label to expand/collapse button on parent/child treeitems

---------

Co-authored-by: Oskar kruger <obk@umbraco.dk>
2025-09-22 11:09:54 +02:00
182b7e0cfa Accessibility changes for the top navbar tabs and settings tabs (#20107)
* Accessibility changes for the top navbar tabs and settings tabs

* fix indention

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
2025-09-22 08:33:58 +00:00
96b37889ae Docs: Updated contribution guidelines to include PR naming best practices (#20180)
* Add naming PR guide to Update contributing-creating-a-pr.md

* Add PR naming guide to contributing-creating-a-pr.md

* Update contributing-creating-a-pr.md

* Updates note on versions and default branch

Updated default branch reference and contribution guidelines.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-20 13:15:53 +00:00
Niels LyngsøandGitHub fa575d1f84 View Context: observe parent activation to make sure children follows along. (#20206)
observe parent activation to make sure children follows along.
2025-09-20 14:03:41 +02:00
Mads RasmussenandGitHub 37f9dea259 Tree: Optimize tree root total children calls (#20192)
* Remove redundant call to #loadTreeRoot in tree context

* Update tree root requests to use take: 0

Changed all tree repository requestTreeRoot methods to call getRootItems with { take: 0 } instead of { take: 1 }. This ensures that no items are fetched when only the total count is needed to determine if children exist, improving efficiency.
2025-09-20 13:57:38 +02:00
61c0ab6759 Management API: Add user data delete endpoint (closes #19793) (#20040)
* Add user data delete endpoint to the management API

* Fix typo and remove unused umbracoMapper

* Applied changes from code review.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-20 10:45:14 +00:00
Niels LyngsøandGitHub 07f0b7c6ae Content/Document Picker: make not existing items appear as not found items (#20198)
make not existing items appear as not found items
2025-09-19 17:45:46 +00:00
Niels LyngsøandGitHub 12ae51d67e Hints: Chore, just renaming files to fit name (#20203)
rename files to fit name
2025-09-19 19:22:16 +02:00
Niels LyngsøandGitHub 3a196ef996 Icons: add badge icon (#20201)
add badge icon
2025-09-19 19:01:19 +02:00
Niels LyngsøandGitHub c1b74b6883 Badge: Make badge go on top (#20196)
* umb badge and implementation

* only show variant selector hint if hint on none active variant
2025-09-19 13:18:35 +02:00
Mads Rasmussen 5921950ea0 remove console.log 2025-09-19 12:53:25 +02:00
Nhu DinhandGitHub 0f11ee3357 E2E nightly pipeline: Fix the Tiptap failing tests (#20194)
* Updated default value of tiptap toolbar and tiptap extensions

* Bumped version

* Updated default extension value of Tiptap
2025-09-19 16:00:43 +07:00
Niels LyngsøandGitHub eb19346432 View Context: Implement browser title (#20145)
* POC browser title

* support view-alias as null

* provide tab view contexts

* refactor view context

* refactor workspace implementation of view context

* clean up context + revert title order

* view context for section context

* update type

* disable and re-active parent views

* remove unused import

* remove log

* Implementation of Browser Title

* implement more browser titles

* sort imports

* remove unused imports

* use _internal_

* lint updates

* reactive titles

* fix hints for root tab

* implement use of UmbEntityDetailWorkspaceContextBase
2025-09-18 18:50:03 +00:00
ec23f7008e Interaction Memory: Feature for picker modal state retention (#18305) (#20159)
* set property type unique on context

* set the value

* observe property type unique from content picker property editor

* remove unused

* observe data type unique

* wip picker memories

* append memory option to the picker data model

* split into methods

* initialize memory context

* rename arg

* make memory module

* export constants

* allow nested memories

* pass memory from input document to picker context

* Update property-editor-ui-content-picker.element.ts

* fix import

* prefix with interaction

* clean up

* fix import

* rename module

* Update vite.config.ts

* update module name

* observe after search is initialized

* use memory manager in all places

* make picker modal base element

* update types

* add memory for document picker property editor

* store tree item picker expansion state in interaction memory

* Update picker-modal-base.element.ts

* remove the memory if we have no expansion state

* delete memory if it doesn't include anything

* clear picker input memories if nothing comes from the modal

* Refactor interaction memory handling in picker input

Moved the passing of interaction memories from the document picker input context to the core picker input context.  Renamed the method for setting memories from the modal for clarity and consistency.

* only dispatch an event if the value changes

* remove unused

* observe to support close on escape

* add comments

* fix type error

* fix typings

* Replaces data type-based memory keys with config hash-based keys

* dont store picker search in interaction memory

* Rename interaction memory key in picker modal base

* Remove error throw for missing interaction memory

* Refactor interaction memory handling in content picker

Replaces the single 'memory' property with an 'interactionMemories' array and updates event handling to support multiple interaction memories. Adjusts property types, event listeners, and child component bindings to accommodate this change.

* Refactor content picker to use interaction memories

Replaces the previous memory handling with a new approach using interaction memories, including unique hash generation based on config. Updates event handling and property names to align with the new interaction memory model, improving state management and consistency.

* remove debugger

* rename const

* wip media picker memories

* remove args

* simplify memory model

* update internal value before dispatching event

* remove unused

* Update property-type-based-property.element.ts

* rename method

* simplify types

* implement location memory for media picker

* temp type cast

* set location memory when using the breadcrumb

* remove code duplication

* bubble memories from input media to input content

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

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

* fix import

* remove unused method

* Refactor content picker interaction memory management

Introduced UmbPropertyEditorUiInteractionMemoryManager to encapsulate interaction memory logic for property editors. Updated the content picker property editor to use this new manager, removing duplicated memory management code and improving maintainability.

* Refactor interaction memory management in pickers

Replaces custom interaction memory logic in document and media picker property editors with the shared UmbPropertyEditorUiInteractionMemoryManager. Updates unique memory key prefixes for consistency and simplifies related event handling. This improves maintainability and standardizes memory management across property editors.

* export context token

* add js docs

* remove timestamp

* add tests for interaction memory manager

* Added tests for the property editor ui interaction memory manager

* Rename memories to memoriesForPropertyEditor

Renamed the 'memories' property to 'memoriesForPropertyEditor' in the interaction memory manager and updated all references in related property editor components and tests for clarity and consistency.

* Separated out `import type`s + ordering

* remove interaction memory implementation in modal context

* remove interactionMemories from modal interface

* revert to using the umbOpenModal helper

* align property and event name

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-09-18 19:56:31 +02:00
Mads Rasmussen 8b6b2fcdfd fix missing const exports 2025-09-18 18:46:30 +02:00
014e95c2c4 Dynamic Root: Fix missing dynamicRootQueryStep types (closes #19612) (#20183)
* expose content-picker types in a module

* update path

* clean up module

* Update entry-point.ts

* Whitespace tweak

(spaces to tabs)

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-09-18 16:32:12 +00:00
611db11205 Content Variant: Preserve additional URL path in split view navigation (closes #17412) (#20177)
* Preserve additional URL path in split view navigation

Enhances the split view manager to retain any additional pathname segments when updating the browser history, ensuring that navigation state beyond the variant part is preserved.

* Update src/Umbraco.Web.UI.Client/src/packages/core/workspace/controllers/workspace-split-view-manager.controller.ts

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

* format

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-18 17:31:02 +01:00
Jacob OvergaardandGitHub aa2d4f1207 Variants: Implements validation hints to the variant selector (closes #19953) (#20179)
* feat: gets hints and assigns to variants to enable the view to show a badge if there is a hint

* feat: find the first hint on the non-active variant

* feat: protect against non-variants

* feat: ignore invariant variants

* feat: adds a render method for hints

* chore: removes comment

* only add a new hint if the weight is higher
2025-09-18 16:37:21 +01:00
3025dcdf31 Tags Property Editor: Remove tags with keyboard backspace/delete (#19892)
* Remove tags with backspace

* Unused varible

* Manage focusable tag and tabindex updates

* `import`s tidy-up

* Adds `tabindex` and focus outline for each tag

* Removed the tag wrapper container

No longer required.

* Adds support for "Delete" key

* Disables `autocomplete` for new tag input

This conflicts with the suggestions prompt.

* Reverted removal of the tag wrapper container

Required as a "skip tags" tabbing feature

* Uses `UmbChangeEvent`

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-09-18 11:56:11 +00:00
Niels LyngsøandGitHub 1660e69681 Workspace Actions: set attributes on the right element for label to work (#20178)
set attributes on the right element
2025-09-18 12:34:36 +01:00
Andreas ZerbstandGitHub f23050d5c6 V16 QA cherry picked acceptance tests with different configuration (#20106)
* Added appsettings

* Added test setup for different config

* Added appsettings for external login

* Added acceptance tests

* Updated pipelines

* Updated solution file

* V15 QA Added external login provider tests and split pipeline into templates (#20049)

* Added setup for external login

* Started on yaml

* Added test file

* Updated pipeline

* Use env vars

* Added env variables and commented out test we don't need to run

* Removed list from matrix

* Updated condition

* Updated package path

* Updated testFolder

* double slash

* Updated condition

* Updated condition again

* Added port

* Removed redundant values

* Set as env vars

* Added env vars beneath matrix

* Get env

* Updated naming

* Updated usage of values

* Added a check for client id, to see if value set

* Moved env out of pool

* Tried moving env

* Trying to fix the env being empty

* Removed env

* Updated name of variable

* Fixed A cyclical reference

* Updated typo

* More logging

* Reverted change

* Added env

* Added env to tests

* Cleaned up

* Added yaml template files

* Updated nighly pipepline to use templates

* Updated sln

* Split yaml into templates for e2e setup

* Updated pipeline

* Updated solution file

* Set value

* Added if statement

* Added variables

* Set default values

* Updated values

* Updated condition

* Run multiple tests

* Added env

* Updated from parameter to variable

* Fixed condition

* Fixed condition to use actual value

* Updated npx wait on command

* Updated pwsh

* Updated port again

* Updated port value

* Updated wait on

* Updated condition

* Restructured

* Updated var

* Updated run application steps

* Added echo

* Updated to boolean

* Updated conditions

* Updated test template usage

* Added databaseType

* Added another databaseType

* Split up templates

* Fixed indentation

* Updated condition

* updated path

* removed build from path

* Updated conditions for azureAd

* Fixed indentation

* Updated to single qoutes

* Cleaned up

* Removed unused file

* Clarified namin

* Moved

* Updated pipeline, not done

* Updated locator

* Updated pipelines

* Updated test helpers package

* Skipped build stage for default app settings tests

* Updated password var

* Updated locators

* Updated defaultconfig build setup

* Split E2E stage in two

* Added parameter for skipping integration tests

* Cleaned up

* Added ASPNETCORE_URLS

* V15 QA acceptance tests with appsettings (#19550)

* Start of appsetting

* Updated setup of playwright

* Adjusted the pipeline

* Updated appsetting

* Added install test

* Added comments

* Updated pipeline

* Updated development app settings

* Commented tests out

* comment

* Added if statement

* Updated pipeline

* Fixed condition

* Changed to production

* Added a log

* Updated copy item

* Added

* Updated app settings

* Updated pipeline

* Moved playwright login

* Updated pipeline

* Updated app setting

* Updated nightly

* Updated appsettings

* Updated get

* Updated wait on

* Updated appsettings

* Updated connection string

* Updates

* Skips code

* Updated variable

* Updated pipeline

* We want to always retain the trace, to see if the test runs as expected on the pipeline

* Added a temporary wait till port is open

* Fixed condition

* Added missing tcp for wait on

* Updated URL env

* Updated setup

* Fixed string

* Updated locator

* Split tests into SQLite and SQLServer

* Updated pipeline to run all tests

* Retain trace on failure

* Added testFolder var

* Added appsettings and program for delivery api tests

* Updated playwright config

* Split test runners into defaultconfig and different app settings

* Added delivery api tests

* Cleaned up tests

* Bumped version

* Updated pipeline

* Small fixes

* Added password

* Updated connection string

* Fixed

* Removed quotes

* Removed unnecessary connection string

* Added missing password

* Cleaned up

* Cleaned up

* Cleaned up

* Updated to use helpers

* Bumped version

* Updated helper usage

* Added password to variables and a condition

* Added check

* Indented value

* Fixed condition

* More updates

* Updated variable

* Removed settings

* Updated delivery api tests

* Bumped version

* Updated test

* Removed unnecessary variables

* Updates based on copilot comments

* Fixed merge conflict

* Fixed env creation step

* Bumped version

* Updated tests to use new helper

* Updated helper

* Updated to string

* Moved logic to conditions

* bumped version

* Use new name for helper

* Remove echo

* Added variable
2025-09-18 10:58:54 +02:00
fd0ccc529b Property Editors: Improve Missing Property Editor UI and allow save/publish (#20104)
* Initial implementation of non existing property editor

* Adjust `MissingPropertyEditor` to not require registering in PropertyEditorCollection

* Add `MissingPropertyEditor.name` back

* Remove unused dependencies from DataTypeService

* Removed reference to non existing property

* Add parameterless constructor back to MissingPropertyEditor

* Add validation error on document open to property with missing editor

* Update labels

* Removed public editor alias const

* Update src/Umbraco.Web.UI.Client/src/packages/property-editors/missing/manifests.ts

* Add test that checks whether the new MissingPropertyEditor is returned when an editor is not found

* Also check if the editor UI alias is correct in the test

* Apply suggestions from code review

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

* Share property editor instances between properties

* Only store missing property editors in memory in `ContentMapDefinition.MapValueViewModels()`

* Add value converter for the missing property editor to always return a string (same as the Label did previously)

* Small improvements to code block

* Adjust property validation to accept missing property editors

* Return the current value when trying to update a property with a missing editor

Same logic as for when the property is readonly.

* Fix failing unit tests

* Small fix

* Add unit test

* Remove client validation

* UI adjustments

* Adjustments from code review

* Adjust test

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-18 08:55:58 +02:00
Lee KelleherandGitHub 061be01e89 Extension Insights: Fixes CSS alignment (fixes #20170) (#20174)
Extension Insights: Fixes CSS alignment

Fixes #20170.
2025-09-18 08:51:09 +02:00
Andy Butland e415ad22e9 Updated obsoletion messages on IPublishedContent Parent and Children properties. 2025-09-18 06:44:03 +02:00
8a48d17580 Add Accessibility for TipTap RTE input fields (#20157)
* Add Accessibility for RTE input fields

* add localization key for EN

* Adds `aria-required` attribute

---------

Co-authored-by: Oskar kruger <obk@umbraco.dk>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-09-17 13:36:16 +01:00
7483f20191 Misleading profile button label (#20161)
* Add Accessibility for RTE input fields

* fix indentation

* update method with singular fetch

* Update misleading profile button label and localizations

* Revert "update method with singular fetch"

This reverts commit 5360df95bb.

* Update methodname

* Revert XML changes and simplify localization keys

* Restores "openCloseBackofficeProfileOptions" localization key

---------

Co-authored-by: Oskar kruger <obk@umbraco.dk>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-09-17 13:35:51 +01:00
BerglindVandGitHub 311776a0a9 Release Notes: Improved readability for Accessibility tag in the Update release.yml (#20135)
* Update release.yml

* Update release.yml

* Update release.yml

* Update release.yml
2025-09-17 11:33:53 +02:00
Laura NetoandGitHub cc6ce1e80f Mark MemberService.GetMembersByPropertyValue() methods as obsolete (#20092) 2025-09-17 10:06:42 +02:00
Niels LyngsøandGitHub b8b72dd9d3 Content Type Designer: Fix to prevent dragging inherited properties (#20136)
do not drag inherited properties
2025-09-17 10:01:54 +02:00
Lee KelleherandGitHub 04cf97bd90 Member workspace: Fix for loading inside modal (#20163)
* Member workspace (in modal) fix

Fixes #20085.

* Updated with member workspace constants
2025-09-17 09:50:04 +02:00
de8545456d Improvement - Content type filters : Add Validation for allowed children and root (#19903)
* Validate content type filter restrictions when creating content at root.

* Validate content type filter restrictions when creating content as child.

* Update tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentEditingServiceTests.Create.cs

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-17 09:38:12 +02:00
Kenn JacobsenandAndy Butland 546849d825 Make notification service able to handle segmented content (#20160) 2025-09-17 06:55:29 +02:00
Markus JohanssonandGitHub aba82dd522 #19736 adjust drag placeholder for document picker (#19737) 2025-09-16 20:11:35 +00:00
Jacob OvergaardandGitHub 4e300490b2 Generated Partial Views: Improves the generated code slightly for a better developer experience (#20151)
improves the partials to remove an extra semicolon, store a value, and replace an obsoleted key
2025-09-16 18:11:06 +02:00
8cc1d4bcae Avoid throwing an exception on getting references when migrating content with changed data types (16) (#20155)
* Avoid throwing an exception on getting references when migrating content with changed data types.

* Reintroduced generic catch to avoid functional breakage

---------

Co-authored-by: kjac <kja@umbraco.dk>
2025-09-16 11:58:08 +00:00
f6d0667e91 V16 - Renaming signs to flags (#20109)
* Renaming the providers, collection builder and model

* Renaming the items using signs

* Renaming in controllers

* Renaming mapping flags

* Renaming sign tests to flags

* Changing the test files names from signs to flags

* Updated a couple more references.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-16 11:40:48 +00:00
Niels LyngsøandGitHub 5fd01e000c Property Value Preset builder caller args (#20111)
* update types for callArgs

* note for Engage

* implement callArgs type updates

* tests

* implement callArgs for Value Preset Builder

* updated fallback and console.log feedback

* change to warn

* remove unused import

* JS docs

* TODO

* lint fix
2025-09-16 12:07:54 +02:00
Kenn JacobsenandGitHub ef1aaf8bce Cherry-pick #20142 for V16 (#20147)
* Support querystring and anchor for local links in Delivery API output (#20142)

* Support querystring and anchor for local links in Delivery API output

* Add default implementation for backwards compat

* Add default implementation for backwards compat (also on the interface)

* Fix default implementation

* Add extra tests proving that querystring/postfix can be handled for local links in both legacy and current format.
2025-09-16 11:33:40 +02:00
Jacob OvergaardandGitHub 4ae3bb3835 AppSettings: Improves the templates so they reflect the latest recommended settings (#20148)
hides obsolete appsettings from the template and renames `TimeOutInMinutes` to `TimeOut` and updates its format
2025-09-16 11:30:43 +02:00
Lee KelleherandGitHub 9aa9d4499f Fixes regression with hidden tab labels in the Content Editor (#20140)
Fixes regression with hidden tabs in the Content Editor

Regression occurred in #19255, originally fixed in #19370.
2025-09-16 09:15:56 +02:00
Lee KelleherandGitHub 343a07de71 Tiptap RTE: Fixes undo when RTE is emptied (#20133)
* Tiptap RTE: prevent `undefined` value

If the `value` becomes `undefined`, then the block data can't be tracked (for undo/redo).
The scenario comes when a user "selects all" contents, cuts it, and pasted it back in.

Fixes #20076

* Tiptap RTE: fixes selection white text bug

* Tiptap RTE: amends heading styles (for first-child)
2025-09-16 09:14:20 +02:00
Mads RasmussenandGitHub 4207e0360c Reload section root on repeated header section click (#20141)
* Reload section root on repeated header section click

Adds logic to reload the root of a section if its header is clicked while already active. This improves navigation consistency by resetting the section view when the user clicks the current section again.

* Update backoffice-header-sections.element.ts
2025-09-15 17:56:22 +00:00
cbf5665f15 V16: Fix member validation endpoints (#20116)
* Call the validation of member data

* Fix return status

* Refactor to remove duplicate code

* Update src/Umbraco.Infrastructure/Services/MemberEditingService.cs

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

* Removed `disableNotifications` from members validation

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-09-15 17:14:39 +00:00
Jacob OvergaardandGitHub 40fe4995e8 V16: keepUserLoggedIn has no effect (#20123)
* feat: exports all current-user config-related items

* fix: observes the current-user config for the 'keepUserLoggedIn' value and simply try to refresh the token when the worker makes an attempt to log out the user

* 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)

* fix: checks for `keepUserLoggedIn` directly

* Revert "chore: moves current-user-allow-mfa condition to the 'current-user' package to avoid circular dependencies (and because it naturally belongs there)"

This reverts commit 17bebfba41.

* Revert "fix: moves current user config repository and related dependencies to the 'current-user' package"

This reverts commit 0c11462898.

* Revert "feat: exports all current-user config-related items"

This reverts commit a6586aff1d.

* fix: avoids depending on 'resources'
2025-09-15 15:11:01 +01:00
3b6be8e7c4 Feature: Redirect to the last visited path when navigating between sections (#20084)
* redirect to the last visited path in a section

* Update src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-sections.element.ts

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

* Update backoffice-header-sections.element.ts

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-15 14:08:15 +01:00
88dd30b6c5 Repositories: Use NPoco extensions in DatabaseCacheRepository (#19955)
* fix sql syntax issues

* fix some Copilote comments

* fix another Copilot comment removing raw sql strings

* fix review comments

* Usings and comments.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-15 11:22:29 +00:00
84c8c954c7 Adding validation for missing media items (#20101)
* Adding a check to see if the posted value's source path isn't null or empty.

* Moving validation logic to proper files

* Moved logic to a required validator

* Adding tests to ensure validation works

* Minor tidy up: XML header comments, re-use in tests, clarified test names.

* Adding unit tests for file upload validation

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-15 08:40:49 +00:00
d50f8e6718 Block editors: resolves clipboard UFM label (#20102)
* Block item copy rendered UFM label to clipboard

* Fixes clipboard entry's icon

* Clipboard entry: sets title

and fallback on the unique (instead of empty string)

* Clipboard entry: CSS for flat menu structure

* Clipboard entry: replace condition with `when`

+ imports tidy-up

* Imports tidy-up

* Fixed UFM Virtual Render's nested text retrieval

Previously, it'd placed nested text after the parent's text,
now it remains nested.

* Update src/Umbraco.Web.UI.Client/src/packages/ufm/controllers/ufm-virtual-render.controller.ts

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-15 08:56:32 +02:00
5ddcf44256 Independently manage integrated authentication and trust certificate fields on installer from database settings (#20070)
* Independently manage integrated authentication and trust certificate fields on installer from database settings.

* 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-09-12 21:16:57 +02:00
87350d7451 Add Vietnamese to UI culture (#20061)
* add vietnamsese to ui culture

* update vietnamese

* update vietnamese

* add vietnamese

* Update src/Umbraco.Web.UI.Client/src/assets/lang/vi.ts

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

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-10 13:06:23 +00:00
dependabot[bot]andJacob Overgaard 9bad0483d8 Bump vite from 7.1.3 to 7.1.5 in /src/Umbraco.Web.UI.Client
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.3 to 7.1.5.
- [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.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.5
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-10 14:32:07 +02:00
d6c181457c Non existing property editor (#19997)
* Initial implementation of non existing property editor

* Adjust `MissingPropertyEditor` to not require registering in PropertyEditorCollection

* Add `MissingPropertyEditor.name` back

* Remove unused dependencies from DataTypeService

* Removed reference to non existing property

* Add parameterless constructor back to MissingPropertyEditor

* Add validation error on document open to property with missing editor

* Update labels

* Removed public editor alias const

* Update src/Umbraco.Web.UI.Client/src/packages/property-editors/missing/manifests.ts

* Add test that checks whether the new MissingPropertyEditor is returned when an editor is not found

* Also check if the editor UI alias is correct in the test

* Apply suggestions from code review

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

* Share property editor instances between properties

* Only store missing property editors in memory in `ContentMapDefinition.MapValueViewModels()`

* Add value converter for the missing property editor to always return a string (same as the Label did previously)

* Small improvements to code block

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-10 11:20:06 +02:00
Andy ButlandandGitHub c93d4d9318 Correct the population of the parent for sibling items when retrieved under a folder (#20083)
Correct the population of the parent for sibling items when retrieved under a folder.
2025-09-10 07:45:36 +02:00
Andy ButlandandGitHub 4ba01394b8 Updated contributing guidelines to welcome contributions on bugfixes that aren't specifically marked as "up for grabs" (#20086)
* Updated contributing guidelines to welcome contributions on bugfixes that aren't specifically marked as "up for grabs".

* Updated index page.
2025-09-09 16:51:53 +02:00
4294b5a078 Resolve composition properties on a block for the RTE. (#20105)
* dont rellay on only resolved based on properties, but also scan the CompositionPropertyTypes to resolve compistion inside the black

* Refactored to helper method.

---------

Co-authored-by: Lucas Bach Bisgaard <lucas.bisgaard@kraftvaerk.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-09 16:23:49 +02:00
be8fee199f Move persistence of relations from repository into notification handlers (#20095)
* Move persistance of relations from repository into notification handlers.

* Applied suggestions from code review.

* Apply suggestions from code review

Co-authored-by: Ronald Barendse <ronald@barend.se>

* Apply suggestions from code review

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

* Fixed build.

* Fixed failing integration tests.

---------

Co-authored-by: Ronald Barendse <ronald@barend.se>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-09 16:03:20 +02:00
Andy ButlandandGitHub 00092b5061 Fixes divide by zero error on retrieving take 0 from the recycle bin root API endpoint (#20096)
Fixes divide by zero error on retrieving take 0 from the recycle bin root API endpoint.
2025-09-09 14:48:00 +02:00
Nicklas KramerandGitHub d51561b202 V16 - Introducing signs to variants (#20053)
* Adding signs to variants and adjusting HasPendingChangesSignProvider.cs

* HasPendingChangesSignProvider.cs now populates variants & refactoring to move logic to DocumentPresentationFactory.cs

* Working HasScheduleSignProvider.cs to provide variant signs

* Refactoring ISignProvider.cs to take an IEnumerable again

* Moving code from controllers to factories

* Refactoring HasPendingChangesSignProvider.cs to use the right Interface method

* Refactoring HasScheduleSignProvider.cs to be less bloated, and more readable (hopefully)

* Refactoring tests to look at variants and include a list

* Changing instantiation to be better

* Fixed minor logic issue in HasScheduleSignProvider.cs

* Refactoring to include just 1 database call.

* Adjusting tests to use the new methods.

* Reverted breaking changes
2025-09-09 10:31:10 +02:00
955f0c8d63 New document segments endpoint (#20088)
* Added `cultures` property to the Segment models

* Added new endpoint to return the segments of a specific document.

* Mark additional properties and methods as obsolete

* Small indentation fix

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-09 09:48:17 +02:00
fa39908899 Converting DateTime.MinValue to sqlDateTime's minimum value (#20019)
* Converting DateTime.MinValue to sqlDateTime's minimum value

* Changing code to be a bit less hacky

* Changing hard coded value to a variable based on SqlDateTime

* Removing unused code

* Moving date converter logic to DateTimePropertyEditor.cs

* Replacing tests with proper version

* Removing unused import

* Removing unused imports again

* Creating new logic, to ensure formatting is more precise.

* Rewriting tests to be more precise and include testing on odd format separators

* Used parsing to determine timeonly date picker data type configuration format.
Fixed casing on key for data type configuration format.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-09 09:47:49 +02:00
Nhu DinhandGitHub 0712fda069 V16 QA Added acceptance tests for regression issue #17742 (#19996)
* Added tests for create content with content picker with predefined allowed types

* Added tests for content with multi node tree picker

* Bumped version

* Make all tests for content with multi node tree picker run in the pipeline

* Reverted

* Reverted npm command
2025-09-09 09:12:39 +02:00
Lee KelleherandGitHub e30916dd96 Dropdown: Allow empty option (#20099)
Fixes #20029.

If a dropdown property-editor is not marked as mandatory
and is in single-mode, then an empty option is added to
the top of the dropdown, so that the value can be unset.

This doesn't apply to multiple-mode, as values can be deselected.
2025-09-09 08:53:34 +02:00
3cc5715069 Fix spell error from Segmment to Segment (#20010)
* Fix spell error from Segmment to Segment

* Change for fix the misspell interface in a non breaking way

---------

Co-authored-by: Lucas Bach Bisgaard <lucas.bisgaard@kraftvaerk.com>
2025-09-09 07:18:29 +02:00
472be3d40e Content Type Designer tabs sorting improvement (#20074)
* fix style and localization

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

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

* fix class name as well

* minor fixes to sorting of tabs

* clean up

* add data-marks

* Updated package version to include test fixes for tab name

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Andreas Zerbst <andr317c@live.dk>
2025-09-05 09:53:27 +02:00
Niels LyngsøandGitHub f07d691ff6 JS-fix: test the existence of collection as well (#20075)
test the existence of collection as well
2025-09-05 08:43:03 +02:00
45b87c278d Content Type Designer: fix style and localization (#20073)
* fix style and localization

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

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

* fix class name as well

* add data-marks

* Updated package version to include test fixes for tab name

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Andreas Zerbst <andr317c@live.dk>
2025-09-05 08:26:55 +02:00
f06da1c7fa Fixes exception when converting UDIs in a PropertyValueConverter (#20011)
* Changed to use TryParse

* Changed to be a null check instead

* Update to "is false" syntax and add unit tests.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-05 06:19:06 +00:00
Niels LyngsøandGitHub da05e280af Fix space between name and description on content-types (#20028)
set line-height to avoid having too much space
2025-09-04 20:05:48 +02:00
Lee KelleherandGitHub 1b5734d1c8 Tiptap RTE: Capabilities Tidy-up (#20078)
* Updates RTE mock data

* UFM: Adds fallback for "monospace" font-family

* Removes the Font Family/Sizes Menu extension

This feature is not ready yet.

* Tighten up Tiptap config buttons style

* Fixes bug with Collections context-token

Unrelated to Tiptap, but causes data-types to throw an error.
Bug introduced in PR #20033

* Deprecations for v17
2025-09-04 15:20:06 +02:00
Andy ButlandandGitHub 91ce518063 Verify that the request cache is available before using it in PublishedContentFactory (#20062)
* Verify that the request cache is available before using it in PublishedContentFactory.

* Initialized variables as null.
2025-09-04 10:13:48 +02:00
Jacob Overgaard 44371e0e7c Merge branch 'release/16.2' 2025-09-04 09:28:35 +02:00
aab3f6d10f Perform value detection at all property value levels (#20067)
* Perform value detection at all property value levels

* Added null cases for tests.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-04 08:38:11 +02:00
de227993de Create the document URLs lock database record introduced in 16 but required in a 15 migration (#20064)
* Create the document URLs lock database record introduced in 16 but required in a 15 migration.

* Apply suggestions from code review

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

* Revert "Apply suggestions from code review"

This reverts commit 0a4ee48787.

* Revert "Create the document URLs lock database record introduced in 16 but required in a 15 migration."

This reverts commit 42ccaf985e.

* Moved lock record creation to premigration to ensure it's available when rebuilding URLs when migrating through 15 to latest.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: kjac <kja@umbraco.dk>
2025-09-03 18:05:59 +00:00
Andy ButlandandGitHub f118293454 Support identity insert with migrating media type labels and adding new nodes (#20069)
Support identity insert with migrating media type labels and adding new nodes.
2025-09-03 14:39:08 +00:00
Niels LyngsøandGitHub d988cb9375 Workspace View Manifest Overrides (#20033)
* POC of manifest overrides

* clean up

* Collection Manager for Content Workspaces, refactor of existing solution

* lint fix
2025-09-03 14:32:41 +00:00
Jacob OvergaardandGitHub 59a34f6d49 V16: Creating a new sub template should not show Discard Changes dialog (#20068)
fix: sets the masterTemplate at each opportunity to avoid Discard Changes dialog
2025-09-03 14:09:35 +00:00
06bbec433b Add authorizer for content blueprints and handle delete of blueprints in ServerEventSender (#20063)
* Add authorizer for content blueprints and handle delete of blueprints in ServerEventSender.

* Update src/Umbraco.Cms.Api.Management/ServerEvents/Authorizers/DocumentBlueprintEventAuthorizer.cs

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

* Apply suggestions from code review

Co-authored-by: Laura Neto <12862535+lauraneto@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Laura Neto <12862535+lauraneto@users.noreply.github.com>
2025-09-03 13:31:54 +00:00
f5322b5bee Fix issue with newly created template under an existing one (#19669)
* Fix issue with newly created template under an existing one.

* feat: allows to set masterTemplate as preset

* fix: create new sub-templates with a preset already set for the master template (if applicable)

* fix: always resets master template, because you could be coming from an existing editor

* fix: always set the master template even if it is null

* fix: adds updateCurrent to also update the underlying _data model

also refactor function a bit

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-09-03 15:17:20 +02:00
Jacob Overgaard 4eae48e794 bump version to 16.2.0 2025-09-03 14:23:09 +02:00
355f5c373f Database creation/migration for UFM Label editor data-types (#20055)
* Database creation/migration for Label editor data-type

The server-side migration to compliment the client-side feature #19610

* Update src/Umbraco.Infrastructure/Migrations/Upgrade/V_16_3_0/MigrateMediaTypeLabelProperties.cs

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

* Updated integration test with new data-type items count

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

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>

* Apply suggestion from @kjac

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>

* Apply suggestion from @kjac

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>

* Apply suggestion from @kjac

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>

* Apply suggestion from @kjac

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>

* Apply suggestion from @kjac

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>

* Updated to use `switch` pattern matching

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-09-03 09:45:56 +00:00
Lee KelleherandGitHub 96070fb6ab Tiptap RTE: Migration to auto-enable new capabilities (#20057)
* Tiptap RTE: Migration to auto-enable new capabilities

The server-side migration to compliment the client-side feature #20042

* Updated db creation script

with latest RTE capabilities

* Corrected class name typo "Capabilities" 🤦

* Updated default RTE install with TextDirection and TextIndent capabilities
2025-09-03 11:13:29 +02:00
Andreas ZerbstandGitHub 1d6490b5a8 v16 QA Updated flaky tests (#20052)
Updated flaky tests
2025-09-03 09:03:38 +02:00
843344f06a Tiptap RTE: Unbundled "Rich Text Essentials" - Toggle basics on/off per data type (#20042)
* Tiptap RTE: Starter Kit separation

- Created extensions for each Tiptap capability/extension
- Deprecated native `StarterKit` Tiptap extension
- Re-organized all Tiptap extensions into their own feature folders
- Other minor amends/tweaks to improve accessibility

* Update src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/view-source/manifests.ts

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

* 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>

* Minor lint

* Mark the "external" Tiptap exports as deprecated

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-01 17:15:35 +02:00
Jacob OvergaardandGitHub ae44f301cf Removes 'rollup' and 'glob' from NOTICES.txt (#20043)
* removes the mention of rollup in NOTICES.txt and makes it clear that the file only contains first-party mentions

* removes the mention of 'glob'
2025-09-01 13:10:37 +02:00
Jacob Overgaard ddf0819153 chore(eslint): fixes 2025-09-01 13:06:51 +02:00
Jacob OvergaardandGitHub 126d0f55bf V16: Moves icon dependencies to 'core' package.json (#20041)
* build(deps-dev): moves icon dependencies to 'core' package + upgrades them to latest

* chore: regenerates icons

* chore(eslint): fixes eslint warnings
2025-09-01 07:32:07 +00:00
28b90bcfea structure manager refactor implementation (#19970)
* todos

* navigation context

* replace raw manifests with view context

* Array State has method

* rename to hint and much more

* Notes for later

* correcting one word

* more notes

* update JS Docs

* update tests for getHasOne

* fix context api usage

* update code for v.16

* correct test

* export UMB_WORKSPACE_VIEW_CONTEXT

* minor corrections

* rename to _hintMap

* refactor part 1

* update version number in comment

* clear method for array states

* declare hint import map

* mega refactor

* final corrections for working POC

* clean up path logic

* implement scaffold

* propagation and inheritance from view to workspace

* separate types from classes

* refactor to view context

* rename editor navigation context to editor context

* propagate removals

* clean up notes

* Hints for Content Tabs

* use const path

* handle gone parent

* added comments on something to be looked at

* hints context types

* contentTypeMergedContainers

* lint fixes

* public contentTypeMergedContainers

* refactor property structure helper class

* a few notes for Presets

* set variant ID instead of parsing it to the constructor

* do not inject root to the path

* adjust structure manager logic

* UmbPropertyTypeContainerMergedModel type update

* correct mergedContainersOfParentIdAndType

* refactor to utilize new observable for better outcome and performance

* fix lint errors

* fix missing import

* Apply suggestion from @Copilot

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

* Update src/Umbraco.Web.UI.Client/src/packages/core/hint/context/hints.controller.ts

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

* Update src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-validation-to-hints.manager.ts

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

* Update src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-validation-to-hints.manager.ts

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

* clean up

* remove console.log

* declare new exports of core

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

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

* clean up

* fix const export

* remove root from hints path

* also check for invariant

* name more as legacy

* fix eslint

* fix container id setting

* fix resetting inherited property

* fix re-rendering problem

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-08-29 18:05:26 +00:00
7f720ddf7c Implement root property validation for member workspace (#19972)
* todos

* navigation context

* replace raw manifests with view context

* Array State has method

* rename to hint and much more

* Notes for later

* correcting one word

* more notes

* update JS Docs

* update tests for getHasOne

* fix context api usage

* update code for v.16

* correct test

* export UMB_WORKSPACE_VIEW_CONTEXT

* minor corrections

* rename to _hintMap

* refactor part 1

* update version number in comment

* clear method for array states

* declare hint import map

* mega refactor

* final corrections for working POC

* clean up path logic

* implement scaffold

* propagation and inheritance from view to workspace

* separate types from classes

* refactor to view context

* rename editor navigation context to editor context

* propagate removals

* clean up notes

* Hints for Content Tabs

* use const path

* handle gone parent

* added comments on something to be looked at

* hints context types

* contentTypeMergedContainers

* lint fixes

* public contentTypeMergedContainers

* refactor property structure helper class

* a few notes for Presets

* set variant ID instead of parsing it to the constructor

* do not inject root to the path

* adjust structure manager logic

* UmbPropertyTypeContainerMergedModel type update

* correct mergedContainersOfParentIdAndType

* fix lint errors

* fix missing import

* Update src/Umbraco.Web.UI.Client/src/packages/core/hint/context/hints.controller.ts

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

* Update src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-validation-to-hints.manager.ts

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

* Update src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-validation-to-hints.manager.ts

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

* clean up

* remove console.log

* fix validation context initialization

* add member workspace view consts

* setup validation badges for member workspace root fields

* declare new exports of core

* Update src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/value-validator/valueValidator.controller.ts

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

* add comment

* fix example

* fix comment

* fix member workspace failed request

* remove console log

* enable server side validation

* fix circlular dependency

* fix lint errors

* export conts

* fix import

* clean up

* fix type

* fix password validation case

* chore(eslint): reorders imports and cleans unused variables

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-08-29 08:12:57 +00:00
Jacob Overgaard d32348d6b2 bump to rc3 2025-08-29 07:57:39 +02:00
e5f613401f Section Sidebar Menu Expansion (#19810)
* wip section menu expansion

* make section context local to each section

* split kind manifest from element file

* make generic entity expansion manager

* wip menu context

* add collapsed and expanded events

* Export new expansion entity event modules

* rename events

* dispatch events

* Set tree expansion changes in the menu context

* expand menu from workspace

* do not allow undefined

* make menu item feature folder

* Update menu-variant-tree-structure-workspace-context-base.ts

* menu: pass expansion as prop to prevent dependency on the section sidebar

* use correct event

* Add event listener support to extension slot element

Introduces an 'events' property to UmbExtensionSlotElement, allowing dynamic assignment and removal of event listeners on extension components. Event listeners are added when extensions are permitted and removed on disconnect, improving extensibility and event handling for extension slots.

* Add entity expansion event handling to sidebar menu

Introduces handlers for entity expansion and collapse events in the section sidebar menu. This change enables the menu to respond to expansion state changes by updating the context accordingly.

* Optimize expansion state updates in menu components

Introduces a local expansion state to both section sidebar and tree menu item components to prevent unnecessary updates and rerenders. This improves performance by ensuring state updates only occur when needed.

* only check if we have a local state already

* add bulk expand method

* use bulk expand method

* 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

* 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

* bind expansion to section

* make entity expansion manager generic

* add helper method

* remove temp test data

* include last item in target

* remove unused

* 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

* 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

---------

Co-authored-by: Andreas Zerbst <andr317c@live.dk>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2025-08-28 10:55:37 +00:00
Jacob Overgaard d60be0f16e Merge remote-tracking branch 'origin/release/16.2' 2025-08-28 10:17:01 +02:00
Jacob Overgaard 7836770c68 build: enables workflow_dispatch 2025-08-28 07:24:47 +02:00
cbb1eaec28 Prevents the removal of all user groups from a user (#19995)
* Prevents the removal of all user groups from a user.

* Add additional user group when removing

---------

Co-authored-by: mole <nikolajlauridsen@protonmail.ch>
2025-08-27 11:38:28 +00:00
a2b2ecb221 Fix: rename and implement fallbackRenderMethod (#20005)
* rename and implement fallbackRender

* re introducing method as part of the name

* rename impls

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
Co-authored-by: Andrej Davidovič <andrejd@cdata.com>
2025-08-27 13:11:58 +02:00
279409d6ee Fix: Workspace Editor slotted fallback content should be displayed when no routes (#20006)
* fix + notes

* Apply suggestion from @Copilot

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

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-27 13:09:21 +02:00
f0c5ecf086 Add request caching around published content factory (#19990)
* Add request caching around published content factory.

* Fixed ordering of log message parameters.

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

* Invert if to reduce nesting

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: mole <nikolajlauridsen@protonmail.ch>
2025-08-27 11:01:44 +00:00
019563b226 Fix: custom block view rendering (#19962)
* Fix: custom block view rendering

* chore: formatting

* chore: formatting

* chore: marks render method as class property to bind it properly to the class so it can run private methods and does not lose its context

see also #extensionSlotRenderMethod

---------

Co-authored-by: Andrej Davidovič <andrejd@cdata.com>
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-08-27 09:46:27 +02:00
Andreas ZerbstandGitHub e82da17f2c V16 QA added acceptance test for issue 10431 (#19999)
* Added test

* Bumped version
2025-08-27 08:28:16 +02:00
Andy ButlandandGitHub da7454e987 Ensure the directory exists before creating the SQLite database (#19980)
Ensure the directory exists before creating the SQLite database.
2025-08-26 19:54:19 +02:00
b23906a6b6 V16: Unwarranted redirect after auth (#19935)
* fix: uses isAuthorized to check if user is logged in before terminating the observer

* feat: adds new function to redirect to stored path

* fix: always redirect to stored path even on failure

the user may have landed up on the page by mistake

* Revert "fix: always redirect to stored path even on failure"

This reverts commit 0c0cc0253c.

* fix: sends back the result

* fix: waits for the initial authorization request to come back before listening to the authorization signal (and then only listen once for it)

also check if the request was null, which means we can safely redirect the user

* docs: clarify what happens

* chore: converts the promise code to async/await pattern

* fix: tokenResponse should validate its internal object state

* feat: allows function to force a window redirect

* fix: checks if the user happens to already be authorized, because then we do not need a new code check

* Update src/Umbraco.Web.UI.Client/src/packages/core/utils/path/stored-path.function.ts

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-26 15:48:46 +01:00
ae8411ed7b Tiptap RTE: Toolbar configuration sorter (#19901)
* Fix issue dragging tiptap toolbar buttons

* Moved the `.items` CSS rules to the group element

* Refactored the toolbar-group element

- Renamed "toolbar-item-click" event to "remove", to show intent
- Reordered the method names alphabetically
- Renamed `value` to `items`, to show intent
- Removed `toolbarValue`, as not required
- Added `data-mark` for menu/styleMenu buttons

* Renamed/relocated "umb-tiptap-toolbar-group-configuration" element

* Updated tag name

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: leekelleher <leekelleher@gmail.com>
2025-08-26 13:32:34 +00:00
Jacob OvergaardandGitHub 1bd9583cf1 V16: Bump @hey-api/openapi-ts to 0.81.1 (#19982)
* build(deps): bump @hey-api/openapi-ts to 0.81.1 and pin the version to ensure compatibility between backoffice and extensions

* chore: regenerate api types and replace where necessary

* feat: pin version of @hey-api/openapi-ts and regenerate umbraco-extension files

* chore: removes unused 'client' field

* build(deps-dev): bump @hey-api/openapi-ts to 0.81.1 for the login app
2025-08-26 12:40:06 +01:00
Jacob OvergaardandGitHub a0cea4bcb4 build(deps-dev): bump vite from v6 to v7 (#19974)
* build(deps-dev): bump vite from 7.1.2 to 7.1.3

* build(deps-dev): bump vite from v6 to v7
2025-08-26 12:29:22 +01:00
Jacob OvergaardandGitHub 6a4c7794b8 V16: Replacing media files does not update the preview (#19912)
* fix: adds documentation to the UmbImagingRepository and makes the internal store optional, and deprecates an old method

* fix: uses new method to request thumbnails

* fix: ensures the internal data store has at least been attempted to be consumed before proceeding

* feat: adds methods to clear cached resized images

* feat: awaits the store before attempting to clear cache

* fix: attempts to clear the imaging cache when a media item entity is updated or deleted

* fix: awaits the store

* fix: set unique as property

* fix: ensures that the imaging component reloads its thumbnail if it has already been loaded once

* chore: removes duplicate check for isLoading

* chore: cleans imports

* feat: marks method as internal so that we may change it later on

* disable eslint check
2025-08-26 11:23:12 +02:00
Nhu DinhandGitHub bd967b3f75 V16 QA Fixed the E2E failing tests (#19993)
Bumped version
2025-08-26 15:27:58 +07:00
Nhu DinhandGitHub fb5b997090 V16 QA Added acceptance tests for removing user groups from a user (#19994)
* Added tests for removing user group from a user

* Added skip tag and related issue to the failing tests

* Bumped version
2025-08-26 15:26:16 +07:00
Jacob Overgaard 577a43eafe Merge remote-tracking branch 'origin/release/16.2' 2025-08-26 10:15:01 +02:00
Niels LyngsøandGitHub d063f344bc Block workspace view validation badges (#19987)
block workspace view validation badges
2025-08-25 13:26:53 +02:00
2537411fc6 Fix issue console error when navigating to document type template tab (#19913)
Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
2025-08-25 10:59:02 +00:00
9f887846bc Allow open split view using the keyboard (#19927)
* Allow open split view using the keyboard

* Add localize method to the label

* Add a method to localize terms with a language parameter

* adjust localization wording

* support segments in label

* localize texts

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
2025-08-25 09:16:32 +00:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>AndyButlandAndy Butland
1085eebb84 Fix language server events to emit 'Updated' instead of 'Created' for updates (#19941)
* Initial plan

* Fix language server events to emit 'Updated' instead of 'Created' for updates

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

* Removed the added test and instead added the assertion of the added behaviour to an existing test.

* Applied the same test and fix to the dictionary item service.

---------

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: Andy Butland <abutland73@gmail.com>
2025-08-22 13:55:03 +02:00
Mads RasmussenandGitHub fee6722ecc Add timestamps to cache entries and server events (#19976) 2025-08-22 13:49:46 +02:00
7f2d515763 Workspace view navigation context (#19255)
* todos

* navigation context

* replace raw manifests with view context

* Array State has method

* rename to hint and much more

* Notes for later

* correcting one word

* more notes

* update JS Docs

* update tests for getHasOne

* fix context api usage

* update code for v.16

* correct test

* export UMB_WORKSPACE_VIEW_CONTEXT

* minor corrections

* rename to _hintMap

* refactor part 1

* update version number in comment

* clear method for array states

* declare hint import map

* mega refactor

* final corrections for working POC

* clean up path logic

* implement scaffold

* propagation and inheritance from view to workspace

* separate types from classes

* refactor to view context

* rename editor navigation context to editor context

* propagate removals

* clean up notes

* Hints for Content Tabs

* use const path

* handle gone parent

* added comments on something to be looked at

* hints context types

* contentTypeMergedContainers

* lint fixes

* public contentTypeMergedContainers

* refactor property structure helper class

* a few notes for Presets

* set variant ID instead of parsing it to the constructor

* do not inject root to the path

* adjust structure manager logic

* UmbPropertyTypeContainerMergedModel type update

* correct mergedContainersOfParentIdAndType

* fix lint errors

* fix missing import

* Update src/Umbraco.Web.UI.Client/src/packages/core/hint/context/hints.controller.ts

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

* Update src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-validation-to-hints.manager.ts

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

* Update src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-validation-to-hints.manager.ts

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-22 13:46:01 +02:00
Andy Butland 486f1baecb Merge branch 'main' of https://github.com/umbraco/Umbraco-CMS 2025-08-22 09:29:43 +02:00
Andy Butland 28cb2c12e4 Fixed map definitions. 2025-08-22 09:29:30 +02:00
Niels LyngsøandGitHub 2bfeb74a46 fix validation context initialization (#19973) 2025-08-22 08:36:57 +02:00
032236318f Feature: Inflight Request Cache for Document Types and Data Types (#19956)
* extend controller base

* extend controller base

* add package for management api

* add signalr as external package

* connect to server event hub

* do no act on undefined

* add event subject

* correct alias

* export token

* add helper methods

* cache server responses

* fix import

* use helpers

* add detail request manager

* implement for document type

* implement for data type

* add method for update

* add support for create method

* align code

* Update detail-request.manager.ts

* move explicit naming

* move into folder

* collect server code in folder

* add implementation for data type request manager

* implement for document type

* only cache when we have connection to the server events

* poc inflight request cache

* clean up

* update

* add management api inflight request cache

* Update document-type-detail.server.request-manager.ts

* Update src/Umbraco.Web.UI.Client/src/packages/management-api/detail/detail-data.request-manager.ts

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-22 08:36:13 +02:00
dependabot[bot]andJacob Overgaard 7425d10bbe Bump vite-plugin-static-copy in /src/Umbraco.Web.UI.Client
Bumps [vite-plugin-static-copy](https://github.com/sapphi-red/vite-plugin-static-copy) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/sapphi-red/vite-plugin-static-copy/releases)
- [Changelog](https://github.com/sapphi-red/vite-plugin-static-copy/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sapphi-red/vite-plugin-static-copy/compare/vite-plugin-static-copy@3.1.1...vite-plugin-static-copy@3.1.2)

---
updated-dependencies:
- dependency-name: vite-plugin-static-copy
  dependency-version: 3.1.2
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-22 08:26:38 +02:00
fd5de7212e Feature: add client runtime cache for all item endpoints (#19960)
* extend controller base

* extend controller base

* add package for management api

* add signalr as external package

* connect to server event hub

* do no act on undefined

* add event subject

* correct alias

* export token

* add helper methods

* cache server responses

* fix import

* use helpers

* add detail request manager

* implement for document type

* implement for data type

* add method for update

* add support for create method

* align code

* Update detail-request.manager.ts

* move explicit naming

* move into folder

* collect server code in folder

* add implementation for data type request manager

* implement for document type

* only cache when we have connection to the server events

* update

* fix imports

* introduce item cache

* call trough get items controller

* remove log

* add unit tests for item cache

* Create cache.test.ts

* use sync method to lookup data type item

* use correct alias

* remove unused code

* data type item cache

* add client document type item cache

* add client cache for dictionary items

* add media item client cache

* add client cache for media type item

* add client cache for member and member type items

* add member group item cache

* split detail cache invalidation from request manager

* introduce item cache invalidation manager

* remove arg

* add data type item cache manager

* add memeber group item cache invalidation manager

* remove unused

* invalidate documents when document types changes

* align naming

* add method to get unique

* add dictionary item cache manager

* use server model instead of mapping

* call method

* update args

* update document type item cache invalidation

* add cache invalidation for member items

* update

* update

* update

* add item caching for languages

* add template item cache

* cache stylesheet items

* add caching for script items

* cache partial view items

* cache user items

* cache user group items

* add document blueprint item cache

* Removed readonly Signs property to re-align client models.

* Regenerate client types.

* Applied changes from code review.

* cache static file items

* add webhook item cache

* update mocks with signs data

* update mocks

* fix lint error

* fix eslint errors

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-08-22 08:26:04 +02:00
Mads RasmussenandGitHub 6f51a7946b Generate new Server API Models (#19958)
* update mocks with signs data

* update mocks

* fix lint error
2025-08-21 09:23:08 +00:00
Andy ButlandandGitHub 0b1d8dd188 Fix to management API models reverting addition of read-only property (#19951)
* Removed readonly Signs property to re-align client models.

* Regenerate client types.

* Applied changes from code review.
2025-08-21 09:32:39 +02:00
806fde4c25 Navigating the App Language Selector with the keyboard (#19887)
* dropdown keyboard accessibility issue

* Eslint update

* Improve accessibility of the app language dropdown.

* Bring back combobox list element

* Add keyboard support for arrowup and arrowdown

* use change event for value change

* Change button element for a div as trigger

* Unused import

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
2025-08-20 16:21:43 +02:00
Andy ButlandandGitHub 1a65f27ac1 Handle content blueprint server events (#19949)
Handle content blueprint server events.
2025-08-20 13:26:37 +00:00
Andy ButlandandGitHub 43f0ff0957 Adds id to the webhook item response model (#19947)
Adds id to the webhook item response model.
2025-08-20 15:15:40 +02:00
cebfb21eec Introduced sign providers for trees and implemented one for documents with schedule pending (#19806)
* Create sign provider collection and call registered providers on rendering a page of tree item view models.
Re-work tree controller constructors to provide registered providers as a collection.

* Stub implementation of sign provider for documents with a scheduled publish pending.

* Complete implementation of tree sign for pending scheduled publish.

* Added integration test for new method on IContentService.

* Added unit test for HasScheduleSignProvider.

* Apply suggestions from code review

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

* Tidied usings and clarified method header comments.

* Adding a fixed prefix to all future signs, and removing the provider property

* Adding a sign for protected tree documents.

* Adding IsProtectedSignProviderTest.cs & correcting HasScheduleSignProviderTests.cs to no longer assert the provider

* Fixing minor things in accordance with CR

* Adding collection items compatibility

* Introduced IHasSigns interface to provide more re-use across trees and collections.
Fixed updates to base content controllers (no need to introduce a new type variable).
Removed passing entities for populating tree signs (we aren't using it, so simplifies things).

* Refactoring a bit to make existing code less duplicated and fixing some constructor obsoletion

* Introducing a has pending changes sign.

* Applying changes based on CR

* Introducing tests for HasPendingChangesSignProvider.cs and stopped the use of contentService

* Introducing tests for HasPendingChangesSignProvider.cs and slight logic change

* Introduced HasCollectionSignProvider.cs and tests.

* Introducing collection signs to Media Tree & Media Collection items

* Introducing Plain Items and tests. Refactoring tests as well

* Introduced alternative CanProvideSigns() implementation on IsProtectedSignProvider.cs

* Slight refactoring to reduce bloating.

* Adding [ActivatorUtilitiesConstructor] since it threw an error otherwise

* Minor cleanup.

* Updated OpenApi.json.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: NillasKA <kramernicklas@gmail.com>
2025-08-20 10:32:23 +01:00
467e55c5aa Refactoring DatabaseCacheRepository to de-duplicate code and remove warnings (#19942)
* Refactoring to de-duplicate code and remove warnings.

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

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-20 10:48:50 +02:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>iOvergaardCopilot
5068b93f44 Add comprehensive GitHub Copilot instructions for Umbraco CMS development (#19946)
* Initial plan

* Add comprehensive GitHub Copilot instructions for Umbraco CMS development

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

* Remove hardcoded versions from copilot instructions

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

* added instructions on how to test and lint the frontend

* Update .github/copilot-instructions.md

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

* update instructions on how to disable the frontend build

* Update .github/copilot-instructions.md

Co-authored-by: Copilot <175728472+Copilot@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: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-20 10:44:24 +02:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>iOvergaard
9f5827f0ca Fix TipTap word count extension state sharing between multiple editors (#19943)
* Initial plan

* Fix TipTap word count extension state sharing by using configure()

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>
2025-08-20 09:42:01 +02:00
4852e49041 Update dotnet new umbraco-extension template with newer @hey-api/openapi-ts (#19825)
* Updated hey-api as the client-fetch is bundled as part of @hey-api/openapi-ts in newer versions

* Regenerated a new package-lock.json file

* Fix typescript issue

* Update templates/UmbracoExtension/Client/package.json

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

* Updated client dependencies

* Vite, TypeScript, hey-api
* Chalk & Cross-Env for the generate-client script

* Explicitly remove package-lock.json as it will be out of sync due to UMBRACO_VERSION_FROM_TEMPLATE

* Regenerated Hey API client that now ships client rather than dependancy

* Vite and Hey-API were already out of date (updated to the very latest)

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-08-20 08:04:17 +02:00
Nhu DinhandGitHub 1c157b2910 V16 QA Fixed failing E2E test (#19931)
Bumped version
2025-08-19 14:49:14 +07:00
56c49b0aeb Feature: Client Cache for Document Item Models (#19923)
* extend controller base

* extend controller base

* add package for management api

* add signalr as external package

* connect to server event hub

* do no act on undefined

* add event subject

* correct alias

* export token

* add helper methods

* cache server responses

* fix import

* use helpers

* add detail request manager

* implement for document type

* implement for data type

* add method for update

* add support for create method

* align code

* Update detail-request.manager.ts

* move explicit naming

* move into folder

* collect server code in folder

* add implementation for data type request manager

* implement for document type

* only cache when we have connection to the server events

* update

* fix imports

* introduce item cache

* call trough get items controller

* remove log

* add unit tests for item cache

* Create cache.test.ts

* use sync method to lookup data type item

* use correct alias

* remove unused code

* split detail cache invalidation from request manager

* introduce item cache invalidation manager

* remove unused

* invalidate documents when document types changes

* align naming

* add method to get unique

* use server model instead of mapping

* call method

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-08-19 08:42:48 +02:00
Andy ButlandandGitHub a5f9bba481 Ensure appropriate create and update dates are set on updated dictionary items to allow distinguishing between created and update for server events. (#19925) 2025-08-18 11:42:36 +02:00
Mads RasmussenandGitHub a39bc0e269 Feature: Client Cache for Document Type and Data Type Detail Models (#19904)
* extend controller base

* extend controller base

* add package for management api

* add signalr as external package

* connect to server event hub

* do no act on undefined

* add event subject

* correct alias

* export token

* add helper methods

* cache server responses

* fix import

* use helpers

* add detail request manager

* implement for document type

* implement for data type

* add method for update

* add support for create method

* align code

* Update detail-request.manager.ts

* move explicit naming

* move into folder

* collect server code in folder

* add implementation for data type request manager

* implement for document type

* only cache when we have connection to the server events

* update

* fix imports

* Create cache.test.ts

* use sync method to lookup data type item

* use correct alias
2025-08-18 09:57:52 +02:00
Jacob Overgaard 7c3f4f99de build(deps-dev): bump typescript from 5.8.3 to 5.9.2 2025-08-15 10:19:37 +02:00
Jacob Overgaard df507a5354 test: rearrange attributes 2025-08-15 10:19:37 +02:00
Jacob Overgaard 79f6a179c1 chore(eslint): ignores .storybook files 2025-08-15 10:19:37 +02:00
Jacob Overgaard 22339309a5 build(deps-dev): lock storybook to 9.0.14 and typescript to 5.8.3 to ensure compatibility with old version of MSW (v1) 2025-08-15 10:19:37 +02:00
e2d7bb660c Fix and enable some unit tests which are not running locally or on pipeline builds (#19910)
* fix some test which are not running

* resolve code review comments

* Moved cleaned up tests to unit tests (as they are unit tests, not integration tests).
Removed tests marked as no longer necessary.
Update tests name to better reflect test case.

* Made explict test faster so it could run on the pipeline.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-08-15 07:23:39 +00:00
74c9510b56 Chore: upgrade typescript to 5.9 + other minors (#19914)
* update typescript

* update other minors

* revert storybook version

* package lock update

* revert storybook update

* chore: generate new icons

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-08-15 08:34:53 +02:00
Andy Butland b77a63a929 Bumped LTS version in template to 13.10.0. 2025-08-14 09:51:01 +01:00
Andy ButlandandGitHub b8b61cd326 Fixed behaviour on database cache rebuild to update only for requested content types (#19905)
Fixed behaviour on database cache rebuild to update only for requested content types.
2025-08-14 09:13:35 +02:00
Jacob Overgaard 4f84885133 set version to 16.3.0-rc 2025-08-14 07:32:45 +02:00
1687 changed files with 48284 additions and 15290 deletions
+10 -3
View File
@@ -37,7 +37,7 @@ In order to work with the Umbraco source code locally, first make sure you have
### Familiarizing yourself with the code
Umbraco is a .NET application using C#. The solution is broken down into multiple projects. There are several class libraries. The `Umbraco.Web.UI` project is the main project that hosts the back office and login screen. This is the project you will want to run to see your changes.
Umbraco is a .NET application using C#. The solution is broken down into multiple projects. There are several class libraries. The `Umbraco.Web.UI` project is the main project that hosts the back office and login screen. This is the project you will want to run to see your changes.
There are two web projects in the solution with client-side assets based on TypeScript, `Umbraco.Web.UI.Client` and `Umbraco.Web.UI.Login`.
@@ -73,13 +73,20 @@ Just be careful not to include this change in your PR.
Conversely, if you are working on front-end only, you want to build the back-end once and then run it. Before you do so, update the configuration in `appSettings.json` to add the following under `Umbraco:Cms:Security`:
```
```json
"BackOfficeHost": "http://localhost:5173",
"AuthorizeCallbackPathName": "/oauth_complete",
"AuthorizeCallbackLogoutPathName": "/logout",
"AuthorizeCallbackErrorPathName": "/error"
"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.
Then run Umbraco from the command line.
```
+1 -1
View File
@@ -61,7 +61,7 @@ This guide describes each step to make your first contribution:
## Further contribution guides
- [Before you start](contributing-before-you-start.md)
- [Finding your first issue: Up for grabs](contributing-first-issue.md)
- [Finding your first issue](contributing-first-issue.md)
- [Contributing to the new backoffice](https://docs.umbraco.com/umbraco-backoffice/)
- [Unwanted changes](contributing-unwanted-changes.md)
- [Other ways to contribute](contributing-other-ways-to-contribute.md)
+10 -1
View File
@@ -38,9 +38,17 @@ Some important documentation links to get you started:
- [Getting to know Umbraco](https://docs.umbraco.com/umbraco-cms/fundamentals/get-to-know-umbraco)
- [Tutorials for creating a basic website and customizing the editing experience](https://docs.umbraco.com/umbraco-cms/tutorials/overview)
## Backoffice Preview
Want to see the latest backoffice UI in action? Check out our live preview:
**[backofficepreview.umbraco.com](https://backofficepreview.umbraco.com/)**
This preview is automatically deployed from the main branch and showcases the latest backoffice features and improvements. It runs from mock data and persistent edits are not supported.
## Get help
If you need a bit of feedback while building your Umbraco projects, we are [chatty on Discord](https://discord.umbraco.com). Our Discord server serves both a social space but also has channels for questions and answers. Feel free to lurk or join in with your own questions. Or just post your daily Wordle score, up to you!
If you need a bit of feedback while building your Umbraco projects, we are [chatty on Discord](https://discord.umbraco.com). Our Discord server serves as a social space for all Umbracians. If you have any questions or need some help with a problem, head over to our [dedicated forum](https://forum.umbraco.com/) where the Umbraco Community will be happy to help.
## Looking to contribute back to Umbraco?
@@ -52,3 +60,4 @@ You came to the right place! Our GitHub repository is available for all kinds of
Umbraco is contribution-focused and community-driven. If you want to contribute back to the Umbraco source code, please check out our [guide to contributing](CONTRIBUTING.md).
### Tip: You should not run Umbraco from source code found here. Umbraco is extremely extensible and can do whatever you need. Instead, [install Umbraco as noted above](#looking-to-install-umbraco) and then [extend it any way you want to](https://docs.umbraco.com/umbraco-cms/extending/).
+21 -3
View File
@@ -7,9 +7,26 @@ We recommend you to [sync with our repository][sync fork] before you submit your
GitHub will have picked up on the new branch you've pushed and will offer to create a Pull Request. Click that green button and away you go.
![Create a pull request](img/createpullrequest.png)
We like to use [git flow][git flow] as much as possible, but don't worry if you are not familiar with it. The most important thing you need to know is that when you fork the Umbraco repository, the default branch is set to `contrib`. This is the branch you should be targeting.
We like to use [git flow][git flow] as much as possible, but don't worry if you are not familiar with it. The most important thing you need to know is that when you fork the Umbraco repository, the default branch is set to `main`. This is the branch you should be targeting.
Please note: we are no longer accepting features for v8 and below but will continue to merge security fixes as and when they arise.
We welcome PRs for features and bugfixes for different versions according to the [published support and EOL schedule][support-and-eol].
We don't have rules for naming PRs - so name them as you prefer. At HQ we do have a best practice on clear and concise PR naming, so if you would like to use the format feel free to do so.
Our convention of doing it is:
_Area: Description (closes #IssueID)_
1. Start by specifying the area. Fx the feature name(UFM, Tiptap etc.) or specific section (migrations, relations, segmentation).
2. In your description, where applicable, mention type of PR (Build, Bump, Fix, Refactor etc.).
4. Good practise is to make sure you describe specifically the change and/or impact of change.<br>
Example: Writing "Extension Insights: Fixes CSS alignment" instead of "Fixed issue".
6. Add (closes #IssueID) behind description, if your PR resolves an issue.
That's it!
## The review process
[review process]: #the-review-process
@@ -48,4 +65,5 @@ There will be times that we really like your proposed changes and well finish
[making larger changes]: contributing-before-you-start.md#making-large-changes
[pr or package]: contributing-before-you-start.md#pull-request-or-package
[Core collabs]: contributing-core-collabs-team.md
[Core collabs]: contributing-core-collabs-team.md
[support-and-eol]: https://umbraco.com/products/knowledge-center/long-term-support-and-end-of-life/
+11 -8
View File
@@ -1,6 +1,8 @@
## Finding your first issue: Up for grabs
## Finding your first issue
Umbraco HQ will regularly mark newly created issues on the issue tracker with [the `community/up-for-grabs` tag][up for grabs issues]. This means that the proposed changes are wanted in Umbraco but the HQ does not have the time to make them at this time. We encourage anyone to pick them up and help out.
Umbraco HQ will regularly mark newly created issues on the issue tracker with [the `community/up-for-grabs` tag][up for grabs issues]. This means that the proposed changes are wanted in Umbraco but the HQ does not have the time to make them at this time. In adding the label we will endeavour to provide some guidelines on how to go about the implementation, such that it aligns with the project. We encourage anyone to pick them up and help out.
You don't need to restrict yourselves to issues that are specifically marked as "up for grabs" though. If you are running into a bug you have reported or found on the [issue tracker][issue tracker], it's not necessary to wait for HQ response. Feel free to dive in and try to provide a fix, raising questions as you need if you have concerns about the modifications necessary to resolve the problem.
If you do start working on something, make sure to leave a small comment on the issue saying something like: "I'm working on this". That way other people stumbling upon the issue know they don't need to pick it up, someone already has.
@@ -11,15 +13,15 @@ Great question! The short version goes like this:
1. **Fork**
Create a fork of [`Umbraco-CMS` on GitHub][Umbraco CMS repo]
![Fork the repository](img/forkrepository.png)
1. **Clone**
When GitHub has created your fork, you can clone it in your favorite Git tool
![Clone the fork](img/clonefork.png)
![Clone the fork](img/clonefork.png)
1. **Switch to the correct branch**
Switch to the `contrib` branch
@@ -90,4 +92,5 @@ You can get in touch with [the core contributors team][core collabs] in multiple
[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/
[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
[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
+198
View File
@@ -0,0 +1,198 @@
# Umbraco CMS Development Guide
Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
## Working Effectively
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
## 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.
## 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
### 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
### Frontend Tests
- 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)
## 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
### API Projects
- **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
### 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
## Common Tasks
### Running Umbraco in Different Modes
**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`
4. Application uses compiled frontend from `wwwroot/umbraco/backoffice/`
**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"
}
```
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)
5. Changes to TypeScript/Lit files hot reload automatically
**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
### 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
## Clean Up / Reset
To reset development environment:
```bash
# Remove configuration and database
rm src/Umbraco.Web.UI/appsettings.json
rm -rf src/Umbraco.Web.UI/umbraco/Data
# Full clean (removes all untracked files)
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
## 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)
## 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 |
Always wait for commands to complete rather than canceling and retrying.
+2 -2
View File
@@ -7,7 +7,7 @@ changelog:
- duplicate
- wontfix
categories:
- title: 🙌 Notable Changes
- title: 🙌 Notable Changes
labels:
- category/notable
- title: 💥 Breaking Changes
@@ -23,7 +23,7 @@ changelog:
- title: 📦 Dependencies
labels:
- dependencies
- title: 🌈 A11Y
- title: 🌈 Accessibility Improvements
labels:
- accessibility
- category/accessibility
+1
View File
@@ -17,6 +17,7 @@ on:
- main
- v*/dev
- v*/main
workflow_dispatch:
jobs:
build_and_deploy_job:
+1
View File
@@ -17,6 +17,7 @@ on:
- main
- v*/dev
- v*/main
workflow_dispatch:
env:
NODE_OPTIONS: --max_old_space_size=16384
@@ -0,0 +1,163 @@
name: Create a release discussions for each new version label
on:
schedule:
- cron: "0 * * * *" # every hour
workflow_dispatch: # allow manual runs
permissions:
contents: read
discussions: write
issues: read
pull-requests: read
jobs:
reconcile:
runs-on: ubuntu-latest
steps:
- name: Reconcile release/* labels → discussions
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const categoryName = "Releases";
// 24h cutoff
const since = new Date(Date.now() - 24*60*60*1000).toISOString();
core.info(`Scanning issues/PRs updated since ${since}`);
// fetch repo + discussion categories
const repoData = await github.graphql(`
query($owner:String!, $repo:String!){
repository(owner:$owner, name:$repo){
id
discussionCategories(first:100){ nodes { id name } }
}
}
`, { owner, repo });
const repoId = repoData.repository.id;
const category = repoData.repository.discussionCategories.nodes.find(c => c.name === categoryName);
if (!category) {
core.setFailed(`Discussion category "${categoryName}" not found`);
return;
}
const categoryId = category.id;
// paginate issues/PRs updated in last 24h
for await (const { data: items } of github.paginate.iterator(
github.rest.issues.listForRepo,
{ owner, repo, state: "all", since, per_page: 100 }
)) {
for (const item of items) {
const releaseLabels = (item.labels || [])
.map(l => (typeof l === "string" ? l : l.name)) // always get the name
.filter(n => typeof n === "string" && n.startsWith("release/") && n !== "release/no-notes");
if (releaseLabels.length === 0) continue;
core.info(`#${item.number}: ${releaseLabels.join(", ")}`);
for (const labelName of releaseLabels) {
const version = labelName.substring("release/".length);
const titleTarget = `Release: ${version}`;
// search discussions
let discussionId = null;
let cursor = null;
while (true) {
const page = await github.graphql(`
query($owner:String!, $repo:String!, $cursor:String){
repository(owner:$owner, name:$repo){
discussions(first:50, after:$cursor){
nodes{
id
title
url
category{ name }
labels(first:50){ nodes{ name } }
}
pageInfo{ hasNextPage endCursor }
}
}
}
`, { owner, repo, cursor });
const nodes = page.repository.discussions.nodes;
const byLabel = nodes.find(d =>
d.category?.name === categoryName &&
d.labels?.nodes?.some(l => l.name === labelName)
);
if (byLabel) { discussionId = byLabel.id; break; }
const byTitle = nodes.find(d =>
d.category?.name === categoryName &&
d.title === titleTarget
);
if (byTitle) { discussionId = byTitle.id; break; }
if (!page.repository.discussions.pageInfo.hasNextPage) break;
cursor = page.repository.discussions.pageInfo.endCursor;
}
if (!discussionId) {
core.info(`→ Creating discussion for ${labelName}`);
const body =
`**Release date:** TODO (YYYY-MM-DD)\n\n` +
`### Links\n` +
`- [Issues and pull requests marked for version ${version}](https://github.com/${owner}/${repo}/issues?q=label%3A${encodeURIComponent(labelName)})\n`;
const created = await github.graphql(`
mutation($repoId:ID!, $catId:ID!, $title:String!, $body:String!){
createDiscussion(input:{
repositoryId:$repoId,
categoryId:$catId,
title:$title,
body:$body
}){ discussion{ id url } }
}
`, { repoId, catId: categoryId, title: titleTarget, body });
discussionId = created.createDiscussion.discussion.id;
// lock the discussion to prevent replies
await github.graphql(`
mutation($id:ID!){
lockLockable(input:{ lockableId:$id }) {
clientMutationId
}
}
`, { id: discussionId });
core.info(`🔒 Locked discussion ${discussionId}`);
} else {
core.info(`→ Found existing discussion for ${labelName}`);
}
// ensure label exists
let labelId;
try {
await github.rest.issues.getLabel({ owner, repo, name: labelName });
} catch (e) {
if (e.status === 404) {
await github.rest.issues.createLabel({
owner, repo, name: labelName, color: "0E8A16"
});
} else { throw e; }
}
const labelNode = await github.graphql(`
query($owner:String!, $repo:String!, $name:String!){
repository(owner:$owner, name:$repo){ label(name:$name){ id } }
}
`, { owner, repo, name: labelName });
labelId = labelNode.repository.label?.id;
if (!labelId) continue;
// add label to discussion
await github.graphql(`
mutation($id:ID!, $labels:[ID!]!){
addLabelsToLabelable(input:{ labelableId:$id, labelIds:$labels }) {
clientMutationId
}
}
`, { id: discussionId, labels: [labelId] });
core.info(`✓ ${labelName} attached to discussion`);
}
}
}
+5 -1
View File
@@ -101,10 +101,14 @@
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "https://localhost:44339",
"UMBRACO__CMS__WEBROUTING__UMBRACOAPPLICATIONURL": "https://localhost:44339",
"UMBRACO__CMS__SECURITY__BACKOFFICEHOST": "http://localhost:5173",
"UMBRACO__CMS__SECURITY__AUTHORIZECALLBACKPATHNAME": "/oauth_complete",
"UMBRACO__CMS__SECURITY__AUTHORIZECALLBACKLOGOUTPATHNAME": "/logout",
"UMBRACO__CMS__SECURITY__AUTHORIZECALLBACKERRORPATHNAME": "/error"
"UMBRACO__CMS__SECURITY__AUTHORIZECALLBACKERRORPATHNAME": "/error",
"UMBRACO__CMS__SECURITY__KEEPUSERLOGGEDIN": "true",
"UMBRACO__CMS__SECURITY__BACKOFFICETOKENCOOKIE__ENABLED": "true",
"UMBRACO__CMS__SECURITY__BACKOFFICETOKENCOOKIE__SAMESITE": "None"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Umbraco.Web.UI/Views"
+4 -1
View File
@@ -1,5 +1,8 @@
{
"cSpell.words": ["unprovide"],
"cSpell.words": [
"unprovide",
"Unproviding"
],
"eslint.useFlatConfig": true,
"eslint.workingDirectories": [
"./src/Umbraco.Web.UI.Client/",
+2 -5
View File
@@ -14,14 +14,11 @@
<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.Common" Version="4.13.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.13.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" 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.EntityFrameworkCore.Design" 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" />
@@ -76,8 +73,8 @@
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.Map" Version="2.0.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.7" />
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.1.4" />
<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" />
</ItemGroup>
<!-- Transitive pinned versions (only required because our direct dependencies have vulnerable versions of transitive dependencies) -->
+3 -41
View File
@@ -3,7 +3,9 @@ Third-Party Notices
This file contains notices and attributions for third-party software used in the Umbraco CMS project.
It is not a license and does not grant any rights to use the third-party software.
Third-party software may contain dependencies that are not explicitly listed here.
This notice is not a license and does not grant any rights to use the third-party software.
Umbraco CMS is licensed under the MIT License, which can be found in the LICENSE file.
@@ -113,14 +115,6 @@ Copyright: 2023 Shannon Deminick
---
Glob: A library for matching file paths using glob patterns
URL: https://github.com/isaacs/node-glob
License: ISC License
Copyright: 2009-2023 Isaac Z. Schlueter and Contributors
---
Globals: A library for managing global variables in JavaScript
URL: https://github.com/sindresorhus/globals
@@ -356,38 +350,6 @@ Copyright: Titus Wormer
---
Rollup: A module bundler for JavaScript
URL: https://rollupjs.org/
License: MIT License
Copyright: 2015-present Rollup contributors
---
Rollup Plugins: A collection of Rollup plugins
URL: https://github.com/rollup/plugins
License: MIT License
Copyright: 2019-present Rollup Plugins contributors
---
Rollup-plugin-esbuild: A Rollup plugin for using esbuild
URL: https://github.com/egoist/rollup-plugin-esbuild
License: MIT License
Copyright: 2020 EGOIST
---
Rollup-plugin-import-css: A Rollup plugin for importing CSS files
URL: https://github.com/jleeson/rollup-plugin-import-css
License: MIT License
Copyright: 2020 Jacob Leeson
---
rxjs: Reactive Extensions for JavaScript
URL: https://rxjs.dev/
+151 -434
View File
@@ -34,6 +34,10 @@ parameters:
displayName: Upload API docs
type: boolean
default: false
- name: uploadDependencyTrack
displayName: Upload BOMs to Dependency Track
type: boolean
default: false
- name: forceReleaseTestFilter
displayName: Force to use the release test filters
type: boolean
@@ -103,6 +107,15 @@ stages:
command: build
projects: $(solution)
arguments: "--configuration $(buildConfiguration) --no-restore --property:ContinuousIntegrationBuild=true --property:GeneratePackageOnBuild=true --property:PackageOutputPath=$(Build.ArtifactStagingDirectory)/nupkg"
- powershell: |
dotnet tool install --global CycloneDX
dotnet-CycloneDX $(solution) --output $(Build.ArtifactStagingDirectory)/bom --filename bom-dotnet.xml
displayName: 'Generate Backend BOM'
- powershell: |
npm install --global @cyclonedx/cyclonedx-npm
cyclonedx-npm -o $(Build.ArtifactStagingDirectory)\bom\bom-login.xml --ignore-npm-errors --verbose
displayName: Generate Login UI BOM
workingDirectory: src/Umbraco.Web.UI.Login
- task: PublishPipelineArtifact@1
displayName: Publish nupkg
inputs:
@@ -113,6 +126,11 @@ stages:
inputs:
targetPath: $(Build.SourcesDirectory)
artifactName: build_output
- task: PublishPipelineArtifact@1
displayName: Publish Backend BOM
inputs:
targetPath: $(Build.ArtifactStagingDirectory)/bom
artifactName: bom-backend
- job: B
displayName: Build Bellissima Package
@@ -124,6 +142,11 @@ stages:
lfs: false,
fetchDepth: 500
- template: templates/backoffice-install.yml
- powershell: |
npm install --global @cyclonedx/cyclonedx-npm
cyclonedx-npm -o $(Build.ArtifactStagingDirectory)/bom/bom-backoffice.xml --ignore-npm-errors --verbose
displayName: Generate Backoffice UI BOM
workingDirectory: src/Umbraco.Web.UI.Client
- script: npm run build:for:npm
displayName: Run build:for:npm
workingDirectory: src/Umbraco.Web.UI.Client
@@ -140,6 +163,35 @@ stages:
inputs:
targetPath: $(Build.ArtifactStagingDirectory)/npm
artifactName: npm
- publish: $(Build.ArtifactStagingDirectory)/bom
artifact: bom-frontend
displayName: 'Publish Frontend BOM'
- stage: E2E_BOM
displayName: E2E Tests BOM Generation
dependsOn: []
jobs:
- job:
displayName: E2E Generate BOM
pool:
vmImage: "ubuntu-latest"
steps:
- checkout: self
submodules: false
lfs: false,
fetchDepth: 500
- template: templates/e2e-install.yml
parameters:
nodeVersion: ${{ variables.nodeVersion }}
npm_config_cache: ${{ variables.npm_config_cache }}
- powershell: |
npm install --global @cyclonedx/cyclonedx-npm
cyclonedx-npm -o $(Build.ArtifactStagingDirectory)/bom/bom-e2e.xml --ignore-npm-errors --verbose
displayName: Generate E2E Tests BOM
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
- publish: $(Build.ArtifactStagingDirectory)/bom
artifact: bom-e2e
displayName: 'Publish E2E BOM'
- stage: Build_Docs
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.buildApiDocs}}))
@@ -522,160 +574,72 @@ stages:
# Connection string
CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=Umbraco;Mode=Memory;Cache=Shared;Foreign Keys=True;Pooling=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.Sqlite
DatabaseType: SQLite
additionalEnvironmentVariables: false
strategy:
matrix:
LinuxPart1Of3:
vmImage: "ubuntu-latest"
testFolder: "DefaultConfig"
testCommand: "npm run smokeTestSqlite -- --shard=1/3"
LinuxPart2Of3:
vmImage: "ubuntu-latest"
testFolder: "DefaultConfig"
testCommand: "npm run smokeTestSqlite -- --shard=2/3"
LinuxPart3Of3:
vmImage: "ubuntu-latest"
testFolder: "DefaultConfig"
testCommand: "npm run smokeTestSqlite -- --shard=3/3"
WindowsPart1Of3:
vmImage: "windows-latest"
testFolder: "DefaultConfig"
testCommand: "npm run smokeTestSqlite -- --shard=1/3"
WindowsPart2Of3:
vmImage: "windows-latest"
testFolder: "DefaultConfig"
testCommand: "npm run smokeTestSqlite -- --shard=2/3"
WindowsPart3Of3:
vmImage: "windows-latest"
testFolder: "DefaultConfig"
testCommand: "npm run smokeTestSqlite -- --shard=3/3"
pool:
vmImage: $(vmImage)
steps:
# Setup test environment
- task: DownloadPipelineArtifact@2
displayName: Download NuGet artifacts
inputs:
artifact: nupkg
path: $(Agent.BuildDirectory)/app/nupkg
- task: NodeTool@0
displayName: Use Node.js $(nodeVersion)
retryCountOnTaskFailure: 3
inputs:
versionSpec: $(nodeVersion)
- task: UseDotNet@2
displayName: Use .NET SDK from global.json
inputs:
useGlobalJson: true
# Setup test environment Template
- template: nightly-E2E-setup-template.yml
parameters:
nodeVersion: ${{ variables.nodeVersion }}
PlaywrightUserEmail: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL }}
PlaywrightPassword: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD }}
ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }}
npm_config_cache: ${{ variables.npm_config_cache }}
- pwsh: |
"UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL)
UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
URL=$(ASPNETCORE_URLS)
STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json
CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env
displayName: Generate .env
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
# Cache and restore NPM packages
- task: Cache@2
displayName: Cache NPM packages
inputs:
key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json'
restoreKeys: |
npm_e2e | "$(Agent.OS)"
npm_e2e
path: $(npm_config_cache)
- script: npm ci --no-fund --no-audit --prefer-offline
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
displayName: Restore NPM packages
# Build application
- pwsh: |
$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 restore UmbracoProject
cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject
dotnet build UmbracoProject --configuration $(buildConfiguration) --no-restore
displayName: Restore project
workingDirectory: $(Agent.BuildDirectory)/app
- pwsh: |
dotnet build UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-restore
dotnet dev-certs https
displayName: Build application
workingDirectory: $(Agent.BuildDirectory)/app
condition: succeeded()
# Run application
- bash: |
nohup dotnet run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 &
echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!"
displayName: Run application (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
workingDirectory: $(Agent.BuildDirectory)/app
# Run application Template
- template: nightly-E2E-run-application-template.yml
parameters:
DatabaseType: ${{ variables.DatabaseType }}
buildConfiguration: ${{ variables.buildConfiguration }}
additionalEnvironmentVariables: ${{ variables.additionalEnvironmentVariables }}
- pwsh: |
$process = Start-Process dotnet "run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log
Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)"
displayName: Run application (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
workingDirectory: $(Agent.BuildDirectory)/app
# Wait for application to start responding to requests
- pwsh: npx wait-on -v --interval 1000 --timeout 120000 $(ASPNETCORE_URLS)
displayName: Wait for application
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Install Playwright and dependencies
- pwsh: npx playwright install chromium
displayName: Install Playwright only with Chromium browser
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Test
- pwsh: $(testCommand)
displayName: Run Playwright tests
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
env:
CI: true
CommitId: $(Build.SourceVersion)
AgentOs: $(Agent.OS)
# Stop application
- bash: kill -15 $(AcceptanceTestProcessId)
displayName: Stop application (Linux)
condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux'))
- pwsh: Stop-Process -Id $(AcceptanceTestProcessId)
displayName: Stop application (Windows)
condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT'))
# Copy artifacts
- pwsh: |
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse
}
displayName: Copy Playwright results
condition: succeededOrFailed()
# Copy console error log
- pwsh: |
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/console-errors.json) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/console-errors.json $(Build.ArtifactStagingDirectory)
}
displayName: Copy console error log
condition: succeededOrFailed()
# Publish test artifacts
- task: PublishPipelineArtifact@1
displayName: Publish test artifacts
condition: succeededOrFailed()
inputs:
targetPath: $(Build.ArtifactStagingDirectory)
artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)"
# Publish test results
- task: PublishTestResults@2
displayName: "Publish test results"
condition: succeededOrFailed()
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: '*.xml'
searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results"
testRunTitle: "$(Agent.JobName)"
# Run tests Template
- template: nightly-E2E-run-tests-template.yml
parameters:
testCommand: $(testCommand)
ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }}
DatabaseType: ${{ variables.DatabaseType }}
- job:
displayName: E2E Smoke Tests (SQL Server)
@@ -683,354 +647,106 @@ stages:
# Connection string
CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
DatabaseType: SQLServer
additionalEnvironmentVariables: false
strategy:
matrix:
${{ if eq(parameters.sqlServerLinuxAcceptanceTests, True) }}:
LinuxPart1Of3:
testCommand: "npm run smokeTest -- --shard=1/3"
vmImage: "ubuntu-latest"
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
testFolder: "DefaultConfig"
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
LinuxPart2Of3:
testCommand: "npm run smokeTest -- --shard=2/3"
vmImage: "ubuntu-latest"
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
testFolder: "DefaultConfig"
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
LinuxPart3Of3:
testCommand: "npm run smokeTest -- --shard=3/3"
vmImage: "ubuntu-latest"
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
testFolder: "DefaultConfig"
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
WindowsPart1Of3:
vmImage: "windows-latest"
testFolder: "DefaultConfig"
testCommand: "npm run smokeTest -- --shard=1/3"
WindowsPart2Of3:
vmImage: "windows-latest"
testFolder: "DefaultConfig"
testCommand: "npm run smokeTest -- --shard=2/3"
WindowsPart3Of3:
vmImage: "windows-latest"
testFolder: "DefaultConfig"
testCommand: "npm run smokeTest -- --shard=3/3"
pool:
vmImage: $(vmImage)
steps:
# Setup test environment
- task: DownloadPipelineArtifact@2
displayName: Download NuGet artifacts
inputs:
artifact: nupkg
path: $(Agent.BuildDirectory)/app/nupkg
- task: NodeTool@0
displayName: Use Node.js $(nodeVersion)
inputs:
versionSpec: $(nodeVersion)
- task: UseDotNet@2
displayName: Use .NET SDK from global.json
inputs:
useGlobalJson: true
# Setup test environment Template
- template: nightly-E2E-setup-template.yml
parameters:
nodeVersion: ${{ variables.nodeVersion }}
PlaywrightUserEmail: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL }}
PlaywrightPassword: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD }}
ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }}
npm_config_cache: ${{ variables.npm_config_cache }}
- pwsh: |
"UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL)
UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
URL=$(ASPNETCORE_URLS)
STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json
CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env
displayName: Generate .env
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
# Cache and restore NPM packages
- task: Cache@2
displayName: Cache NPM packages
inputs:
key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json'
restoreKeys: |
npm_e2e | "$(Agent.OS)"
npm_e2e
path: $(npm_config_cache)
- script: npm ci --no-fund --no-audit --prefer-offline
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
displayName: Restore NPM packages
# Build application
- pwsh: |
$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 restore UmbracoProject
cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject
dotnet build UmbracoProject --configuration $(buildConfiguration) --no-restore
displayName: Restore project
workingDirectory: $(Agent.BuildDirectory)/app
- pwsh: |
dotnet build UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-restore
dotnet dev-certs https
displayName: Build application
workingDirectory: $(Agent.BuildDirectory)/app
condition: succeeded()
# Start SQL Server
- powershell: docker run --name mssql -d -p 1433:1433 -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$(SA_PASSWORD)" mcr.microsoft.com/mssql/server:2022-latest
displayName: Start SQL Server Docker image (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
# Run application Template
- template: nightly-E2E-run-application-template.yml
parameters:
SA_PASSWORD: ${{ variables.SA_PASSWORD }}
buildConfiguration: ${{ variables.buildConfiguration }}
DatabaseType: ${{ variables.DatabaseType }}
additionalEnvironmentVariables: ${{ variables.additionalEnvironmentVariables }}
- pwsh: SqlLocalDB start MSSQLLocalDB
displayName: Start SQL Server LocalDB (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
# Run tests Template
- template: nightly-E2E-run-tests-template.yml
parameters:
testCommand: $(testCommand)
ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }}
DatabaseType: ${{ variables.DatabaseType }}
# Run application
- bash: |
nohup dotnet run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 &
echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!"
displayName: Run application (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
workingDirectory: $(Agent.BuildDirectory)/app
- pwsh: |
$process = Start-Process dotnet "run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log
Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)"
displayName: Run application (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
workingDirectory: $(Agent.BuildDirectory)/app
# Wait for application to start responding to requests
- pwsh: npx wait-on -v --interval 1000 --timeout 120000 $(ASPNETCORE_URLS)
displayName: Wait for application
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Install Playwright and dependencies
- pwsh: npx playwright install chromium
displayName: Install Playwright only with Chromium browser
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Test
- pwsh: $(testCommand)
displayName: Run Playwright tests
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
env:
CI: true
CommitId: $(Build.SourceVersion)
AgentOs: $(Agent.OS)
# Stop application
- bash: kill -15 $(AcceptanceTestProcessId)
displayName: Stop application (Linux)
condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux'))
- pwsh: Stop-Process -Id $(AcceptanceTestProcessId)
displayName: Stop application (Windows)
condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT'))
# Stop SQL Server
- pwsh: docker stop mssql
displayName: Stop SQL Server Docker image (Linux)
condition: eq(variables['Agent.OS'], 'Linux')
- pwsh: SqlLocalDB stop MSSQLLocalDB
displayName: Stop SQL Server LocalDB (Windows)
condition: eq(variables['Agent.OS'], 'Windows_NT')
# Copy artifacts
- pwsh: |
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse
}
displayName: Copy Playwright results
condition: succeededOrFailed()
# Copy console error log
- pwsh: |
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/console-errors.json) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/console-errors.json $(Build.ArtifactStagingDirectory)
}
displayName: Copy console error log
condition: succeededOrFailed()
# Publish test artifacts
- task: PublishPipelineArtifact@1
displayName: Publish test artifacts
condition: succeededOrFailed()
inputs:
targetPath: $(Build.ArtifactStagingDirectory)
artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)"
# Publish test results
- task: PublishTestResults@2
displayName: "Publish test results"
condition: succeededOrFailed()
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: '*.xml'
searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results"
testRunTitle: "$(Agent.JobName)"
- job:
displayName: E2E Release Tests (SQL Server)
variables:
# Connection string
CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
condition: eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True')
strategy:
matrix:
WindowsPart1Of3:
vmImage: "windows-latest"
testCommand: "npm run releaseTest -- --shard=1/3"
WindowsPart2Of3:
vmImage: "windows-latest"
testCommand: "npm run releaseTest -- --shard=2/3"
WindowsPart3Of3:
vmImage: "windows-latest"
testCommand: "npm run releaseTest -- --shard=3/3"
pool:
vmImage: $(vmImage)
steps:
# Setup test environment
- task: DownloadPipelineArtifact@2
displayName: Download NuGet artifacts
inputs:
artifact: nupkg
path: $(Agent.BuildDirectory)/app/nupkg
- task: NodeTool@0
displayName: Use Node.js $(nodeVersion)
inputs:
versionSpec: $(nodeVersion)
- task: UseDotNet@2
displayName: Use .NET SDK from global.json
inputs:
useGlobalJson: true
- pwsh: |
"UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL)
UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
URL=$(ASPNETCORE_URLS)
STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json
CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env
displayName: Generate .env
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
# Cache and restore NPM packages
- task: Cache@2
displayName: Cache NPM packages
inputs:
key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json'
restoreKeys: |
npm_e2e | "$(Agent.OS)"
npm_e2e
path: $(npm_config_cache)
- script: npm ci --no-fund --no-audit --prefer-offline
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
displayName: Restore NPM packages
# Build application
- pwsh: |
$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 restore UmbracoProject
cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject
dotnet build UmbracoProject --configuration $(buildConfiguration) --no-restore
dotnet dev-certs https
displayName: Build application
workingDirectory: $(Agent.BuildDirectory)/app
# Start SQL Server
- powershell: docker run --name mssql -d -p 1433:1433 -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$(SA_PASSWORD)" mcr.microsoft.com/mssql/server:2022-latest
displayName: Start SQL Server Docker image (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
- pwsh: SqlLocalDB start MSSQLLocalDB
displayName: Start SQL Server LocalDB (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
# Run application
- bash: |
nohup dotnet run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 &
echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!"
displayName: Run application (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
workingDirectory: $(Agent.BuildDirectory)/app
- pwsh: |
$process = Start-Process dotnet "run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log
Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)"
displayName: Run application (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
workingDirectory: $(Agent.BuildDirectory)/app
# Wait for application to start responding to requests
- pwsh: npx wait-on -v --interval 1000 --timeout 120000 $(ASPNETCORE_URLS)
displayName: Wait for application
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Install Playwright and dependencies
- pwsh: npx playwright install chromium
displayName: Install Playwright only with Chromium browser
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Test
- pwsh: $(testCommand)
displayName: Run Playwright tests
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
env:
CI: true
CommitId: $(Build.SourceVersion)
AgentOs: $(Agent.OS)
# Stop application
- bash: kill -15 $(AcceptanceTestProcessId)
displayName: Stop application (Linux)
condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux'))
- pwsh: Stop-Process -Id $(AcceptanceTestProcessId)
displayName: Stop application (Windows)
condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT'))
# Stop SQL Server
- pwsh: docker stop mssql
displayName: Stop SQL Server Docker image (Linux)
condition: eq(variables['Agent.OS'], 'Linux')
- pwsh: SqlLocalDB stop MSSQLLocalDB
displayName: Stop SQL Server LocalDB (Windows)
condition: eq(variables['Agent.OS'], 'Windows_NT')
# Copy artifacts
- pwsh: |
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse
}
displayName: Copy Playwright results
condition: succeededOrFailed()
# Copy console error log
- pwsh: |
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/console-errors.json) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/console-errors.json $(Build.ArtifactStagingDirectory)
}
displayName: Copy console error log
condition: succeededOrFailed()
# Publish test artifacts
- task: PublishPipelineArtifact@1
displayName: Publish test artifacts
condition: succeededOrFailed()
inputs:
targetPath: $(Build.ArtifactStagingDirectory)
artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)"
# Publish test results
- task: PublishTestResults@2
displayName: "Publish test results"
condition: succeededOrFailed()
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: '*.xml'
searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results"
testRunTitle: "$(Agent.JobName)"
- stage: Dependency_Track
displayName: Dependency Track
dependsOn:
- Build
- E2E_BOM
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.uploadDependencyTrack}}))
variables:
# Determine Umbraco version based on whether it's a public release or not. If public release, use major version, else use full NuGet package version.
umbracoVersion: $[ iif(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), stageDependencies.Build.A.outputs['build.NBGV_VersionMajor'], stageDependencies.Build.A.outputs['build.NBGV_NuGetPackageVersion']) ]
jobs:
- template: templates/dependency-track.yml
parameters:
projectName: "Umbraco-CMS"
umbracoVersion: $(umbracoVersion)
projects:
- name: "Backend"
artifact: "bom-backend"
bomFilePath: "bom-dotnet.xml"
- name: "Login"
artifact: "bom-backend"
bomFilePath: "bom-login.xml"
- name: "Backoffice"
artifact: "bom-frontend"
bomFilePath: "bom-backoffice.xml"
- name: "E2E"
artifact: "bom-e2e"
bomFilePath: "bom-e2e.xml"
###############################################
## Release
@@ -1237,4 +953,5 @@ stages:
storage: umbracoapidocs
ContainerName: "$web"
BlobPrefix: v$(umbracoMajorVersion)/ui-api
CleanTargetBeforeCopy: true
CleanTargetBeforeCopy: true
+74
View File
@@ -0,0 +1,74 @@
parameters:
- name: testFolder
type: string
default: ''
- name: buildConfiguration
type: string
default: ''
- name: additionalEnvironmentVariables
type: string
default: 'false'
steps:
- pwsh: |
dotnet restore UmbracoProject
cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject
displayName: Restore project
workingDirectory: $(Agent.BuildDirectory)/app
# Update application to use necessary app settings
- pwsh: |
$sourcePath = "$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/tests/${{ parameters.testFolder }}/AdditionalSetup"
$destinationPath = "UmbracoProject"
$jsonFiles = Get-ChildItem -Path $sourcePath -Filter "*.json"
if ($jsonFiles) {
$jsonFiles | ForEach-Object {
Write-Host "Copying: $($_.FullName)"
Copy-Item -Path $_.FullName -Destination $destinationPath -Force
}
} else {
Write-Host "No JSON files found."
}
displayName: Update application to use necessary app settings
workingDirectory: $(Agent.BuildDirectory)/app
# Update application to use necessary App_Plugins
- pwsh: |
$sourcePath = "$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/tests/${{ parameters.testFolder }}/AdditionalSetup"
$destinationPath = "UmbracoProject"
$appPluginsFolders = Get-ChildItem -Path $sourcePath -Directory -Filter "App_Plugins"
if ($appPluginsFolders) {
foreach ($folder in $appPluginsFolders) {
Write-Host "Copying folder: $($folder.FullName)"
Copy-Item -Path $folder.FullName -Destination $destinationPath -Recurse -Force
}
} else {
Write-Host "No App_Plugins found."
}
displayName: Update application to use necessary app plugins
workingDirectory: $(Agent.BuildDirectory)/app
# Update application to use necessary classes
- pwsh: |
$sourcePath = "$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/tests/${{ parameters.testFolder }}/AdditionalSetup"
$destinationPath = "UmbracoProject"
$csharpFiles = Get-ChildItem -Path $sourcePath -Filter "*.cs"
if ($csharpFiles) {
$csharpFiles | ForEach-Object {
Write-Host "Copying: $($_.FullName)"
Copy-Item -Path $_.FullName -Destination $destinationPath -Force
}
} else {
Write-Host "No C# files found."
}
displayName: Update application to use necessary classes
workingDirectory: $(Agent.BuildDirectory)/app
- pwsh: |
dotnet build UmbracoProject --configuration ${{ parameters.buildConfiguration }} --no-restore
dotnet dev-certs https
displayName: Build application
workingDirectory: $(Agent.BuildDirectory)/app
condition: and(succeeded(), eq(variables['additionalEnvironmentVariables'], 'false'))
@@ -0,0 +1,45 @@
parameters:
- name: SA_PASSWORD
type: string
default: ''
- name: buildConfiguration
type: string
default: ''
- name: additionalEnvironmentVariables
type: string
default: 'false'
- name: DatabaseType
type: string
default: ''
steps:
# Skips the SQLServer setup if the databaseType does not match
- ${{ if eq(parameters.DatabaseType, 'SQLServer') }}:
# Start SQL Server Linux
- powershell: docker run --name mssql -d -p 1433:1433 -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=${{ parameters.SA_PASSWORD }}" mcr.microsoft.com/mssql/server:2022-latest
displayName: Start SQL Server Docker image (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
# Start SQL Server LocalDB Windows
- pwsh: SqlLocalDB start MSSQLLocalDB
displayName: Start SQL Server LocalDB (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
# Run application for Linux
- bash: |
nohup dotnet run --project UmbracoProject --configuration ${{ parameters.buildConfiguration }} --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 &
echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!"
displayName: Run application (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), eq(variables['additionalEnvironmentVariables'], 'false'))
workingDirectory: $(Agent.BuildDirectory)/app
# Run application for Windows
- pwsh: |
$process = Start-Process dotnet "run --project UmbracoProject --configuration ${{ parameters.buildConfiguration }} --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log
Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)"
displayName: Run application (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), eq(variables['additionalEnvironmentVariables'], 'false'))
workingDirectory: $(Agent.BuildDirectory)/app
+105
View File
@@ -0,0 +1,105 @@
parameters:
- name: ASPNETCORE_URLS
type: string
default: ''
- name: testCommand
type: string
default: ''
- name: port
type: string
default: ''
- name: AZUREB2CTESTUSEREMAIL
type: string
default: ''
- name: AZUREB2CTESTUSERPASSWORD
type: string
default: ''
- name: DatabaseType
type: string
default: ''
steps:
# Ensures we have the package wait-on installed
- pwsh: npm install wait-on
displayName: Install wait-on package
# Wait for either the port of the aspnetcore url
- pwsh: |
$Port = "${{ parameters.port }}"
$Url = "${{ parameters.ASPNETCORE_URLS }}"
if ($Port -ne "") {
Write-Host "Waiting on TCP port $Port"
npx wait-on -v --interval 1000 --timeout 120000 "tcp:$Port"
} else {
Write-Host "Waiting on URL $Url"
npx wait-on -v --interval 1000 --timeout 120000 "$Url"
}
displayName: Wait for application
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Install Playwright and dependencies
- pwsh: npx playwright install chromium
displayName: Install Playwright only with Chromium browser
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Test
- pwsh: ${{ parameters.testCommand }}
displayName: Run Playwright tests
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
env:
CI: true
CommitId: $(Build.SourceVersion)
AgentOs: $(Agent.OS)
AZUREADB2CTESTUSEREMAIL: ${{ parameters.AZUREB2CTESTUSEREMAIL }}
AZUREADB2CTESTUSERPASSWORD: ${{ parameters.AZUREB2CTESTUSERPASSWORD }}
# Stop application
- bash: kill -15 $(AcceptanceTestProcessId)
displayName: Stop application (Linux)
condition: and(succeededOrFailed(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux'))
- pwsh: Stop-Process -Id $(AcceptanceTestProcessId)
displayName: Stop application (Windows)
condition: and(succeededOrFailed(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT'))
- ${{ if eq(parameters.DatabaseType, 'SQLServer') }}:
# Stop SQL Server
- pwsh: docker stop mssql
displayName: Stop SQL Server Docker image (Linux)
condition: and(succeededOrFailed(), eq(variables['Agent.OS'], 'Linux'))
- pwsh: SqlLocalDB stop MSSQLLocalDB
displayName: Stop SQL Server LocalDB (Windows)
condition: and(succeededOrFailed(), eq(variables['Agent.OS'], 'Windows_NT'))
# Copy artifacts
- pwsh: |
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse
}
displayName: Copy Playwright results
condition: succeededOrFailed()
# Publish
- task: PublishPipelineArtifact@1
displayName: Publish test artifacts
condition: succeededOrFailed()
inputs:
targetPath: $(Build.ArtifactStagingDirectory)
artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)"
# Publish test results
- task: PublishTestResults@2
displayName: "Publish test results"
condition: succeededOrFailed()
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: '*.xml'
searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results"
testRunTitle: "$(Agent.JobName)"
+50
View File
@@ -0,0 +1,50 @@
parameters:
- name: nodeVersion
type: string
default: ''
- name: PlaywrightUserEmail
type: string
default: ''
- name: PlaywrightPassword
type: string
default: ''
- name: ASPNETCORE_URLS
type: string
default: ''
- name: npm_config_cache
type: string
default: ''
steps:
- task: DownloadPipelineArtifact@2
displayName: Download NuGet artifacts
inputs:
artifact: nupkg
path: $(Agent.BuildDirectory)/app/nupkg
- task: UseDotNet@2
displayName: Use .NET SDK from global.json
inputs:
useGlobalJson: true
- template: templates/e2e-install.yml
parameters:
nodeVersion: ${{ parameters.nodeVersion }}
npm_config_cache: ${{ parameters.npm_config_cache }}
PlaywrightUserEmail: ${{ parameters.PlaywrightUserEmail }}
PlaywrightPassword: ${{ parameters.PlaywrightPassword }}
ASPNETCORE_URLS: ${{ parameters.ASPNETCORE_URLS }}
# Install Template
- pwsh: |
$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
displayName: Install Template
workingDirectory: $(Agent.BuildDirectory)/app
+424 -279
View File
@@ -4,17 +4,25 @@ pr: none
trigger: none
schedules:
- cron: '0 0 * * *'
displayName: Daily midnight build
- cron: '0 6 * * *'
displayName: Daily 6 AM build (v16/dev)
branches:
include:
- v15/dev
- main
- v16/dev
parameters:
# Skipped due to DB locks
- name: sqliteAcceptanceTests
displayName: Run SQLite Acceptance Tests
- name: skipIntegrationTests
displayName: Skip integration tests
type: boolean
default: false
- name: differentAppSettingsAcceptanceTests
displayName: Run acceptance tests with different app settings
type: boolean
default: false
- name: skipDefaultConfigAcceptanceTests
displayName: Skip tests with DefaultConfig
type: boolean
default: false
@@ -100,9 +108,193 @@ stages:
targetPath: $(Build.ArtifactStagingDirectory)/npm
artifactName: npm
- stage: E2E
displayName: E2E Tests
- stage: Integration
displayName: Integration Tests
dependsOn: Build
condition: ${{ eq(parameters.skipIntegrationTests, false) }}
jobs:
# Integration Tests (SQLite)
- job:
timeoutInMinutes: 180
displayName: Integration Tests (SQLite)
strategy:
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:
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:
vmImage: "ubuntu-latest"
# Filter tests that are part of the Umbraco.Infrastructure.Service namespace
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)"
macOSPart1Of3:
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:
vmImage: "macOS-latest"
# Filter tests that are part of the Umbraco.Infrastructure.Service namespace
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
macOSPart3Of3:
vmImage: "macOS-latest"
# Filter tests that are not part of the Umbraco.Infrastructure namespace.
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
pool:
vmImage: $(vmImage)
variables:
Tests__Database__DatabaseType: "Sqlite"
steps:
- checkout: self
submodules: false
lfs: false,
fetchDepth: 1
fetchFilter: tree:0
# Setup test environment
- task: DownloadPipelineArtifact@2
displayName: Download build artifacts
inputs:
artifact: build_output
path: $(Build.SourcesDirectory)
- task: UseDotNet@2
displayName: Use .NET SDK from global.json
inputs:
useGlobalJson: true
# Test
- task: DotNetCoreCLI@2
displayName: Run dotnet test
inputs:
command: test
projects: "tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj"
testRunTitle: Integration Tests SQLite - $(Agent.OS)
arguments: '--filter "$(testFilter)" --configuration $(buildConfiguration) --no-build'
# Integration Tests (SQL Server)
- job:
timeoutInMinutes: 180
displayName: Integration Tests (SQL Server)
variables:
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:
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:
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:
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:
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:
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:
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)"
pool:
vmImage: $(vmImage)
steps:
# Setup test environment
- task: DownloadPipelineArtifact@2
displayName: Download build artifacts
inputs:
artifact: build_output
path: $(Build.SourcesDirectory)
- task: UseDotNet@2
displayName: Use .NET SDK from global.json
inputs:
useGlobalJson: true
# Start SQL Server
- powershell: docker run --name mssql -d -p 1433:1433 -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$(SA_PASSWORD)" mcr.microsoft.com/mssql/server:2022-latest
displayName: Start SQL Server Docker image (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
- powershell: |
$maxAttempts = 12
$attempt = 0
$status = ""
while (($status -ne 'running') -and ($attempt -lt $maxAttempts)) {
Start-Sleep -Seconds 5
# We use the docker inspect command to check the status of the container. If the container is not running, we wait 5 seconds and try again. And if reaches 12 attempts, we fail the build.
$status = docker inspect -f '{{.State.Status}}' mssql
if ($status -ne 'running') {
Write-Host "Waiting for SQL Server to be ready... Attempt $($attempt + 1)"
$attempt++
}
}
if ($status -eq 'running') {
Write-Host "SQL Server container is running"
docker ps -a
} else {
Write-Host "SQL Server did not become ready in time. Last known status: $status"
docker logs mssql
exit 1
}
displayName: Wait for SQL Server to be ready (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
- pwsh: SqlLocalDB start MSSQLLocalDB
displayName: Start SQL Server LocalDB (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
# Test
- task: DotNetCoreCLI@2
displayName: Run dotnet test
inputs:
command: test
projects: "tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj"
testRunTitle: Integration Tests SQL Server - $(Agent.OS)
arguments: '--filter "$(testFilter)" --configuration $(buildConfiguration) --no-build'
# Stop SQL Server
- pwsh: docker stop mssql
displayName: Stop SQL Server Docker image (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
- pwsh: SqlLocalDB stop MSSQLLocalDB
displayName: Stop SQL Server LocalDB (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
- stage: DefaultConfigE2E
displayName: Default Config E2E Tests
dependsOn: Integration
condition: always()
variables:
npm_config_cache: $(Pipeline.Workspace)/.npm_e2e
# Enable console logging in Release mode
@@ -128,369 +320,322 @@ stages:
# E2E Tests
- job:
displayName: E2E Tests (SQLite)
condition: eq(${{parameters.sqliteAcceptanceTests}}, True)
timeoutInMinutes: 180
condition: ${{ eq(parameters.skipDefaultConfigAcceptanceTests, false) }}
variables:
# Connection string
CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=Umbraco;Mode=Memory;Cache=Shared;Foreign Keys=True;Pooling=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.Sqlite
DatabaseType: SQLite
additionalEnvironmentVariables: false
strategy:
matrix:
LinuxPart1Of3:
vmImage: "ubuntu-latest"
testFolder: "DefaultConfig"
testCommand: "npm run testSqlite -- --shard=1/3"
LinuxPart2Of3:
vmImage: "ubuntu-latest"
testFolder: "DefaultConfig"
testCommand: "npm run testSqlite -- --shard=2/3"
LinuxPart3Of3:
vmImage: "ubuntu-latest"
testFolder: "DefaultConfig"
testCommand: "npm run testSqlite -- --shard=3/3"
WindowsPart1Of3:
vmImage: "windows-latest"
testFolder: "DefaultConfig"
testCommand: "npm run testSqlite -- --shard=1/3"
WindowsPart2Of3:
vmImage: "windows-latest"
testFolder: "DefaultConfig"
testCommand: "npm run testSqlite -- --shard=2/3"
WindowsPart3Of3:
vmImage: "windows-latest"
testFolder: "DefaultConfig"
testCommand: "npm run testSqlite -- --shard=3/3"
pool:
vmImage: $(vmImage)
steps:
# Setup test environment
- task: DownloadPipelineArtifact@2
displayName: Download NuGet artifacts
inputs:
artifact: nupkg
path: $(Agent.BuildDirectory)/app/nupkg
- task: NodeTool@0
displayName: Use Node.js $(nodeVersion)
retryCountOnTaskFailure: 3
inputs:
versionSpec: $(nodeVersion)
- task: UseDotNet@2
displayName: Use .NET SDK from global.json
inputs:
useGlobalJson: true
# Setup test environment Template
- template: nightly-E2E-setup-template.yml
parameters:
nodeVersion: ${{ variables.nodeVersion }}
PlaywrightUserEmail: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL }}
PlaywrightPassword: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD }}
ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }}
npm_config_cache: ${{ variables.npm_config_cache }}
- pwsh: |
"UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL)
UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
URL=$(ASPNETCORE_URLS)
STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json
CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env
displayName: Generate .env
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
# Cache and restore NPM packages
- task: Cache@2
displayName: Cache NPM packages
inputs:
key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json'
restoreKeys: |
npm_e2e | "$(Agent.OS)"
npm_e2e
path: $(npm_config_cache)
- script: npm ci --no-fund --no-audit --prefer-offline
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
displayName: Restore NPM packages
# Build application
- pwsh: |
$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 restore UmbracoProject
cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject
dotnet build UmbracoProject --configuration $(buildConfiguration) --no-restore
displayName: Restore project
workingDirectory: $(Agent.BuildDirectory)/app
- pwsh: |
dotnet build UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-restore
dotnet dev-certs https
displayName: Build application
workingDirectory: $(Agent.BuildDirectory)/app
condition: succeeded()
# Run application
- bash: |
nohup dotnet run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 &
echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!"
displayName: Run application (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
workingDirectory: $(Agent.BuildDirectory)/app
# Run application Template
- template: nightly-E2E-run-application-template.yml
parameters:
DatabaseType: ${{ variables.DatabaseType }}
buildConfiguration: ${{ variables.buildConfiguration }}
additionalEnvironmentVariables: ${{ variables.additionalEnvironmentVariables }}
- pwsh: |
$process = Start-Process dotnet "run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log
Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)"
displayName: Run application (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
workingDirectory: $(Agent.BuildDirectory)/app
# Ensures we have the package wait-on installed
- pwsh: npm install wait-on
displayName: Install wait-on package
# Wait for application to start responding to requests
- pwsh: npx wait-on -v --interval 1000 --timeout 120000 $(ASPNETCORE_URLS)
displayName: Wait for application
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Install Playwright and dependencies
- pwsh: npx playwright install chromium
displayName: Install Playwright only with Chromium browser
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Test
- pwsh: $(testCommand)
displayName: Run Playwright tests
continueOnError: true
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
env:
CI: true
CommitId: $(Build.SourceVersion)
AgentOs: $(Agent.OS)
# Stop application
- bash: kill -15 $(AcceptanceTestProcessId)
displayName: Stop application (Linux)
condition: and(succeeded(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux'))
- pwsh: Stop-Process -Id $(AcceptanceTestProcessId)
displayName: Stop application (Windows)
condition: and(succeeded(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT'))
# Copy artifacts
- pwsh: |
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse
}
displayName: Copy Playwright results
condition: succeededOrFailed()
# Copy console error log
- pwsh: |
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/console-errors.json) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/console-errors.json $(Build.ArtifactStagingDirectory)
}
displayName: Copy console error log
condition: succeededOrFailed()
# Publish
- task: PublishPipelineArtifact@1
displayName: Publish test artifacts
condition: succeededOrFailed()
inputs:
targetPath: $(Build.ArtifactStagingDirectory)
artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)"
# Publish test results
- task: PublishTestResults@2
displayName: "Publish test results"
condition: succeededOrFailed()
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: '*.xml'
searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results"
testRunTitle: "$(Agent.JobName)"
# Run tests Template
- template: nightly-E2E-run-tests-template.yml
parameters:
testCommand: $(testCommand)
ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }}
DatabaseType: ${{ variables.DatabaseType }}
- job:
displayName: E2E Tests (SQL Server)
timeoutInMinutes: 180
condition: ${{ eq(parameters.skipDefaultConfigAcceptanceTests, false) }}
variables:
# Connection string
CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
DatabaseType: SQLServer
SA_PASSWORD: UmbracoAcceptance123!
additionalEnvironmentVariables: false
strategy:
matrix:
LinuxPart1Of3:
testCommand: "npm run test -- --shard=1/3"
testFolder: "DefaultConfig"
vmImage: "ubuntu-latest"
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
LinuxPart2Of3:
testCommand: "npm run test -- --shard=2/3"
testFolder: "DefaultConfig"
vmImage: "ubuntu-latest"
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
LinuxPart3Of3:
testCommand: "npm run test -- --shard=3/3"
testFolder: "DefaultConfig"
vmImage: "ubuntu-latest"
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
WindowsPart1Of3:
vmImage: "windows-latest"
testCommand: "npm run test -- --shard=1/3"
testFolder: "DefaultConfig"
vmImage: "windows-latest"
WindowsPart2Of3:
vmImage: "windows-latest"
testCommand: "npm run test -- --shard=2/3"
WindowsPart3Of3:
testFolder: "DefaultConfig"
vmImage: "windows-latest"
WindowsPart3Of3:
testCommand: "npm run test -- --shard=3/3"
testFolder: "DefaultConfig"
vmImage: "windows-latest"
pool:
vmImage: $(vmImage)
steps:
# Setup test environment
- task: DownloadPipelineArtifact@2
displayName: Download NuGet artifacts
inputs:
artifact: nupkg
path: $(Agent.BuildDirectory)/app/nupkg
- task: NodeTool@0
displayName: Use Node.js $(nodeVersion)
inputs:
versionSpec: $(nodeVersion)
- task: UseDotNet@2
displayName: Use .NET SDK from global.json
inputs:
useGlobalJson: true
# Setup test environment Template
- template: nightly-E2E-setup-template.yml
parameters:
nodeVersion: ${{ variables.nodeVersion }}
PlaywrightUserEmail: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL }}
PlaywrightPassword: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD }}
ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }}
npm_config_cache: ${{ variables.npm_config_cache }}
- pwsh: |
"UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL)
UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
URL=$(ASPNETCORE_URLS)
STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json
CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env
displayName: Generate .env
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
# Cache and restore NPM packages
- task: Cache@2
displayName: Cache NPM packages
inputs:
key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json'
restoreKeys: |
npm_e2e | "$(Agent.OS)"
npm_e2e
path: $(npm_config_cache)
- script: npm ci --no-fund --no-audit --prefer-offline
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
displayName: Restore NPM packages
# Build application
- pwsh: |
$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 restore UmbracoProject
cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject
dotnet build UmbracoProject --configuration $(buildConfiguration) --no-restore
displayName: Restore project
workingDirectory: $(Agent.BuildDirectory)/app
- pwsh: |
dotnet build UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-restore
dotnet dev-certs https
displayName: Build application
workingDirectory: $(Agent.BuildDirectory)/app
condition: succeeded()
# Start SQL Server
- powershell: docker run --name mssql -d -p 1433:1433 -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$(SA_PASSWORD)" mcr.microsoft.com/mssql/server:2022-latest
displayName: Start SQL Server Docker image (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
# Run application Template
- template: nightly-E2E-run-application-template.yml
parameters:
SA_PASSWORD: ${{ variables.SA_PASSWORD }}
buildConfiguration: ${{ variables.buildConfiguration }}
DatabaseType: ${{ variables.DatabaseType }}
additionalEnvironmentVariables: ${{ variables.additionalEnvironmentVariables }}
- pwsh: SqlLocalDB start MSSQLLocalDB
displayName: Start SQL Server LocalDB (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
# Run tests Template
- template: nightly-E2E-run-tests-template.yml
parameters:
testCommand: $(testCommand)
ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }}
DatabaseType: ${{ variables.DatabaseType }}
# Run application
- stage: AdditionalConfigE2E
displayName: Additional Config E2E Tests
dependsOn: DefaultConfigE2E
condition: always()
variables:
npm_config_cache: $(Pipeline.Workspace)/.npm_e2e
ASPNETCORE_URLS: https://localhost:44331
PlaywrightPassword: UmbracoAcceptance123!
PlaywrightUserEmail: playwright@umbraco.com
jobs:
- job:
displayName: E2E Tests with Different App settings (SQL Server)
condition: ${{ or(eq(parameters.differentAppSettingsAcceptanceTests, true), eq(parameters.skipDefaultConfigAcceptanceTests, true)) }}
timeoutInMinutes: 180
variables:
SA_PASSWORD: UmbracoAcceptance123!
DatabaseType: SQLServer
strategy:
matrix:
# UnattendedInstallConfig
WindowsUnattendedInstallConfig:
vmImage: "windows-latest"
testFolder: "UnattendedInstallConfig"
testCommand: "npx playwright test --project=unattendedInstallConfig --grep=InstallSQLServer"
port: 44331
additionalEnvironmentVariables: false
# DeliveryApiConfig
WindowsDeliveryApiConfig:
vmImage: "windows-latest"
testFolder: "DeliveryApi"
port: ''
testCommand: "npx playwright test --project=deliveryApi"
CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
additionalEnvironmentVariables: false
LinuxDeliveryApiConfig:
vmImage: "ubuntu-latest"
testFolder: "DeliveryApi"
port: ''
testCommand: "npx playwright test --project=deliveryApi"
CONNECTIONSTRINGS__UMBRACODBDSN: Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
additionalEnvironmentVariables: false
# ExternalLogin AzureADB2C
WindowsExternalLoginAzureADB2C:
vmImage: "windows-latest"
testFolder: "ExternalLogin\\AzureADB2C"
testCommand: "npx playwright test --project=externalLoginAzureADB2C"
port: 44331
packageName: "Microsoft.AspNetCore.Authentication.OpenIdConnect"
packageVersion: "9.0.8"
CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
additionalEnvironmentVariables: true
# ExtensionRegistry
WindowsExtensionRegistry:
vmImage: "windows-latest"
testFolder: "ExtensionRegistry"
port: ''
testCommand: "npx playwright test --project=extensionRegistry"
CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
additionalEnvironmentVariables: false
LinuxExtensionRegistry:
vmImage: "ubuntu-latest"
testFolder: "ExtensionRegistry"
port: ''
testCommand: "npx playwright test --project=extensionRegistry"
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:
# Setup test environment Template
- template: nightly-E2E-setup-template.yml
parameters:
nodeVersion: ${{ variables.nodeVersion }}
PlaywrightUserEmail: ${{ variables.PlaywrightUserEmail }}
PlaywrightPassword: ${{ variables.PlaywrightPassword }}
ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }}
npm_config_cache: ${{ variables.npm_config_cache }}
# Install NuGet package if specified in the matrix
- pwsh: |
Write-Host "Installing package $(packageName) version $(packageVersion)"
dotnet add package $(packageName) --version $(packageVersion)
displayName: "Install NuGet package: $(packageName)"
workingDirectory: $(Agent.BuildDirectory)/app/UmbracoProject
condition: and(succeeded(), ne(variables['packageName'], ''), ne(variables['packageVersion'], ''))
# Build application Template
- template: nightly-E2E-build-template.yml
parameters:
testFolder: $(testFolder)
buildConfiguration: ${{ variables.buildConfiguration }}
additionalEnvironmentVariables: $(additionalEnvironmentVariables)
# Build application for AzureADB2C
- pwsh: |
dotnet build UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-restore
dotnet dev-certs https
displayName: Build application for AzureADB2C
workingDirectory: $(Agent.BuildDirectory)/app
env:
AZUREADB2CDOMAIN: $(AZUREB2CDOMAIN)
AZUREADB2CTENANT: $(AZUREB2CTENANT)
AZUREADB2CPOLICY: $(AZUREB2CPOLICY)
AZUREADB2CCLIENTID: $(AZUREB2CCLIENTID)
AZUREADB2CCLIENTSECRET: $(AZUREB2CCLIENTSECRET)
condition: and(succeeded(), eq(variables['testFolder'], 'ExternalLogin\AzureADB2C'))
# Run application Template
- template: nightly-E2E-run-application-template.yml
parameters:
SA_PASSWORD: ${{ variables.SA_PASSWORD }}
additionalEnvironmentVariables: $(additionalEnvironmentVariables)
buildConfiguration: ${{ variables.buildConfiguration }}
DatabaseType: ${{ variables.DatabaseType }}
# Run application for Linux with additional Environment Variables for Azure AD
- bash: |
nohup dotnet run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 &
nohup dotnet run --project UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 &
echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!"
displayName: Run application (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), eq(variables['testFolder'], 'ExternalLogin\AzureADB2C'))
workingDirectory: $(Agent.BuildDirectory)/app
env:
AZUREADB2CDOMAIN: $(AZUREB2CDOMAIN)
AZUREADB2CTENANT: $(AZUREB2CTENANT)
AZUREADB2CPOLICY: $(AZUREB2CPOLICY)
AZUREADB2CCLIENTID: $(AZUREB2CCLIENTID)
AZUREADB2CCLIENTSECRET: $(AZUREB2CCLIENTSECRET)
# Run application for Windows with additional Environment Variables for Azure AD
- pwsh: |
$process = Start-Process dotnet "run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log
$process = Start-Process dotnet "run --project UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log
Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)"
displayName: Run application (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), eq(variables['testFolder'], 'ExternalLogin\AzureADB2C'))
workingDirectory: $(Agent.BuildDirectory)/app
# Ensures we have the package wait-on installed
- pwsh: npm install wait-on
displayName: Install wait-on package
# Wait for application to start responding to requests
- pwsh: npx wait-on -v --interval 1000 --timeout 120000 $(ASPNETCORE_URLS)
displayName: Wait for application
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Install Playwright and dependencies
- pwsh: npx playwright install chromium
displayName: Install Playwright only with Chromium browser
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Test
- pwsh: $(testCommand)
displayName: Run Playwright tests
continueOnError: true
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
env:
CI: true
CommitId: $(Build.SourceVersion)
AgentOs: $(Agent.OS)
AZUREADB2CDOMAIN: $(AZUREB2CDOMAIN)
AZUREADB2CTENANT: $(AZUREB2CTENANT)
AZUREADB2CPOLICY: $(AZUREB2CPOLICY)
AZUREADB2CCLIENTID: $(AZUREB2CCLIENTID)
AZUREADB2CCLIENTSECRET: $(AZUREB2CCLIENTSECRET)
# Stop application
- bash: kill -15 $(AcceptanceTestProcessId)
displayName: Stop application (Linux)
condition: and(succeeded(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux'))
- pwsh: Stop-Process -Id $(AcceptanceTestProcessId)
displayName: Stop application (Windows)
condition: and(succeeded(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT'))
# Stop SQL Server
- pwsh: docker stop mssql
displayName: Stop SQL Server Docker image (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
- pwsh: SqlLocalDB stop MSSQLLocalDB
displayName: Stop SQL Server LocalDB (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
# Copy artifacts
- pwsh: |
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse
}
displayName: Copy Playwright results
condition: succeededOrFailed()
# Copy console error log
- pwsh: |
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/console-errors.json) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/console-errors.json $(Build.ArtifactStagingDirectory)
}
displayName: Copy console error log
condition: succeededOrFailed()
# Publish
- task: PublishPipelineArtifact@1
displayName: Publish test artifacts
condition: succeededOrFailed()
inputs:
targetPath: $(Build.ArtifactStagingDirectory)
artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)"
# Publish test results
- task: PublishTestResults@2
displayName: "Publish test results"
condition: succeededOrFailed()
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: '*.xml'
searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results"
testRunTitle: "$(Agent.JobName)"
# Run tests Template
- template: nightly-E2E-run-tests-template.yml
parameters:
testCommand: $(testCommand)
ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }}
port: $(port)
AZUREB2CTESTUSEREMAIL: $(AZUREB2CTESTUSEREMAIL)
AZUREB2CTESTUSERPASSWORD: $(AZUREB2CTESTUSERPASSWORD)
DatabaseType: ${{ variables.DatabaseType }}
- stage: NotifySlackBot
displayName: Notify Slack on Failure
dependsOn: E2E
dependsOn: DefaultConfigE2E
# This stage will only run if the E2E tests fail or succeed with issues
condition: or(
eq(dependencies.E2E.result, 'failed'),
eq(dependencies.E2E.result, 'succeededWithIssues'))
condition: or(eq(dependencies.DefaultConfigE2E.result, 'failed'), eq(dependencies.DefaultConfigE2E.result, 'succeededWithIssues'))
jobs:
- job: PostToSlack
displayName: Send Slack Notification
+1 -1
View File
@@ -8,8 +8,8 @@ schedules:
displayName: Daily midnight build
branches:
include:
- v10/dev
- v13/dev
- v17/dev
- main
steps:
+56
View File
@@ -0,0 +1,56 @@
parameters:
- name: projectName
type: string
- name: umbracoVersion
type: string
- name: projects
type: object
jobs:
- job: Create_DT_Project
displayName: Create Dependency Track Project
steps:
- 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')
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" \
-H "X-Api-Key: $(DT_API_KEY)" \
-H "Content-Type: application/json" \
-d '{"name": "${{ parameters.projectName }}", "version": "${{ parameters.umbracoVersion }}", "collectionLogic": "AGGREGATE_DIRECT_CHILDREN"}' \
| jq -r '.uuid')
if [ -z "$project_id" ] || [ "$project_id" == "null" ]; then
echo "Failed to create project '${{ parameters.projectName }}' version '${{ parameters.umbracoVersion }}'."
exit 1
fi
echo "Created project '${{ parameters.projectName }}' with version '${{ parameters.umbracoVersion }}' (ID: $project_id)."
fi
displayName: Ensure main project exists in Dependency Track
- ${{ each project in parameters.projects }}:
- job:
displayName: Upload ${{ project.name }} BOM
dependsOn: Create_DT_Project
steps:
- checkout: none
- download: current
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 }}"
displayName: Upload ${{ project.name }} BOM to Dependency Track
+49
View File
@@ -0,0 +1,49 @@
parameters:
- name: nodeVersion
type: string
default: ''
- name: npm_config_cache
type: string
default: ''
- name: PlaywrightUserEmail
type: string
default: ''
- name: PlaywrightPassword
type: string
default: ''
- name: ASPNETCORE_URLS
type: string
default: ''
steps:
- task: NodeTool@0
displayName: Use Node.js $(nodeVersion)
inputs:
versionSpec: $(nodeVersion)
- pwsh: |
"UMBRACO_USER_LOGIN=${{ parameters.PlaywrightUserEmail }}
UMBRACO_USER_PASSWORD=${{ parameters.PlaywrightPassword }}
URL=${{ parameters.ASPNETCORE_URLS }}
STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json
CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env
displayName: Generate .env
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
# Cache and restore NPM packages
- task: Cache@2
displayName: Cache NPM packages
inputs:
key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json'
restoreKeys: |
npm_e2e | "$(Agent.OS)"
npm_e2e
path: ${{ parameters.npm_config_cache }}
- script: npm ci --no-fund --no-audit --prefer-offline
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest
displayName: Restore NPM packages
+1 -1
View File
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "9.0.100",
"version": "9.0.306",
"rollForward": "latestFeature",
"allowPrerelease": false
}
@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Http;
using Umbraco.Cms.Core.DeliveryApi;
namespace Umbraco.Cms.Api.Common.Accessors;
public sealed class RequestContextOutputExpansionStrategyAccessor : RequestContextServiceAccessorBase<IOutputExpansionStrategy>, IOutputExpansionStrategyAccessor
{
public RequestContextOutputExpansionStrategyAccessor(IHttpContextAccessor httpContextAccessor)
: base(httpContextAccessor)
{
}
}
@@ -0,0 +1,20 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace Umbraco.Cms.Api.Common.Accessors;
public abstract class RequestContextServiceAccessorBase<T>
where T : class
{
private readonly IHttpContextAccessor _httpContextAccessor;
protected RequestContextServiceAccessorBase(IHttpContextAccessor httpContextAccessor)
=> _httpContextAccessor = httpContextAccessor;
public bool TryGetValue([NotNullWhen(true)] out T? requestStartNodeService)
{
requestStartNodeService = _httpContextAccessor.HttpContext?.RequestServices.GetService<T>();
return requestStartNodeService is not null;
}
}
@@ -42,7 +42,7 @@ public class ConfigureUmbracoSwaggerGenOptions : IConfigureOptions<SwaggerGenOpt
swaggerGenOptions.DocInclusionPredicate((name, api) =>
{
if (api.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor
&& controllerActionDescriptor.MethodInfo.HasMapToApiAttribute(name))
&& controllerActionDescriptor.HasMapToApiAttribute(name))
{
return true;
}
@@ -0,0 +1,230 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using OpenIddict.Server;
using OpenIddict.Validation;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Web.Common.Security;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Common.DependencyInjection;
internal sealed class HideBackOfficeTokensHandler
: IOpenIddictServerHandler<OpenIddictServerEvents.ApplyTokenResponseContext>,
IOpenIddictServerHandler<OpenIddictServerEvents.ApplyAuthorizationResponseContext>,
IOpenIddictServerHandler<OpenIddictServerEvents.ExtractTokenRequestContext>,
IOpenIddictValidationHandler<OpenIddictValidationEvents.ProcessAuthenticationContext>,
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";
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IDataProtectionProvider _dataProtectionProvider;
private readonly BackOfficeTokenCookieSettings _backOfficeTokenCookieSettings;
private readonly GlobalSettings _globalSettings;
public HideBackOfficeTokensHandler(
IHttpContextAccessor httpContextAccessor,
IDataProtectionProvider dataProtectionProvider,
IOptions<BackOfficeTokenCookieSettings> backOfficeTokenCookieSettings,
IOptions<GlobalSettings> globalSettings)
{
_httpContextAccessor = httpContextAccessor;
_dataProtectionProvider = dataProtectionProvider;
_backOfficeTokenCookieSettings = backOfficeTokenCookieSettings.Value;
_globalSettings = globalSettings.Value;
}
/// <summary>
/// This is invoked when tokens (access and refresh tokens) are issued to a client. For the back-office client,
/// we will intercept the response, write the tokens from the response into HTTP-only cookies, and redact the
/// tokens from the response, so they are not exposed to the client.
/// </summary>
public ValueTask HandleAsync(OpenIddictServerEvents.ApplyTokenResponseContext context)
{
if (context.Request?.ClientId is not Constants.OAuthClientIds.BackOffice)
{
// Only ever handle the back-office client.
return ValueTask.CompletedTask;
}
HttpContext httpContext = GetHttpContext();
if (context.Response.AccessToken is not null)
{
SetCookie(httpContext, AccessTokenCookieKey, context.Response.AccessToken);
context.Response.AccessToken = RedactedTokenValue;
}
if (context.Response.RefreshToken is not null)
{
SetCookie(httpContext, RefreshTokenCookieKey, context.Response.RefreshToken);
context.Response.RefreshToken = RedactedTokenValue;
}
return ValueTask.CompletedTask;
}
/// <summary>
/// This is invoked when a PKCE code is issued to the client. For the back-office client, we will intercept the
/// response, write the PKCE code from the response into a HTTP-only cookie, and redact the code from the response,
/// so it's not exposed to the client.
/// </summary>
public ValueTask HandleAsync(OpenIddictServerEvents.ApplyAuthorizationResponseContext context)
{
if (context.Request?.ClientId is not Constants.OAuthClientIds.BackOffice)
{
// Only ever handle the back-office client.
return ValueTask.CompletedTask;
}
if (context.Response.Code is not null)
{
SetCookie(GetHttpContext(), PkceCodeCookieKey, context.Response.Code);
context.Response.Code = RedactedTokenValue;
}
return ValueTask.CompletedTask;
}
/// <summary>
/// This is invoked when requesting new tokens.
/// </summary>
public ValueTask HandleAsync(OpenIddictServerEvents.ExtractTokenRequestContext context)
{
if (context.Request?.ClientId != Constants.OAuthClientIds.BackOffice)
{
// Only ever handle the back-office client.
return ValueTask.CompletedTask;
}
// Handle when the PKCE code is being exchanged for an access token.
if (context.Request.Code == RedactedTokenValue
&& TryGetCookie(PkceCodeCookieKey, out var code))
{
context.Request.Code = code;
// We won't need the PKCE cookie after this, let's remove it.
RemoveCookie(GetHttpContext(), PkceCodeCookieKey);
}
else
{
// PCKE codes should always be redacted. If we got here, someone might be trying to pass another PKCE
// code. For security reasons, explicitly discard the code (if any) to be on the safe side.
context.Request.Code = null;
}
// Handle when a refresh token is being exchanged for a new access token.
if (context.Request.RefreshToken == RedactedTokenValue
&& TryGetCookie(RefreshTokenCookieKey, out var refreshToken))
{
context.Request.RefreshToken = refreshToken;
}
else
{
// If we got here, either the refresh token was not redacted, or nothing was found in the refresh token cookie.
// If OpenIddict found a refresh token, it could be an old token that is potentially still valid. For security
// reasons, we cannot accept that; at this point, we expect the refresh tokens to be explicitly redacted.
context.Request.RefreshToken = null;
}
return ValueTask.CompletedTask;
}
/// <summary>
/// This is invoked when extracting the auth context for a client request.
/// </summary>
public ValueTask HandleAsync(OpenIddictValidationEvents.ProcessAuthenticationContext context)
{
// For the back-office client, this only happens when an access token is sent to the API.
if (context.AccessToken != RedactedTokenValue)
{
return ValueTask.CompletedTask;
}
if (TryGetCookie(AccessTokenCookieKey, out var accessToken))
{
context.AccessToken = accessToken;
}
return ValueTask.CompletedTask;
}
public void Handle(UserLogoutSuccessNotification notification)
{
HttpContext? context = _httpContextAccessor.HttpContext;
if (context 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,
// so the cookie clean-up is mostly cosmetic.
return;
}
context.Response.Cookies.Delete(AccessTokenCookieKey);
context.Response.Cookies.Delete(RefreshTokenCookieKey);
}
private HttpContext GetHttpContext()
=> _httpContextAccessor.GetRequiredHttpContext();
private void SetCookie(HttpContext httpContext, string key, string value)
{
var cookieValue = EncryptionHelper.Encrypt(value, _dataProtectionProvider);
RemoveCookie(httpContext, key);
httpContext.Response.Cookies.Append(key, cookieValue, GetCookieOptions(httpContext));
}
private void RemoveCookie(HttpContext httpContext, string key)
=> httpContext.Response.Cookies.Delete(key, GetCookieOptions(httpContext));
private CookieOptions GetCookieOptions(HttpContext httpContext) =>
new()
{
// Prevent the client-side scripts from accessing the cookie.
HttpOnly = true,
// Mark the cookie as essential to the application, to enforce it despite any
// data collection consent options. This aligns with how ASP.NET Core Identity
// does when writing cookies for cookie authentication.
IsEssential = true,
// Cookie path must be root for optimal security.
Path = "/",
// For optimal security, the cooke must be secure. However, Umbraco allows for running development
// environments over HTTP, so we need to take that into account here.
// Thus, we will make the cookie secure if:
// - HTTPS is explicitly enabled by config (default for production environments), or
// - The current request is over HTTPS (meaning the environment supports it regardless of config).
Secure = _globalSettings.UseHttps || httpContext.Request.IsHttps,
// SameSite is configurable (see BackOfficeTokenCookieSettings for defaults):
SameSite = ParseSameSiteMode(_backOfficeTokenCookieSettings.SameSite),
};
private bool TryGetCookie(string key, [NotNullWhen(true)] out string? value)
{
if (GetHttpContext().Request.Cookies.TryGetValue(key, out var cookieValue))
{
value = EncryptionHelper.Decrypt(cookieValue, _dataProtectionProvider);
return true;
}
value = null;
return false;
}
private static SameSiteMode ParseSameSiteMode(string sameSiteMode) =>
Enum.TryParse(sameSiteMode, ignoreCase: true, out SameSiteMode result)
? result
: throw new ArgumentException($"The provided {nameof(sameSiteMode)} value could not be parsed into as SameSiteMode value.", nameof(sameSiteMode));
}
@@ -9,6 +9,7 @@ using Umbraco.Cms.Api.Common.Security;
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;
@@ -28,6 +29,11 @@ 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 =>
@@ -113,6 +119,28 @@ public static class UmbracoBuilderAuthExtensions
{
configuration.UseSingletonHandler<ProcessRequestContextHandler>().SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.ResolveRequestUri.Descriptor.Order - 1);
});
if (hideBackOfficeTokens)
{
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);
});
}
})
// Register the OpenIddict validation components.
@@ -137,9 +165,25 @@ public static class UmbracoBuilderAuthExtensions
{
configuration.UseSingletonHandler<ProcessRequestContextHandler>().SetOrder(OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers.ResolveRequestUri.Descriptor.Order - 1);
});
if (hideBackOfficeTokens)
{
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);
});
}
});
builder.Services.AddRecurringBackgroundJob<OpenIddictCleanupJob>();
builder.Services.ConfigureOptions<ConfigureOpenIddict>();
if (hideBackOfficeTokens)
{
builder.AddNotificationHandler<UserLogoutSuccessNotification, HideBackOfficeTokensHandler>();
}
}
}
@@ -0,0 +1,36 @@
using Microsoft.AspNetCore.Mvc.Abstractions;
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Common.Configuration;
namespace Umbraco.Extensions;
/// <summary>
/// Provides extension methods for <see cref="ActionDescriptor"/> to work with <see cref="MapToApiAttribute"/>.
/// </summary>
public static class ActionDescriptorApiCommonExtensions
{
/// <summary>
/// Determines whether the <see cref="ActionDescriptor"/> has a <see cref="MapToApiAttribute"/> with the specified API name.
/// The check is made in runtime to support attributes added in runtime.
/// </summary>
/// <param name="actionDescriptor">The action descriptor to inspect.</param>
/// <param name="apiName">The API name to check for.</param>
/// <returns>
/// <c>true</c> if the <see cref="MapToApiAttribute"/> 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 ActionDescriptor actionDescriptor, string apiName)
{
var value = actionDescriptor.GetMapToApiAttributeValue();
return value == apiName
|| (value is null && apiName == DefaultApiConfiguration.ApiName);
}
private static string? GetMapToApiAttributeValue(this ActionDescriptor actionDescriptor)
{
IEnumerable<MapToApiAttribute> mapToApiAttributes = actionDescriptor?.EndpointMetadata?.OfType<MapToApiAttribute>() ?? [];
return mapToApiAttributes.SingleOrDefault()?.ApiName;
}
}
@@ -0,0 +1,148 @@
using Umbraco.Cms.Core.DeliveryApi;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Common.Rendering;
public class ElementOnlyOutputExpansionStrategy : IOutputExpansionStrategy
{
protected const string All = "$all";
protected const string None = "";
protected const string ExpandParameterName = "expand";
protected const string FieldsParameterName = "fields";
private readonly IApiPropertyRenderer _propertyRenderer;
protected Stack<Node?> ExpandProperties { get; } = new();
protected Stack<Node?> IncludeProperties { get; } = new();
public ElementOnlyOutputExpansionStrategy(
IApiPropertyRenderer propertyRenderer)
{
_propertyRenderer = propertyRenderer;
}
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}");
public virtual IDictionary<string, object?> MapMediaProperties(IPublishedContent media, bool skipUmbracoProperties = true)
{
if (media.ItemType != PublishedItemType.Media)
{
throw new ArgumentException($"Invalid item type. This method can only be used with item type {PublishedItemType.Media}, got: {media.ItemType}");
}
IPublishedProperty[] properties = media
.Properties
.Where(p => skipUmbracoProperties is false || p.Alias.StartsWith("umbraco") is false)
.ToArray();
return properties.Any()
? MapProperties(properties)
: new Dictionary<string, object?>();
}
public virtual IDictionary<string, object?> MapElementProperties(IPublishedElement element)
=> MapProperties(element.Properties, true);
private IDictionary<string, object?> MapProperties(IEnumerable<IPublishedProperty> properties, bool forceExpandProperties = false)
{
Node? currentExpandProperties = ExpandProperties.Count > 0 ? ExpandProperties.Peek() : null;
if (ExpandProperties.Count > 1 && currentExpandProperties is null && forceExpandProperties is false)
{
return new Dictionary<string, object?>();
}
Node? currentIncludeProperties = IncludeProperties.Count > 0 ? IncludeProperties.Peek() : null;
var result = new Dictionary<string, object?>();
foreach (IPublishedProperty property in properties)
{
Node? nextIncludeProperties = GetNextProperties(currentIncludeProperties, property.Alias);
if (currentIncludeProperties is not null && currentIncludeProperties.Items.Any() && nextIncludeProperties is null)
{
continue;
}
Node? nextExpandProperties = GetNextProperties(currentExpandProperties, property.Alias);
IncludeProperties.Push(nextIncludeProperties);
ExpandProperties.Push(nextExpandProperties);
result[property.Alias] = GetPropertyValue(property);
ExpandProperties.Pop();
IncludeProperties.Pop();
}
return result;
}
private Node? GetNextProperties(Node? currentProperties, string propertyAlias)
=> currentProperties?.Items.FirstOrDefault(i => i.Key == All)
?? currentProperties?.Items.FirstOrDefault(i => i.Key == "properties")?.Items.FirstOrDefault(i => i.Key == All || i.Key == propertyAlias);
private object? GetPropertyValue(IPublishedProperty property)
=> _propertyRenderer.GetPropertyValue(property, ExpandProperties.Peek() is not null);
protected sealed class Node
{
public string Key { get; private set; } = string.Empty;
public List<Node> Items { get; } = new();
public static Node Parse(string value)
{
// verify that there are as many start brackets as there are end brackets
if (value.CountOccurrences("[") != value.CountOccurrences("]"))
{
throw new ArgumentException("Value did not contain an equal number of start and end brackets");
}
// verify that the value does not start with a start bracket
if (value.StartsWith("["))
{
throw new ArgumentException("Value cannot start with a bracket");
}
// verify that there are no empty brackets
if (value.Contains("[]"))
{
throw new ArgumentException("Value cannot contain empty brackets");
}
var stack = new Stack<Node>();
var root = new Node { Key = "root" };
stack.Push(root);
var currentNode = new Node();
root.Items.Add(currentNode);
foreach (char c in value)
{
switch (c)
{
case '[': // Start a new node, child of the current node
stack.Push(currentNode);
currentNode = new Node();
stack.Peek().Items.Add(currentNode);
break;
case ',': // Start a new node, but at the same level of the current node
currentNode = new Node();
stack.Peek().Items.Add(currentNode);
break;
case ']': // Back to parent of the current node
currentNode = stack.Pop();
break;
default: // Add char to current node key
currentNode.Key += c;
break;
}
}
return root;
}
}
}
@@ -35,27 +35,35 @@ public static class UmbracoBuilderExtensions
builder.Services.AddScoped<IRequestStartItemProvider, RequestStartItemProvider>();
builder.Services.AddScoped<RequestContextOutputExpansionStrategy>();
builder.Services.AddScoped<RequestContextOutputExpansionStrategyV2>();
builder.Services.AddScoped<IOutputExpansionStrategy>(provider =>
{
HttpContext? httpContext = provider.GetRequiredService<IHttpContextAccessor>().HttpContext;
ApiVersion? apiVersion = httpContext?.GetRequestedApiVersion();
if (apiVersion is null)
{
return provider.GetRequiredService<RequestContextOutputExpansionStrategyV2>();
}
// V1 of the Delivery API uses a different expansion strategy than V2+
return apiVersion.MajorVersion == 1
? provider.GetRequiredService<RequestContextOutputExpansionStrategy>()
: provider.GetRequiredService<RequestContextOutputExpansionStrategyV2>();
});
builder.Services.AddUnique<IOutputExpansionStrategy>(
provider =>
{
HttpContext? httpContext = provider.GetRequiredService<IHttpContextAccessor>().HttpContext;
ApiVersion? apiVersion = httpContext?.GetRequestedApiVersion();
if (apiVersion is null)
{
return provider.GetRequiredService<RequestContextOutputExpansionStrategyV2>();
}
// V1 of the Delivery API uses a different expansion strategy than V2+
return apiVersion.MajorVersion == 1
? provider.GetRequiredService<RequestContextOutputExpansionStrategy>()
: provider.GetRequiredService<RequestContextOutputExpansionStrategyV2>();
},
ServiceLifetime.Scoped);
builder.Services.AddSingleton<IRequestCultureService, RequestCultureService>();
builder.Services.AddSingleton<IRequestSegmmentService, RequestSegmentService>();
builder.Services.AddSingleton<IRequestSegmentService, RequestSegmentService>();
builder.Services.AddSingleton<IRequestRoutingService, RequestRoutingService>();
builder.Services.AddSingleton<IRequestRedirectService, RequestRedirectService>();
builder.Services.AddSingleton<IRequestPreviewService, RequestPreviewService>();
builder.Services.AddSingleton<IOutputExpansionStrategyAccessor, RequestContextOutputExpansionStrategyAccessor>();
// Webooks register a more basic implementation, remove it.
builder.Services.AddUnique<IOutputExpansionStrategyAccessor, RequestContextOutputExpansionStrategyAccessor>(ServiceLifetime.Singleton);
builder.Services.AddSingleton<IRequestStartItemProviderAccessor, RequestContextRequestStartItemProviderAccessor>();
builder.Services.AddSingleton<IApiAccessService, ApiAccessService>();
builder.Services.AddSingleton<IApiContentQueryService, ApiContentQueryService>();
builder.Services.AddSingleton<IApiContentQueryProvider, ApiContentQueryProvider>();
@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Umbraco.Cms.Core.DeliveryApi;
using Umbraco.Cms.Core.Models.PublishedContent;
@@ -16,12 +16,12 @@ internal sealed class ContextualizeFromAcceptHeadersAttribute : TypeFilterAttrib
private sealed class LocalizeFromAcceptLanguageHeaderAttributeFilter : IActionFilter
{
private readonly IRequestCultureService _requestCultureService;
private readonly IRequestSegmmentService _requestSegmentService;
private readonly IRequestSegmentService _requestSegmentService;
private readonly IVariationContextAccessor _variationContextAccessor;
public LocalizeFromAcceptLanguageHeaderAttributeFilter(
IRequestCultureService requestCultureService,
IRequestSegmmentService requestSegmentService,
IRequestSegmentService requestSegmentService,
IVariationContextAccessor variationContextAccessor)
{
_requestCultureService = requestCultureService;
@@ -1,62 +1,25 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Api.Common.Rendering;
using Umbraco.Cms.Core.DeliveryApi;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Delivery.Rendering;
internal sealed class RequestContextOutputExpansionStrategyV2 : IOutputExpansionStrategy
internal sealed class RequestContextOutputExpansionStrategyV2 : ElementOnlyOutputExpansionStrategy, IOutputExpansionStrategy
{
private const string All = "$all";
private const string None = "";
private const string ExpandParameterName = "expand";
private const string FieldsParameterName = "fields";
private readonly IApiPropertyRenderer _propertyRenderer;
private readonly ILogger<RequestContextOutputExpansionStrategyV2> _logger;
private readonly Stack<Node?> _expandProperties;
private readonly Stack<Node?> _includeProperties;
public RequestContextOutputExpansionStrategyV2(
IHttpContextAccessor httpContextAccessor,
IApiPropertyRenderer propertyRenderer,
ILogger<RequestContextOutputExpansionStrategyV2> logger)
: base(propertyRenderer)
{
_propertyRenderer = propertyRenderer;
_logger = logger;
_expandProperties = new Stack<Node?>();
_includeProperties = new Stack<Node?>();
InitializeExpandAndInclude(httpContextAccessor);
}
public 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}");
public IDictionary<string, object?> MapMediaProperties(IPublishedContent media, bool skipUmbracoProperties = true)
{
if (media.ItemType != PublishedItemType.Media)
{
throw new ArgumentException($"Invalid item type. This method can only be used with item type {PublishedItemType.Media}, got: {media.ItemType}");
}
IPublishedProperty[] properties = media
.Properties
.Where(p => skipUmbracoProperties is false || p.Alias.StartsWith("umbraco") is false)
.ToArray();
return properties.Any()
? MapProperties(properties)
: new Dictionary<string, object?>();
}
public IDictionary<string, object?> MapElementProperties(IPublishedElement element)
=> MapProperties(element.Properties, true);
private void InitializeExpandAndInclude(IHttpContextAccessor httpContextAccessor)
{
string? QueryValue(string key) => httpContextAccessor.HttpContext?.Request.Query[key];
@@ -66,7 +29,7 @@ internal sealed class RequestContextOutputExpansionStrategyV2 : IOutputExpansion
try
{
_expandProperties.Push(Node.Parse(toExpand));
ExpandProperties.Push(Node.Parse(toExpand));
}
catch (ArgumentException ex)
{
@@ -76,7 +39,7 @@ internal sealed class RequestContextOutputExpansionStrategyV2 : IOutputExpansion
try
{
_includeProperties.Push(Node.Parse(toInclude));
IncludeProperties.Push(Node.Parse(toInclude));
}
catch (ArgumentException ex)
{
@@ -84,102 +47,4 @@ internal sealed class RequestContextOutputExpansionStrategyV2 : IOutputExpansion
throw new ArgumentException($"Could not parse the '{FieldsParameterName}' parameter: {ex.Message}");
}
}
private IDictionary<string, object?> MapProperties(IEnumerable<IPublishedProperty> properties, bool forceExpandProperties = false)
{
Node? currentExpandProperties = _expandProperties.Peek();
if (_expandProperties.Count > 1 && currentExpandProperties is null && forceExpandProperties is false)
{
return new Dictionary<string, object?>();
}
Node? currentIncludeProperties = _includeProperties.Peek();
var result = new Dictionary<string, object?>();
foreach (IPublishedProperty property in properties)
{
Node? nextIncludeProperties = GetNextProperties(currentIncludeProperties, property.Alias);
if (currentIncludeProperties is not null && currentIncludeProperties.Items.Any() && nextIncludeProperties is null)
{
continue;
}
Node? nextExpandProperties = GetNextProperties(currentExpandProperties, property.Alias);
_includeProperties.Push(nextIncludeProperties);
_expandProperties.Push(nextExpandProperties);
result[property.Alias] = GetPropertyValue(property);
_expandProperties.Pop();
_includeProperties.Pop();
}
return result;
}
private Node? GetNextProperties(Node? currentProperties, string propertyAlias)
=> currentProperties?.Items.FirstOrDefault(i => i.Key == All)
?? currentProperties?.Items.FirstOrDefault(i => i.Key == "properties")?.Items.FirstOrDefault(i => i.Key == All || i.Key == propertyAlias);
private object? GetPropertyValue(IPublishedProperty property)
=> _propertyRenderer.GetPropertyValue(property, _expandProperties.Peek() is not null);
private sealed class Node
{
public string Key { get; private set; } = string.Empty;
public List<Node> Items { get; } = new();
public static Node Parse(string value)
{
// verify that there are as many start brackets as there are end brackets
if (value.CountOccurrences("[") != value.CountOccurrences("]"))
{
throw new ArgumentException("Value did not contain an equal number of start and end brackets");
}
// verify that the value does not start with a start bracket
if (value.StartsWith("["))
{
throw new ArgumentException("Value cannot start with a bracket");
}
// verify that there are no empty brackets
if (value.Contains("[]"))
{
throw new ArgumentException("Value cannot contain empty brackets");
}
var stack = new Stack<Node>();
var root = new Node { Key = "root" };
stack.Push(root);
var currentNode = new Node();
root.Items.Add(currentNode);
foreach (char c in value)
{
switch (c)
{
case '[': // Start a new node, child of the current node
stack.Push(currentNode);
currentNode = new Node();
stack.Peek().Items.Add(currentNode);
break;
case ',': // Start a new node, but at the same level of the current node
currentNode = new Node();
stack.Peek().Items.Add(currentNode);
break;
case ']': // Back to parent of the current node
currentNode = stack.Pop();
break;
default: // Add char to current node key
currentNode.Key += c;
break;
}
}
return root;
}
}
}
@@ -3,7 +3,7 @@ using Umbraco.Cms.Core.DeliveryApi;
namespace Umbraco.Cms.Api.Delivery.Services;
internal sealed class RequestSegmentService : RequestHeaderHandler, IRequestSegmmentService
internal sealed class RequestSegmentService : RequestHeaderHandler, IRequestSegmentService, IRequestSegmmentService
{
public RequestSegmentService(IHttpContextAccessor httpContextAccessor)
: base(httpContextAccessor)
@@ -1,9 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<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:
@@ -12,7 +11,6 @@
-->
<WarningsNotAsErrors>$(WarningsNotAsErrors),ASP0019,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" />
@@ -1,7 +1,10 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Content;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
@@ -18,8 +21,19 @@ public abstract class ContentCollectionControllerBase<TContent, TCollectionRespo
where TVariantResponseModel : VariantResponseModelBase
{
private readonly IUmbracoMapper _mapper;
private readonly FlagProviderCollection _flagProviders;
protected ContentCollectionControllerBase(IUmbracoMapper mapper) => _mapper = mapper;
protected ContentCollectionControllerBase(IUmbracoMapper mapper, FlagProviderCollection flagProvider)
{
_mapper = mapper;
_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)
@@ -48,6 +62,9 @@ public abstract class ContentCollectionControllerBase<TContent, TCollectionRespo
return Ok(pageViewModel);
}
/// <summary>
/// Creates a collection result from the provided collection response models and total number of items.
/// </summary>
protected IActionResult CollectionResult(List<TCollectionResponseModel> collectionResponseModels, long totalNumberOfItems)
{
var pageViewModel = new PagedViewModel<TCollectionResponseModel>
@@ -1,6 +1,8 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
@@ -9,11 +11,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.DataType.Tree;
[ApiVersion("1.0")]
public class AncestorsDataTypeTreeController : DataTypeTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public AncestorsDataTypeTreeController(IEntityService entityService, IDataTypeService dataTypeService)
: base(entityService, dataTypeService)
{
}
[ActivatorUtilitiesConstructor]
public AncestorsDataTypeTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IDataTypeService dataTypeService)
: base(entityService, flagProviders, dataTypeService)
{
}
[HttpGet("ancestors")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(IEnumerable<DataTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,20 +1,29 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Services.Flags;
namespace Umbraco.Cms.Api.Management.Controllers.DataType.Tree;
[ApiVersion("1.0")]
public class ChildrenDataTypeTreeController : DataTypeTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public ChildrenDataTypeTreeController(IEntityService entityService, IDataTypeService dataTypeService)
: base(entityService, dataTypeService)
{
}
[ActivatorUtilitiesConstructor]
public ChildrenDataTypeTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IDataTypeService dataTypeService)
: base(entityService, flagProviders, dataTypeService)
{
}
[HttpGet("children")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<DataTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,9 +1,12 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Controllers.Tree;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Services;
@@ -19,8 +22,17 @@ public class DataTypeTreeControllerBase : FolderTreeControllerBase<DataTypeTreeI
{
private readonly IDataTypeService _dataTypeService;
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public DataTypeTreeControllerBase(IEntityService entityService, IDataTypeService dataTypeService)
: base(entityService) =>
: this(
entityService,
StaticServiceProvider.Instance.GetRequiredService<FlagProviderCollection>(),
dataTypeService)
{
}
public DataTypeTreeControllerBase(IEntityService entityService, FlagProviderCollection flagProviders, IDataTypeService dataTypeService)
: base(entityService, flagProviders) =>
_dataTypeService = dataTypeService;
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.DataType;
@@ -1,21 +1,29 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.ViewModels.DataType.Item;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Services.Flags;
namespace Umbraco.Cms.Api.Management.Controllers.DataType.Tree;
[ApiVersion("1.0")]
public class RootDataTypeTreeController : DataTypeTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public RootDataTypeTreeController(IEntityService entityService, IDataTypeService dataTypeService)
: base(entityService, dataTypeService)
{
}
[ActivatorUtilitiesConstructor]
public RootDataTypeTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IDataTypeService dataTypeService)
: base(entityService, flagProviders, dataTypeService)
{
}
[HttpGet("root")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<DataTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,6 +1,8 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
@@ -8,11 +10,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.DataType.Tree;
public class SiblingsDataTypeTreeController : DataTypeTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public SiblingsDataTypeTreeController(IEntityService entityService, IDataTypeService dataTypeService)
: base(entityService, dataTypeService)
{
}
[ActivatorUtilitiesConstructor]
public SiblingsDataTypeTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IDataTypeService dataTypeService)
: base(entityService, flagProviders, dataTypeService)
{
}
[HttpGet("siblings")]
[ProducesResponseType(typeof(SubsetViewModel<DataTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
public async Task<ActionResult<SubsetViewModel<DataTypeTreeItemResponseModel>>> Siblings(CancellationToken cancellationToken, Guid target, int before, int after, bool foldersOnly = false)
@@ -1,6 +1,8 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
@@ -9,11 +11,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.Dictionary.Tree;
[ApiVersion("1.0")]
public class AncestorsDictionaryTreeController : DictionaryTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public AncestorsDictionaryTreeController(IEntityService entityService, IDictionaryItemService dictionaryItemService)
: base(entityService, dictionaryItemService)
{
}
[ActivatorUtilitiesConstructor]
public AncestorsDictionaryTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IDictionaryItemService dictionaryItemService)
: base(entityService, flagProviders, dictionaryItemService)
{
}
[HttpGet("ancestors")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(IEnumerable<NamedEntityTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,21 +1,30 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
namespace Umbraco.Cms.Api.Management.Controllers.Dictionary.Tree;
[ApiVersion("1.0")]
public class ChildrenDictionaryTreeController : DictionaryTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public ChildrenDictionaryTreeController(IEntityService entityService, IDictionaryItemService dictionaryItemService)
: base(entityService, dictionaryItemService)
{
}
[ActivatorUtilitiesConstructor]
public ChildrenDictionaryTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IDictionaryItemService dictionaryItemService)
: base(entityService, flagProviders, dictionaryItemService)
{
}
[HttpGet("children")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<NamedEntityTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,10 +1,13 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Controllers.Tree;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.Common.Authorization;
@@ -18,8 +21,17 @@ namespace Umbraco.Cms.Api.Management.Controllers.Dictionary.Tree;
// tree controller base. We'll keep it though, in the hope that we can mend EntityService.
public class DictionaryTreeControllerBase : NamedEntityTreeControllerBase<NamedEntityTreeItemResponseModel>
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public DictionaryTreeControllerBase(IEntityService entityService, IDictionaryItemService dictionaryItemService)
: base(entityService) =>
: this(
entityService,
StaticServiceProvider.Instance.GetRequiredService<FlagProviderCollection>(),
dictionaryItemService)
{
}
public DictionaryTreeControllerBase(IEntityService entityService, FlagProviderCollection flagProviders, IDictionaryItemService dictionaryItemService)
: base(entityService, flagProviders) =>
DictionaryItemService = dictionaryItemService;
// dictionary items do not currently have a known UmbracoObjectType, so we'll settle with Unknown for now
@@ -1,10 +1,12 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
namespace Umbraco.Cms.Api.Management.Controllers.Dictionary.Tree;
@@ -16,6 +18,12 @@ public class RootDictionaryTreeController : DictionaryTreeControllerBase
{
}
[ActivatorUtilitiesConstructor]
public RootDictionaryTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IDictionaryItemService dictionaryItemService)
: base(entityService, flagProviders, dictionaryItemService)
{
}
[HttpGet("root")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<NamedEntityTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -0,0 +1,85 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.ViewModels.Segment;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Actions;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Security.Authorization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Management.Controllers.Document;
[Obsolete("This controller is temporary and will be removed in a future release (planned for v20). A more permanent solution will follow.")]
[ApiVersion("1.0")]
public class AvailableSegmentsController : DocumentControllerBase
{
private readonly IAuthorizationService _authorizationService;
private readonly ISegmentService _segmentService;
private readonly IUmbracoMapper _umbracoMapper;
public AvailableSegmentsController(
IAuthorizationService authorizationService,
ISegmentService segmentService,
IUmbracoMapper umbracoMapper)
{
_authorizationService = authorizationService;
_segmentService = segmentService;
_umbracoMapper = umbracoMapper;
}
[HttpGet("{id:guid}/available-segment-options")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<SegmentResponseModel>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetAvailableSegmentOptions(
Guid id,
CancellationToken cancellationToken,
int skip = 0,
int take = 100)
{
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
User,
ContentPermissionResource.WithKeys(ActionBrowse.ActionLetter, id),
AuthorizationPolicies.ContentPermissionByResource);
if (!authorizationResult.Succeeded)
{
return Forbidden();
}
Attempt<PagedModel<Core.Models.Segment>?, SegmentOperationStatus> pagedAttempt =
await _segmentService.GetPagedSegmentsForDocumentAsync(id, skip, take);
if (pagedAttempt.Success is false)
{
return MapFailure(pagedAttempt.Status);
}
var viewModel = new PagedViewModel<SegmentResponseModel>
{
Items = _umbracoMapper.MapEnumerable<Core.Models.Segment, SegmentResponseModel>(pagedAttempt.Result!.Items),
Total = pagedAttempt.Result!.Total,
};
return Ok(viewModel);
}
private IActionResult MapFailure(SegmentOperationStatus status)
=> OperationStatusResult(
status,
problemDetailsBuilder => status switch
{
_ => StatusCode(
StatusCodes.Status500InternalServerError,
problemDetailsBuilder
.WithTitle("Unknown segment operation status.")
.Build()),
});
}
@@ -1,10 +1,13 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Document.Collection;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Security;
@@ -20,16 +23,33 @@ public class ByKeyDocumentCollectionController : DocumentCollectionControllerBas
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
private readonly IDocumentCollectionPresentationFactory _documentCollectionPresentationFactory;
[ActivatorUtilitiesConstructor]
public ByKeyDocumentCollectionController(
IContentListViewService contentListViewService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
IUmbracoMapper mapper,
IDocumentCollectionPresentationFactory documentCollectionPresentationFactory,
FlagProviderCollection flagProviders)
: base(mapper, flagProviders)
{
_contentListViewService = contentListViewService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
_documentCollectionPresentationFactory = documentCollectionPresentationFactory;
}
[Obsolete("Please use the constructor with all parameters. Scheduled to be removed in V18")]
public ByKeyDocumentCollectionController(
IContentListViewService contentListViewService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
IUmbracoMapper mapper,
IDocumentCollectionPresentationFactory documentCollectionPresentationFactory)
: base(mapper)
: this(
contentListViewService,
backOfficeSecurityAccessor,
mapper,
documentCollectionPresentationFactory,
StaticServiceProvider.Instance.GetRequiredService<FlagProviderCollection>())
{
_contentListViewService = contentListViewService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
_documentCollectionPresentationFactory = documentCollectionPresentationFactory;
}
[HttpGet("{id:guid}")]
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Controllers.Content;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Document;
using Umbraco.Cms.Api.Management.ViewModels.Document.Collection;
using Umbraco.Cms.Core;
@@ -17,6 +18,12 @@ namespace Umbraco.Cms.Api.Management.Controllers.Document.Collection;
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocuments)]
public abstract class DocumentCollectionControllerBase : ContentCollectionControllerBase<IContent, DocumentCollectionResponseModel, DocumentValueResponseModel, DocumentVariantResponseModel>
{
protected DocumentCollectionControllerBase(IUmbracoMapper mapper, FlagProviderCollection flagProviders)
: base(mapper, flagProviders)
{
}
[Obsolete("Please use the constructor with all parameters. Scheduled to be removed in V18")]
protected DocumentCollectionControllerBase(IUmbracoMapper mapper)
: base(mapper)
{
@@ -1,31 +1,60 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.ViewModels.Document;
using Umbraco.Cms.Core.Actions;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Security.Authorization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Management.Controllers.Document;
[ApiVersion("1.0")]
public class DomainsController : DocumentControllerBase
{
private readonly IAuthorizationService _authorizationService;
private readonly IDomainService _domainService;
private readonly IUmbracoMapper _umbracoMapper;
public DomainsController(IDomainService domainService, IUmbracoMapper umbracoMapper)
[ActivatorUtilitiesConstructor]
public DomainsController(IAuthorizationService authorizationService, IDomainService domainService, IUmbracoMapper umbracoMapper)
{
_authorizationService = authorizationService;
_domainService = domainService;
_umbracoMapper = umbracoMapper;
}
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public DomainsController(IDomainService domainService, IUmbracoMapper umbracoMapper)
: this(
StaticServiceProvider.Instance.GetRequiredService<IAuthorizationService>(),
domainService,
umbracoMapper)
{
}
[MapToApiVersion("1.0")]
[HttpGet("{id:guid}/domains")]
[ProducesResponseType(typeof(DomainsResponseModel), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<IActionResult> Domains(CancellationToken cancellationToken, Guid id)
{
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
User,
ContentPermissionResource.WithKeys(ActionBrowse.ActionLetter, id),
AuthorizationPolicies.ContentPermissionByResource);
if (!authorizationResult.Succeeded)
{
return Forbidden();
}
IDomain[] assignedDomains = (await _domainService.GetAssignedDomainsAsync(id, true))
.OrderBy(d => d.SortOrder)
.ToArray();
@@ -1,6 +1,7 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.ViewModels.Document.Item;
using Umbraco.Cms.Core.Models;
@@ -15,7 +16,10 @@ public class ItemDocumentItemController : DocumentItemControllerBase
private readonly IEntityService _entityService;
private readonly IDocumentPresentationFactory _documentPresentationFactory;
public ItemDocumentItemController(IEntityService entityService, IDocumentPresentationFactory documentPresentationFactory)
[ActivatorUtilitiesConstructor]
public ItemDocumentItemController(
IEntityService entityService,
IDocumentPresentationFactory documentPresentationFactory)
{
_entityService = entityService;
_documentPresentationFactory = documentPresentationFactory;
@@ -24,20 +28,20 @@ public class ItemDocumentItemController : DocumentItemControllerBase
[HttpGet]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(IEnumerable<DocumentItemResponseModel>), StatusCodes.Status200OK)]
public Task<IActionResult> Item(
public async Task<IActionResult> Item(
CancellationToken cancellationToken,
[FromQuery(Name = "id")] HashSet<Guid> ids)
{
if (ids.Count is 0)
{
return Task.FromResult<IActionResult>(Ok(Enumerable.Empty<DocumentItemResponseModel>()));
return Ok(Enumerable.Empty<DocumentItemResponseModel>());
}
IEnumerable<IDocumentEntitySlim> documents = _entityService
.GetAll(UmbracoObjectTypes.Document, ids.ToArray())
.OfType<IDocumentEntitySlim>();
IEnumerable<DocumentItemResponseModel> documentItemResponseModels = documents.Select(_documentPresentationFactory.CreateItemResponseModel);
return Task.FromResult<IActionResult>(Ok(documentItemResponseModels));
IEnumerable<DocumentItemResponseModel> responseModels = documents.Select(_documentPresentationFactory.CreateItemResponseModel);
return Ok(responseModels);
}
}
@@ -0,0 +1,24 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.ViewModels.Document.RecycleBin;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Api.Management.Controllers.Document.RecycleBin;
[ApiVersion("1.0")]
public class SiblingsDocumentRecycleBinController : DocumentRecycleBinControllerBase
{
public SiblingsDocumentRecycleBinController(IEntityService entityService, IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, documentPresentationFactory)
{
}
[HttpGet("siblings")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(SubsetViewModel<DocumentRecycleBinItemResponseModel>), StatusCodes.Status200OK)]
public async Task<ActionResult<SubsetViewModel<DocumentRecycleBinItemResponseModel>>> Siblings(CancellationToken cancellationToken, Guid target, int before, int after, Guid? dataTypeId = null)
=> await GetSiblings(target, before, after);
}
@@ -1,8 +1,10 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Security;
@@ -13,6 +15,7 @@ namespace Umbraco.Cms.Api.Management.Controllers.Document.Tree;
[ApiVersion("1.0")]
public class AncestorsDocumentTreeController : DocumentTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public AncestorsDocumentTreeController(
IEntityService entityService,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
@@ -32,6 +35,28 @@ public class AncestorsDocumentTreeController : DocumentTreeControllerBase
{
}
[ActivatorUtilitiesConstructor]
public AncestorsDocumentTreeController(
IEntityService entityService,
FlagProviderCollection flagProviders,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
IDataTypeService dataTypeService,
IPublicAccessService publicAccessService,
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IDocumentPresentationFactory documentPresentationFactory)
: base(
entityService,
flagProviders,
userStartNodeEntitiesService,
dataTypeService,
publicAccessService,
appCaches,
backofficeSecurityAccessor,
documentPresentationFactory)
{
}
[HttpGet("ancestors")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(IEnumerable<DocumentTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,19 +1,22 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
namespace Umbraco.Cms.Api.Management.Controllers.Document.Tree;
[ApiVersion("1.0")]
public class ChildrenDocumentTreeController : DocumentTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public ChildrenDocumentTreeController(
IEntityService entityService,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
@@ -33,6 +36,28 @@ public class ChildrenDocumentTreeController : DocumentTreeControllerBase
{
}
[ActivatorUtilitiesConstructor]
public ChildrenDocumentTreeController(
IEntityService entityService,
FlagProviderCollection flagProviders,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
IDataTypeService dataTypeService,
IPublicAccessService publicAccessService,
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IDocumentPresentationFactory documentPresentationFactory)
: base(
entityService,
flagProviders,
userStartNodeEntitiesService,
dataTypeService,
publicAccessService,
appCaches,
backofficeSecurityAccessor,
documentPresentationFactory)
{
}
[HttpGet("children")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<DocumentTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,13 +1,16 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Controllers.Tree;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Security;
@@ -26,6 +29,7 @@ public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBa
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
private readonly IDocumentPresentationFactory _documentPresentationFactory;
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
protected DocumentTreeControllerBase(
IEntityService entityService,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
@@ -34,7 +38,29 @@ public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBa
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, userStartNodeEntitiesService, dataTypeService)
: this(
entityService,
StaticServiceProvider.Instance.GetRequiredService<FlagProviderCollection>(),
userStartNodeEntitiesService,
dataTypeService,
publicAccessService,
appCaches,
backofficeSecurityAccessor,
documentPresentationFactory)
{
}
[ActivatorUtilitiesConstructor]
protected DocumentTreeControllerBase(
IEntityService entityService,
FlagProviderCollection flagProviders,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
IDataTypeService dataTypeService,
IPublicAccessService publicAccessService,
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, flagProviders, userStartNodeEntitiesService, dataTypeService)
{
_publicAccessService = publicAccessService;
_appCaches = appCaches;
@@ -1,19 +1,22 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
namespace Umbraco.Cms.Api.Management.Controllers.Document.Tree;
[ApiVersion("1.0")]
public class RootDocumentTreeController : DocumentTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public RootDocumentTreeController(
IEntityService entityService,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
@@ -33,6 +36,28 @@ public class RootDocumentTreeController : DocumentTreeControllerBase
{
}
[ActivatorUtilitiesConstructor]
public RootDocumentTreeController(
IEntityService entityService,
FlagProviderCollection flagProviders,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
IDataTypeService dataTypeService,
IPublicAccessService publicAccessService,
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IDocumentPresentationFactory documentPresentationFactory)
: base(
entityService,
flagProviders,
userStartNodeEntitiesService,
dataTypeService,
publicAccessService,
appCaches,
backofficeSecurityAccessor,
documentPresentationFactory)
{
}
[HttpGet("root")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<DocumentTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,9 +1,11 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Security;
@@ -14,6 +16,7 @@ namespace Umbraco.Cms.Api.Management.Controllers.Document.Tree;
[ApiVersion("1.0")]
public class SiblingsDocumentTreeController : DocumentTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public SiblingsDocumentTreeController(
IEntityService entityService,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
@@ -33,6 +36,28 @@ public class SiblingsDocumentTreeController : DocumentTreeControllerBase
{
}
[ActivatorUtilitiesConstructor]
public SiblingsDocumentTreeController(
IEntityService entityService,
FlagProviderCollection flagProviders,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
IDataTypeService dataTypeService,
IPublicAccessService publicAccessService,
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IDocumentPresentationFactory documentPresentationFactory)
: base(
entityService,
flagProviders,
userStartNodeEntitiesService,
dataTypeService,
publicAccessService,
appCaches,
backofficeSecurityAccessor,
documentPresentationFactory)
{
}
[HttpGet("siblings")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(SubsetViewModel<DocumentTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,13 +1,19 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.ViewModels.Document;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Actions;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Security.Authorization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Management.Controllers.Document;
@@ -15,17 +21,30 @@ namespace Umbraco.Cms.Api.Management.Controllers.Document;
[ApiVersion("1.0")]
public class UpdateDomainsController : DocumentControllerBase
{
private readonly IAuthorizationService _authorizationService;
private readonly IDomainService _domainService;
private readonly IUmbracoMapper _umbracoMapper;
private readonly IDomainPresentationFactory _domainPresentationFactory;
public UpdateDomainsController(IDomainService domainService, IUmbracoMapper umbracoMapper, IDomainPresentationFactory domainPresentationFactory)
[ActivatorUtilitiesConstructor]
public UpdateDomainsController(IAuthorizationService authorizationService, IDomainService domainService, IUmbracoMapper umbracoMapper, IDomainPresentationFactory domainPresentationFactory)
{
_authorizationService = authorizationService;
_domainService = domainService;
_umbracoMapper = umbracoMapper;
_domainPresentationFactory = domainPresentationFactory;
}
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public UpdateDomainsController(IDomainService domainService, IUmbracoMapper umbracoMapper, IDomainPresentationFactory domainPresentationFactory)
: this(
StaticServiceProvider.Instance.GetRequiredService<IAuthorizationService>(),
domainService,
umbracoMapper,
domainPresentationFactory)
{
}
[MapToApiVersion("1.0")]
[HttpPut("{id:guid}/domains")]
[ProducesResponseType(StatusCodes.Status200OK)]
@@ -37,6 +56,16 @@ public class UpdateDomainsController : DocumentControllerBase
Guid id,
UpdateDomainsRequestModel updateModel)
{
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
User,
ContentPermissionResource.WithKeys(ActionAssignDomain.ActionLetter, id),
AuthorizationPolicies.ContentPermissionByResource);
if (!authorizationResult.Succeeded)
{
return Forbidden();
}
DomainsUpdateModel domainsUpdateModel = _umbracoMapper.Map<DomainsUpdateModel>(updateModel)!;
Attempt<DomainUpdateResult, DomainOperationStatus> result = await _domainService.UpdateDomainsAsync(id, domainsUpdateModel);
@@ -1,33 +1,63 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.ViewModels.Document;
using Umbraco.Cms.Core.Actions;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Security.Authorization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Management.Controllers.Document;
[ApiVersion("1.0")]
public class UpdateNotificationsController : DocumentControllerBase
{
private readonly IAuthorizationService _authorizationService;
private readonly IContentEditingService _contentEditingService;
private readonly INotificationService _notificationService;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
public UpdateNotificationsController(IContentEditingService contentEditingService, INotificationService notificationService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
[ActivatorUtilitiesConstructor]
public UpdateNotificationsController(IAuthorizationService authorizationService, IContentEditingService contentEditingService, INotificationService notificationService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
{
_authorizationService = authorizationService;
_contentEditingService = contentEditingService;
_notificationService = notificationService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public UpdateNotificationsController(IContentEditingService contentEditingService, INotificationService notificationService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
: this(
StaticServiceProvider.Instance.GetRequiredService<IAuthorizationService>(),
contentEditingService,
notificationService,
backOfficeSecurityAccessor)
{
}
[MapToApiVersion("1.0")]
[HttpPut("{id:guid}/notifications")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<IActionResult> UpdateNotifications(CancellationToken cancellationToken, Guid id, UpdateDocumentNotificationsRequestModel updateModel)
{
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
User,
ContentPermissionResource.WithKeys(ActionBrowse.ActionLetter, id),
AuthorizationPolicies.ContentPermissionByResource);
if (!authorizationResult.Succeeded)
{
return Forbidden();
}
IContent? content = await _contentEditingService.GetAsync(id);
if (content == null)
{
@@ -1,7 +1,9 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
@@ -10,11 +12,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.DocumentBlueprint.Tree;
[ApiVersion("1.0")]
public class AncestorsDocumentBlueprintTreeController : DocumentBlueprintTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public AncestorsDocumentBlueprintTreeController(IEntityService entityService, IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, documentPresentationFactory)
{
}
[ActivatorUtilitiesConstructor]
public AncestorsDocumentBlueprintTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, flagProviders, documentPresentationFactory)
{
}
[HttpGet("ancestors")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(IEnumerable<DocumentBlueprintTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,8 +1,10 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
@@ -11,11 +13,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.DocumentBlueprint.Tree;
[ApiVersion("1.0")]
public class ChildrenDocumentBlueprintTreeController : DocumentBlueprintTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public ChildrenDocumentBlueprintTreeController(IEntityService entityService, IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, documentPresentationFactory)
{
}
[ActivatorUtilitiesConstructor]
public ChildrenDocumentBlueprintTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, flagProviders, documentPresentationFactory)
{
}
[HttpGet("children")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<DocumentBlueprintTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,10 +1,13 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Controllers.Tree;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Services;
@@ -19,8 +22,17 @@ public class DocumentBlueprintTreeControllerBase : FolderTreeControllerBase<Docu
{
private readonly IDocumentPresentationFactory _documentPresentationFactory;
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public DocumentBlueprintTreeControllerBase(IEntityService entityService, IDocumentPresentationFactory documentPresentationFactory)
: base(entityService)
: this(
entityService,
StaticServiceProvider.Instance.GetRequiredService<FlagProviderCollection>(),
documentPresentationFactory)
{
}
public DocumentBlueprintTreeControllerBase(IEntityService entityService, FlagProviderCollection flagProviders, IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, flagProviders)
=> _documentPresentationFactory = documentPresentationFactory;
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.DocumentBlueprint;
@@ -1,8 +1,10 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
@@ -11,11 +13,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.DocumentBlueprint.Tree;
[ApiVersion("1.0")]
public class RootDocumentBlueprintTreeController : DocumentBlueprintTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public RootDocumentBlueprintTreeController(IEntityService entityService, IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, documentPresentationFactory)
{
}
[ActivatorUtilitiesConstructor]
public RootDocumentBlueprintTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, flagProviders, documentPresentationFactory)
{
}
[HttpGet("root")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<DocumentBlueprintTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,7 +1,9 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
@@ -9,11 +11,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.DocumentBlueprint.Tree;
public class SiblingsDocumentBlueprintTreeController : DocumentBlueprintTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public SiblingsDocumentBlueprintTreeController(IEntityService entityService, IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, documentPresentationFactory)
{
}
[ActivatorUtilitiesConstructor]
public SiblingsDocumentBlueprintTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, flagProviders, documentPresentationFactory)
{
}
[HttpGet("siblings")]
[ProducesResponseType(typeof(SubsetViewModel<DocumentBlueprintTreeItemResponseModel>), StatusCodes.Status200OK)]
public async Task<ActionResult<SubsetViewModel<DocumentBlueprintTreeItemResponseModel>>> Siblings(
@@ -1,6 +1,8 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
@@ -9,11 +11,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.DocumentType.Tree;
[ApiVersion("1.0")]
public class AncestorsDocumentTypeTreeController : DocumentTypeTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public AncestorsDocumentTypeTreeController(IEntityService entityService, IContentTypeService contentTypeService)
: base(entityService, contentTypeService)
{
}
[ActivatorUtilitiesConstructor]
public AncestorsDocumentTypeTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IContentTypeService contentTypeService)
: base(entityService, flagProviders, contentTypeService)
{
}
[HttpGet("ancestors")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(IEnumerable<DocumentTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,20 +1,29 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.Services;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Api.Management.Controllers.DocumentType.Tree;
[ApiVersion("1.0")]
public class ChildrenDocumentTypeTreeController : DocumentTypeTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public ChildrenDocumentTypeTreeController(IEntityService entityService, IContentTypeService contentTypeService)
: base(entityService, contentTypeService)
{
}
[ActivatorUtilitiesConstructor]
public ChildrenDocumentTypeTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IContentTypeService contentTypeService)
: base(entityService, flagProviders, contentTypeService)
{
}
[HttpGet("children")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<DocumentTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,9 +1,12 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Controllers.Tree;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Services;
@@ -18,8 +21,17 @@ public class DocumentTypeTreeControllerBase : FolderTreeControllerBase<DocumentT
{
private readonly IContentTypeService _contentTypeService;
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public DocumentTypeTreeControllerBase(IEntityService entityService, IContentTypeService contentTypeService)
: base(entityService) =>
: this(
entityService,
StaticServiceProvider.Instance.GetRequiredService<FlagProviderCollection>(),
contentTypeService)
{
}
public DocumentTypeTreeControllerBase(IEntityService entityService, FlagProviderCollection flagProviders, IContentTypeService contentTypeService)
: base(entityService, flagProviders) =>
_contentTypeService = contentTypeService;
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.DocumentType;
@@ -1,20 +1,29 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.Services;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Api.Management.Controllers.DocumentType.Tree;
[ApiVersion("1.0")]
public class RootDocumentTypeTreeController : DocumentTypeTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public RootDocumentTypeTreeController(IEntityService entityService, IContentTypeService contentTypeService)
: base(entityService, contentTypeService)
{
}
[ActivatorUtilitiesConstructor]
public RootDocumentTypeTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IContentTypeService contentTypeService)
: base(entityService, flagProviders, contentTypeService)
{
}
[HttpGet("root")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<DocumentTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,6 +1,8 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
@@ -8,11 +10,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.DocumentType.Tree;
public class SiblingsDocumentTypeTreeController : DocumentTypeTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public SiblingsDocumentTypeTreeController(IEntityService entityService, IContentTypeService contentTypeService)
: base(entityService, contentTypeService)
{
}
[ActivatorUtilitiesConstructor]
public SiblingsDocumentTypeTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IContentTypeService contentTypeService)
: base(entityService, flagProviders, contentTypeService)
{
}
[HttpGet("siblings")]
[ProducesResponseType(typeof(SubsetViewModel<DocumentTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
public async Task<ActionResult<SubsetViewModel<DocumentTypeTreeItemResponseModel>>> Siblings(
@@ -1,10 +1,13 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Media.Collection;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Security;
@@ -20,16 +23,33 @@ public class ByKeyMediaCollectionController : MediaCollectionControllerBase
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
private readonly IMediaCollectionPresentationFactory _mediaCollectionPresentationFactory;
[ActivatorUtilitiesConstructor]
public ByKeyMediaCollectionController(
IMediaListViewService mediaListViewService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
IUmbracoMapper mapper,
IMediaCollectionPresentationFactory mediaCollectionPresentationFactory,
FlagProviderCollection flagProviders)
: base(mapper, flagProviders)
{
_mediaListViewService = mediaListViewService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
_mediaCollectionPresentationFactory = mediaCollectionPresentationFactory;
}
[Obsolete("Please use the constructor with all parameters. Scheduled to be removed in Umbraco 18")]
public ByKeyMediaCollectionController(
IMediaListViewService mediaListViewService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
IUmbracoMapper mapper,
IMediaCollectionPresentationFactory mediaCollectionPresentationFactory)
: base(mapper)
: this(
mediaListViewService,
backOfficeSecurityAccessor,
mapper,
mediaCollectionPresentationFactory,
StaticServiceProvider.Instance.GetRequiredService<FlagProviderCollection>())
{
_mediaListViewService = mediaListViewService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
_mediaCollectionPresentationFactory = mediaCollectionPresentationFactory;
}
[HttpGet]
@@ -1,10 +1,14 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Controllers.Content;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Document.Collection;
using Umbraco.Cms.Api.Management.ViewModels.Media;
using Umbraco.Cms.Api.Management.ViewModels.Media.Collection;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services.OperationStatus;
@@ -17,6 +21,12 @@ namespace Umbraco.Cms.Api.Management.Controllers.Media.Collection;
[Authorize(Policy = AuthorizationPolicies.SectionAccessMedia)]
public abstract class MediaCollectionControllerBase : ContentCollectionControllerBase<IMedia, MediaCollectionResponseModel, MediaValueResponseModel, MediaVariantResponseModel>
{
protected MediaCollectionControllerBase(IUmbracoMapper mapper, FlagProviderCollection flagProviders)
: base(mapper, flagProviders)
{
}
[Obsolete("Please use the constructor with all parameters. Scheduled to be removed in Umbraco 18")]
protected MediaCollectionControllerBase(IUmbracoMapper mapper)
: base(mapper)
{
@@ -1,8 +1,12 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Document.Item;
using Umbraco.Cms.Api.Management.ViewModels.Media.Item;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
@@ -15,23 +19,35 @@ public class ItemMediaItemController : MediaItemControllerBase
{
private readonly IEntityService _entityService;
private readonly IMediaPresentationFactory _mediaPresentationFactory;
private readonly FlagProviderCollection _flagProviders;
public ItemMediaItemController(IEntityService entityService, IMediaPresentationFactory mediaPresentationFactory)
[ActivatorUtilitiesConstructor]
public ItemMediaItemController(
IEntityService entityService,
IMediaPresentationFactory mediaPresentationFactory,
FlagProviderCollection flagProviders)
{
_entityService = entityService;
_mediaPresentationFactory = mediaPresentationFactory;
_flagProviders = flagProviders;
}
[Obsolete("Please use the constructor with all parameters. Scheduled for removal in Umbraco 18")]
public ItemMediaItemController(IEntityService entityService, IMediaPresentationFactory mediaPresentationFactory)
: this(entityService, mediaPresentationFactory, StaticServiceProvider.Instance.GetRequiredService<FlagProviderCollection>())
{
}
[HttpGet]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(IEnumerable<MediaItemResponseModel>), StatusCodes.Status200OK)]
public Task<IActionResult> Item(
public async Task<IActionResult> Item(
CancellationToken cancellationToken,
[FromQuery(Name = "id")] HashSet<Guid> ids)
{
if (ids.Count is 0)
{
return Task.FromResult<IActionResult>(Ok(Enumerable.Empty<MediaItemResponseModel>()));
return Ok(Enumerable.Empty<MediaItemResponseModel>());
}
IEnumerable<IMediaEntitySlim> media = _entityService
@@ -39,6 +55,16 @@ public class ItemMediaItemController : MediaItemControllerBase
.OfType<IMediaEntitySlim>();
IEnumerable<MediaItemResponseModel> responseModels = media.Select(_mediaPresentationFactory.CreateItemResponseModel);
return Task.FromResult<IActionResult>(Ok(responseModels));
await PopulateFlags(responseModels);
return Ok(responseModels);
}
private async Task PopulateFlags(IEnumerable<MediaItemResponseModel> itemViewModels)
{
foreach (IFlagProvider signProvider in _flagProviders.Where(x => x.CanProvideFlags<DocumentItemResponseModel>()))
{
await signProvider.PopulateFlagsAsync(itemViewModels);
}
}
}
@@ -0,0 +1,24 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.ViewModels.Media.RecycleBin;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Api.Management.Controllers.Media.RecycleBin;
[ApiVersion("1.0")]
public class SiblingsMediaRecycleBinController : MediaRecycleBinControllerBase
{
public SiblingsMediaRecycleBinController(IEntityService entityService, IMediaPresentationFactory mediaPresentationFactory)
: base(entityService, mediaPresentationFactory)
{
}
[HttpGet("siblings")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(SubsetViewModel<MediaRecycleBinItemResponseModel>), StatusCodes.Status200OK)]
public async Task<ActionResult<SubsetViewModel<MediaRecycleBinItemResponseModel>>> Siblings(CancellationToken cancellationToken, Guid target, int before, int after, Guid? dataTypeId = null)
=> await GetSiblings(target, before, after);
}
@@ -1,8 +1,10 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Security;
@@ -13,6 +15,7 @@ namespace Umbraco.Cms.Api.Management.Controllers.Media.Tree;
[ApiVersion("1.0")]
public class AncestorsMediaTreeController : MediaTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public AncestorsMediaTreeController(
IEntityService entityService,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
@@ -24,6 +27,19 @@ public class AncestorsMediaTreeController : MediaTreeControllerBase
{
}
[ActivatorUtilitiesConstructor]
public AncestorsMediaTreeController(
IEntityService entityService,
FlagProviderCollection flagProviders,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
IDataTypeService dataTypeService,
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IMediaPresentationFactory mediaPresentationFactory)
: base(entityService, flagProviders, userStartNodeEntitiesService, dataTypeService, appCaches, backofficeSecurityAccessor, mediaPresentationFactory)
{
}
[HttpGet("ancestors")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(IEnumerable<MediaTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,9 +1,11 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Security;
@@ -14,6 +16,7 @@ namespace Umbraco.Cms.Api.Management.Controllers.Media.Tree;
[ApiVersion("1.0")]
public class ChildrenMediaTreeController : MediaTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public ChildrenMediaTreeController(
IEntityService entityService,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
@@ -25,6 +28,19 @@ public class ChildrenMediaTreeController : MediaTreeControllerBase
{
}
[ActivatorUtilitiesConstructor]
public ChildrenMediaTreeController(
IEntityService entityService,
FlagProviderCollection flagProviders,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
IDataTypeService dataTypeService,
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IMediaPresentationFactory mediaPresentationFactory)
: base(entityService, flagProviders, userStartNodeEntitiesService, dataTypeService, appCaches, backofficeSecurityAccessor, mediaPresentationFactory)
{
}
[HttpGet("children")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<MediaTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,12 +1,15 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Controllers.Tree;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Security;
@@ -24,6 +27,7 @@ public class MediaTreeControllerBase : UserStartNodeTreeControllerBase<MediaTree
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
private readonly IMediaPresentationFactory _mediaPresentationFactory;
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public MediaTreeControllerBase(
IEntityService entityService,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
@@ -31,7 +35,27 @@ public class MediaTreeControllerBase : UserStartNodeTreeControllerBase<MediaTree
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IMediaPresentationFactory mediaPresentationFactory)
: base(entityService, userStartNodeEntitiesService, dataTypeService)
: this(
entityService,
StaticServiceProvider.Instance.GetRequiredService<FlagProviderCollection>(),
userStartNodeEntitiesService,
dataTypeService,
appCaches,
backofficeSecurityAccessor,
mediaPresentationFactory)
{
}
[ActivatorUtilitiesConstructor]
public MediaTreeControllerBase(
IEntityService entityService,
FlagProviderCollection flagProviders,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
IDataTypeService dataTypeService,
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IMediaPresentationFactory mediaPresentationFactory)
: base(entityService, flagProviders, userStartNodeEntitiesService, dataTypeService)
{
_appCaches = appCaches;
_backofficeSecurityAccessor = backofficeSecurityAccessor;
@@ -1,9 +1,11 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Security;
@@ -14,6 +16,7 @@ namespace Umbraco.Cms.Api.Management.Controllers.Media.Tree;
[ApiVersion("1.0")]
public class RootMediaTreeController : MediaTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public RootMediaTreeController(
IEntityService entityService,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
@@ -25,6 +28,19 @@ public class RootMediaTreeController : MediaTreeControllerBase
{
}
[ActivatorUtilitiesConstructor]
public RootMediaTreeController(
IEntityService entityService,
FlagProviderCollection flagProviders,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
IDataTypeService dataTypeService,
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IMediaPresentationFactory mediaPresentationFactory)
: base(entityService, flagProviders, userStartNodeEntitiesService, dataTypeService, appCaches, backofficeSecurityAccessor, mediaPresentationFactory)
{
}
[HttpGet("root")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<MediaTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,8 +1,10 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Security;
@@ -12,6 +14,7 @@ namespace Umbraco.Cms.Api.Management.Controllers.Media.Tree;
public class SiblingsMediaTreeController : MediaTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public SiblingsMediaTreeController(
IEntityService entityService,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
@@ -23,6 +26,19 @@ public class SiblingsMediaTreeController : MediaTreeControllerBase
{
}
[ActivatorUtilitiesConstructor]
public SiblingsMediaTreeController(
IEntityService entityService,
FlagProviderCollection flagProviders,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
IDataTypeService dataTypeService,
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IMediaPresentationFactory mediaPresentationFactory)
: base(entityService, flagProviders, userStartNodeEntitiesService, dataTypeService, appCaches, backofficeSecurityAccessor, mediaPresentationFactory)
{
}
[HttpGet("siblings")]
[ProducesResponseType(typeof(SubsetViewModel<MediaTreeItemResponseModel>), StatusCodes.Status200OK)]
public async Task<ActionResult<SubsetViewModel<MediaTreeItemResponseModel>>> Siblings(CancellationToken cancellationToken, Guid target, int before, int after, Guid? dataTypeId = null)
@@ -1,6 +1,8 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
@@ -9,11 +11,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.MediaType.Tree;
[ApiVersion("1.0")]
public class AncestorsMediaTypeTreeController : MediaTypeTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public AncestorsMediaTypeTreeController(IEntityService entityService, IMediaTypeService mediaTypeService)
: base(entityService, mediaTypeService)
{
}
[ActivatorUtilitiesConstructor]
public AncestorsMediaTypeTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IMediaTypeService mediaTypeService)
: base(entityService, flagProviders, mediaTypeService)
{
}
[HttpGet("ancestors")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(IEnumerable<MediaTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,7 +1,9 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
@@ -10,11 +12,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.MediaType.Tree;
[ApiVersion("1.0")]
public class ChildrenMediaTypeTreeController : MediaTypeTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public ChildrenMediaTypeTreeController(IEntityService entityService, IMediaTypeService mediaTypeService)
: base(entityService, mediaTypeService)
{
}
[ActivatorUtilitiesConstructor]
public ChildrenMediaTypeTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IMediaTypeService mediaTypeService)
: base(entityService, flagProviders, mediaTypeService)
{
}
[HttpGet("children")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<MediaTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,9 +1,12 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Controllers.Tree;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Services;
@@ -19,8 +22,17 @@ public class MediaTypeTreeControllerBase : FolderTreeControllerBase<MediaTypeTre
{
private readonly IMediaTypeService _mediaTypeService;
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public MediaTypeTreeControllerBase(IEntityService entityService, IMediaTypeService mediaTypeService)
: base(entityService) =>
: this(
entityService,
StaticServiceProvider.Instance.GetRequiredService<FlagProviderCollection>(),
mediaTypeService)
{
}
public MediaTypeTreeControllerBase(IEntityService entityService, FlagProviderCollection flagProviders, IMediaTypeService mediaTypeService)
: base(entityService, flagProviders) =>
_mediaTypeService = mediaTypeService;
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.MediaType;
@@ -1,7 +1,9 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
@@ -10,11 +12,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.MediaType.Tree;
[ApiVersion("1.0")]
public class RootMediaTypeTreeController : MediaTypeTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public RootMediaTypeTreeController(IEntityService entityService, IMediaTypeService mediaTypeService)
: base(entityService, mediaTypeService)
{
}
[ActivatorUtilitiesConstructor]
public RootMediaTypeTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IMediaTypeService mediaTypeService)
: base(entityService, flagProviders, mediaTypeService)
{
}
[HttpGet("root")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<MediaTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,6 +1,8 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
@@ -8,11 +10,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.MediaType.Tree;
public class SiblingsMediaTypeTreeController : MediaTypeTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public SiblingsMediaTypeTreeController(IEntityService entityService, IMediaTypeService mediaTypeService)
: base(entityService, mediaTypeService)
{
}
[ActivatorUtilitiesConstructor]
public SiblingsMediaTypeTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IMediaTypeService mediaTypeService)
: base(entityService, flagProviders, mediaTypeService)
{
}
[HttpGet("siblings")]
[ProducesResponseType(typeof(SubsetViewModel<MediaTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
public async Task<ActionResult<SubsetViewModel<MediaTypeTreeItemResponseModel>>> Siblings(
@@ -1,7 +1,8 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Controllers.Tree;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
@@ -15,10 +16,16 @@ namespace Umbraco.Cms.Api.Management.Controllers.MemberGroup.Tree;
[Authorize(Policy = AuthorizationPolicies.TreeAccessMemberGroups)]
public class MemberGroupTreeControllerBase : NamedEntityTreeControllerBase<NamedEntityTreeItemResponseModel>
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public MemberGroupTreeControllerBase(IEntityService entityService)
: base(entityService)
{
}
public MemberGroupTreeControllerBase(IEntityService entityService, FlagProviderCollection flagProviders)
: base(entityService, flagProviders)
{
}
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.MemberGroup;
}
@@ -1,20 +1,29 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Api.Management.Services.Flags;
using Microsoft.Extensions.DependencyInjection;
namespace Umbraco.Cms.Api.Management.Controllers.MemberGroup.Tree;
[ApiVersion("1.0")]
public class RootMemberGroupTreeController : MemberGroupTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public RootMemberGroupTreeController(IEntityService entityService)
: base(entityService)
{
}
[ActivatorUtilitiesConstructor]
public RootMemberGroupTreeController(IEntityService entityService, FlagProviderCollection flagProviders)
: base(entityService, flagProviders)
{
}
[HttpGet("root")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<NamedEntityTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -1,9 +1,12 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Controllers.Tree;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Services;
@@ -18,10 +21,20 @@ public class MemberTypeTreeControllerBase : NamedEntityTreeControllerBase<Member
{
private readonly IMemberTypeService _memberTypeService;
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public MemberTypeTreeControllerBase(IEntityService entityService, IMemberTypeService memberTypeService)
: base(entityService) =>
: this(
entityService,
StaticServiceProvider.Instance.GetRequiredService<FlagProviderCollection>(),
memberTypeService)
{
}
public MemberTypeTreeControllerBase(IEntityService entityService, FlagProviderCollection flagProviders, IMemberTypeService memberTypeService)
: base(entityService, flagProviders) =>
_memberTypeService = memberTypeService;
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.MemberType;
protected override MemberTypeTreeItemResponseModel[] MapTreeItemViewModels(Guid? parentKey, IEntitySlim[] entities)
@@ -1,20 +1,29 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.Services;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Api.Management.Controllers.MemberType.Tree;
[ApiVersion("1.0")]
public class RootMemberTypeTreeController : MemberTypeTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public RootMemberTypeTreeController(IEntityService entityService, IMemberTypeService memberTypeService)
: base(entityService, memberTypeService)
{
}
[ActivatorUtilitiesConstructor]
public RootMemberTypeTreeController(IEntityService entityService, FlagProviderCollection flagProviders, IMemberTypeService memberTypeService)
: base(entityService, flagProviders, memberTypeService)
{
}
[HttpGet("root")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<MemberTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
@@ -0,0 +1,28 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.Flags;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Api.Management.Controllers.MemberType.Tree;
public class SiblingMemberTypeTreeController : MemberTypeTreeControllerBase
{
public SiblingMemberTypeTreeController(
IEntityService entityService,
FlagProviderCollection flagProviders,
IMemberTypeService memberTypeService)
: base(entityService, flagProviders, memberTypeService)
{
}
[HttpGet("siblings")]
[ProducesResponseType(typeof(SubsetViewModel<MemberTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
public async Task<ActionResult<SubsetViewModel<MemberTypeTreeItemResponseModel>>> Siblings(
CancellationToken cancellationToken,
Guid target,
int before,
int after)
=> await GetSiblings(target, before, after);
}
@@ -1,6 +1,8 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Services.FileSystem;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.IO;
@@ -9,6 +11,20 @@ namespace Umbraco.Cms.Api.Management.Controllers.PartialView.Tree;
[ApiVersion("1.0")]
public class AncestorsPartialViewTreeController : PartialViewTreeControllerBase
{
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
[ActivatorUtilitiesConstructor]
public AncestorsPartialViewTreeController(IPartialViewTreeService partialViewTreeService)
: base(partialViewTreeService)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public AncestorsPartialViewTreeController(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
: base(partialViewTreeService, fileSystems)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public AncestorsPartialViewTreeController(FileSystems fileSystems)
: base(fileSystems)
{
@@ -1,15 +1,31 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.IO;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.FileSystem;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.IO;
namespace Umbraco.Cms.Api.Management.Controllers.PartialView.Tree;
[ApiVersion("1.0")]
public class ChildrenPartialViewTreeController : PartialViewTreeControllerBase
{
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
[ActivatorUtilitiesConstructor]
public ChildrenPartialViewTreeController(IPartialViewTreeService partialViewTreeService)
: base(partialViewTreeService)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public ChildrenPartialViewTreeController(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
: base(partialViewTreeService, fileSystems)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public ChildrenPartialViewTreeController(FileSystems fileSystems)
: base(fileSystems)
{
@@ -1,7 +1,8 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Controllers.Tree;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Services.FileSystem;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Web.Common.Authorization;
@@ -13,9 +14,28 @@ namespace Umbraco.Cms.Api.Management.Controllers.PartialView.Tree;
[Authorize(Policy = AuthorizationPolicies.TreeAccessPartialViews)]
public class PartialViewTreeControllerBase : FileSystemTreeControllerBase
{
public PartialViewTreeControllerBase(FileSystems fileSystems)
=> FileSystem = fileSystems.PartialViewsFileSystem ??
throw new ArgumentException("Missing partial views file system", nameof(fileSystems));
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
public PartialViewTreeControllerBase(IPartialViewTreeService partialViewTreeService)
: base(partialViewTreeService)
{
FileSystem = null!;
}
// FileSystem is required therefore, we can't remove it without some wizardry. When obsoletion is due, remove this.
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public PartialViewTreeControllerBase(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
: base(partialViewTreeService)
{
FileSystem = fileSystems.PartialViewsFileSystem ??
throw new ArgumentException("Missing scripts file system", nameof(fileSystems));
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public PartialViewTreeControllerBase(FileSystems fileSystems)
: base()
=> FileSystem = fileSystems.PartialViewsFileSystem ??
throw new ArgumentException("Missing scripts file system", nameof(fileSystems));
[Obsolete("Included in the service class. Scheduled to be removed in Umbraco 19.")]
protected override IFileSystem FileSystem { get; }
}
@@ -1,15 +1,31 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.IO;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.FileSystem;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.IO;
namespace Umbraco.Cms.Api.Management.Controllers.PartialView.Tree;
[ApiVersion("1.0")]
public class RootPartialViewTreeController : PartialViewTreeControllerBase
{
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
[ActivatorUtilitiesConstructor]
public RootPartialViewTreeController(IPartialViewTreeService partialViewTreeService)
: base(partialViewTreeService)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public RootPartialViewTreeController(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
: base(partialViewTreeService, fileSystems)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public RootPartialViewTreeController(FileSystems fileSystems)
: base(fileSystems)
{
@@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.FileSystem;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.IO;
namespace Umbraco.Cms.Api.Management.Controllers.PartialView.Tree;
public class SiblingsPartialViewTreeController : PartialViewTreeControllerBase
{
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
[ActivatorUtilitiesConstructor]
public SiblingsPartialViewTreeController(IPartialViewTreeService partialViewTreeService)
: base(partialViewTreeService)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public SiblingsPartialViewTreeController(IPartialViewTreeService partialViewTreeService, FileSystems fileSystems)
: base(partialViewTreeService, fileSystems)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public SiblingsPartialViewTreeController(FileSystems fileSystems)
: base(fileSystems)
{
}
[HttpGet("siblings")]
[ProducesResponseType(typeof(SubsetViewModel<FileSystemTreeItemPresentationModel>), StatusCodes.Status200OK)]
public async Task<ActionResult<SubsetViewModel<FileSystemTreeItemPresentationModel>>> Siblings(
CancellationToken cancellationToken,
string path,
int before,
int after)
=> await GetSiblings(path, before, after);
}
@@ -4,6 +4,7 @@ using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Controllers.Content;
using Umbraco.Cms.Api.Management.ViewModels.Item;
using Umbraco.Cms.Api.Management.ViewModels.RecycleBin;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Services;
@@ -45,6 +46,24 @@ public abstract class RecycleBinControllerBase<TItem> : ContentControllerBase
return Task.FromResult<ActionResult<PagedViewModel<TItem>>>(Ok(result));
}
protected async Task<ActionResult<SubsetViewModel<TItem>>> GetSiblings(Guid target, int before, int after)
{
IEntitySlim[] siblings = GetSiblingEntities(target, before, after, out var totalBefore, out var totalAfter);
if (siblings.Length == 0)
{
return NotFound();
}
IEntitySlim entity = siblings.First();
Guid? parentKey = GetParentKey(entity);
TItem[] treeItemViewModels = MapRecycleBinViewModels(parentKey, siblings);
SubsetViewModel<TItem> result = SubsetViewModel(treeItemViewModels, totalBefore, totalAfter);
return Ok(result);
}
protected virtual TItem MapRecycleBinViewModel(Guid? parentKey, IEntitySlim entity)
{
if (entity == null)
@@ -136,4 +155,27 @@ public abstract class RecycleBinControllerBase<TItem> : ContentControllerBase
private PagedViewModel<TItem> PagedViewModel(IEnumerable<TItem> treeItemViewModels, long totalItems)
=> new() { Total = totalItems, Items = treeItemViewModels };
protected SubsetViewModel<TItem> SubsetViewModel(IEnumerable<TItem> treeItemViewModels, long totalBefore, long totalAfter)
=> new() { TotalBefore = totalBefore, TotalAfter = totalAfter, Items = treeItemViewModels };
protected virtual IEntitySlim[] GetSiblingEntities(Guid target, int before, int after, out long totalBefore, out long totalAfter) =>
_entityService
.GetTrashedSiblings(
target,
[ItemObjectType],
before,
after,
out totalBefore,
out totalAfter,
ordering: Ordering.By(nameof(Infrastructure.Persistence.Dtos.NodeDto.Text)))
.ToArray();
/// <summary>
/// Gets the parent key for an entity, or root if null or no parent.
/// </summary>
protected virtual Guid? GetParentKey(IEntitySlim entity) =>
entity.ParentId > 0
? _entityService.GetKey(entity.ParentId, ItemObjectType).Result
: Constants.System.RootKey;
}
@@ -1,6 +1,8 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Services.FileSystem;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.IO;
@@ -9,6 +11,20 @@ namespace Umbraco.Cms.Api.Management.Controllers.Script.Tree;
[ApiVersion("1.0")]
public class AncestorsScriptTreeController : ScriptTreeControllerBase
{
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
[ActivatorUtilitiesConstructor]
public AncestorsScriptTreeController(IScriptTreeService scriptTreeService)
: base(scriptTreeService)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public AncestorsScriptTreeController(IScriptTreeService scriptTreeService, FileSystems fileSystems)
: base(scriptTreeService, fileSystems)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public AncestorsScriptTreeController(FileSystems fileSystems)
: base(fileSystems)
{
@@ -1,15 +1,31 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.IO;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.FileSystem;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.IO;
namespace Umbraco.Cms.Api.Management.Controllers.Script.Tree;
[ApiVersion("1.0")]
public class ChildrenScriptTreeController : ScriptTreeControllerBase
{
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
[ActivatorUtilitiesConstructor]
public ChildrenScriptTreeController(IScriptTreeService scriptTreeService)
: base(scriptTreeService)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public ChildrenScriptTreeController(IScriptTreeService scriptTreeService, FileSystems fileSystems)
: base(scriptTreeService, fileSystems)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public ChildrenScriptTreeController(FileSystems fileSystems)
: base(fileSystems)
{
@@ -1,15 +1,31 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.IO;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.FileSystem;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.IO;
namespace Umbraco.Cms.Api.Management.Controllers.Script.Tree;
[ApiVersion("1.0")]
public class RootScriptTreeController : ScriptTreeControllerBase
{
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
[ActivatorUtilitiesConstructor]
public RootScriptTreeController(IScriptTreeService scriptTreeService)
: base(scriptTreeService)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public RootScriptTreeController(IScriptTreeService scriptTreeService, FileSystems fileSystems)
: base(scriptTreeService, fileSystems)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public RootScriptTreeController(FileSystems fileSystems)
: base(fileSystems)
{
@@ -1,7 +1,8 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Controllers.Tree;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Services.FileSystem;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Web.Common.Authorization;
@@ -13,9 +14,28 @@ namespace Umbraco.Cms.Api.Management.Controllers.Script.Tree;
[Authorize(Policy = AuthorizationPolicies.TreeAccessScripts)]
public class ScriptTreeControllerBase : FileSystemTreeControllerBase
{
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
public ScriptTreeControllerBase(IScriptTreeService scriptTreeService)
: base(scriptTreeService)
{
FileSystem = null!;
}
// FileSystem is required therefore, we can't remove it without some wizardry. When obsoletion is due, remove this.
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public ScriptTreeControllerBase(IScriptTreeService scriptTreeService, FileSystems fileSystems)
: base(scriptTreeService)
{
FileSystem = fileSystems.ScriptsFileSystem ??
throw new ArgumentException("Missing scripts file system", nameof(fileSystems));
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public ScriptTreeControllerBase(FileSystems fileSystems)
: base()
=> FileSystem = fileSystems.ScriptsFileSystem ??
throw new ArgumentException("Missing scripts file system", nameof(fileSystems));
[Obsolete("Included in the service class. Scheduled to be removed in Umbraco 19.")]
protected override IFileSystem FileSystem { get; }
}
@@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Services.FileSystem;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.IO;
namespace Umbraco.Cms.Api.Management.Controllers.Script.Tree;
public class SiblingsScriptTreeController : ScriptTreeControllerBase
{
// TODO Remove the static service provider, and replace with base when the other constructors are obsoleted.
[ActivatorUtilitiesConstructor]
public SiblingsScriptTreeController(IScriptTreeService scriptTreeService)
: base(scriptTreeService)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public SiblingsScriptTreeController(IScriptTreeService scriptTreeService, FileSystems fileSystems)
: base(scriptTreeService, fileSystems)
{
}
[Obsolete("Please use the constructor taking all parameters. Scheduled to be removed in Umbraco 19.")]
public SiblingsScriptTreeController(FileSystems fileSystems)
: base(fileSystems)
{
}
[HttpGet("siblings")]
[ProducesResponseType(typeof(SubsetViewModel<FileSystemTreeItemPresentationModel>), StatusCodes.Status200OK)]
public async Task<ActionResult<SubsetViewModel<FileSystemTreeItemPresentationModel>>> Siblings(
CancellationToken cancellationToken,
string path,
int before,
int after)
=> await GetSiblings(path, before, after);
}
@@ -1,6 +1,8 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Services.FileSystem;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.IO;
@@ -9,11 +11,18 @@ namespace Umbraco.Cms.Api.Management.Controllers.StaticFile.Tree;
[ApiVersion("1.0")]
public class AncestorsStaticFileTreeController : StaticFileTreeControllerBase
{
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
public AncestorsStaticFileTreeController(IPhysicalFileSystem physicalFileSystem)
: base(physicalFileSystem)
{
}
[ActivatorUtilitiesConstructor]
public AncestorsStaticFileTreeController(IPhysicalFileSystem physicalFileSystem, IPhysicalFileSystemTreeService fileSystemTreeService)
: base(physicalFileSystem, fileSystemTreeService)
{
}
[HttpGet("ancestors")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(IEnumerable<FileSystemTreeItemPresentationModel>), StatusCodes.Status200OK)]

Some files were not shown because too many files have changed in this diff Show More