Compare commits

...
Author SHA1 Message Date
Andy Butland 0a98a47db0 Update Examine to 3.8.0. 2026-06-07 21:17:09 +02:00
Andy Butland 18a9da080a Bumped version to 13.15.0-rc 2026-04-30 08:07:45 +02:00
Andy Butland 6104efdd47 Merge branch 'release/13.14.0' into v13/dev 2026-04-30 08:06:57 +02:00
Andy Butland 7d400e7742 Bumped version to 13.14.0 2026-04-30 06:36:08 +02:00
Andy Butland 8295f833f8 Bump version to 13.14.0-rc3. 2026-04-23 16:40:12 +02:00
8d6645b0ef Surface controllers: validate redirect url in public surface controllers (v13) (#22565)
* Fix: prevent open redirect in public surface controllers by validating RedirectUrl with Url.IsLocalUrl

* Fixed spacing in comment.

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-04-23 16:39:31 +02:00
a0cb9b2826 Surface controllers: validate redirect url in public surface controllers (v13) (#22565)
* Fix: prevent open redirect in public surface controllers by validating RedirectUrl with Url.IsLocalUrl

* Fixed spacing in comment.

---------

Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-04-23 16:37:53 +02:00
Andy Butland b2ff910a92 Bumped version to 13.14.0-rc2. 2026-04-17 14:03:37 +02:00
GinoandAndy Butland b70d2b482d Dependencies: Pin System.Security.Cryptography.Xml to resolve vulnerability warning (Umbraco 13) (#22513)
* Update System.Security.Cryptography.Xml - fix vulnerability

* Update Directory.Packages.props

use correct/latest version of the system.security.cryptohraphy.xml package.

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

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-04-17 14:02:46 +02:00
ac22eef92f Dependencies: Pin System.Security.Cryptography.Xml to resolve vulnerability warning (Umbraco 13) (#22513)
* Update System.Security.Cryptography.Xml - fix vulnerability

* Update Directory.Packages.props

use correct/latest version of the system.security.cryptohraphy.xml package.

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

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-04-17 11:59:55 +00:00
24ef23e133 Backport #21910 to V13 (#22314)
* Backport #21910 to V13

* Update src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.ContentType.cs

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

* Backport #21856 as well

* Fix issue where removing a property type wasn't detecting and returning the expected change type.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2026-04-07 09:07:07 +02:00
Andy Butland c8fb306e67 Bumped version to 13.14.0-rc. 2026-03-12 06:46:44 +01:00
Andy Butland 667c2f5a47 Set version to 13.13.1. 2026-03-06 11:58:38 +01:00
Andy ButlandandGitHub 8230efd722 Media Picker: Fix media picker folder navigation with "ignore user start nodes" enabled (closes #21840 for 13) (#21899)
Fix media picker folder navigation with "ignore user start nodes" enabled.
2026-03-06 11:02:35 +01:00
Ronald BarendseandGitHub f420b0b894 Remove legacy Windows path length checks and related tests (#21884)
Removed explicit 260-character path length checks from PhysicalFileSystem.GetFullPath and deleted associated unit tests. Updated tests to focus on path normalization and validity, and improved path assertions for clarity and cross-platform compatibility. No longer enforce or test for legacy Windows path length restrictions.
2026-02-24 11:18:11 +01:00
Andy Butland b196ee89b7 Merge branch 'release/13.13' into v13/dev
# Conflicts:
#	version.json
2025-12-18 06:39:51 +01:00
Andy Butland b998afcd93 Merge branch 'release/13.13' of https://github.com/umbraco/Umbraco-CMS into release/13.13 2025-12-17 15:59:36 +01:00
Andy Butland 8c0bdb4b0b Bump version to 13.13.0. 2025-12-17 15:59:23 +01:00
66d090b4e3 Media picker: Fix image selection after upload when media picker presents multiple pages (closes #21115) (#21117)
* Fix image selection after upload when media picker presents multiple pages.

* Apply suggestions from code review

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-17 08:25:03 +01:00
Andy Butland b779123946 Members: Fix GetLockoutEnd to return DateTimeOffSet max rather than DateTime max (closes #21155) (#21157)
Fix GetLockoutEnd to return DateTimeOffSet max rather than DateTime max, to align with type of function result and avoid chance of overflow when timezone is minus UTC.
2025-12-16 07:04:56 +01:00
Andy ButlandandGitHub ed36b31576 Members: Fix GetLockoutEnd to return DateTimeOffSet max rather than DateTime max (closes #21155) (#21157)
Fix GetLockoutEnd to return DateTimeOffSet max rather than DateTime max, to align with type of function result and avoid chance of overflow when timezone is minus UTC.
2025-12-16 05:44:49 +00:00
Andy Butland 4871c0bab1 Merge branch 'release/13.13' into v13/dev
# Conflicts:
#	version.json
2025-12-11 07:17:23 +01:00
bfe567f075 Updates dependencies to latest patch for 13.13 (#21108)
* Update server-side dependencies to latest patch releases.

* Update client-side dependencies.

* Bumped version to 13.13.0-rc3.

* Reverted change to node version.

* Apply suggestions from code review

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-10 14:12:44 +00:00
Andy Butland 328c43c225 Merge branch 'release/13.12.1' into release/13.13
# Conflicts:
#	version.json
2025-12-09 06:25:00 +01:00
Andy Butland 56de595457 Merge branch 'release/13.12.1' into v13/dev
# Conflicts:
#	version.json
2025-12-09 06:24:24 +01:00
Andy ButlandandGitHub 7505efd433 Merge commit from fork
* Ensure imported dictionary item is only processed from the expected temporary uploads folder.

* Ensured content type upload input is only a file and not a string that can be interpretted as a file path.

* Amend dictionary import to extract file name and prepend path rather than rely on provided path.

* Tidied usings.

* Ensure file name cannot contain path separator characters.
2025-12-09 05:15:53 +01:00
Andy Butland 934126ae6e Bump version to 13.13.0-rc2. 2025-12-05 15:25:16 +01:00
Bjarne FyrstenborgandAndy Butland 632f04afd2 Rich text Editor: Ensure readonly attribute has been observed (#21004)
* Set toolbar and readonly when readonly attribute has been observed

* Simplify
2025-12-05 15:24:27 +01:00
Bjarne FyrstenborgandGitHub edae01a152 Rich text Editor: Ensure readonly attribute has been observed (#21004)
* Set toolbar and readonly when readonly attribute has been observed

* Simplify
2025-12-05 15:22:12 +01:00
d418f4f4d5 Media Picker: Present root nodes for users with more than one media root node assigned (closes #20967) (#20993)
* Presented root nodes in media picker for users with more than one media root node assigned.

* Apply suggestions from code review

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

* Improved variable names.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-01 13:11:34 +00:00
Andy ButlandandGitHub da677c42ed Media Picker Fixed folder navigation when returning from an empty folder (closes #20975) (#20987)
* Fixed media picker folder navigation when returning from an empty folder.

* Applied suggestions from code review.
2025-12-01 12:59:12 +00:00
Andy ButlandandGitHub 467db7736c Content picker: Remove multi-node content picker entries by UDI to account for index not aligning with stored values (#20950)
* Remove multi-node content picker entries by udi to account for index not aligning with stored values.

* Handled issues raised in code review.
2025-12-01 12:49:19 +00:00
Bjarne FyrstenborgandGitHub aced6916ff Content editing: Correct handling of read-only of name field when A action is removed in SendingContentNotification (#20072)
Set header name to readonly
2025-12-01 06:36:31 +00:00
Bjarne FyrstenborgandGitHub dc0005f394 Block Grid: Streamline readonly with other property editors (#21007)
* Align cursor style and readonly mode for other property editors

* Show disabled actions as in other property editors when readonly

* Disable sortmode as well when readonly

* Formatting

* Ensure delete action remains disabled if readonly
2025-12-01 05:53:28 +00:00
Andy ButlandandGitHub 0da1146fcd Content picker: Allow selections on a content picker on a read-only document to be opened (closes #20816) (#20819)
Allow selections on a content picker on a read-only document to be opened.
2025-11-24 16:50:28 +00:00
a7b4481c6c Redirects: Fix self referencing redirects for 13 (#20908)
* Ported fix to 13

* Mocking context and cache

* Removing unused parameter in constructor

* Removed a couple of unused variables in the tests.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-11-20 15:29:58 +00:00
Andy Butland 90ec85386b Merge branch 'release/13.12' into release/13.12.1
# Conflicts:
#	version.json
2025-11-20 06:47:36 +01:00
Andy Butland 9baaad40d1 Merge branch 'release/13.12' into v13/dev
# Conflicts:
#	version.json
2025-11-20 06:46:39 +01:00
Andy Butland 7dcc7303c2 Bumped version to 13.12.0. 2025-11-19 18:49:49 +01:00
Andy Butland 7edc6ec11d Bump version to 13.12.1. 2025-11-17 09:30:40 +01:00
Andy Butland f2589b5af5 Fix memory leak with IOptionsMonitor.OnChange and non-singleton registered components (closes #20709 for 13) (#20722)
* Fix memory leak with IOptionsMonitor.OnChange and non-singleton registered components.

* Added XML docs.

* Apply fix to DeliveryApiContentIndexingNotificationHandler.

* Dispose disposable data editors in ValueEditorCache.

* Removed unnecessary refactoring and clarified code comments.
2025-11-06 17:30:31 +01:00
Andy ButlandandGitHub 23b09b1a7e Fix memory leak with IOptionsMonitor.OnChange and non-singleton registered components (closes #20709 for 13) (#20722)
* Fix memory leak with IOptionsMonitor.OnChange and non-singleton registered components.

* Added XML docs.

* Apply fix to DeliveryApiContentIndexingNotificationHandler.

* Dispose disposable data editors in ValueEditorCache.

* Removed unnecessary refactoring and clarified code comments.
2025-11-06 15:02:57 +00:00
Andy Butland 951f9a9e46 Bump version to 13.12.0-rc2. 2025-11-06 10:41:41 +01:00
Andy Butland 8949fd2d22 Dependencies: Update dotnet sdk and node development dependency to latest secure version of current major (13) (#20729)
Update dotnet sdk and node development dependency to latest secure version of current major.
2025-11-05 21:25:44 +01:00
Andy ButlandandGitHub 5d6f4f2135 Dependencies: Update dotnet sdk and node development dependency to latest secure version of current major (13) (#20729)
Update dotnet sdk and node development dependency to latest secure version of current major.
2025-11-05 21:24:27 +01:00
Sven GeusensandAndy Butland 21520eebd6 Webhooks: Register OutputExpansionStrategy for webhooks if Delivery API is not enabled (closes #20272 for 13) (#20721)
V13 implementation off #20559 (Webhooks: Register OutputExpansionStrategy for webhooks if Delivery API is not enabled)
2025-11-03 19:11:05 +01:00
Sven GeusensandGitHub 87e20ceec6 Webhooks: Register OutputExpansionStrategy for webhooks if Delivery API is not enabled (closes #20272 for 13) (#20721)
V13 implementation off #20559 (Webhooks: Register OutputExpansionStrategy for webhooks if Delivery API is not enabled)
2025-11-03 19:10:14 +01:00
Laura NetoandGitHub f7b874d3ef Dependency track for V13 pipeline (#20702)
* Dependency track for V13 pipeline

* Rename bom-backend to bom-build
2025-10-31 16:03:14 +01:00
ArjanandAndy Butland dedc77c883 Backoffice Caching: Added cache buster for block list stylesheets (closes #20683) (#20686)
Fixed, added cache buster to Block List Editor stylesheets
2025-10-30 16:24:33 +01:00
ArjanandGitHub 8110b80318 Backoffice Caching: Added cache buster for block list stylesheets (closes #20683) (#20686)
Fixed, added cache buster to Block List Editor stylesheets
2025-10-30 16:23:37 +01:00
Andy Butland 9de905cba3 Bumped version to 13.13.0-rc. 2025-10-29 08:02:54 +01:00
Andy ButlandandGitHub 802fa70154 Notifications: Populate IncludeDescendants on ContentPublishedNotification when publishing branch (#20578)
Populate IncludeDescendants on ContentPublishedNotification when publishing branch.
2025-10-22 13:23:19 +02:00
bhcoderandGitHub ebf397702d Grid (Legacy): Fix for RTE drag and drop issue (closes #16591) (#20498)
* #16591- Fix RTE drag and drop issue in Grid Layout (Legacy)

* Change case for variable
2025-10-14 16:07:54 +02:00
388e5f9239 Dependencies: Fixed dependency conflicts when installing Microsoft.EntityFrameworkCore.Design (closes #17839) (#20480)
* Add explicit references to Microsoft.CodeAnalysis.* packages to fix conflicts when installing Microsoft.EntityFrameworkCore.Design

* 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 11:19:32 +02:00
Andy Butland d20cdd4930 Merge branch 'v13/dev' of https://github.com/umbraco/Umbraco-CMS into v13/dev 2025-10-09 07:58:28 +02:00
Andy Butland a604b2053e Merge branch 'release/13.11' into v13/dev
# Conflicts:
#	version.json
2025-10-09 07:58:11 +02:00
Andy Butland c177c305ba Bumped version to 13.11.0. 2025-10-09 06:40:37 +02:00
e840a0c7c4 Do not convert calculated lockedOutUntil time to UTC (#17007)
* Do not convert calculated lockedOutUntil time to UTC

* Handle conversion to UTC when we know we have server time values in IdentityMapDefinition for members.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-10-08 16:15:36 +02:00
c9d5c7f95b Repositories: v13 UserService.GetAllInGroup fix (#20425)
* Fix: Used FetchByGroups to batch queries to stop SQL limit bug

* Tidied up indentation.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-10-08 13:44:32 +00:00
Andy ButlandandGitHub 4c9d02e90b Configuration: Fixed integer overflow in setting MaxRequestBodySize and MultipartBodyLengthLimit (#20348)
Fixed integer overflow in setting MaxRequestBodySize and MultipartBodyLengthLimit.
2025-10-02 13:34:47 +02:00
Andy Butland d39395a1ff Bump version to 13.11.0-rc2. 2025-10-02 09:51:12 +02:00
Andy Butlandandkjac d45ddc315a Caching: Fixes regression of the caching of null representations for missing dictionary items (#20344)
* Fixed regression with handling of null representation in cache.

* Added integration test to verify behaviour.

* Use helper methods.

* Amend tests so they verify that cached null values are also removed

---------

Co-authored-by: kjac <kja@umbraco.dk>
2025-10-02 09:50:07 +02:00
7d87ce35bc Caching: Fixes regression of the caching of null representations for missing dictionary items (#20344)
* Fixed regression with handling of null representation in cache.

* Added integration test to verify behaviour.

* Use helper methods.

* Amend tests so they verify that cached null values are also removed

---------

Co-authored-by: kjac <kja@umbraco.dk>
2025-10-02 07:46:40 +00:00
mcl-szandGitHub d4805faee7 Prevent RTE being marked as dirty when content contains empty paragraph (#20335)
Prevent RTE being marked as dirty when content containt empty paragraph

In Tinymce, an empty paragraph is marked with a non-breaking space character.
When saving the content, the DOMParser replaces this empty non-breaking space character with an &nbsp; entity.
This causes the editor to always be dirty when the content contains an empty paragraph.
By making these the same befor the comparison, this is prevented.
2025-10-01 16:05:57 +02:00
3deccdbcf3 Dependencies: Update ImageSharp to latest patch releases (13) (#20275)
Update ImageSharp dependencies.

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-09-29 08:09:01 +00:00
9ad24ac2a7 [v13] Media Picker: implement paging using getPagedChildren (no server changes) (#20202)
* Avoid throwing an exception on getting references when migrating content with changed data types.

* Revert and handle exception at the consumer side

* Clean up

* Fix issue 12364

fix bug for Media Picker is slow when you have a large number of images at the root folder #12364

* Revert "Fix issue 12364"

This reverts commit 4d867345eb.

* Media Picker: use getPagedChildren with paging & sorting

Replaces folder view calls to entityResource.getChildren with
entityResource.getPagedChildren in MediaPickerController.

- Adds orderBy/Direction and increases default pageSize to 200
- Resets pagination when entering folders or clearing search
- Updates changePagination to work for both search and folder views
- Keeps legacy behaviour for searchMedia (no breaking server changes)

* Update mediapicker.controller.js

* Update mediapicker.controller.js

* Update mediapicker.controller.js

* Update mediapicker.controller.js

* Fix default values for paging.

* Refactor Media Picker controller: simplify pagination and clarify comments

- Removed unnecessary helper methods (pickPositive / pickNonNegative) and assign pagination values directly,
  as backend always provides valid positive/ non-negative numbers.
- Renamed or removed review-only comments to keep codebase clean.
- Clarified purpose of resetting `vm.searchOptions.filter` to explain why the filter is cleared after loading items.
- Adjusted indentation and minor formatting for consistency.

* Media Picker: fix lost paging on reopen by chaining navigation promises

Ensures the Media Picker always loads a paged result when restoring the last
visited folder.

- run(), ensureWithinStartNode(), gotoStartNode(), and gotoFolder() now return
  and chain the same promise flow.
- gotoFolder() resolves path → sets current folder → resets pagination →
  calls getChildren() (which uses getPagedChildren).
- Fixed cases where reopening the picker showed all items with no pager.
- Kept existing UX: filter is cleared on folder navigation to start unfiltered.
- Minor indentation/formatting cleanups.

* Tidied up indentation.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-09-26 07:34:15 +00:00
Andy Butland 0751728b05 Merge branch 'release/13.10.1' into release/13.11
# Conflicts:
#	version.json
2025-09-23 07:08:09 +02:00
Andy Butland e504011b76 Merge branch 'release/13.10.1' into v13/dev
# Conflicts:
#	version.json
2025-09-23 07:06:44 +02:00
Andy ButlandandGitHub 4d9ea1e2e3 Property editors: Ensure values no longer allowed as options in checkbox list configuration are removed from the value, such that they will be removed from the persisted data when saved (#20208)
Ensure values no longer allowed as options in checkbox list configuration are removed from the value, such that they will be removed from the persisted data when saved.
2025-09-22 10:48:36 +02:00
6796829908 Querying extensions: Allow ContentAtRoot() to accept culture (#20129)
* Allow ContentAtRoot to accept culture

* Update comments about old TypedContentAtRoot

* Remove old method as new is optional parameter

* Change binary breaking change

* Obsolete methods

* Reverted to non-optional overloads.

* Used nullable string.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-09-22 08:27:41 +00:00
67f7d99696 Allow fetching multiple content and media by Guid and Udi (#15289)
* Allow fetching content by Guid and Udi

* Get media by Guid and Udi ids

* Fix array parameters

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

---------

Co-authored-by: Ronald Barendse <ronald@barend.se>
2025-09-20 18:56:55 +02:00
Rick ButterfieldandGitHub 27e3184550 Add support for UrlMode parameter in HtmlLocalLinkParser (#20200)
* Add support for `UrlMode` in `HtmlLocalLinkParser`

* Fix broken tests
2025-09-20 12:48:50 +02:00
Rick ButterfieldandGitHub c98fd7b219 UX: Fix overflow of umb-editor-header if the description is locked and too long (#20190)
Fix overflow of `umb-editor-header` if the description is locked and too long
2025-09-19 08:46:17 +02:00
Kenn JacobsenandGitHub 301be352ae Make notification service able to handle segmented content (#20160) 2025-09-17 06:51:10 +02:00
Andy Butland 7c180f6e4f Merge branch 'v13/dev' of https://github.com/umbraco/Umbraco-CMS into v13/dev
# Conflicts:
#	src/Umbraco.Core/Models/DeliveryApi/IApiContentRoute.cs
2025-09-16 12:22:58 +02:00
e119186e34 Avoid throwing an exception on getting references when migrating content with changed data types (#20079)
* Avoid throwing an exception on getting references when migrating content with changed data types.

* Revert and handle exception at the consumer side

* Clean up

---------

Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
2025-09-16 09:58:07 +00:00
Kenn JacobsenandGitHub a282002c30 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
2025-09-16 08:25:22 +02:00
Andy Butland a2c36d602f Bump version to 13.10.1. 2025-09-16 07:55:33 +02:00
Andy Butland a45917f132 Update Smidge dependencies. 2025-09-16 07:54:56 +02:00
Andy Butland fe358b8eab Merge branch 'v13/bugfix/18475-locallinks-parsing' of https://github.com/umbraco/Umbraco-CMS into v13/dev 2025-09-16 06:47:56 +02:00
kjac 60f84f7378 Support querystring and anchor for local links in Delivery API output 2025-09-15 19:54:00 +02:00
Andy ButlandandGitHub 45f7b7ad98 Retain original backoffice location on login after timeout (#19984)
Retain original backoffice location on login after timeout.
2025-09-15 17:53:17 +00:00
85aa6d3b57 Add Arabic (ar) backoffice translation (#19896)
* feat: Add Arabic (ar) backoffice translation

* Make Arabic language general until having special words for other
countries.

* Corrected the language header

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-08-15 09:06:51 +00:00
Andy Butland 33e201c4b5 Merge branch 'release/13.10' into v13/dev
# Conflicts:
#	version.json
2025-08-14 09:48:50 +01:00
Andy Butland c2890e15c3 Bumped version to 13.10.0 2025-08-14 07:39:22 +01:00
784e09e240 Use a regex to filter our invalid culture codes rather than relying on the culture being installed on the operating system (#19821)
* Use a regex to filter our invalid culture codes rather than relying on the culture being installed on the operating system.

* Update to more restrictive regex

Co-authored-by: Nuklon <Nuklon@users.noreply.github.com>

---------

Co-authored-by: Nuklon <Nuklon@users.noreply.github.com>
2025-08-06 10:34:08 +02:00
214f3fbc64 Umbraco Engage UmbracoUrlAlias Fix - Fixes #19654 (#19827)
* Fixes #19654

Adds the propertyAlias to the VariationContext so that products implementing the GetSegment method are aware which propertyAlias it's being called for

* Re-implement original variation context for backwards compatibility

* Fixes hidden overload method

Ensures the `GetSegment` method overload is not hidden when a null `propertyAlias` is passed.

* Resolve backward compatibility issues.

* Improved comments.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-08-04 11:40:00 +02:00
b622e7e4fb Content picker search with start node configured not taking user start nodes into account (#19800)
* Fix users being able to see nodes they don't have access to when using the picker search

* Readability and naming improvements

* Additional fixes

* Adjust tests

* Additional fixes

* Small improvement

* Replaced the root ids with constants

* Update src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs

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

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-08-01 11:50:14 +02:00
Andy Butland a352c42742 Merge branch 'release/13.9.3' into release/13.10
# Conflicts:
#	version.json
2025-07-29 07:17:06 +02:00
Andy Butland 8eff28b80c Merge branch 'release/13.9.3' into v13/dev
# Conflicts:
#	version.json
2025-07-29 07:16:23 +02:00
9f37db18d1 Merge commit from fork
Co-authored-by: kjac <kja@umbraco.dk>
2025-07-29 05:10:52 +02:00
0d94f8bfaf Fix issue with use of EF Core scopes within notification handlers (take 2 - handling scopes with a base parent) (#19797)
* Add integration tests that shows the problem

* Fix the problem and add explenation

* Improved comments slightly to help when we come back here!
Moved tests alongside existing ones related to scopes.
Removed long running attribute from tests (they are quite fast).

* Fixed casing in comment.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-07-25 13:09:27 +02:00
AaronandGitHub 509a2e18d0 The filter param should be urlencoded (#19774) 2025-07-23 11:54:22 +02:00
Andy Butland 22f748161a Bumped version to 13.11.0-rc. 2025-07-22 10:11:54 +02:00
Andy ButlandandGitHub 59ad07209b Retrieve only user external logins when invalidate following removal of backoffice external user login (#19766)
* Retrieve only user external logins when invalidate following removal of backoffice external user login.

* Improved variable name.
2025-07-22 07:50:05 +00:00
Kenn JacobsenandGitHub 67abecc252 Add defensive coding to the member application initializer (#19760) 2025-07-21 12:24:51 +02:00
Andy Butland ebd0017f6e Bumped version to 13.9.3. 2025-07-21 11:53:33 +02:00
Andy ButlandandGitHub 417f15197e Parse update date before sorting in media list view (#19711)
* Parse update date before sorting in media list view.

* Moved function placement.
2025-07-16 09:45:17 +01:00
ce40103c4f Add support for programmatic creation of property types providing the data type key (#19720)
* Add support for programmatic creation of property types providing the data type key.

* Add integration tests

---------

Co-authored-by: kjac <kja@umbraco.dk>
2025-07-15 13:57:48 +02:00
Andy ButlandandGitHub 2748fdfc48 Adds variation by the header name Accept-Language to the delivery API output cache policy (#19709)
* Adds variation by the header name Accept-Language to the develivery API output cache policy

* Removed obsolete constructor (not necessary as the class is internal).

* Introduce contants for header names.
2025-07-11 15:51:05 +02:00
Andy ButlandandGitHub 53cc663bde Register no-op implementation of IMemberPartialViewCacheInvalidator in headless setups (#19666)
* Register no-op implementation of IMemberPartialViewCacheInvalidator in headless setups.

* Tidied usings.
2025-07-08 15:40:53 +02:00
Andy ButlandandGitHub 13a2cd71c4 Clear member cache by older user name when member user name is updated. (#19672)
* Clear member cache by older user name when member user name is updated.

* Added unit test.
2025-07-07 14:01:36 +02:00
kowsandGitHub a60ccd389b #16772 partial fix backoffice redirect after login (#19663)
* #16772 partial fix backoffice redirect after login

* #16772 partial fix backoffice OpenId redirect after login
2025-07-07 10:51:50 +02:00
Andy Butland fd95dc3915 Merge branch 'release/13.9.2' into v13/dev
# Conflicts:
#	version.json
2025-07-02 06:29:04 +02:00
Andy ButlandandGitHub 990e379ea8 Ensures that null values aren't used to create a CompositeStringStringKey (#19646)
Ensures that null values aren't used to create a CompositeStringStringKey.
2025-07-01 12:11:04 +00:00
1f5c21c631 Fix pagination in Content Delivery API Index Helper (#19606)
* Refactor descendant enumeration in DeliveryApiContentIndexHelper

Improved loop condition to allow for processing of more than 10.000 descendants for indexing.

* Add failing test for original issue.

* Renamed variable for clarity.

---------

Co-authored-by: Brynjar Þorsteinsson <brynjar@vettvangur.is>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-07-01 08:11:00 +02:00
Kenn JacobsenandGitHub b4144564c8 Merge commit from fork 2025-06-24 08:39:17 +02:00
4ad18dc963 Preserve word boundaries when indexing RTE content with <br> tags (#19540)
* Preserve word boundaries when indexing RTE content with <br> tags

Replace <br> tags with spaces before HTML stripping to prevent word
concatenation in Examine index. Fixes issue where "John Smith<br>Company ABC"
was indexed as "John SmithCompany ABC" instead of "John Smith Company ABC".

- Add regex to replace <br> variants with spaces in RichTextPropertyIndexValueFactory
- Handles <br>, <br/> with spaces and attributes
- Maintains existing StripHtml() behavior for all other HTML tags

* Added unit test with test cases for expected index values

* Regex tweak to avoid matches on <break> <branything> etc

* Tweaked tests as per PR feedback

* Update tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/RichTextPropertyIndexValueFactoryTests.cs

Updated tests

Co-authored-by: Kenn Jacobsen <post@kennjacobsen.dk>

* Tweaked test expectations

---------

Co-authored-by: Emma Garland <emma.garland@rocksolidknowledge.com>
Co-authored-by: Emma L Garland <emmagarland77@gmail.com>
Co-authored-by: Kenn Jacobsen <post@kennjacobsen.dk>
2025-06-23 09:34:51 +00:00
Andy Butland 881535af7d Merge branch 'v13/dev' into v13/main 2025-06-13 07:20:11 +02:00
Laura NetoandGitHub 1e66fb6ab3 Add a warning to the CreateContentFromBlueprint method xml docs (#19542)
Add a remark to `ContentService.CreateContentFromBlueprint`
2025-06-13 07:15:19 +02:00
Andy Butland e1b9e5efad Merge branch 'release/13.9.1' into v13/dev
# Conflicts:
#	version.json
2025-06-10 09:56:08 +02:00
Andy Butland f6dbe0f33e Bumped version to 13.9.2. 2025-06-10 08:33:39 +02:00
Andy ButlandandGitHub 28570b43e8 Fix issues with removal of user logins on change to external login provider configuration (13) (#19511)
* Ensure to delete related tokens when removing logins for removed external login providers.
Ensure to avoid removing logins for members.

* Applied suggestions from code review.

* Removed unnecessary <= check.
2025-06-10 08:19:52 +02:00
Andy Butland a01382d756 Merge branch 'release/13.9' into v13/dev 2025-06-05 10:00:12 +02:00
Andy Butland 1d6e7f15b9 Bumped version to 13.9.1. 2025-06-05 07:47:57 +02:00
Andy Butland 97cc3ca581 Bumped version to 13.9.0. 2025-06-05 06:24:08 +02:00
Sven GeusensandGitHub 0bcae3e87b Fix null member edgecase (#19463) 2025-06-03 03:32:03 +00:00
ebd228c3d7 Ensure tag operations are case insensitive on insert across database types (#19439)
* Ensure tag operations are case insensitve on insert across database types.

* Ensure tags provided in a single property are case insensitively distinct when saving the tags and relationships.

* Update src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TagRepository.cs

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

* Handle case sensitivity on insert with tag groups too.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-30 09:05:22 +02:00
Andy ButlandandGitHub 4b83a74bdb Resolved exception thrown from NuCache locking mechanism on near-simultaneous content publish requests (#19434)
* Removed the check before the wait that threw the recursive lock exception.
Added additional check to ensure we don't release a lock that's already released.

* Removed unnecessary check on releasing the lock.
2025-05-28 14:33:47 +02:00
Rowan BottemaandGitHub b348b84b63 Lock appropriate tree for media operations (#19422)
The MediaService currently locks the ContentTree for GetPagedOfType(s) operations, but it's querying the MediaTree. This ensures we lock the correct tree.
2025-05-27 06:51:30 +02:00
Andy Butland 7f4a8d5974 Merge branch 'v13/dev' into v13/main 2025-05-27 06:49:05 +02:00
Andy ButlandandGitHub 4d8ca457ec Removes unnecessary newlines from rich text as JSON delivery API output (#19391)
* Removes unnecessary newlines from rich text as JSON delivery API output.

* Fix case from PR feedback.
2025-05-23 12:19:43 +02:00
Andy ButlandandGitHub d677e948f1 Fix issue with requests to delivery API by path where URL segment contains special characters (#19390)
Fix issue with requests to delivery API by path where URL segment contains special characters.
2025-05-23 09:41:15 +02:00
Andy Butland d4e6af50bd Merge branch 'release/13.9' into v13/dev 2025-05-22 10:15:39 +02:00
Andy Butland c609cafa9b Fixed check for navigation to list after delete of member (#19364)
Fixed check for navigation to list after delete of member.
2025-05-22 10:14:52 +02:00
Andy ButlandandGitHub 5556b0fe0c Fixed check for navigation to list after delete of member (#19364)
Fixed check for navigation to list after delete of member.
2025-05-22 10:13:31 +02:00
Andy Butland eee04f4703 fix: check for NullRepresentationInCache in AppCacheExtensions (#19350)
* fix: add appcache null check

* Moved constant into standard location.
Removed now unnecessary comment.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
# Conflicts:
#	src/Umbraco.Core/Cache/AppCacheExtensions.cs
#	src/Umbraco.Core/Constants-Cache.cs
2025-05-22 08:58:37 +02:00
127c6c4b53 Optimize the member save as part of the member login process, by-passing locking and audit steps and handling only the expected update properties (#19308)
* Optimize the member save as part of the member login process, by-passing locking and audit steps and handling only the expected update properties.

* Added unit test to verify new behaviour.

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

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

* Updates from code review.

* Improved comments.

* Add state information to notification indicating whether a member is saved via only the update of login properties.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Migaroez <geusens@gmail.com>
2025-05-20 15:27:42 +01:00
7d6a1e54e6 Optimize the member save as part of the member login process, by-passing locking and audit steps and handling only the expected update properties (#19308)
* Optimize the member save as part of the member login process, by-passing locking and audit steps and handling only the expected update properties.

* Added unit test to verify new behaviour.

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

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

* Updates from code review.

* Improved comments.

* Add state information to notification indicating whether a member is saved via only the update of login properties.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Migaroez <geusens@gmail.com>
2025-05-20 13:21:23 +00:00
David ChallenerandGitHub 8433b2b637 User invite email fails if visited more than once without completing (#17901)
Don't change securitystamp until user created
2025-05-14 10:03:53 +01:00
87b2153794 Adds support for retrieval of data type references when data type is routed using a GUID (#19184)
* Adds support for retrieval of data type references when data type is routed using a GUID.

* Fixed typos in comments.

* Use IIdKeyMap to resolve ID instead of fetching datatype

* Use IDKeyMap instead

---------

Co-authored-by: mole <nikolajlauridsen@protonmail.ch>
2025-05-14 10:54:00 +02:00
Andy ButlandandGitHub 0fc5b2a95c Avoids unnecessary additional domain save notification publishing when sorting an already sorted collection of domains (#19106)
Avoids unnecessary additional domain save notification publishings when sorting an already sorted collection of domains.
2025-05-14 10:25:49 +02:00
Andreas ZerbstandGitHub 6c2f93e85d V13 QA Updated linux test images to match actual (#19309)
Updated linux images
2025-05-14 07:19:32 +00:00
4f1604fea1 V13/bugfix/partial cache (#19314)
* Make sure that each optional section of the cachekey always starts and ends with a -

* Move secondary logic of clearing the membercaches into its own replacable class

* Regsiter the new implementation

* Add a mock to the integration tests as appCaches are disabled

* Added header comments to components.

* Refactored cache key into a method and exposed for testing.
Added unit tests to verify behaviour.

* Verified also that regex matches only the supplied member and asserted on the key itself.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-05-14 08:46:07 +02:00
059766291b Fix for block grid styling regression issue #15973 (#19014)
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-05-13 06:36:20 +02:00
e471c1fc8b [V13] User notifications not sent correctly when having more than 400 users (#18370)
Co-authored-by: Vedran Antović <vedran.antovic@isatis.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-05-13 06:35:42 +02:00
0fb91ef859 add single blocklist delete (#18073)
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-05-12 10:36:39 +00:00
Andy Butland c0a0a34cbb Backport variable name updates and fix found porting the invalidation of sessions after removal of external login provider to 16. 2025-05-09 14:57:08 +02:00
Andy ButlandandGitHub 3d44a6fdf9 Invalidate external login session on removal of provider (#19273) 2025-05-09 10:22:13 +00:00
0eee307622 Added custom RichTextRegexValidator to validate markup instead of JSON (#19045)
Co-authored-by: Migaroez <geusens@gmail.com>
2025-05-08 14:51:06 +02:00
6a4aa04b47 Handle existing RichTextEditorValue when parsing from markup or JSON structure (#19266)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-08 10:29:40 +02:00
643f073945 TimedScope improvements and login duration clean-up (#19243)
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-05-07 18:30:49 +02:00
Andy Butland 5ba40c65fc Merge branch 'release/13.8.1' into v13/dev
# Conflicts:
#	version.json
2025-05-06 07:13:40 +02:00
Andy Butland dcbbed4160 Fixed failures in unit tests. 2025-05-06 05:35:21 +02:00
Andy Butland e94e165593 Fixed pipeline definition. 2025-05-06 05:17:05 +02:00
Andy ButlandandGitHub 34709be6cc Merge commit from fork
* Backport user enumeration fix.

* Bump gitversion.

* Remove full path details from exception when requesting a path outside of the physical file system's root.

* Adds randomness to failed login duration.
2025-05-06 05:11:04 +02:00
83107bb31a Fix: #18421 - Added Max Length validation to PropertyTypeBasic Alias (#18427)
Co-authored-by: Daniel Nelson <daniel.nelson@cantarus.com>
2025-05-05 15:11:47 +02:00
Kenn JacobsenandGitHub 3caa43a5bf Fix root path regression for the Delivery API (#19216) 2025-05-03 15:26:20 +02:00
1efe860a89 V13: Clear Member Username Cache in Load Balanced Environments (#19191)
* Clear usernamekey

* Odd explaining comment

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

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

* Make UserNameCachePrefix readonly for better immutabilityly

* Move prefix to CacheKeys constants

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-29 18:03:28 +02:00
Andy Butland d49965549c Merge branch 'v13/contrib' into v13/dev 2025-04-29 14:26:00 +02:00
Dave WoestenborghsandGitHub 8d85259025 Upgrade examine to 3.7.1 (#19186) 2025-04-29 14:15:27 +02:00
Andreas ZerbstandGitHub 097d0456cc V13 QA updated pipeline for acceptance to avoid issue when installing playwright (#19140)
* Updated pipeline to install only the chromium browser

* Added junit as reporter for acceptance tests
2025-04-29 11:48:38 +00:00
mole a62fa93c77 Use windows agent for nuget push 2025-04-28 10:58:29 +02:00
Andy Butland a22dcf05f2 Merge branch 'v13/contrib' into v13/dev 2025-04-28 06:46:10 +02:00
afa6fa5ca2 attempted fix for Save and Preview button #15360 (#19138)
Co-authored-by: Navya Sinha <navya.sinha@method4.co.uk>
2025-04-25 21:28:46 +02:00
c6effef622 Handle file paths as not found in delivery API by route requests (#19063)
* Handle file paths as not found in delivery API by route requests.

* Move check earlier to handle redirect logic as well.

* Spelling: Changed "resolveable" to "resolvable"

---------

Co-authored-by: kjac <kja@umbraco.dk>
2025-04-22 06:46:33 +02:00
Andy Butland ab31fbb0aa Bump version to 13.8.1. 2025-04-17 11:14:17 +02:00
Andy Butland 7d9a2c6e70 Merge branch 'release/13.8.0' into v13/dev
# Conflicts:
#	version.json
2025-04-10 11:19:32 +02:00
Andy Butland a486d5df33 Bumped version to 13.8.0. 2025-04-10 07:38:20 +02:00
Andy Butland 3c8a621817 Merge branch 'v13/dev' into v13/contrib 2025-04-04 08:01:38 +02:00
Andy Butland 0e0aca55af Verify endpoint selection candidates with host attribute are ignored if request doesn't match the configured hosts. (#18820) 2025-04-04 06:54:56 +02:00
0f02584143 attempted fix for Datepicker v13 issue #16008 (#18903)
Co-authored-by: Navya Sinha <navya.sinha@method4.co.uk>
2025-04-04 03:52:48 +02:00
a3db45609a Move database cache rebuild to a background task with polling (13) (#18922)
* Converts rebuild database cache operation to submit and poll.

* Update src/Umbraco.Web.UI.Client/src/views/dashboard/settings/publishedsnapshotcache.controller.js

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

* Handle HTTP error in status retrieval.

* Fixed test build.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-03 21:58:56 +02:00
Andy ButlandandGitHub d60a2a217c Verify endpoint selection candidates with host attribute are ignored if request doesn't match the configured hosts. (#18820) 2025-04-03 21:13:45 +02:00
Andy Butland 3e9ff6b5cb Check we have matched a preview URL by ID when exiting preview. (#18841) 2025-04-03 10:39:47 +02:00
Andy ButlandandGitHub 05a7d337de Check we have matched a preview URL by ID when exiting preview. (#18841) 2025-04-03 10:32:27 +02:00
Jacob OvergaardandGitHub fdca086a47 build(deps): bump @umbraco-ui/uui from 1.12.2 to 1.13.0 (#18830) 2025-03-26 14:32:29 +00:00
Andreas ZerbstandGitHub 42a81beeac V13 hotfix sqlserver integration tests (#18744)
* Disabled encrypt

* Skips integration tests for SQl Server on releases

* Removed encrypt
2025-03-20 11:59:03 +01:00
Andy Butland 9284b9e0b1 Make preview check for delivery API content case insensitive. (#18731) 2025-03-20 06:58:57 +01:00
Andy ButlandandGitHub eb91f4fef4 Make preview check for delivery API content case insensitive. (#18731) 2025-03-20 06:53:43 +01:00
Andy Butland 68acc2aa51 Bumped version to 13.9.0-rc. 2025-03-19 16:39:34 +01:00
Andy ButlandandGitHub 5570583f70 Fixes issue with macro rendering in an RTE when GUIDs are used for backoffice document routes (#18691)
* Fixes issue with macro rendering in an RTE when GUIDs are used for backoffice document routes.

* Fixed null reference error.
2025-03-18 11:10:23 +01:00
Andy ButlandandGitHub eb979625d1 Render folders before files in static files picker. (#18701) 2025-03-18 11:05:54 +01:00
Andy ButlandandGitHub e1e5f527e4 Updates dependency on Examine to 3.7. (#18676) 2025-03-17 09:13:09 +01:00
Lan Nguyen ThuyandJacob Overgaard f016dbd223 Fix issue text overflow when user name is too long 2025-03-13 11:07:57 +01:00
Andy Butland b2aa04ae1a Merge branch 'release/13.7.2' into v13/dev
# Conflicts:
#	version.json
2025-03-12 12:18:45 +01:00
Andy ButlandandGitHub e270adc50f Reverts UI updates from publish with desdendants dialog. (#18647) 2025-03-12 08:10:34 +01:00
Andy Butland fd5b8de533 Bumped version to 13.7.2 2025-03-12 07:25:35 +01:00
Andy Butland e3d9b042c2 Avoids collection was modified issue when flowing identities to the authenticated user's principal. (#18527) 2025-03-12 07:24:50 +01:00
Andy Butland f4ae796b30 Merge branch 'v13/dev' of https://github.com/umbraco/Umbraco-CMS into v13/dev 2025-03-11 09:18:25 +01:00
Andy Butland 367d0386b6 Merge branch 'release/13.7.1' into v13/dev
# Conflicts:
#	version.json
2025-03-11 09:18:12 +01:00
5b54bed406 Merge commit from fork
* Fixed parsing of node if in content and media permission querystring handlers to retrieve expected value when multiple are provided in the querystring.

* Add HttpPost attributes to backoffice endpoints that should only accept post requests.

* Bumped version to 13.6.1.

* Narrow PermissionQueryString parsing to the releveant UmbracoObjectType

* Add missed update from v10

---------

Co-authored-by: Sven Geusens <sge@umbraco.dk>
2025-03-11 05:11:08 +01:00
Anders BjernerandMichael Latouche d3b60277d7 Fixed typo in TinyMCE's da.js 2025-03-10 15:38:50 +01:00
twoday-rolandkockandAndy Butland 2ec6ff4ebc bumped imagesharp to prevent CVE-2025-27598 (#18602) 2025-03-09 08:43:52 +01:00
twoday-rolandkockandGitHub 78a8d017a2 bumped imagesharp to prevent CVE-2025-27598 (#18602) 2025-03-09 08:41:06 +01:00
1a205d8727 Handle multiline statements in migrations (#18478)
* Handle multiline statements in migrations

* Fixed failing unit tests and incorrect obsoletion.

* Fixed failing integration tests.

* Applied suggestion from code review.

---------

Co-authored-by: Sven Geusens <sge@umbraco.dk>
2025-03-04 17:27:33 +01:00
Andy ButlandandGitHub 396b5ea211 Avoids collection was modified issue when flowing identities to the authenticated user's principal. (#18527) 2025-03-04 12:12:29 +01:00
Andy ButlandandGitHub 3cf9099f56 Remove version from models builder generated code header when configured to do so. (#18501) 2025-03-04 11:52:20 +01:00
Andy Butland 451b5f96e7 Use windows for build agent to avoid hanging issues with Linux. 2025-02-28 06:57:35 +01:00
Andy Butland 15ebae5025 Bumped version to 13.7.1. 2025-02-28 06:57:00 +01:00
Andy Butland 32bfbadb8e Merge branch 'release/13.7.0' into v13/dev
# Conflicts:
#	version.json
2025-02-27 06:47:15 +01:00
Kenn JacobsenandGitHub 5ed09ebefa Backport ShowUnroutableContentWarnings to V13 (#18479) 2025-02-26 20:13:27 +01:00
Andy Butland eaea7a67ba Bump version to 13.7.0. 2025-02-26 08:14:26 +01:00
Andy ButlandandGitHub de2114b8c5 Adds warning to publish descendants dialog when force re-publish is selected. (#18409) 2025-02-22 09:39:58 +01:00
Andy ButlandandGitHub 64607ddbaa Disable webhook firing if disable in configuration. (#18383) 2025-02-19 12:17:04 +01:00
Andy ButlandandGitHub db1d999721 Avoid exception when attempting to find member by Id when Id is not an expected Guid or integer, as can be the case with external member providers. (#18320) 2025-02-17 12:47:52 +01:00
Andy ButlandandGitHub 18047a7cfb Only filter post retrieval of entities for start nodes if working with entities that support start nodes. (#18287) 2025-02-17 12:45:51 +01:00
Andy ButlandandGitHub def7ebd48c Html encodes the user's name in the invite email. (#18343) 2025-02-17 12:25:12 +01:00
8c2b1ebdc5 V13: Introduce publishNotifications method on IMembershipMemberService (#18207)
* Introduce publishNotifications method on IMembershipMemberService.cs

* Fix test

* Add PublishNotificationSaveOptions

* Fix up according to comments

* Use numeric values for flag enum

* Update src/Umbraco.Core/Services/MemberService.cs

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

* Update src/Umbraco.Core/Services/MemberService.cs

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

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-02-12 13:30:41 +01:00
Andy ButlandandGitHub 9227517a50 Split force for publish descendants into separate options for publish unpublish and re-publish unedited (13) (#18249)
* Split force for publish descendents into separate options for publish unpublish and re-publish unedited.

* Added integration task verifying updated behaviour.

* Variant integration test.

* Update test data controller.

* Remove usued function parameters.

* Refactor to enum.

* Fixed flags enum.

* Variable name refactor.

* Applied changes from code review.

* Refactored method name.

* Aligned js boolean checks.
2025-02-12 11:30:27 +00:00
Andy Butland 5322d0f7b5 Bumped version to 13.8.0-rc. 2025-02-12 08:13:58 +01:00
Andy ButlandandGitHub a282cc5691 Backport use of thread delay over sleep and handle dispose in FileSystemMainDomLock (#18151)
* Backport use of thread delay over sleep and handle dispose in FileSystemMainDomLock (from PRs #18119 and #18147)

* Applied suggestion from code review.
2025-02-12 08:06:50 +01:00
Andy ButlandandGitHub 048f8bcdf9 Fixed userResource request to get all users. (#18105) 2025-02-11 17:06:18 +01:00
Andy ButlandandGitHub 7bcbc748d4 URL encodes member user names when passing information for public access setting such that those with user names as emails containing a plus will be included in the rule. (#18142) 2025-02-11 13:36:19 +01:00
Andy ButlandandGitHub f30e6cfe7f Prevents folder selection in media picker when used from the multi URL picker. (#18288) 2025-02-11 09:18:01 +01:00
Andy ButlandandGitHub 026e80e026 Avoid an exception on sign out when the principal is populated from an incomplete external login (#18078)
* Avoid an exception on signout when the principal is populated from an incomplete external login.

* Tidied up comment.
2025-02-10 12:51:35 +01:00
Andy ButlandandGitHub a3b77cff63 Add validation to prevent update of a user or member to an invalid username (13) (#18261)
* Add validation to prevent update of a user or member to an invalid username.

* Avoid password manager updates of user name field on user details screen.
2025-02-10 10:40:53 +01:00
Niels LyngsøandGitHub 4ca68d6995 Add client-side validation for RTE (#18257) 2025-02-07 11:43:04 +00:00
Sven GeusensandGitHub 280cb7f2b1 Fix issues in newly added buttongroup localization (#18254)
* Fix #18253 nullref exception

* Fix #18239 by listening to broader scope changes
2025-02-07 07:00:34 +01:00
095a73132c Review: Allow Duplicate Email for Members (#16202)
* init

* Aligned default values on security settings.

* Added validator for security settings.

* Provide default implementation for get members by email.

* Refactored constructor of MemberController.

* Validate on unique member email only when configured to do so.

* Further code tidy and use of DI in constructor.

* Used new constructor in tests.

* Add unit test for modified behaviour.

* Removed validator for security settings (it's not necessary, I got confused with users and members).

* Spelling.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-02-05 12:38:40 +01:00
Andy Butland cfb0fc23ac Merge branch 'v13/dev' into v13/contrib 2025-02-04 12:35:49 +01:00
6620aca9fe Cache null dictionary values by key (#15576)
* Add CacheNullValues option to RepositoryCachePolicy

* Cache null values in DictionaryByKeyRepository

* Fixed issue with nullable reference.

* Updated logic for caching of null values.

* Update src/Umbraco.Infrastructure/Cache/DefaultRepositoryCachePolicy.cs

Co-authored-by: Sven Geusens <geusens@gmail.com>

* Made the NullValueRepresentation overwritable in a generic manner

* Improve generic NullValueCachePolicyResolver

* Revert Commits and clarify logic with comment

This reverts commit 8befb43792 "Improve generic NullValueCachePolicyResolver"
Also reverts 8adf0a2 - Made the NullValueRepresentation overwritable in a generic manner
And 8adf0a2 - Made the NullValueRepresentation overwritable in a generic manner

* Update src/Umbraco.Infrastructure/Cache/DefaultRepositoryCachePolicy.cs

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Sven Geusens <geusens@gmail.com>
Co-authored-by: Sven Geusens <sge@umbraco.dk>
2025-02-04 12:29:21 +01:00
2422a02b23 Set Smidge cachebuster type (#18198)
* Set Smidge cachebuster type

* Amend exception and fix typo

* Minor tweak to comment and exception message.

---------

Co-authored-by: Simon Hartfield <simon.hartfield@googlemail.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-02-04 11:00:44 +00:00
b4a9dc0770 V13: Fix members while using basic auth. (#18206)
* Flow additional identities to new principal

* Add extension to more easily get member identity

* Ensure the member is used instead of the backoffice user in `MemberManager`

* Update snippet

* Fix the comment that I broke

* Update src/Umbraco.Web.Common/Extensions/MemberClaimsPrincipalExtensions.cs

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

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-02-03 19:48:08 +01:00
Andy ButlandandGitHub e7411244fd Show notifications menu only to users with permission for the feature. (#18184) 2025-02-03 13:24:58 +01:00
Kenn JacobsenandGitHub 9f357173c3 Enforce user start nodes for media uploads through the RTE (#18204) 2025-02-03 12:50:23 +01:00
Andy Butland 7dc6e3ed5b Merge branch 'v13/dev' into v13/contrib 2025-01-31 16:16:22 +01:00
Gareth WrightandGitHub 5dfff212d0 Update auth.element.ts (#18192)
auth_username => general_username
2025-01-31 14:59:25 +01:00
17615f966b Fix out of memory of 2gb+ (max 4gb) error introduced by #14657 SVG xss security fix (#17421)
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-01-31 13:46:56 +00:00
Kenn JacobsenandGitHub 7a2d6b6c63 More robust resolving of Delivery API redirects (#18160) 2025-01-31 14:26:07 +01:00
Sven Geusens 343867f6ee Merge branch 'release/13.6' into v13/dev and version bump
# Conflicts:
#	src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs
#	version.json
2025-01-30 16:27:49 +01:00
Bjarke BergandSven Geusens b9837ac77c Optimize Azure pipeline (#17674)
* Only fetch single commit

* Hopefully fixes Nerdbank.GitVersioning.GitException: Shallow clone lacks the objects required to calculate version height. Use full clones or clones with a history at least as deep as the last version height resetting change.

* Do not checkout again

* More test pipeline

* Another attempt

* yet another attempt

* more attempts

* Revert "more attempts"

This reverts commit 5694d97ba6.

* Test without building backoffice and login explicitly

* Fix mem leak in integration tests

* Fixes sqlserver lock test
# Conflicts:
#	build/azure-pipelines.yml
2025-01-30 14:10:05 +01:00
Sven Geusens bb73ec6c68 Set release version 2025-01-30 09:52:50 +01:00
Andy ButlandandGitHub ee2d7bbb1b Excluded tags from trashed content (#18164) 2025-01-29 21:10:11 +01:00
Andy ButlandandGitHub 5d48bc7371 Provides an option to remove the inessential version number from the generated models (#18081)
* Provides an option to remove the inessential version number from the generated models.

* Clarified comment.
2025-01-29 13:33:49 +01:00
Kenn JacobsenandGitHub 59a4649528 Do not allow editing read-only properties by clicking their labels (#18152)
* Do not allow editing read-only properties by clicking their labels

* Simplify the fix :)

* Fix linting issue
2025-01-29 10:29:58 +01:00
Andy ButlandandGitHub f54b6033ab Added Resharper test assemblies to exclude list on TypeFinder. (#18145) 2025-01-29 10:27:17 +01:00
Jacob OvergaardandGitHub bf340cd7d4 fix: remove unused parameters and documentation (#18095)
this fixes an issue where unused parameters were published as supported, but they were in fact never supported.
2025-01-28 10:53:00 +00:00
Andy Butland 313417cb91 Tidied up XML header comment in ITagQuery. 2025-01-28 10:52:21 +01:00
7850078623 Redirect to the published URL when exiting preview (#18114)
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-01-24 18:46:28 +00:00
44bf3b77b3 Make it possible to reset media picker crops (#18110)
Co-authored-by: Andy Butland <abutland73@gmail.com>
2025-01-24 13:41:30 +00:00
Kenn JacobsenandGitHub 8485458896 Add clientside validation to webhook events (#18089) 2025-01-23 17:01:55 +01:00
Nikolaj Geisle 3d253f5f06 bump version 2025-01-23 11:15:26 +01:00
Nikolaj Geisle 64287d7e1a Merge branch 'release/13.5.3' into release/13.6
# Conflicts:
#	version.json
2025-01-23 11:14:47 +01:00
Andy ButlandandGitHub edc78a5a4c Handles migration case where an expected constraint is renamed but the constraint does not exist. (#18063) 2025-01-22 12:26:06 +01:00
1a18d6c035 Replaced deprecated navigator.platform with navigator.userAgent for platform detection. (#17373)
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2025-01-21 13:56:40 +01:00
Andy Butland 3e877182b5 Merge branch 'v13/contrib' into v13/dev 2025-01-21 09:47:20 +01:00
Martin VennevoldandGitHub 0e4f883bc1 Fix create child issue in list view with infinite editor (#13355). (#17637) 2025-01-21 09:44:22 +01:00
Andy Butland 265cc62ab8 Merge branch 'release/13.6' into v13/dev 2025-01-21 09:21:36 +01:00
Andy Butland 800c16970e Merge branch 'release/13.6' of https://github.com/umbraco/Umbraco-CMS into release/13.6 2025-01-21 09:20:11 +01:00
Andy Butland 6bd11bf233 Fixes failing front-end unit test to align with new behaviour from PR #17818 (
Allow skipSelect blueprints only when one blueprint exists).
2025-01-21 09:19:31 +01:00
Kenn Jacobsen a2fd82a3f3 Add NoopCurrentMemberClaimsProvider so Umbraco can boot without the Delivery API enabled (#18049) 2025-01-21 09:13:31 +01:00
Kenn JacobsenandGitHub 7567990da1 Add NoopCurrentMemberClaimsProvider so Umbraco can boot without the Delivery API enabled (#18049) 2025-01-21 09:10:43 +01:00
Zeegaan c7d157bbfd build on ubuntu 2025-01-21 09:02:00 +01:00
Nikolaj Geisle 2161edb871 Bump version 2025-01-21 07:41:06 +01:00
Nikolaj Geisle e77e9c5691 Format sql statement (#17354)
(cherry picked from commit aa9f194d76)
2025-01-20 18:27:54 +01:00
Nikolaj Geisle e934a943b5 build on windows 2025-01-20 17:37:04 +01:00
Andy ButlandandGitHub 95eb58587b Merge commit from fork 2025-01-20 14:14:28 +01:00
25628a8b76 Lucene Package Update to Address CVE-2024-43383 (#17942)
* Update Lucene Package to 4.8.0-beta00017

* Add Package Reference

---------

Co-authored-by: Sebastiaan Janssen <sebastiaan@umbraco.com>
2025-01-20 11:45:35 +01:00
Callum WhyteandAndy Butland 7552e315fb Allow skipSelect blueprints only when one blueprint exists (#17818) 2025-01-20 11:45:26 +01:00
Callum WhyteandGitHub d15b5b221e Allow skipSelect blueprints only when one blueprint exists (#17818) 2025-01-20 10:27:54 +01:00
60e3fa97a9 Lucene Package Update to Address CVE-2024-43383 (#17942)
* Update Lucene Package to 4.8.0-beta00017

* Add Package Reference

---------

Co-authored-by: Sebastiaan Janssen <sebastiaan@umbraco.com>
2025-01-20 10:04:02 +01:00
Sven Geusens fd1bd51aff bump version 2025-01-16 10:10:04 +01:00
Sven Geusens b12620af9c Merge branch 'release/13.6' into v13/dev 2025-01-16 10:08:43 +01:00
Sven Geusens d1af59fdf0 Merge branch 'v13/dev' into release/13.6 2025-01-15 22:13:42 +01:00
Andreas ZerbstandGitHub 1b050ebc47 V13 QA added parallelization for E2E tests (#17995)
* Added script for running tests

* Added parallelization for our E2E tests

* Removed line

* Removed condition
2025-01-15 22:12:45 +01:00
Sven Geusens ff3e457c58 Merge branch 'v13/dev' into release/13.6 2025-01-15 20:55:41 +01:00
Andy ButlandandGitHub 2ffaede07b Added a maxlength setting to the regular expression pattern field. (#17982) 2025-01-15 20:54:54 +01:00
Sven GeusensandGitHub 2a83f57262 Do not re-map schedule modal on error as it leads to broken bindings (#17990) 2025-01-15 17:35:23 +01:00
Kenn JacobsenandGitHub 5be9193978 Do not show "Save and preview" by default (#17986) 2025-01-15 15:42:37 +01:00
Niels LyngsøandSven Geusens d3f264fc54 Fix: only fire event when instance is available (issue 17847) (#17987)
* only fire event when instance is available

* avoid js issue when no markup
2025-01-15 14:01:33 +01:00
Niels LyngsøandGitHub 04ea002bf3 Fix: only fire event when instance is available (issue 17847) (#17987)
* only fire event when instance is available

* avoid js issue when no markup
2025-01-15 13:56:53 +01:00
Kenn JacobsenandGitHub 8f42a3a0ea Make the RTE handle inline elements gracefully for JSON (#17983) 2025-01-15 11:26:03 +01:00
Kenn JacobsenandGitHub 4dd8fbe25d Enforce the "Save and preview" button after when switching content app (#17985) 2025-01-15 09:55:00 +01:00
Sven GeusensandGitHub ee727c43e1 Added localizations for composite key of missing New (media) Audit item (#17981) 2025-01-14 21:08:47 +01:00
Jacob OvergaardandGitHub 395213946b build(deps): update @microsoft/signalr from 7.0.12 to 8.0.7 (#17973) 2025-01-14 15:44:52 +00:00
Andy ButlandandGitHub ed0b236208 Resets send to approval dialog on close such that when re-opening the selection and operation can still be triggered. (#17956) 2025-01-14 16:25:26 +01:00
Kenn JacobsenandGitHub 8d56e75257 Add Vary header to all content API operations (#17962)
* Add Vary header to all content API operations

* Make attribute sealed
2025-01-14 15:54:19 +01:00
Jacob OvergaardandGitHub b3c27415d1 build(deps): bump @umbraco-ui/uui from 1.11.0 to 1.12.2 (#17972) 2025-01-14 15:48:34 +01:00
Justin NevilleandNikolaj Geisle 9839b0ee66 Fixes issue with the IVirtualPageController not setting PublishedRequest content via FindContent (#15121)
* Fixes issue https://github.com/umbraco/Umbraco-CMS/issues/12834

* Re-worked based on feedback

* Use dependency injection

* Only call new constructor

---------

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
(cherry picked from commit fd9c1a0e9c)
2025-01-14 13:57:00 +01:00
f3f57f9748 Improve YSOD rendering in the client (#17940)
* Conditionally render error details and make sure something is rendered

* V13 QA updated locator for failing E2E test (#17825)

* Added timeouts

* Nullcheck user in Content permission handlers (#17846)

---------

Co-authored-by: Sven Geusens <sge@umbraco.dk>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>

---------

Co-authored-by: Andreas Zerbst <73799582+andr317c@users.noreply.github.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2025-01-14 13:38:07 +01:00
Nikolaj GeisleandGitHub 85d16a4354 V13: Show validation error, when saving invalid media (#17961)
* Don't save invalid media

* Revert "Don't save invalid media"

This reverts commit c4b6acf542.

* Still allow save, but show validation error, not popup error

* Add clarifying comment
2025-01-14 11:18:36 +01:00
Kenn JacobsenandGitHub c0517c3562 Allow filtering for multiple content types (#17960) 2025-01-14 10:40:45 +01:00
92e535a1c9 V13 QA updated locator for failing E2E test (#17825)
* Added timeouts

* Nullcheck user in Content permission handlers (#17846)

---------

Co-authored-by: Sven Geusens <sge@umbraco.dk>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
2025-01-14 09:12:41 +01:00
Kenn JacobsenandGitHub 7411c2552b Added integration test to verify the Delivery API contract (#17949) 2025-01-13 15:15:37 +01:00
Andy ButlandandGitHub f2410f376e Corrected property names and structure used in JSON schema generation for configuration of default data to install. (#17946) 2025-01-13 11:05:51 +01:00
Kenn JacobsenandGitHub a9313c1367 Make the Delivery API "ancestors" selector work with preview (#17938) 2025-01-13 07:47:14 +01:00
Nikolaj Geisleandkjac 5b3b9d770a V13: Add rcl files to static files tree (#17671)
* Refactor to use IWebhostEnvironment for wwwroot files.

* Obsolete ctor

* Add ActivatorUtiiliesConstructor

* Refactor logic to include folders

* Fix so we can see files in wwwroot/App_Plugins

* Cosmetic: Use invariant comparison for the "umbraco" folder".

---------

Co-authored-by: kjac <kja@umbraco.dk>
2025-01-09 10:02:34 +01:00
4a54215fa1 V13: Add rcl files to static files tree (#17671)
* Refactor to use IWebhostEnvironment for wwwroot files.

* Obsolete ctor

* Add ActivatorUtiiliesConstructor

* Refactor logic to include folders

* Fix so we can see files in wwwroot/App_Plugins

* Cosmetic: Use invariant comparison for the "umbraco" folder".

---------

Co-authored-by: kjac <kja@umbraco.dk>
2025-01-09 09:36:32 +01:00
Sven Geusensandnikolajlauridsen 52fbb34ddd Nullcheck user in Content permission handlers (#17846) 2025-01-07 15:07:42 +01:00
Sven GeusensandGitHub c96dc7eaa1 Nullcheck user in Content permission handlers (#17846) 2025-01-07 14:52:40 +01:00
Jacob Overgaard f19409ba5b Revert "bump version"
This reverts commit 679c42ef90.
2025-01-06 12:51:10 +01:00
Jacob Overgaard 679c42ef90 bump version 2025-01-06 12:46:03 +01:00
Elitsa MarinovskaandGitHub 3304c6f472 Enable anonymous calls to HandleLogout action (#17807) 2024-12-16 07:26:12 +01:00
Nikolaj GeisleandGitHub 34f09fa260 V13: Clear username cache (#17815)
* Clear member username cache on delete

* Also refresh cache on update
2024-12-16 08:18:30 +02:00
Jacob Overgaard fd4efd38cd Merge branch 'v13/dev' into release/13.6 2024-12-13 10:07:46 +01:00
Jacob Overgaard e4e9bfc3e9 bump version to 13.6.0-rc2 2024-12-13 09:58:13 +01:00
Jacob Overgaard 3774dbe38c Merge remote-tracking branch 'origin/v13/contrib' into v13/dev 2024-12-13 09:56:46 +01:00
Dan ListerandJacob Overgaard 4ef50642ad Fixed an issue where bold markdown would not work inside a property description 2024-12-13 09:53:36 +01:00
Bjarke BergandGitHub f95fb78022 Merge pull request #17800 from umbraco/v13/bugfix/corrupt-examine-dashboard
Make sure Examine dashboard still functions when an index is corrupt
2024-12-12 12:07:06 +01:00
Sven Geusens 0f9a8bde61 Broadend the error catching when building the examine dashboard 2024-12-12 09:38:35 +01:00
Sven Geusens 129d369c1a Version bump to release 2024-12-11 13:46:38 +01:00
Sven Geusens dd2c591a5b Merge branch 'release/13.6' into v13/dev 2024-12-11 13:45:35 +01:00
Kenn JacobsenandNikolaj Geisle 16749a724d Add (un)publishing details to TreeChange notifications (#17757)
(cherry picked from commit 404a62aa0b)
2024-12-09 11:42:53 +01:00
Kenn JacobsenandNikolaj Geisle a627930b58 Warn about un-routable content at publish time (#17705)
(cherry picked from commit 2d9cfc880b)
2024-12-09 11:42:46 +01:00
Kenn JacobsenandGitHub 404a62aa0b Add (un)publishing details to TreeChange notifications (#17757) 2024-12-09 11:36:48 +01:00
Kenn JacobsenandGitHub 2d9cfc880b Warn about un-routable content at publish time (#17705) 2024-12-09 11:07:32 +01:00
Kenn JacobsenandGitHub ee8bdfc4ac Add "user info" endpoint for the Delivery API (#17719)
* Add "user info" endpoint for the Delivery API

* Add comment about public and unsealed class
2024-12-09 09:57:21 +01:00
Bjarke BergandGitHub b55d484863 Updated NuGet Packages for v13 (#17704)
* Update nuget packages

* Cherry pick fixes from v15

* Fixed: Projects that use central package version management should not define the version on the PackageReference items but on the PackageVersion items

* rollback update in tool
2024-12-03 08:51:23 +01:00
Ronald BarendseandGitHub ed0058a503 Fix PanicException: failed to get child with id= after updating content types (#17702)
* Remove tree node from hierarchy after clearing branch

* Refactor ClearBranchLocked to split clearing child and siblings
2024-12-03 08:00:18 +01:00
Jacob OvergaardandGitHub b0aed393d6 fix: keep the lead char when editing an existing link (#17667) 2024-11-28 14:04:59 +00:00
Sven Geusens a1ea203281 Merge branch 'v10/dev' into v13/dev
# Conflicts:
#	src/Directory.Packages.props
#	src/Umbraco.Infrastructure/Manifest/ManifestParser.cs
2024-11-26 21:08:22 +01:00
Kenn JacobsenandGitHub 4590739fa5 Add ASCII file name conversion (#17580) 2024-11-26 11:26:44 +01:00
7c617f2976 Revert #14234 add update error message for DB connection failures (#17612)
* Revert #14234

* Make the boot failure message more descriptive when unable to connect to DB

* Update src/Umbraco.Infrastructure/Runtime/RuntimeState.cs

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

* Revert changes

* Obsolete InstallMissingDatabase from V16

---------

Co-authored-by: Ronald Barendse <ronald@barend.se>
2024-11-26 11:06:40 +01:00
Lee KelleherandGitHub 9141f61708 V13: Dropzone, upload complete callback with processed file array (#17631)
* Dropzone, upload complete callback with processed file array

* Media card: cosmetic fix for image border-radius

The image's square corners were poking out.
2024-11-25 15:51:16 +01:00
ba4120050f Add cache key to GetByUserName (#17350)
* Add cache key to GetByUserName

* Remove constants

* create new cache policy for member repository

---------

Co-authored-by: Elitsa <elm@umbraco.dk>
2024-11-25 15:27:22 +01:00
Kenn JacobsenandGitHub cbd4dc6e0d Handle "all slashes" routes (#17596) 2024-11-22 09:06:10 +01:00
c7014e159b Sort manifest file paths alphabetically (#14466)
* Sort manifest file paths alphabetically

* Update src/Umbraco.Infrastructure/Manifest/ManifestParser.cs

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

---------

Co-authored-by: Ronald Barendse <ronald@barend.se>
2024-11-21 16:19:48 +01:00
Sven Geusensandnikolajlauridsen 9febbc7db1 Distinguish between default zero and intentional zero sort order for new documents (#17517)
* Distinguish between default value and initial zero

* Update special value comment documentation

* Redid solution with dirty/new entity tracking

* rework copy branch sortorder fix

* Change == false to is false

---------

Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>
2024-11-21 09:09:25 +01:00
Kenn JacobsenandGitHub ec8e10f406 Fix RTE console error when blocks are not available (#17582) 2024-11-21 08:53:59 +01:00
EalseandJacob Overgaard 3233b24a4e fix: infinite editor stuck in create action (#15945) 2024-11-20 10:00:50 +01:00
Anders BjernerandJacob Overgaard 5775e9cb18 Added fix for <umb-button-group /> label issues 2024-11-20 09:59:27 +01:00
MoleandGitHub 570005f5e1 Run both cms and package migrations in upgrader (#17575)
* Run both cms and package migrations in upgrader

* Use correct setting
2024-11-20 08:38:33 +01:00
Bjarne FyrstenborgandLaura Neto fbaa3440f2 Don't shrink crops container on right side 2024-11-18 20:11:48 +01:00
Bjarne FyrstenborgandJacob Overgaard e662468ecc Set TinyMCE to readonly 2024-11-18 08:51:04 +01:00
Bjarne FyrstenborgandMatthew-Wise c8efc30654 Fallback value when setttings element type doesn't exists as it can have reference, but no available element type 2024-11-17 17:05:28 +00:00
marwaldvandJacob Overgaard 79d117026f Add swiss languages as backoffice languages
Add missing backoffice languages as properties etc. are translated via translation section (e.g. with "#Backoffice Settings"). At the moment the swiss languages always need to be added via the umbraco/Config folder.
2024-11-09 12:29:44 +01:00
Casper LundgreenandGitHub 2709054bdb Fix NameIdentifier is not an integer. (TryParse implemented) (#17414)
Issue: 17383
2024-11-08 13:27:53 +01:00
MoleandGitHub 2d4230c001 Include create date in audit item (#17447) 2024-11-08 08:58:38 +01:00
Zeegaan f7729508cf Merge branch 'release/13.5' into v13/dev
# Conflicts:
#	version.json
2024-10-23 08:57:30 +02:00
Zeegaan 31e1acce67 Merge remote-tracking branch 'origin/release/10.8.7' into v10/dev
# Conflicts:
#	version.json
2024-10-23 08:48:08 +02:00
Andreas ZerbstandGitHub aa1f3df76b Updated to match locator (#17334) 2024-10-23 08:37:24 +02:00
Sven GeusensandNikolaj Geisle 3431f76320 Prevents XSS when viewing an uploaded SVG
from the media-info and image-preview components.
2024-10-21 12:50:12 +02:00
ElitsaandNikolaj Geisle 1bc5466a8d Update the HttpContext's user with the authenticated user's principal 2024-10-21 12:45:38 +02:00
ElitsaandNikolaj Geisle d83daf44d3 Reduce the time when getRemainingTimeoutSeconds request is made from 30s to 20s, so fewer calls are made 2024-10-21 12:44:40 +02:00
ElitsaandNikolaj Geisle 2ad5ecd5d5 Make sure that the client shows the login screen as close to the server's timout time as possible 2024-10-21 12:44:39 +02:00
Sven GeusensandNikolaj Geisle 35c51a029a Prevents XSS when viewing an uploaded SVG
from the media-info and image-preview components.
2024-10-21 12:42:06 +02:00
ElitsaandNikolaj Geisle 8c1128c85b Update the HttpContext's user with the authenticated user's principal 2024-10-21 12:41:42 +02:00
ElitsaandNikolaj Geisle c9021ab2d2 Reduce the time when getRemainingTimeoutSeconds request is made from 30s to 20s, so fewer calls are made 2024-10-21 12:41:18 +02:00
ElitsaandNikolaj Geisle 67a71f8f82 Make sure that the client shows the login screen as close to the server's timout time as possible 2024-10-21 12:41:17 +02:00
Andreas ZerbstandAndreas Zerbst 97d74f39e7 Updated version of messagepack (#17321)
(cherry picked from commit 8a22672c7f)
2024-10-21 11:49:13 +02:00
Andreas ZerbstandAndreas Zerbst edd0a4a4a9 Updated message pack (#17320)
(cherry picked from commit 11270eaaf5)
2024-10-21 11:46:43 +02:00
Andreas ZerbstandGitHub 8a22672c7f Updated version of messagepack (#17321) 2024-10-21 11:15:59 +02:00
Andreas ZerbstandGitHub 11270eaaf5 Updated message pack (#17320) 2024-10-21 11:00:47 +02:00
Justin NevilleandGitHub 1e32d59ecb Query for media using the full file path as well as the original file path for files that contain the sizes in the file name (i.e. image_200x200.jpg) (#17314) 2024-10-21 09:53:17 +02:00
Kenn JacobsenandGitHub eab27123dd MNTP: Re-initialize contextual dialog options upon content creation (#17301) 2024-10-18 09:39:29 +02:00
Kenn JacobsenandGitHub a6253957c8 MNTP: Improve site and root context for dynamic root (#17303) 2024-10-18 09:17:17 +02:00
Jacob OvergaardandGitHub a8f5631144 V13: Update @umbraco-ui/uui to 1.11.0 (#17281)
* build(deps): update @umbraco-ui/uui from 1.7.1 to 1.11.0

* fix: umb-login-element no attributes

Cherry-picked a fix from V14 where the custom login input component was no longer needed, which was fixed because it errors out. This simplifies the login form.

* cherry-pick code to handle 'enter' click from v14
2024-10-17 10:25:02 +01:00
Jason ElkinandNikolaj Geisle 6d98162e19 Unlock form after unsuccessful save and publish. (#17285)
(cherry picked from commit ee37ad0f4b)
2024-10-16 15:09:31 +02:00
Jason ElkinandGitHub ee37ad0f4b Unlock form after unsuccessful save and publish. (#17285) 2024-10-16 15:08:37 +02:00
Bjarke BergandGitHub 2d71b5a63b Updated image sharp to a non vulnerable version (#17290) 2024-10-16 12:16:38 +02:00
Bjarke BergandGitHub f4f83bccbe Added an explicit dependency to Microsoft.Extensions.Caching.Memory to force it to use a non-vulnerable version (#17287) 2024-10-16 10:25:17 +02:00
Ronald BarendseandBjarke Berg 30b114d538 Fix ContentStore locking exceptions in async code (#17246)
* Add ContentCache test

* Use SemaphoreSlim as write lock

* Apply lock imrpovements to SnapDictionary

* Obsolete unused MonitorLock

(cherry picked from commit c3db3457e7)
2024-10-11 09:47:31 +02:00
Ronald BarendseandGitHub c3db3457e7 Fix ContentStore locking exceptions in async code (#17246)
* Add ContentCache test

* Use SemaphoreSlim as write lock

* Apply lock imrpovements to SnapDictionary

* Obsolete unused MonitorLock
2024-10-11 09:45:01 +02:00
Sven GeusensandGitHub 7787af2df1 Fix install url detection (#17241) 2024-10-10 18:09:11 +02:00
Wojciech WróbelandJacob Overgaard 3730109cce Fixed issue #17225. The 'code' html element added as valid to RTE configuration 2024-10-09 14:19:38 +02:00
Nikolaj Geisle b814608c06 Bump version 2024-10-08 12:34:23 +02:00
Nikolaj Geisle 9bab74d30e Bump version.json 2024-10-07 11:55:34 +02:00
Bjarke Berg 63a487bc0b Merge remote-tracking branch 'origin/v13/dev' into v13/dev 2024-09-26 07:54:43 +02:00
Ronald BarendseandBjarke Berg a40eadcfce Add RemoveDefault() extension method to fluent API for CMS webhook events (#15424)
* Add RemoveDefault extension method

* Move default webhook event types to single list

(cherry picked from commit 8f26263178)
2024-09-26 07:54:26 +02:00
9b19d63a6a update ImageSharpMiddlewareOption for fixing invalid width and height (#17126)
Co-authored-by: Lan Nguyen Thuy <lnt@umbraco.dk>
2024-09-25 13:19:09 +02:00
Terence BurridgeandSebastiaan Janssen 6939472f37 Update valid reasons not to have a template on a content node to include having a redirect field
Cherry-picked from: 385a5345b1
2024-09-23 14:30:13 +02:00
Sebastiaan Janssen 0387307301 Merge branch 'v13/dev' into v13/contrib 2024-09-23 14:27:48 +02:00
Bjarke Berg 25a5dde8e9 Merge remote-tracking branch 'origin/release/13.5' into v13/dev
# Conflicts:
#	version.json
2024-09-18 12:55:35 +02:00
Bjarke Berg 2e068bb73e Merge remote-tracking branch 'origin/release/13.5' into release/13.5 2024-09-17 14:51:05 +02:00
Bjarke Berg 842cacde19 Bump version 2024-09-17 14:50:57 +02:00
Dennis ÖhmanandSebastiaan Janssen 6a453a0910 v13.5 - New Swedish translation crashes Umbraco, removed duplicate areas. (#17059)
Cherry-picked from d64bf5de22
2024-09-17 14:04:22 +02:00
Sebastiaan Janssen ff9903b14d Merge branch 'v13/contrib' of https://github.com/umbraco/Umbraco-CMS into v13/contrib 2024-09-17 13:58:37 +02:00
Dennis ÖhmanandGitHub d64bf5de22 v13.5 - New Swedish translation crashes Umbraco, removed duplicate areas. (#17059) 2024-09-17 13:56:58 +02:00
Sebastiaan Janssen 9300d867fb Merge branch 'v13/dev' into v13/contrib 2024-09-17 13:12:04 +02:00
Niels LyngsøandGitHub eb0f8b5c24 fire change event (#17078) 2024-09-17 10:06:16 +02:00
Niels LyngsøandGitHub d58768f26d utilize lock unlock events for readonly mode while saving (#17077) 2024-09-17 09:52:00 +02:00
Niels LyngsøandGitHub 3667217053 only resolve if value is present (#17070) 2024-09-17 09:51:47 +02:00
Sven Geusens b237285703 Merge branch 'release/13.5' into v13/dev and version bump 2024-09-12 15:12:20 +02:00
Sven Geusens a6c5581942 Update version to non RC 2024-09-10 18:10:57 +02:00
Sven GeusensandGitHub 9a088d36b9 Expand RedirectUrl.Url storage type to avoid truncation (#17038)
* Add migration to expand RedirectUrl.Url to varcharMax

* Simplify Migration

* readded notnull attribute
2024-09-10 18:07:20 +02:00
Sven Geusens 407734ff4a Fix null reference exception in CacheValues.For when building the CompositeStringStringKey (#17024)
* Fix null ref exeption based on IPropertyValue.Culture documentation

* Clarify comment
2024-09-10 12:50:02 +02:00
Sven GeusensandGitHub a74d963cfa Fix null reference exception in CacheValues.For when building the CompositeStringStringKey (#17024)
* Fix null ref exeption based on IPropertyValue.Culture documentation

* Clarify comment
2024-09-09 18:13:14 +02:00
Niels LyngsøandNiels Lyngsø 02a47695e7 dispatch change event when embedded media has been added (#17008) 2024-09-09 13:09:31 +02:00
Niels Lyngsø 0f37cd3a45 PR 17018 2024-09-09 13:07:47 +02:00
Yari MariënandGitHub 5fe18bb78c Content.EditorDirectiveController: added formSubmittedValidationFailed broadcast to prevent fields to stay disabled (#17018) 2024-09-09 13:05:12 +02:00
Niels LyngsøandGitHub b16dfa9ca1 dispatch change event when embedded media has been added (#17008) 2024-09-09 12:49:07 +02:00
3e6116fcba No longer shows success message if content moving is cancelled (#15051)
* Fix for issue https://github.com/umbraco/Umbraco-CMS/issues/13923
 - Added AttemptMove method to the ContentService
 - Updated ContentController PostMove method to return ValidationProblem whenever the node is not moved

* Align changes with V14 solution. Make it non breaking.

---------

Co-authored-by: Laura Neto <12862535+lauraneto@users.noreply.github.com>
2024-09-05 14:08:48 +02:00
Ronald BarendseandGitHub ef3bf496e9 Avoid concurrent build of Umbraco.JsonSchema tool and add execution timeouts to Exec build tasks (#17006)
* Disable building Umbraco.JsonSchema and Umbraco.Tests.AcceptanceTest.UmbracoProject

* Add 10 minute timeout to Exec MSBuild tasks
2024-09-04 13:44:19 +02:00
Sebastiaan Janssen bff293213d Merge branch 'v13/dev' into v13/contrib 2024-09-03 11:01:39 +02:00
a47a1775f2 Prevent templates being editable when using Production runtime mode (#16923)
Co-authored-by: Nick Hoang <nick@codeberwery.com>
Co-authored-by: Jason Elkin <jason@bumpdigital.co.uk>
2024-09-03 00:54:32 +01:00
Jason Elkin b2a2df7a8a Ignore Visual Studio's generated launchSettings file. 2024-08-28 14:08:52 +01:00
Sven Geusens 34179f5b7d Merge branch 'release/13.5' into v13/dev and bump version 2024-08-28 10:14:09 +02:00
Sven Geusens d38b744c08 Merge branch 'v13/dev' into release/13.5 2024-08-27 18:54:54 +02:00
Niels LyngsøandGitHub ef58416814 V13: Read only mode while saving (#16961)
* make a clear console error when case happens

* turn Content into readonly mode when submitting form aka saving
2024-08-27 15:01:59 +01:00
Sven GeusensandGitHub 62b7c9468e Fix undefined property read (#16941) 2024-08-27 14:28:04 +01:00
Bjarke BergandGitHub bafdb21b45 Fix mandatory RTE validation (#16962)
* Added a custom RichTextRequiredValidator, to check that the empty richtext object (still with json) can be required or not. We are now testing the markdown needs to have a value

* Fixed namespaced and moved back wrong class

* Cleanup
2024-08-26 15:03:11 +02:00
Bjarke BergandGitHub 79080ffa93 Updated nuget packages and take a explicit dependency on Microsoft.IdentityModel.JsonWebTokens (#16935) 2024-08-21 10:25:39 +02:00
Sven GeusensandGitHub f99f821a6d Fix Mismatching constraint names in old migration (#16891)
* Find the constraint name based on table,column,type name instead of hardcoding it

* removed unnecesary using

* Check constraint rename seperatly from column rename
2024-08-21 09:07:09 +02:00
Niels LyngsøandGitHub 3b51475150 move and rename (#16916) 2024-08-15 12:53:57 +02:00
Sebastiaan Janssen b41de5764b Merge branch 'v13/dev' into v13/contrib 2024-08-15 11:28:55 +02:00
PeterandSebastiaan Janssen bb1c36f591 Making method ExecuteAsync virtual. (#16496)
Co-authored-by: Kvyatkovsky, Petr <pkvyatkovsky@qulix.com>
(cherry picked from commit 3a9ef3810b)
2024-08-15 11:15:24 +02:00
Andrii KudandJason Elkin 09f16c33d1 Issue-15712: MemberDefaultLockoutTimeInMinutes fix. 2024-08-15 08:48:58 +01:00
9d94658372 V13: fix 16663 (#16866)
* fix unit testing

* fix server value update case

* PastePropertyResolver for RTE Blocks

---------

Co-authored-by: leekelleher <leekelleher@gmail.com>
2024-08-13 09:43:20 +01:00
Niels LyngsøandGitHub 81f36df066 V13: fix 15516 (#16864)
* fix unit testing

* fix server value update case

* re initialize Block if it was offloaded
2024-08-13 09:25:24 +01:00
EalseandEmma L Garland 7afc2b9963 fix: uploaded item not selected in media picker 2024-08-12 21:42:35 +01:00
Sven GeusensandGitHub 2e3369a09a Upgrade imagesharp2 dependency (#16883) 2024-08-07 15:20:28 +02:00
Bjarke BergandGitHub 212b2c999f Updated to lastest nuget minor or patch versions (#16871) 2024-08-07 10:17:00 +02:00
mcl-szandSebastiaan Janssen 2761f5cd20 Combining OpenId and OfflineAccess scope (#16220)
* Combining OpenId and OfflineAccess scope

When the client scope is set to "openid offline_access", the returned scope only has the "offline_access" scope. The "openid" scope and the "id_token" are missing. By combining the OpenId and OfflineAccess as return scope, the refresh_token and id_token are returned.

* Update MemberController.cs

Cleaner way, provided by @kjac, to check if the scope has openid and/or offiline_access set.

(cherry picked from commit 55f9b09ab7)
2024-08-06 14:35:59 +02:00
Sebastiaan Janssen d5831fea94 Merge branch 'v13/dev' into v13/contrib 2024-08-06 09:07:06 +02:00
Sebastiaan Janssen 71afa21701 Revert "Configure WebP encoder to use Lossy by default (#16769)"
This reverts commit ee0fd7dd5f.
2024-08-06 09:04:27 +02:00
Daniël KnippersandGitHub ee0fd7dd5f Configure WebP encoder to use Lossy by default (#16769)
This greatly reduces file sizes in many cases compared to the default
Lossless setting that seems to be used for PNGs.
2024-07-16 20:48:32 +01:00
Vlael LayugandGitHub 4e746d367e Use parentElement instead of parent when setting focus for modals with tabs (#16257)
* added null checker for umb-tab when it's trying to get the elm.parent

* use parentElement instead of parent
2024-06-22 13:01:04 +01:00
Christian YngvessonandGitHub b60665d096 Feature/swedish translations (#16582)
* Added some translations

* Update sv.xml - Minor typo
2024-06-14 16:18:18 +01:00
395 changed files with 26217 additions and 4729 deletions
+1 -1
View File
@@ -35,7 +35,7 @@
<EnableStrictModeForCompatibleTfms>true</EnableStrictModeForCompatibleTfms>
</PropertyGroup>
<!-- Calculate version only once for the whole repository -->
<!-- Calculate version only once for the whole repository -->
<PropertyGroup>
<GitVersionBaseDirectory>$(MSBuildThisFileDirectory)</GitVersionBaseDirectory>
</PropertyGroup>
+43 -32
View File
@@ -5,35 +5,36 @@
</PropertyGroup>
<!-- Global packages (private, build-time packages for all projects) -->
<ItemGroup>
<GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.6.133" />
<GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.6.139" />
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.507" />
<GlobalPackageReference Include="Umbraco.Code" Version="2.1.0" />
<GlobalPackageReference Include="Umbraco.Code" Version="2.2.0" />
<GlobalPackageReference Include="Umbraco.GitVersioning.Extensions" Version="0.2.0" />
</ItemGroup>
<!-- Microsoft packages -->
<ItemGroup>
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.6" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.6" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.6" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.6" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.22" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.22" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.10.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.10.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.22" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.22" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.6" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.22" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="8.0.6" />
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="8.0.6" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="8.0.22" />
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="8.0.22" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="8.0.0" />
<PackageVersion Include="System.Runtime.Caching" Version="8.0.0" />
<PackageVersion Include="System.Runtime.Caching" Version="8.0.1" />
</ItemGroup>
<!-- Umbraco packages -->
<ItemGroup>
@@ -45,49 +46,59 @@
<PackageVersion Include="Asp.Versioning.Mvc" Version="7.1.1" />
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="7.1.0" />
<PackageVersion Include="Dazinator.Extensions.FileProviders" Version="2.0.0" />
<PackageVersion Include="Examine" Version="3.2.1" />
<PackageVersion Include="Examine.Core" Version="3.2.1" />
<PackageVersion Include="HtmlAgilityPack" Version="1.11.61" />
<PackageVersion Include="Examine" Version="3.8.0" />
<PackageVersion Include="Examine.Core" Version="3.8.0" />
<PackageVersion Include="HtmlAgilityPack" Version="1.11.74" />
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.8" />
<PackageVersion Include="MailKit" Version="4.6.0" />
<PackageVersion Include="MailKit" Version="4.8.0" />
<PackageVersion Include="Markdown" Version="2.2.1" />
<PackageVersion Include="MessagePack" Version="2.5.168" />
<PackageVersion Include="MessagePack" Version="2.5.192" />
<PackageVersion Include="MiniProfiler.AspNetCore.Mvc" Version="4.3.8" />
<PackageVersion Include="MiniProfiler.Shared" Version="4.3.8" />
<PackageVersion Include="ncrontab" Version="3.3.3" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="NPoco" Version="5.7.1" />
<PackageVersion Include="NPoco.SqlServer" Version="5.7.1" />
<PackageVersion Include="OpenIddict.Abstractions" Version="4.10.1" />
<PackageVersion Include="OpenIddict.AspNetCore" Version="4.10.1" />
<PackageVersion Include="OpenIddict.EntityFrameworkCore" Version="4.10.1" />
<PackageVersion Include="Serilog" Version="3.1.1" />
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.1" />
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageVersion Include="Serilog.Enrichers.Process" Version="2.0.2" />
<PackageVersion Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageVersion Include="Serilog.Expressions" Version="4.0.0" />
<PackageVersion Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="Serilog.Formatting.Compact" Version="2.0.0" />
<PackageVersion Include="Serilog.Formatting.Compact.Reader" Version="3.0.0" />
<PackageVersion Include="Serilog.Settings.Configuration" Version="8.0.0" />
<PackageVersion Include="Serilog.Settings.Configuration" Version="8.0.4" />
<PackageVersion Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageVersion Include="Serilog.Sinks.Map" Version="1.0.2" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.4" />
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.1.2" />
<PackageVersion Include="Smidge.InMemory" Version="4.4.0" />
<PackageVersion Include="Smidge.Nuglify" Version="4.4.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.12" />
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.1.5" />
<PackageVersion Include="Smidge.InMemory" Version="4.6.0" />
<PackageVersion Include="Smidge.Nuglify" Version="4.6.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.9.0" />
</ItemGroup>
<!-- Transitive pinned versions (only required because our direct dependencies have vulnerable versions of transitive dependencies) -->
<ItemGroup>
<!-- Both Microsoft.EntityFrameworkCore.SqlServer and NPoco.SqlServer bring in a vulnerable version of Azure.Identity -->
<PackageVersion Include="Azure.Identity" Version="1.12.0" />
<PackageVersion Include="Azure.Identity" Version="1.13.2" />
<!-- Dazinator.Extensions.FileProviders brings in a vulnerable version of System.Net.Http -->
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<!-- Examine brings in a vulnerable version of System.Security.Cryptography.Xml -->
<PackageVersion Include="System.Security.Cryptography.Xml" Version="8.0.1" />
<PackageVersion Include="System.Security.Cryptography.Xml" Version="8.0.3" />
<!-- Both Dazinator.Extensions.FileProviders and MiniProfiler.AspNetCore.Mvc bring in a vulnerable version of System.Text.RegularExpressions -->
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
<!-- Both OpenIddict.AspNetCore, Npoco.SqlServer and Microsoft.EntityFrameworkCore.SqlServer bring in a vulnerable version of Microsoft.IdentityModel.JsonWebTokens -->
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.7.1" />
<!-- Examine.Lucene bring in a vulnerable version of Lucene.Net.Replicator -->
<PackageVersion Include="Lucene.Net.Replicator" Version="4.8.0-beta00017" />
<!-- Both OpenIddict.AspNetCore, Microsoft.EntityFrameworkCore.* bring in a vulnerable version of Microsoft.Extensions.Caching.Memory -->
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<!-- Both Azure.Identity, Microsoft.EntityFrameworkCore.SqlServer,NPoco.SqlServer, and more bring in a vulnerable version of System.Text.Json -->
<PackageVersion Include="System.Text.Json" Version="8.0.6" />
<!-- Both Microsoft.EntityFrameworkCore.SqlServer and NPoco.SqlServer bring in a vulnerable version of Microsoft.Data.SqlClient -->
<PackageVersion Include="Microsoft.Data.SqlClient" Version="5.2.3" />
</ItemGroup>
</Project>
</Project>
+181 -50
View File
@@ -25,6 +25,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
@@ -71,8 +75,12 @@ stages:
- job: A
displayName: Build Umbraco CMS
pool:
vmImage: 'ubuntu-latest'
vmImage: 'windows-latest'
steps:
- checkout: self
submodules: false
lfs: false,
fetchDepth: 500
- task: NodeTool@0
displayName: Use Node.js $(nodeVersion)
retryCountOnTaskFailure: 3
@@ -89,6 +97,11 @@ stages:
- script: npm ci --no-fund --no-audit --prefer-offline
workingDirectory: src/Umbraco.Web.UI.Client
displayName: Run npm ci (Backoffice)
- 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
- task: gulp@0
displayName: Run gulp build (Backoffice)
inputs:
@@ -102,6 +115,10 @@ stages:
workingDir: src/Umbraco.Web.UI.Login
verbose: false
customCommand: ci
- powershell: |
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: npm@1
displayName: Run npm build (Login)
inputs:
@@ -125,6 +142,10 @@ 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'
- task: PublishPipelineArtifact@1
displayName: Publish nupkg
inputs:
@@ -135,6 +156,35 @@ stages:
inputs:
targetPath: $(Build.SourcesDirectory)
artifactName: build_output
- task: PublishPipelineArtifact@1
displayName: Publish Backend BOM
inputs:
targetPath: $(Build.ArtifactStagingDirectory)/bom
artifactName: bom-build
- stage: E2E_BOM
displayName: E2E Tests BOM Generation
dependsOn: []
jobs:
- job:
displayName: E2E Generate BOM
pool:
vmImage: "ubuntu-latest"
steps:
- checkout: self
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}}))
@@ -198,6 +248,11 @@ stages:
pool:
vmImage: 'ubuntu-latest'
steps:
- checkout: self
submodules: false
lfs: false,
fetchDepth: 1
fetchFilter: tree:0
- task: NodeTool@0
displayName: Use Node.js 10.15.x
retryCountOnTaskFailure: 3
@@ -249,6 +304,11 @@ stages:
pool:
vmImage: $(vmImage)
steps:
- checkout: self
submodules: false
lfs: false,
fetchDepth: 1
fetchFilter: tree:0
- task: DownloadPipelineArtifact@2
displayName: Download build artifacts
inputs:
@@ -288,6 +348,11 @@ stages:
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
@@ -319,7 +384,9 @@ stages:
# Integration Tests (SQL Server)
- job:
timeoutInMinutes: 120
condition: or(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), ${{parameters.sqlServerIntegrationTests}})
# We are currently encountering issues when running SQL Server Linux tests Microsoft.Data.SqlClient.SqlException (0x80131904)
# condition: or(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), ${{parameters.sqlServerIntegrationTests}})
condition: eq(${{parameters.sqlServerIntegrationTests}}, True)
displayName: Integration Tests (SQL Server)
strategy:
matrix:
@@ -415,10 +482,24 @@ stages:
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.Sqlite
strategy:
matrix:
Linux:
vmImage: 'ubuntu-latest'
Windows:
vmImage: 'windows-latest'
LinuxPart1Of3:
vmImage: "ubuntu-latest"
testCommand: "npm run test -- --shard=1/3"
LinuxPart2Of3:
vmImage: "ubuntu-latest"
testCommand: "npm run test -- --shard=2/3"
LinuxPart3Of3:
vmImage: "ubuntu-latest"
testCommand: "npm run test -- --shard=3/3"
WindowsPart1Of3:
vmImage: "windows-latest"
testCommand: "npm run test -- --shard=1/3"
WindowsPart2Of3:
vmImage: "windows-latest"
testCommand: "npm run test -- --shard=2/3"
WindowsPart3Of3:
vmImage: "windows-latest"
testCommand: "npm run test -- --shard=3/3"
pool:
vmImage: $(vmImage)
steps:
@@ -429,37 +510,18 @@ stages:
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
- pwsh: |
"UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL)
UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
URL=$(ASPNETCORE_URLS)" | 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
- template: templates/e2e-install.yml
parameters:
nodeVersion: $(nodeVersion)
npm_config_cache: $(npm_config_cache)
PlaywrightUserEmail: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL)
PlaywrightPassword: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
ASPNETCORE_URLS: $(ASPNETCORE_URLS)
# Build application
- pwsh: |
@@ -496,12 +558,12 @@ stages:
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Install Playwright and dependencies
- pwsh: npx playwright install --with-deps
displayName: Install Playwright
- pwsh: npx playwright install chromium
displayName: Install Playwright only with Chromium browser
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Test
- pwsh: npm run test --ignore-certificate-errors
- pwsh: $(testCommand)
displayName: Run Playwright tests
continueOnError: true
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
@@ -522,18 +584,28 @@ stages:
# Copy artifacts
- pwsh: |
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results $(Build.ArtifactStagingDirectory) -Recurse
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse
}
displayName: Copy Playwright results
condition: succeededOrFailed()
# Publish
# Publish test artifacts
- task: PublishPipelineArtifact@1
displayName: Publish test artifacts
condition: succeededOrFailed()
inputs:
targetPath: $(Build.ArtifactStagingDirectory)
artifact: 'Acceptance Tests - $(Agent.JobName) - Attempt #$(System.JobAttempt)'
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 Tests (SQL Server)
@@ -544,12 +616,30 @@ stages:
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
strategy:
matrix:
Linux:
vmImage: 'ubuntu-latest'
LinuxPart1Of3:
testCommand: "npm run testSqlite -- --shard=1/3"
vmImage: "ubuntu-latest"
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
CONNECTIONSTRINGS__UMBRACODBDSN: 'Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True'
Windows:
vmImage: 'windows-latest'
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
LinuxPart2Of3:
testCommand: "npm run testSqlite -- --shard=2/3"
vmImage: "ubuntu-latest"
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
LinuxPart3Of3:
testCommand: "npm run testSqlite -- --shard=3/3"
vmImage: "ubuntu-latest"
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
WindowsPart1Of3:
vmImage: "windows-latest"
testCommand: "npm run testSqlite -- --shard=1/3"
WindowsPart2Of3:
vmImage: "windows-latest"
testCommand: "npm run testSqlite -- --shard=2/3"
WindowsPart3Of3:
vmImage: "windows-latest"
testCommand: "npm run testSqlite -- --shard=3/3"
pool:
vmImage: $(vmImage)
steps:
@@ -635,14 +725,13 @@ stages:
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Install Playwright and dependencies
- pwsh: npx playwright install --with-deps
displayName: Install Playwright
- pwsh: npx playwright install chromium
displayName: Install Playwright only with Chromium browser
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Test
- pwsh: npm run test --ignore-certificate-errors
- pwsh: $(testCommand)
displayName: Run Playwright tests
continueOnError: true
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
env:
CI: true
@@ -670,18 +759,56 @@ stages:
# Copy artifacts
- pwsh: |
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results $(Build.ArtifactStagingDirectory) -Recurse
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse
}
displayName: Copy Playwright results
condition: succeededOrFailed()
# Publish
# Publish test artifacts
- task: PublishPipelineArtifact@1
displayName: Publish test artifacts
condition: succeededOrFailed()
inputs:
targetPath: $(Build.ArtifactStagingDirectory)
artifact: 'Acceptance Tests - $(Agent.JobName) - Attempt #$(System.JobAttempt)'
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-build"
bomFilePath: "bom-dotnet.xml"
- name: "Login"
artifact: "bom-build"
bomFilePath: "bom-login.xml"
- name: "Backoffice"
artifact: "bom-build"
bomFilePath: "bom-backoffice.xml"
- name: "E2E"
artifact: "bom-e2e"
bomFilePath: "bom-e2e.xml"
###############################################
## Release
@@ -695,6 +822,8 @@ stages:
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.myGetDeploy}}))
jobs:
- job:
pool:
vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available.
displayName: Push to pre-release feed
steps:
- checkout: none
@@ -721,6 +850,8 @@ stages:
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.nuGetDeploy}}))
jobs:
- job:
pool:
vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available.
displayName: Push to NuGet
steps:
- checkout: none
+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
+47
View File
@@ -0,0 +1,47 @@
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 }}" | 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": "8.0.100",
"version": "8.0.415",
"rollForward": "latestFeature"
}
}
@@ -40,7 +40,9 @@ public static class UmbracoBuilderAuthExtensions
.SetLogoutEndpointUris(
Paths.MemberApi.LogoutEndpoint.TrimStart(Constants.CharArrays.ForwardSlash))
.SetRevocationEndpointUris(
Paths.MemberApi.RevokeEndpoint.TrimStart(Constants.CharArrays.ForwardSlash));
Paths.MemberApi.RevokeEndpoint.TrimStart(Constants.CharArrays.ForwardSlash))
.SetUserinfoEndpointUris(
Paths.MemberApi.UserinfoEndpoint.TrimStart(Constants.CharArrays.ForwardSlash));
// Enable authorization code flow with PKCE
options
@@ -52,7 +54,8 @@ public static class UmbracoBuilderAuthExtensions
options
.UseAspNetCore()
.EnableAuthorizationEndpointPassthrough()
.EnableLogoutEndpointPassthrough();
.EnableLogoutEndpointPassthrough()
.EnableUserinfoEndpointPassthrough();
// Enable reference tokens
// - see https://documentation.openiddict.com/configuration/token-storage.html
@@ -14,6 +14,8 @@ public static class Paths
public static readonly string RevokeEndpoint = EndpointPath($"{EndpointTemplate}/revoke");
public static readonly string UserinfoEndpoint = EndpointPath($"{EndpointTemplate}/userinfo");
// NOTE: we're NOT using /api/v1.0/ here because it will clash with the Delivery API docs
private static string EndpointPath(string relativePath) => $"/umbraco/delivery/api/v1/{relativePath}";
}
@@ -12,6 +12,12 @@
<PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="OpenIddict.Abstractions" />
<PackageReference Include="OpenIddict.AspNetCore" />
<!-- Both OpenIddict.AspNetCore, Npoco.SqlServer and Microsoft.EntityFrameworkCore.SqlServer bring in a vulnerable version of Microsoft.IdentityModel.JsonWebTokens -->
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens"/>
<!-- Take top-level depedendency on OpenIddict.AspNetCore depends on a vulnerable version -->
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
</ItemGroup>
<ItemGroup>
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.OutputCaching;
using Microsoft.AspNetCore.OutputCaching;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Umbraco.Cms.Core.DeliveryApi;
namespace Umbraco.Cms.Api.Delivery.Caching;
@@ -7,9 +8,13 @@ namespace Umbraco.Cms.Api.Delivery.Caching;
internal sealed class DeliveryApiOutputCachePolicy : IOutputCachePolicy
{
private readonly TimeSpan _duration;
private readonly StringValues _varyByHeaderNames;
public DeliveryApiOutputCachePolicy(TimeSpan duration)
=> _duration = duration;
public DeliveryApiOutputCachePolicy(TimeSpan duration, StringValues varyByHeaderNames)
{
_duration = duration;
_varyByHeaderNames = varyByHeaderNames;
}
ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
{
@@ -18,8 +23,14 @@ internal sealed class DeliveryApiOutputCachePolicy : IOutputCachePolicy
.RequestServices
.GetRequiredService<IRequestPreviewService>();
context.EnableOutputCaching = requestPreviewService.IsPreview() is false;
IApiAccessService apiAccessService = context
.HttpContext
.RequestServices
.GetRequiredService<IApiAccessService>();
context.EnableOutputCaching = requestPreviewService.IsPreview() is false && apiAccessService.HasPublicAccess();
context.ResponseExpirationTimeSpan = _duration;
context.CacheVaryByRules.HeaderNames = _varyByHeaderNames;
return ValueTask.CompletedTask;
}
@@ -19,7 +19,7 @@ public class ByRouteContentApiController : ContentApiItemControllerBase
private readonly IRequestRedirectService _requestRedirectService;
private readonly IRequestPreviewService _requestPreviewService;
private readonly IRequestMemberAccessService _requestMemberAccessService;
private const string PreviewContentRequestPathPrefix = $"/{Constants.DeliveryApi.Routing.PreviewContentPathPrefix}";
private const string PreviewContentRequestPathPrefix = $"/{Umbraco.Cms.Core.Constants.DeliveryApi.Routing.PreviewContentPathPrefix}";
[Obsolete($"Please use the constructor that does not accept {nameof(IPublicAccessService)}. Will be removed in V14.")]
public ByRouteContentApiController(
@@ -145,6 +145,11 @@ public class ByRouteContentApiController : ContentApiItemControllerBase
path = DecodePath(path);
path = path.Length == 0 ? "/" : path;
if (_apiContentPathResolver.IsResolvablePath(path) is false)
{
return NotFound();
}
IPublishedContent? contentItem = GetContent(path);
if (contentItem is not null)
{
@@ -15,6 +15,7 @@ namespace Umbraco.Cms.Api.Delivery.Controllers.Content;
[ApiExplorerSettings(GroupName = "Content")]
[LocalizeFromAcceptLanguageHeader]
[ValidateStartItem]
[AddVaryHeader]
[OutputCache(PolicyName = Constants.DeliveryApi.OutputCache.ContentCachePolicy)]
public abstract class ContentApiControllerBase : DeliveryApiControllerBase
{
@@ -0,0 +1,28 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Server.AspNetCore;
using Umbraco.Cms.Api.Delivery.Routing;
using Umbraco.Cms.Core.DeliveryApi;
namespace Umbraco.Cms.Api.Delivery.Controllers.Security;
[ApiVersion("1.0")]
[ApiController]
[VersionedDeliveryApiRoute(Common.Security.Paths.MemberApi.EndpointTemplate)]
[ApiExplorerSettings(IgnoreApi = true)]
[Authorize(AuthenticationSchemes = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)]
public class CurrentMemberController : DeliveryApiControllerBase
{
private readonly ICurrentMemberClaimsProvider _currentMemberClaimsProvider;
public CurrentMemberController(ICurrentMemberClaimsProvider currentMemberClaimsProvider)
=> _currentMemberClaimsProvider = currentMemberClaimsProvider;
[HttpGet("userinfo")]
public async Task<IActionResult> Userinfo()
{
Dictionary<string, object> claims = await _currentMemberClaimsProvider.GetClaimsAsync();
return Ok(claims);
}
}
@@ -160,11 +160,12 @@ public class MemberController : DeliveryApiControllerBase
claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken);
}
if (request.GetScopes().Contains(OpenIddictConstants.Scopes.OfflineAccess))
{
// "offline_access" scope is required to use refresh tokens
memberPrincipal.SetScopes(OpenIddictConstants.Scopes.OfflineAccess);
}
// "openid" and "offline_access" are the only scopes allowed for members; explicitly ensure we only add those
// NOTE: the "offline_access" scope is required to use refresh tokens
IEnumerable<string> allowedScopes = request
.GetScopes()
.Intersect(new[] { OpenIddictConstants.Scopes.OpenId, OpenIddictConstants.Scopes.OfflineAccess });
memberPrincipal.SetScopes(allowedScopes);
return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, memberPrincipal);
}
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Umbraco.Cms.Api.Common.DependencyInjection;
using Umbraco.Cms.Api.Delivery.Accessors;
using Umbraco.Cms.Api.Delivery.Caching;
@@ -33,26 +34,33 @@ 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<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>();
@@ -60,6 +68,7 @@ public static class UmbracoBuilderExtensions
builder.Services.AddSingleton<IApiMediaQueryService, ApiMediaQueryService>();
builder.Services.AddTransient<IMemberApplicationManager, MemberApplicationManager>();
builder.Services.AddTransient<IRequestMemberAccessService, RequestMemberAccessService>();
builder.Services.AddTransient<ICurrentMemberClaimsProvider, CurrentMemberClaimsProvider>();
builder.Services.ConfigureOptions<ConfigureUmbracoDeliveryApiSwaggerGenOptions>();
builder.AddUmbracoApiOpenApiUI();
@@ -107,12 +116,20 @@ public static class UmbracoBuilderExtensions
if (outputCacheSettings.ContentDuration.TotalSeconds > 0)
{
options.AddPolicy(Constants.DeliveryApi.OutputCache.ContentCachePolicy, new DeliveryApiOutputCachePolicy(outputCacheSettings.ContentDuration));
options.AddPolicy(
Constants.DeliveryApi.OutputCache.ContentCachePolicy,
new DeliveryApiOutputCachePolicy(
outputCacheSettings.ContentDuration,
new StringValues([Constants.DeliveryApi.HeaderNames.AcceptLanguage, Constants.DeliveryApi.HeaderNames.StartItem])));
}
if (outputCacheSettings.MediaDuration.TotalSeconds > 0)
{
options.AddPolicy(Constants.DeliveryApi.OutputCache.MediaCachePolicy, new DeliveryApiOutputCachePolicy(outputCacheSettings.MediaDuration));
options.AddPolicy(
Constants.DeliveryApi.OutputCache.MediaCachePolicy,
new DeliveryApiOutputCachePolicy(
outputCacheSettings.MediaDuration,
Constants.DeliveryApi.HeaderNames.StartItem));
}
});
@@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Mvc.Filters;
namespace Umbraco.Cms.Api.Delivery.Filters;
public sealed class AddVaryHeaderAttribute : ActionFilterAttribute
{
private const string Vary = "Accept-Language, Preview, Start-Item";
public override void OnResultExecuting(ResultExecutingContext context)
=> context.HttpContext.Response.Headers.Vary = context.HttpContext.Response.Headers.Vary.Count > 0
? $"{context.HttpContext.Response.Headers.Vary}, {Vary}"
: Vary;
}
@@ -1,4 +1,4 @@
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using Umbraco.Cms.Api.Delivery.Configuration;
@@ -21,7 +21,7 @@ internal sealed class SwaggerContentDocumentationFilter : SwaggerDocumentationFi
operation.Parameters.Add(new OpenApiParameter
{
Name = "Accept-Language",
Name = Core.Constants.DeliveryApi.HeaderNames.AcceptLanguage,
In = ParameterLocation.Header,
Required = false,
Description = "Defines the language to return. Use this when querying language variant content items.",
@@ -37,7 +37,7 @@ internal sealed class SwaggerContentDocumentationFilter : SwaggerDocumentationFi
operation.Parameters.Add(new OpenApiParameter
{
Name = "Preview",
Name = Core.Constants.DeliveryApi.HeaderNames.Preview,
In = ParameterLocation.Header,
Required = false,
Description = "Whether to request draft content.",
@@ -46,7 +46,7 @@ internal sealed class SwaggerContentDocumentationFilter : SwaggerDocumentationFi
operation.Parameters.Add(new OpenApiParameter
{
Name = "Start-Item",
Name = Core.Constants.DeliveryApi.HeaderNames.StartItem,
In = ParameterLocation.Header,
Required = false,
Description = "URL segment or GUID of a root content item.",
@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
@@ -63,7 +63,7 @@ internal abstract class SwaggerDocumentationFilterBase<TBaseController>
protected void AddApiKey(OpenApiOperation operation) =>
operation.Parameters.Add(new OpenApiParameter
{
Name = "Api-Key",
Name = Core.Constants.DeliveryApi.HeaderNames.ApiKey,
In = ParameterLocation.Header,
Required = false,
Description = "API key specified through configuration to authorize access to the API.",
@@ -6,6 +6,7 @@ using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Infrastructure.Security;
namespace Umbraco.Cms.Api.Delivery.Handlers;
@@ -16,16 +17,21 @@ internal sealed class InitializeMemberApplicationNotificationHandler : INotifica
private readonly ILogger<InitializeMemberApplicationNotificationHandler> _logger;
private readonly DeliveryApiSettings _deliveryApiSettings;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly IServerRoleAccessor _serverRoleAccessor;
private static readonly SemaphoreSlim _locker = new(1);
private static bool _isInitialized = false;
public InitializeMemberApplicationNotificationHandler(
IRuntimeState runtimeState,
IOptions<DeliveryApiSettings> deliveryApiSettings,
ILogger<InitializeMemberApplicationNotificationHandler> logger,
IServiceScopeFactory serviceScopeFactory)
IServiceScopeFactory serviceScopeFactory,
IServerRoleAccessor serverRoleAccessor)
{
_runtimeState = runtimeState;
_logger = logger;
_serviceScopeFactory = serviceScopeFactory;
_serverRoleAccessor = serverRoleAccessor;
_deliveryApiSettings = deliveryApiSettings.Value;
}
@@ -36,34 +42,55 @@ internal sealed class InitializeMemberApplicationNotificationHandler : INotifica
return;
}
// we cannot inject the IMemberApplicationManager because it ultimately takes a dependency on the DbContext ... and during
// install that is not allowed (no connection string means no DbContext)
using IServiceScope scope = _serviceScopeFactory.CreateScope();
IMemberApplicationManager memberApplicationManager = scope.ServiceProvider.GetRequiredService<IMemberApplicationManager>();
if (_deliveryApiSettings.MemberAuthorization?.AuthorizationCodeFlow?.Enabled is not true)
if (_serverRoleAccessor.CurrentServerRole is ServerRole.Subscriber)
{
await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
// subscriber instances should not alter the member application
return;
}
if (ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls) is false)
try
{
await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
return;
}
await _locker.WaitAsync(cancellationToken);
if (_isInitialized)
{
return;
}
if (_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls.Any()
&& ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls) is false)
_isInitialized = true;
// we cannot inject the IMemberApplicationManager because it ultimately takes a dependency on the DbContext ... and during
// install that is not allowed (no connection string means no DbContext)
using IServiceScope scope = _serviceScopeFactory.CreateScope();
IMemberApplicationManager memberApplicationManager = scope.ServiceProvider.GetRequiredService<IMemberApplicationManager>();
if (_deliveryApiSettings.MemberAuthorization?.AuthorizationCodeFlow?.Enabled is not true)
{
await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
return;
}
if (ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls) is false)
{
await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
return;
}
if (_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls.Any()
&& ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls) is false)
{
await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
return;
}
await memberApplicationManager.EnsureMemberApplicationAsync(
_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls,
_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls,
cancellationToken);
}
finally
{
await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
return;
_locker.Release();
}
await memberApplicationManager.EnsureMemberApplicationAsync(
_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls,
_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls,
cancellationToken);
}
private bool ValidateRedirectUrls(Uri[] redirectUrls)
@@ -1,6 +1,5 @@
using Umbraco.Cms.Api.Delivery.Indexing.Filters;
using Umbraco.Cms.Core.DeliveryApi;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Delivery.Querying.Filters;
@@ -15,15 +14,15 @@ public sealed class ContentTypeFilter : IFilterHandler
/// <inheritdoc/>
public FilterOption BuildFilterOption(string filter)
{
var alias = filter.Substring(ContentTypeSpecifier.Length);
var filterValue = filter.Substring(ContentTypeSpecifier.Length);
var negate = filterValue.StartsWith('!');
var aliases = filterValue.TrimStart('!').Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
return new FilterOption
{
FieldName = ContentTypeFilterIndexer.FieldName,
Values = alias.IsNullOrWhiteSpace() == false
? new[] { alias.TrimStart('!') }
: Array.Empty<string>(),
Operator = alias.StartsWith('!')
Values = aliases,
Operator = negate
? FilterOperation.IsNot
: FilterOperation.Is
};
@@ -1,5 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Delivery.Indexing.Selectors;
using Umbraco.Cms.Core.DeliveryApi;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Extensions;
@@ -10,10 +12,21 @@ public sealed class AncestorsSelector : QueryOptionBase, ISelectorHandler
{
private const string AncestorsSpecifier = "ancestors:";
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
private readonly IRequestPreviewService _requestPreviewService;
public AncestorsSelector(IPublishedSnapshotAccessor publishedSnapshotAccessor, IRequestRoutingService requestRoutingService)
: base(publishedSnapshotAccessor, requestRoutingService) =>
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
public AncestorsSelector(IPublishedSnapshotAccessor publishedSnapshotAccessor,
IRequestRoutingService requestRoutingService)
: this(publishedSnapshotAccessor, requestRoutingService, StaticServiceProvider.Instance.GetRequiredService<IRequestPreviewService>())
{
}
public AncestorsSelector(IPublishedSnapshotAccessor publishedSnapshotAccessor, IRequestRoutingService requestRoutingService, IRequestPreviewService requestPreviewService)
: base(publishedSnapshotAccessor, requestRoutingService)
{
_publishedSnapshotAccessor = publishedSnapshotAccessor;
_requestPreviewService = requestPreviewService;
}
/// <inheritdoc />
public bool CanHandle(string query)
@@ -37,10 +50,20 @@ public sealed class AncestorsSelector : QueryOptionBase, ISelectorHandler
};
}
IPublishedSnapshot publishedSnapshot = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot();
IPublishedContentCache contentCache = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot()?.Content
?? throw new InvalidOperationException("Could not obtain the content cache");
IPublishedContent contentItem = publishedSnapshot.Content?.GetById((Guid)id)
?? throw new InvalidOperationException("Could not obtain the content cache");
IPublishedContent? contentItem = contentCache.GetById(_requestPreviewService.IsPreview(), id.Value);
if (contentItem is null)
{
// no such content item, make sure the selector does not yield any results
return new SelectorOption
{
FieldName = AncestorsSelectorIndexer.FieldName,
Values = Array.Empty<string>()
};
}
var ancestorKeys = contentItem.Ancestors().Select(a => a.Key.ToString("D")).ToArray();
@@ -1,62 +1,24 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DeliveryApi;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Extensions;
using Umbraco.Cms.Web.Common.Rendering;
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 +28,7 @@ internal sealed class RequestContextOutputExpansionStrategyV2 : IOutputExpansion
try
{
_expandProperties.Push(Node.Parse(toExpand));
ExpandProperties.Push(Node.Parse(toExpand));
}
catch (ArgumentException ex)
{
@@ -76,7 +38,7 @@ internal sealed class RequestContextOutputExpansionStrategyV2 : IOutputExpansion
try
{
_includeProperties.Push(Node.Parse(toInclude));
IncludeProperties.Push(Node.Parse(toInclude));
}
catch (ArgumentException ex)
{
@@ -84,102 +46,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 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;
}
}
}
@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DeliveryApi;
@@ -29,7 +29,7 @@ internal sealed class ApiAccessService : RequestHeaderHandler, IApiAccessService
private bool IfEnabled(Func<bool> condition) => _deliveryApiSettings.Enabled && condition();
private bool HasValidApiKey() => _deliveryApiSettings.ApiKey.IsNullOrWhiteSpace() == false
&& _deliveryApiSettings.ApiKey.Equals(GetHeaderValue("Api-Key"));
&& _deliveryApiSettings.ApiKey.Equals(GetHeaderValue(Core.Constants.DeliveryApi.HeaderNames.ApiKey));
private bool IfMediaEnabled(Func<bool> condition) => _deliveryApiSettings is { Enabled: true, Media.Enabled: true } && condition();
}
@@ -0,0 +1,44 @@
using OpenIddict.Abstractions;
using Umbraco.Cms.Core.DeliveryApi;
using Umbraco.Cms.Core.Security;
namespace Umbraco.Cms.Api.Delivery.Services;
// NOTE: this is public and unsealed to allow overriding the default claims with minimal effort.
public class CurrentMemberClaimsProvider : ICurrentMemberClaimsProvider
{
private readonly IMemberManager _memberManager;
public CurrentMemberClaimsProvider(IMemberManager memberManager)
=> _memberManager = memberManager;
public virtual async Task<Dictionary<string, object>> GetClaimsAsync()
{
MemberIdentityUser? memberIdentityUser = await _memberManager.GetCurrentMemberAsync();
return memberIdentityUser is not null
? await GetClaimsForMemberIdentityAsync(memberIdentityUser)
: throw new InvalidOperationException("Could not retrieve the current member. This method should only ever be invoked when a member has been authorized.");
}
protected virtual async Task<Dictionary<string, object>> GetClaimsForMemberIdentityAsync(MemberIdentityUser memberIdentityUser)
{
var claims = new Dictionary<string, object>
{
[OpenIddictConstants.Claims.Subject] = memberIdentityUser.Key
};
if (memberIdentityUser.Name is not null)
{
claims[OpenIddictConstants.Claims.Name] = memberIdentityUser.Name;
}
if (memberIdentityUser.Email is not null)
{
claims[OpenIddictConstants.Claims.Email] = memberIdentityUser.Email;
}
claims[OpenIddictConstants.Claims.Role] = await _memberManager.GetRolesAsync(memberIdentityUser);
return claims;
}
}
@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using Umbraco.Cms.Core.DeliveryApi;
namespace Umbraco.Cms.Api.Delivery.Services;
@@ -11,5 +11,5 @@ internal sealed class RequestPreviewService : RequestHeaderHandler, IRequestPrev
}
/// <inheritdoc />
public bool IsPreview() => GetHeaderValue("Preview") == "true";
public bool IsPreview() => string.Equals(GetHeaderValue(Core.Constants.DeliveryApi.HeaderNames.Preview), "true", StringComparison.OrdinalIgnoreCase);
}
@@ -42,31 +42,42 @@ internal sealed class RequestRedirectService : RoutingServiceBase, IRequestRedir
{
requestedPath = requestedPath.EnsureStartsWith("/");
IPublishedContent? startItem = GetStartItem();
// must append the root content url segment if it is not hidden by config, because
// the URL tracking is based on the actual URL, including the root content url segment
if (_globalSettings.HideTopLevelNodeFromPath == false)
if (_globalSettings.HideTopLevelNodeFromPath == false && startItem?.UrlSegment != null)
{
IPublishedContent? startItem = GetStartItem();
if (startItem?.UrlSegment != null)
{
requestedPath = $"{startItem.UrlSegment.EnsureStartsWith("/")}{requestedPath}";
}
requestedPath = $"{startItem.UrlSegment.EnsureStartsWith("/")}{requestedPath}";
}
var culture = _requestCultureService.GetRequestedCulture();
// append the configured domain content ID to the path if we have a domain bound request,
// because URL tracking registers the tracked url like "{domain content ID}/{content path}"
Uri contentRoute = GetDefaultRequestUri(requestedPath);
DomainAndUri? domainAndUri = GetDomainAndUriForRoute(contentRoute);
if (domainAndUri != null)
// important: redirect URLs are always tracked without trailing slashes
requestedPath = requestedPath.TrimEnd("/");
IRedirectUrl? redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl(requestedPath, culture);
// if a redirect URL was not found, try by appending the start item ID because URL tracking might have tracked
// a redirect with "{root content ID}/{content path}"
if (redirectUrl is null && startItem is not null)
{
requestedPath = GetContentRoute(domainAndUri, contentRoute);
culture ??= domainAndUri.Culture;
redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl($"{startItem.Id}{requestedPath}", culture);
}
// still no redirect URL found - try looking for a configured domain if we have a domain bound request,
// because URL tracking might have tracked a redirect with "{domain content ID}/{content path}"
if (redirectUrl is null)
{
Uri contentRoute = GetDefaultRequestUri(requestedPath);
DomainAndUri? domainAndUri = GetDomainAndUriForRoute(contentRoute);
if (domainAndUri is not null)
{
requestedPath = GetContentRoute(domainAndUri, contentRoute);
culture ??= domainAndUri.Culture;
redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl(requestedPath, culture);
}
}
// important: redirect URLs are always tracked without trailing slashes
IRedirectUrl? redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl(requestedPath.TrimEnd("/"), culture);
IPublishedContent? content = redirectUrl != null
? _apiPublishedContentCache.GetById(redirectUrl.ContentKey)
: null;
@@ -58,5 +58,5 @@ internal sealed class RequestStartItemProvider : RequestHeaderHandler, IRequestS
}
/// <inheritdoc/>
public string? RequestedStartItem() => GetHeaderValue("Start-Item");
public string? RequestedStartItem() => GetHeaderValue(Constants.DeliveryApi.HeaderNames.StartItem);
}
@@ -36,7 +36,7 @@ internal abstract class RoutingServiceBase
}
protected static string GetContentRoute(DomainAndUri domainAndUri, Uri contentRoute)
=> $"{domainAndUri.ContentId}{DomainUtilities.PathRelativeToDomain(domainAndUri.Uri, contentRoute.AbsolutePath)}";
=> $"{domainAndUri.ContentId}{DomainUtilities.PathRelativeToDomain(domainAndUri.Uri, contentRoute.LocalPath)}"; // Use LocalPath over AbsolutePath to keep the path decoded.
protected DomainAndUri? GetDomainAndUriForRoute(Uri contentUrl)
{
@@ -13,6 +13,9 @@
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Umbraco.Tests.UnitTests</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Umbraco.Tests.Integration</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>DynamicProxyGenAssembly2</_Parameter1>
</AssemblyAttribute>
@@ -1,3 +1,4 @@
using System.Globalization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Headers;
using Microsoft.Extensions.Options;
@@ -48,16 +49,26 @@ public sealed class ConfigureImageSharpMiddlewareOptions : IConfigureOptions<Ima
return Task.CompletedTask;
}
int width = context.Parser.ParseValue<int>(context.Commands.GetValueOrDefault(ResizeWebProcessor.Width), context.Culture);
if (width <= 0 || width > _imagingSettings.Resize.MaxWidth)
if (context.Commands.Contains(ResizeWebProcessor.Width))
{
context.Commands.Remove(ResizeWebProcessor.Width);
if (!int.TryParse(context.Commands.GetValueOrDefault(ResizeWebProcessor.Width), NumberStyles.Integer,
CultureInfo.InvariantCulture, out var width)
|| width < 0
|| width >= _imagingSettings.Resize.MaxWidth)
{
context.Commands.Remove(ResizeWebProcessor.Width);
}
}
int height = context.Parser.ParseValue<int>(context.Commands.GetValueOrDefault(ResizeWebProcessor.Height), context.Culture);
if (height <= 0 || height > _imagingSettings.Resize.MaxHeight)
if (context.Commands.Contains(ResizeWebProcessor.Height))
{
context.Commands.Remove(ResizeWebProcessor.Height);
if (!int.TryParse(context.Commands.GetValueOrDefault(ResizeWebProcessor.Height), NumberStyles.Integer,
CultureInfo.InvariantCulture, out var height)
|| height < 0
|| height >= _imagingSettings.Resize.MaxHeight)
{
context.Commands.Remove(ResizeWebProcessor.Height);
}
}
return Task.CompletedTask;
@@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" VersionOverride="[2.1.8, 3)" />
<PackageReference Include="SixLabors.ImageSharp" VersionOverride="[2.1.11, 3)" />
<PackageReference Include="SixLabors.ImageSharp.Web" VersionOverride="[2.0.2, 3)" />
</ItemGroup>
@@ -7,7 +7,15 @@
<ItemGroup>
<!-- Take top-level depedendency on Azure.Identity, because Microsoft.EntityFrameworkCore.SqlServer depends on a vulnerable version -->
<PackageReference Include="Azure.Identity" />
<!-- Both Azure.Identity, Microsoft.EntityFrameworkCore.SqlServer,NPoco.SqlServer, and more bring in a vulnerable version of System.Text.Json -->
<PackageReference Include="System.Text.Json" />
<!-- Both Microsoft.EntityFrameworkCore.SqlServer and NPoco.SqlServer bring in a vulnerable version of Microsoft.Data.SqlClient -->
<PackageReference Include="Microsoft.Data.SqlClient" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<!-- Both OpenIddict.AspNetCore, Npoco.SqlServer and Microsoft.EntityFrameworkCore.SqlServer bring in a vulnerable version of Microsoft.IdentityModel.JsonWebTokens -->
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens"/>
</ItemGroup>
<ItemGroup>
@@ -170,7 +170,9 @@ internal class SqlServerEFCoreDistributedLockingMechanism<T> : IDistributedLocki
"A transaction with minimum ReadCommitted isolation level is required.");
}
#pragma warning disable EF1002
var rowsAffected = await dbContext.Database.ExecuteSqlRawAsync(@$"SET LOCK_TIMEOUT {(int)_timeout.TotalMilliseconds};UPDATE umbracoLock WITH (REPEATABLEREAD) SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id={LockId}");
#pragma warning restore EF1002
if (rowsAffected == 0)
{
@@ -127,10 +127,16 @@ internal class EFCoreScope<TDbContext> : CoreScope, IEfCoreScope<TDbContext>
Locks.ClearLocks(InstanceId);
if (ParentScope is null)
// Since we can nest EFCoreScopes in other scopes derived from CoreScope, we should check whether our ParentScope OR the base ParentScope exists.
// Only if neither do do we take responsibility for ensuring the locks are cleared.
// Eventually the highest parent will clear the locks.
// Further, these locks are a reference to the locks of the highest parent anyway (see the constructor of CoreScope).
#pragma warning disable SA1100 // Do not prefix calls with base unless local implementation exists (justification: provides additional clarify here that this is defined on the base class).
if (ParentScope is null && base.HasParentScope is false)
{
Locks.EnsureLocksCleared(InstanceId);
}
#pragma warning restore SA1100 // Do not prefix calls with base unless local implementation exists
_efCoreScopeProvider.PopAmbientScope();
@@ -7,6 +7,14 @@
<ItemGroup>
<!-- Take top-level depedendency on Azure.Identity, because Microsoft.EntityFrameworkCore.SqlServer depends on a vulnerable version -->
<PackageReference Include="Azure.Identity" />
<!-- Take top-level depedendency on Microsoft.Extensions.Caching.Memory, because Microsoft.EntityFrameworkCore.* depends on a vulnerable version -->
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
<!-- Both Azure.Identity, Microsoft.EntityFrameworkCore.SqlServer,NPoco.SqlServer, and more bring in a vulnerable version of System.Text.Json -->
<PackageReference Include="System.Text.Json" />
<!-- Both Microsoft.EntityFrameworkCore.SqlServer and NPoco.SqlServer bring in a vulnerable version of Microsoft.Data.SqlClient -->
<PackageReference Include="Microsoft.Data.SqlClient" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" />
@@ -7,7 +7,15 @@
<ItemGroup>
<!-- Take top-level depedendency on Azure.Identity, because NPoco.SqlServer depends on a vulnerable version -->
<PackageReference Include="Azure.Identity" />
<!-- Both Azure.Identity, Microsoft.EntityFrameworkCore.SqlServer,NPoco.SqlServer, and more bring in a vulnerable version of System.Text.Json -->
<PackageReference Include="System.Text.Json" />
<!-- Both Microsoft.EntityFrameworkCore.SqlServer and NPoco.SqlServer bring in a vulnerable version of Microsoft.Data.SqlClient -->
<PackageReference Include="Microsoft.Data.SqlClient" />
<PackageReference Include="NPoco.SqlServer" />
<!-- Both OpenIddict.AspNetCore, Npoco.SqlServer and Microsoft.EntityFrameworkCore.SqlServer bring in a vulnerable version of Microsoft.IdentityModel.JsonWebTokens -->
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens"/>
</ItemGroup>
<ItemGroup>
@@ -30,13 +30,13 @@
</Target>
<Target Name="BuildBelle">
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Client\" Command="npm ci --no-fund --no-audit --prefer-offline" />
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Client\" Command="npm run build:skip-tests" />
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Client\" Command="npm ci --no-fund --no-audit --prefer-offline" Timeout="600000" />
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Client\" Command="npm run build:skip-tests" Timeout="600000" />
</Target>
<Target Name="BuildLogin">
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Login\" Command="npm ci --no-fund --no-audit --prefer-offline" />
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Login\" Command="npm run build" />
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Login\" Command="npm ci --no-fund --no-audit --prefer-offline" Timeout="600000" />
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Login\" Command="npm run build" Timeout="600000" />
</Target>
<Target Name="CleanStaticAssetsPreconditions" AfterTargets="Clean" Condition="'$(UmbracoBuild)' == ''">
@@ -40,7 +40,7 @@
<!-- Generate JSON schema on build (and before copying to project) -->
<Target Name="GenerateAppsettingsSchema" BeforeTargets="Build;CopyUmbracoJsonSchemaFiles" Condition="!Exists('$(_UmbracoCmsJsonSchemaReference)')">
<Message Text="Generating $(_UmbracoCmsJsonSchemaReference) because it doesn't exist" Importance="high" />
<Exec WorkingDirectory="$(MSBuildThisFileDirectory)..\..\tools\Umbraco.JsonSchema" Command="dotnet run --configuration $(Configuration) -- --outputFile &quot;$(MSBuildThisFileDirectory)$(_UmbracoCmsJsonSchemaReference)&quot;" />
<Exec WorkingDirectory="$(MSBuildThisFileDirectory)..\..\tools\Umbraco.JsonSchema" Command="dotnet run --configuration $(Configuration) -- --outputFile &quot;$(MSBuildThisFileDirectory)$(_UmbracoCmsJsonSchemaReference)&quot;" Timeout="600000" />
</Target>
<!-- Remove generated JSON schema on clean -->
@@ -48,6 +48,13 @@ public static class AppCacheExtensions
return default;
}
// If we've retrieved the specific string that represents null in the cache, return it only if we are requesting it (via a typed request for a string).
// Otherwise consider it a null value.
if (RetrievedNullRepresentationInCache(result))
{
return RequestedNullRepresentationInCache<T>() ? (T)result : default;
}
return result.TryConvertTo<T>().Result;
}
@@ -59,6 +66,18 @@ public static class AppCacheExtensions
return default;
}
// If we've retrieved the specific string that represents null in the cache, return it only if we are requesting it (via a typed request for a string).
// Otherwise consider it a null value.
if (RetrievedNullRepresentationInCache(result))
{
return RequestedNullRepresentationInCache<T>() ? (T)result : default;
}
return result.TryConvertTo<T>().Result;
}
private static bool RetrievedNullRepresentationInCache(object result) => result == (object)Cms.Core.Constants.Cache.NullRepresentationInCache;
private static bool RequestedNullRepresentationInCache<T>() => typeof(T) == typeof(string);
}
+2
View File
@@ -22,4 +22,6 @@ public static class CacheKeys
public const string ContentRecycleBinCacheKey = "recycleBin_content";
public const string MediaRecycleBinCacheKey = "recycleBin_media";
public const string MemberUserNameCachePrefix = "uRepo_userNameKey+";
}
@@ -142,7 +142,9 @@ public static class DistributedCacheExtensions
Id = x.Item.Id,
Key = x.Item.Key,
ChangeTypes = x.ChangeTypes,
Blueprint = x.Item.Blueprint
Blueprint = x.Item.Blueprint,
PublishedCultures = x.PublishedCultures?.ToArray(),
UnpublishedCultures = x.UnpublishedCultures?.ToArray()
});
dc.RefreshByPayload(ContentCacheRefresher.UniqueId, payloads);
@@ -157,15 +159,30 @@ public static class DistributedCacheExtensions
=> dc.RefreshMemberCache(members.AsEnumerable());
public static void RefreshMemberCache(this DistributedCache dc, IEnumerable<IMember> members)
=> dc.RefreshByPayload(MemberCacheRefresher.UniqueId, members.DistinctBy(x => (x.Id, x.Username)).Select(x => new MemberCacheRefresher.JsonPayload(x.Id, x.Username, false)));
=> dc.RefreshByPayload(
MemberCacheRefresher.UniqueId,
GetPayloads(members, false));
[Obsolete("Use the overload accepting IEnumerable instead to avoid allocating arrays. This overload will be removed in Umbraco 13.")]
public static void RemoveMemberCache(this DistributedCache dc, params IMember[] members)
=> dc.RemoveMemberCache(members.AsEnumerable());
public static void RemoveMemberCache(this DistributedCache dc, IEnumerable<IMember> members)
=> dc.RefreshByPayload(MemberCacheRefresher.UniqueId, members.DistinctBy(x => (x.Id, x.Username)).Select(x => new MemberCacheRefresher.JsonPayload(x.Id, x.Username, true)));
=> dc.RefreshByPayload(
MemberCacheRefresher.UniqueId,
GetPayloads(members, true));
// Internal for unit test.
internal static IEnumerable<MemberCacheRefresher.JsonPayload> GetPayloads(IEnumerable<IMember> members, bool removed)
=> members
.DistinctBy(x => (x.Id, x.Username))
.Select(x => new MemberCacheRefresher.JsonPayload(x.Id, x.Username, removed)
{
PreviousUsername = x.HasAdditionalData &&
x.AdditionalData!.TryGetValue(Cms.Core.Constants.Entities.AdditionalDataKeys.MemberPreviousUserName, out var previousUsername)
? previousUsername?.ToString()
: null,
});
#endregion
@@ -0,0 +1,16 @@
namespace Umbraco.Cms.Core.Cache.PartialViewCacheInvalidators;
/// <summary>
/// Defines behaviours for clearing of cached partials views that are configured to be cached individually by member.
/// </summary>
public interface IMemberPartialViewCacheInvalidator
{
/// <summary>
/// Clears the partial view cache items for the specified member ids.
/// </summary>
/// <param name="memberIds">The member Ids to clear the cache for.</param>
/// <remarks>
/// Called from the <see cref="MemberCacheRefresher"/> when a member is saved or deleted.
/// </remarks>
void ClearPartialViewCacheItems(IEnumerable<int> memberIds);
}
@@ -0,0 +1,9 @@
namespace Umbraco.Cms.Core.Cache.PartialViewCacheInvalidators;
internal class NoopMemberPartialViewCacheInvalidator : IMemberPartialViewCacheInvalidator
{
public void ClearPartialViewCacheItems(IEnumerable<int> memberIds)
{
// No operation performed, this is a no-op implementation.
}
}
@@ -182,6 +182,10 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase<ContentCac
public TreeChangeTypes ChangeTypes { get; init; }
public bool Blueprint { get; init; }
public string[]? PublishedCultures { get; init; }
public string[]? UnpublishedCultures { get; init; }
}
#endregion
@@ -1,5 +1,8 @@
// using Newtonsoft.Json;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.Cache.PartialViewCacheInvalidators;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
@@ -15,10 +18,37 @@ public sealed class MemberCacheRefresher : PayloadCacheRefresherBase<MemberCache
public static readonly Guid UniqueId = Guid.Parse("E285DF34-ACDC-4226-AE32-C0CB5CF388DA");
private readonly IIdKeyMap _idKeyMap;
private readonly IMemberPartialViewCacheInvalidator _memberPartialViewCacheInvalidator;
public MemberCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IIdKeyMap idKeyMap, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
: base(appCaches, serializer, eventAggregator, factory) =>
[Obsolete("Use the non obsoleted constructor instead. Scheduled for removal in v17")]
public MemberCacheRefresher(
AppCaches appCaches,
IJsonSerializer serializer,
IIdKeyMap idKeyMap,
IEventAggregator eventAggregator,
ICacheRefresherNotificationFactory factory)
: this(
appCaches,
serializer,
idKeyMap,
eventAggregator,
factory,
StaticServiceProvider.Instance.GetRequiredService<IMemberPartialViewCacheInvalidator>())
{
}
public MemberCacheRefresher(
AppCaches appCaches,
IJsonSerializer serializer,
IIdKeyMap idKeyMap,
IEventAggregator eventAggregator,
ICacheRefresherNotificationFactory factory,
IMemberPartialViewCacheInvalidator memberPartialViewCacheInvalidator)
: base(appCaches, serializer, eventAggregator, factory)
{
_idKeyMap = idKeyMap;
_memberPartialViewCacheInvalidator = memberPartialViewCacheInvalidator;
}
#region Indirect
@@ -40,6 +70,8 @@ public sealed class MemberCacheRefresher : PayloadCacheRefresherBase<MemberCache
public string? Username { get; }
public string? PreviousUsername { get; set; }
public bool Removed { get; }
}
@@ -67,16 +99,36 @@ public sealed class MemberCacheRefresher : PayloadCacheRefresherBase<MemberCache
private void ClearCache(params JsonPayload[] payloads)
{
AppCaches.ClearPartialViewCache();
// Clear the partial views cache for all partials that are cached by member, for the updates members.
_memberPartialViewCacheInvalidator.ClearPartialViewCacheItems(payloads.Select(p => p.Id));
Attempt<IAppPolicyCache?> memberCache = AppCaches.IsolatedCaches.Get<IMember>();
foreach (JsonPayload p in payloads)
{
_idKeyMap.ClearCache(p.Id);
if (memberCache.Success)
if (memberCache.Success is false)
{
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, int>(p.Id));
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(p.Username));
continue;
}
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, int>(p.Id));
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(p.Username));
// This specific cache key was introduced to fix an issue where the member username could not be the same as the member id, because the cache keys collided.
// This is done in a bit of a hacky way, because the cache key is created internally in the repository, but we need to clear it here.
// Ideally, we want to use a shared way of generating the key between this and the repository.
// Additionally, the RepositoryCacheKeys actually caches the string to avoid re-allocating memory; we would like to also use this in the repository
// See:
// https://github.com/umbraco/Umbraco-CMS/pull/17350
// https://github.com/umbraco/Umbraco-CMS/pull/17815
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(CacheKeys.MemberUserNameCachePrefix + p.Username));
// If provided, clear the cache by the previous user name too.
if (string.IsNullOrEmpty(p.PreviousUsername) is false)
{
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(p.PreviousUsername));
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(CacheKeys.MemberUserNameCachePrefix + p.PreviousUsername));
}
}
}
@@ -11,6 +11,7 @@ public class RepositoryCachePolicyOptions
public RepositoryCachePolicyOptions(Func<int> performCount)
{
PerformCount = performCount;
CacheNullValues = false;
GetAllCacheValidateCount = true;
GetAllCacheAllowZeroCount = false;
}
@@ -21,6 +22,7 @@ public class RepositoryCachePolicyOptions
public RepositoryCachePolicyOptions()
{
PerformCount = null;
CacheNullValues = false;
GetAllCacheValidateCount = false;
GetAllCacheAllowZeroCount = false;
}
@@ -30,6 +32,11 @@ public class RepositoryCachePolicyOptions
/// </summary>
public Func<int>? PerformCount { get; set; }
/// <summary>
/// True if the Get method will cache null results so that the db is not hit for repeated lookups
/// </summary>
public bool CacheNullValues { get; set; }
/// <summary>
/// True/false as to validate the total item count when all items are returned from cache, the default is true but this
/// means that a db lookup will occur - though that lookup will probably be significantly less expensive than the
+9 -1
View File
@@ -51,7 +51,15 @@ public class ValueEditorCache : IValueEditorCache
{
foreach (Dictionary<int, IDataValueEditor> editors in _valueEditorCache.Values)
{
editors.Remove(id);
if (editors.TryGetValue(id, out IDataValueEditor? editor))
{
if (editor is IDisposable disposable)
{
disposable.Dispose();
}
editors.Remove(id);
}
}
}
}
+1 -1
View File
@@ -34,7 +34,7 @@ public class TypeFinder : ITypeFinder
"ServiceStack.", "SqlCE4Umbraco,", "Superpower,", // used by Serilog
"System.", "TidyNet,", "TidyNet.", "WebDriver,", "itextsharp,", "mscorlib,", "NUnit,", "NUnit.", "NUnit3.",
"Selenium.", "ImageProcessor", "MiniProfiler.", "Owin,", "SQLite",
"ReSharperTestRunner", "ReSharperTestRunner32", "ReSharperTestRunner64", // These are used by the Jetbrains Rider IDE and Visual Studio ReSharper Extension
"ReSharperTestRunner", "ReSharperTestRunner32", "ReSharperTestRunner64", "ReSharperTestRunnerArm32", "ReSharperTestRunnerArm64", // These are used by the Jetbrains Rider IDE and Visual Studio ReSharper Extension
};
private static readonly ConcurrentDictionary<string, Type?> TypeNamesCache = new();
@@ -160,6 +160,7 @@ public class ContentSettings
internal const bool StaticDisableUnpublishWhenReferenced = false;
internal const bool StaticAllowEditInvariantFromNonDefault = false;
internal const bool StaticShowDomainWarnings = true;
internal const bool StaticShowUnroutableContentWarnings = true;
/// <summary>
/// Gets or sets a value for the content notification settings.
@@ -285,4 +286,10 @@ public class ContentSettings
/// </summary>
[DefaultValue(StaticShowDomainWarnings)]
public bool ShowDomainWarnings { get; set; } = StaticShowDomainWarnings;
/// <summary>
/// Gets or sets a value indicating whether to show unroutable content warnings.
/// </summary>
[DefaultValue(StaticShowUnroutableContentWarnings)]
public bool ShowUnroutableContentWarnings { get; set; } = StaticShowUnroutableContentWarnings;
}
@@ -129,6 +129,7 @@ public class GlobalSettings
/// <summary>
/// Gets or sets a value indicating whether to install the database when it is missing.
/// </summary>
[Obsolete("This option will be removed in V16.")]
[DefaultValue(StaticInstallMissingDatabase)]
public bool InstallMissingDatabase { get; set; } = StaticInstallMissingDatabase;
@@ -16,6 +16,7 @@ public class ModelsBuilderSettings
internal const string StaticModelsDirectory = "~/umbraco/models";
internal const bool StaticAcceptUnsafeModelsDirectory = false;
internal const int StaticDebugLevel = 0;
internal const bool StaticIncludeVersionNumberInGeneratedModels = true;
private bool _flagOutOfDateModels = true;
/// <summary>
@@ -78,4 +79,16 @@ public class ModelsBuilderSettings
/// <remarks>0 means minimal (safe on live site), anything else means more and more details (maybe not safe).</remarks>
[DefaultValue(StaticDebugLevel)]
public int DebugLevel { get; set; } = StaticDebugLevel;
/// <summary>
/// Gets or sets a value indicating whether the version number should be included in generated models.
/// </summary>
/// <remarks>
/// By default this is written to the <see cref="System.CodeDom.Compiler.GeneratedCodeAttribute"/> output in
/// generated code for each property of the model. This can be useful for debugging purposes but isn't essential,
/// and it has the causes the generated code to change every time Umbraco is upgraded. In turn, this leads
/// to unnecessary code file changes that need to be checked into source control. Default is <c>true</c>.
/// </remarks>
[DefaultValue(StaticIncludeVersionNumberInGeneratedModels)]
public bool IncludeVersionNumberInGeneratedModels { get; set; } = StaticIncludeVersionNumberInGeneratedModels;
}
@@ -15,6 +15,7 @@ public class RequestHandlerSettings
{
internal const bool StaticAddTrailingSlash = true;
internal const string StaticConvertUrlsToAscii = "try";
internal const string StaticConvertFileNamesToAscii = "false";
internal const bool StaticEnableDefaultCharReplacements = true;
internal static readonly CharItem[] DefaultCharCollection =
@@ -73,6 +74,22 @@ public class RequestHandlerSettings
/// </summary>
public bool ShouldTryConvertUrlsToAscii => ConvertUrlsToAscii.InvariantEquals("try");
/// <summary>
/// Gets or sets a value indicating whether to convert file names to ASCII (valid values: "true", "try" or "false").
/// </summary>
[DefaultValue(StaticConvertFileNamesToAscii)]
public string ConvertFileNamesToAscii { get; set; } = StaticConvertFileNamesToAscii;
/// <summary>
/// Gets a value indicating whether URLs should be converted to ASCII.
/// </summary>
public bool ShouldConvertFileNamesToAscii => ConvertFileNamesToAscii.InvariantEquals("true");
/// <summary>
/// Gets a value indicating whether URLs should be tried to be converted to ASCII.
/// </summary>
public bool ShouldTryConvertFileNamesToAscii => ConvertFileNamesToAscii.InvariantEquals("try");
/// <summary>
/// Disable all default character replacements
/// </summary>
@@ -8,7 +8,7 @@ namespace Umbraco.Cms.Core.Configuration.Models;
public class RichTextEditorSettings
{
internal const string StaticValidElements =
"+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-s[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],figure,figcaption,cite,video[*],audio[*],picture[*],source[*],canvas[*]";
"+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-s[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],figure,figcaption,cite,video[*],audio[*],picture[*],source[*],canvas[*],code";
internal const string StaticInvalidElements = "font";
@@ -2,6 +2,7 @@
// See LICENSE for more details.
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace Umbraco.Cms.Core.Configuration.Models;
@@ -19,12 +20,16 @@ public class SecuritySettings
internal const bool StaticAllowEditInvariantFromNonDefault = false;
internal const bool StaticAllowConcurrentLogins = false;
internal const string StaticAuthCookieName = "UMB_UCONTEXT";
internal const bool StaticUsernameIsEmail = true;
internal const bool StaticMemberRequireUniqueEmail = true;
internal const string StaticAllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+\\";
internal const int StaticMemberDefaultLockoutTimeInMinutes = 30 * 24 * 60;
internal const int StaticUserDefaultLockoutTimeInMinutes = 30 * 24 * 60;
internal const long StaticUserDefaultFailedLoginDurationInMilliseconds = 1000;
internal const long StaticUserMinimumFailedLoginDurationInMilliseconds = 250;
/// <summary>
/// Gets or sets a value indicating whether to keep the user logged in.
@@ -58,7 +63,14 @@ public class SecuritySettings
/// <summary>
/// Gets or sets a value indicating whether the user's email address is to be considered as their username.
/// </summary>
public bool UsernameIsEmail { get; set; } = true;
[DefaultValue(StaticUsernameIsEmail)]
public bool UsernameIsEmail { get; set; } = StaticUsernameIsEmail;
/// <summary>
/// Gets or sets a value indicating whether the member's email address must be unique.
/// </summary>
[DefaultValue(StaticMemberRequireUniqueEmail)]
public bool MemberRequireUniqueEmail { get; set; } = StaticMemberRequireUniqueEmail;
/// <summary>
/// Gets or sets the set of allowed characters for a username
@@ -116,4 +128,28 @@ public class SecuritySettings
/// </summary>
[DefaultValue(StaticAllowConcurrentLogins)]
public bool AllowConcurrentLogins { get; set; } = StaticAllowConcurrentLogins;
/// <summary>
/// Gets or sets the default duration (in milliseconds) of failed login attempts.
/// </summary>
/// <value>
/// The default duration (in milliseconds) of failed login attempts.
/// </value>
/// <remarks>
/// The user login endpoint ensures that failed login attempts take at least as long as the average successful login.
/// However, if no successful logins have occurred, this value is used as the default duration.
/// </remarks>
[Range(0, int.MaxValue)] // TODO (V17): Change property type to short and update maximum range to short.MaxValue
[DefaultValue(StaticUserDefaultFailedLoginDurationInMilliseconds)]
public long UserDefaultFailedLoginDurationInMilliseconds { get; set; } = StaticUserDefaultFailedLoginDurationInMilliseconds;
/// <summary>
/// Gets or sets the minimum duration (in milliseconds) of failed login attempts.
/// </summary>
/// <value>
/// The minimum duration (in milliseconds) of failed login attempts.
/// </value>
[Range(0, int.MaxValue)] // TODO (V17): Change property type to short and update maximum range to short.MaxValue
[DefaultValue(StaticUserMinimumFailedLoginDurationInMilliseconds)]
public long UserMinimumFailedLoginDurationInMilliseconds { get; set; } = StaticUserMinimumFailedLoginDurationInMilliseconds;
}
+15
View File
@@ -0,0 +1,15 @@
namespace Umbraco.Cms.Core;
public static partial class Constants
{
public static class Cache
{
/// <summary>
/// Defines the string used to represent a null value in the cache.
/// </summary>
/// <remarks>
/// Used in conjunction with the option to cache null values on the repository caches, so we
/// can distinguish a true null "not found" value and a cached null value.</remarks>
public const string NullRepresentationInCache = "*NULL*";
}
}
+31 -3
View File
@@ -1,4 +1,4 @@
namespace Umbraco.Cms.Core;
namespace Umbraco.Cms.Core;
public static partial class Constants
{
@@ -24,14 +24,42 @@ public static partial class Constants
public static class OutputCache
{
/// <summary>
/// Output cache policy name for content
/// Output cache policy name for content.
/// </summary>
public const string ContentCachePolicy = "DeliveryApiContent";
/// <summary>
/// Output cache policy name for media
/// Output cache policy name for media.
/// </summary>
public const string MediaCachePolicy = "DeliveryApiMedia";
}
/// <summary>
/// Constants for Delivery API header names.
/// </summary>
public static class HeaderNames
{
/// <summary>
/// Header name for accept language.
/// </summary>
public const string AcceptLanguage = "Accept-Language";
/// <summary>
/// Header name for API key.
/// </summary>
public const string ApiKey = "Api-Key";
/// <summary>
/// Header name for preview.
/// </summary>
public const string Preview = "Preview";
/// <summary>
/// Header name for start item.
/// </summary>
public const string StartItem = "Start-Item";
}
}
}
+14
View File
@@ -0,0 +1,14 @@
namespace Umbraco.Cms.Core;
public static partial class Constants
{
public static class Entities
{
public static class AdditionalDataKeys
{
public const string MemberPreviousUserName = "previousUsername";
public const string MemberGroupPreviousName = "previousName";
}
}
}
@@ -15,6 +15,27 @@ public class ApiContentPathResolver : IApiContentPathResolver
_apiPublishedContentCache = apiPublishedContentCache;
}
[Obsolete("No longer used in V15. Scheduled for removal in V15.")]
public virtual bool IsResolvablePath(string path)
{
// File requests will blow up with an downstream exception in GetRequiredPublishedSnapshot, which fails due to an UmbracoContext
// not being available for what's considered a static file request.
// See: https://github.com/umbraco/Umbraco-CMS/issues/19051
// Given a URL segment and hence route can't contain a period, we can safely assume that if the last segment of the path contains
// a period, it's a file request and should return null here.
if (IsFileRequest(path))
{
return false;
}
return true;
}
private static bool IsFileRequest(string path) => path
.Split('/', StringSplitOptions.RemoveEmptyEntries)
.LastOrDefault()?
.Contains('.') is true;
public virtual IPublishedContent? ResolveContentPath(string path)
{
path = path.EnsureStartsWith("/");
@@ -4,5 +4,8 @@ namespace Umbraco.Cms.Core.DeliveryApi;
public interface IApiContentPathResolver
{
[Obsolete("No longer used in V15. Scheduled for removal in V15.")]
bool IsResolvablePath(string path) => true;
IPublishedContent? ResolveContentPath(string path);
}
@@ -0,0 +1,12 @@
namespace Umbraco.Cms.Core.DeliveryApi;
public interface ICurrentMemberClaimsProvider
{
/// <summary>
/// Retrieves the claims for the currently logged in member.
/// </summary>
/// <remarks>
/// This is used by the OIDC user info endpoint to supply "current user" info.
/// </remarks>
Task<Dictionary<string, object>> GetClaimsAsync();
}
@@ -0,0 +1,6 @@
namespace Umbraco.Cms.Core.DeliveryApi;
public class NoopCurrentMemberClaimsProvider : ICurrentMemberClaimsProvider
{
public Task<Dictionary<string, object>> GetClaimsAsync() => Task.FromResult(new Dictionary<string, object>());
}
@@ -8,12 +8,14 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Cache.PartialViewCacheInvalidators;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Grid;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Diagnostics;
using Umbraco.Cms.Core.Dictionary;
using Umbraco.Cms.Core.DynamicRoot;
using Umbraco.Cms.Core.Editors;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Features;
@@ -35,7 +37,6 @@ using Umbraco.Cms.Core.Runtime;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.DynamicRoot;
using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Core.Telemetry;
using Umbraco.Cms.Core.Templates;
@@ -341,6 +342,11 @@ namespace Umbraco.Cms.Core.DependencyInjection
// Data type configuration cache
Services.AddUnique<IDataTypeConfigurationCache, DataTypeConfigurationCache>();
Services.AddNotificationHandler<DataTypeCacheRefresherNotification, DataTypeConfigurationCacheRefresher>();
// Partial view cache invalidators (no-op, shipped implementation is added in Umbraco.Web.Website, but we
// need this to ensure we have a service registered for this interface even in headless setups).
// See: https://github.com/umbraco/Umbraco-CMS/issues/19661
Services.AddUnique<IMemberPartialViewCacheInvalidator, NoopMemberPartialViewCacheInvalidator>();
}
}
}
@@ -27,7 +27,9 @@ public class RootDynamicRootOriginFinder : IDynamicRootOriginFinder
return null;
}
var entity = _entityService.Get(query.Context.ParentKey);
// when creating new content, CurrentKey will be null - fallback to using ParentKey
Guid entityKey = query.Context.CurrentKey ?? query.Context.ParentKey;
var entity = _entityService.Get(entityKey);
if (entity is null || _allowedObjectTypes.Contains(entity.NodeObjectType) is false)
{
@@ -20,12 +20,14 @@ public class SiteDynamicRootOriginFinder : RootDynamicRootOriginFinder
public override Guid? FindOriginKey(DynamicRootNodeQuery query)
{
if (query.OriginAlias != SupportedOriginType || query.Context.CurrentKey.HasValue is false)
if (query.OriginAlias != SupportedOriginType)
{
return null;
}
IEntitySlim? entity = _entityService.Get(query.Context.CurrentKey.Value);
// when creating new content, CurrentKey will be null - fallback to using ParentKey
Guid entityKey = query.Context.CurrentKey ?? query.Context.ParentKey;
IEntitySlim? entity = _entityService.Get(entityKey);
if (entity is null || entity.NodeObjectType != Constants.ObjectTypes.Document)
{
return null;
File diff suppressed because it is too large Load Diff
@@ -1469,6 +1469,7 @@ Mange hilsner fra Umbraco robotten
<key alias="publishWithMissingDomain">Der er ikke noget domæne konfigureret for %0%, kontakt vensligst en
administrator, se loggen for mere information
</key>
<key alias="publishWithNoUrl">Dokumentet har ikke nogen URL, muligvis grundet en kollision med et andet dokuments navn. Flere detaljer kan ses under Info.</key>
<key alias="copySuccessMessage">Dit systems information er blevet kopieret til udklipsholderen</key>
<key alias="cannotCopyInformation">Kunne desværre ikke kopiere dit systems information til udklipsholderen</key>
<key alias="webhookSaved">Webhook gemt</key>
File diff suppressed because it is too large Load Diff
@@ -167,6 +167,7 @@
<key alias="move">Media moved</key>
<key alias="copy">Media copied</key>
<key alias="save">Media saved</key>
<key alias="new">Media created</key>
</area>
<area alias="auditTrails">
<key alias="atViewingFor">Viewing for</key>
@@ -190,6 +191,7 @@
<key alias="smallPublishVariant">Publish</key>
<key alias="smallMove">Move</key>
<key alias="smallSave">Save</key>
<key alias="smallNew">New</key>
<key alias="smallSaveVariant">Save</key>
<key alias="smallDelete">Delete</key>
<key alias="smallUnpublish">Unpublish</key>
@@ -292,6 +294,7 @@
Content.
</key>
<key alias="nestedContentDeleteAllItems">Are you sure you want to delete all items?</key>
<key alias="nestedContentDeleteItem">Are you sure you want to delete this item?</key>
<key alias="nestedContentNoContentTypes">No Content Types are configured for this property.</key>
<key alias="nestedContentAddElementType">Add Element Type</key>
<key alias="nestedContentSelectElementTypeModalTitle">Select Element Type</key>
@@ -329,6 +332,7 @@
<key alias="schedulePublishHelp">Select the date and time to publish and/or unpublish the content item.</key>
<key alias="createEmpty">Create new</key>
<key alias="createFromClipboard">Paste from clipboard</key>
<key alias="removeItem">Remove item</key>
<key alias="nodeIsInTrash">This item is in the Recycle Bin</key>
<key alias="noProperties">No content can be added for this item</key>
<key alias="variantSaveNotAllowed">Save is not allowed</key>
@@ -1700,6 +1704,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
<key alias="publishWithMissingDomain">There is no domain configured for %0%, please contact an administrator, see
log for more information
</key>
<key alias="publishWithNoUrl">The document does not have a URL, possibly due to a naming collision with another document. More details can be found under Info.</key>
<key alias="copySuccessMessage">Your system information has successfully been copied to the clipboard</key>
<key alias="cannotCopyInformation">Could not copy your system information to the clipboard</key>
<key alias="webhookSaved">Webhook saved</key>
@@ -165,6 +165,7 @@
<key alias="move">Media moved</key>
<key alias="copy">Media copied</key>
<key alias="save">Media saved</key>
<key alias="new">Media created</key>
</area>
<area alias="auditTrails">
<key alias="atViewingFor">Viewing for</key>
@@ -189,6 +190,7 @@
<key alias="smallPublishVariant">Publish</key>
<key alias="smallMove">Move</key>
<key alias="smallSave">Save</key>
<key alias="smallNew">New</key>
<key alias="smallSaveVariant">Save</key>
<key alias="smallDelete">Delete</key>
<key alias="smallUnpublish">Unpublish</key>
@@ -288,6 +290,7 @@
<![CDATA[<a href="https://docs.umbraco.com/umbraco-cms/fundamentals/data/scheduled-publishing#timezones" target="_blank" rel="noopener">What does this mean?</a>]]></key>
<key alias="nestedContentDeleteItem">Are you sure you want to delete this item?</key>
<key alias="nestedContentDeleteAllItems">Are you sure you want to delete all items?</key>
<key alias="nestedContentDeleteItem">Are you sure you want to delete this item?</key>
<key alias="nestedContentEditorNotSupported">Property %0% uses editor %1% which is not supported by Nested
Content.
</key>
@@ -328,6 +331,7 @@
<key alias="schedulePublishHelp">Select the date and time to publish and/or unpublish the content item.</key>
<key alias="createEmpty">Create new</key>
<key alias="createFromClipboard">Paste from clipboard</key>
<key alias="removeItem">Remove item</key>
<key alias="nodeIsInTrash">This item is in the Recycle Bin</key>
<key alias="variantSaveNotAllowed">Save is not allowed</key>
<key alias="variantPublishNotAllowed">Publish is not allowed</key>
@@ -1732,6 +1736,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
<key alias="publishWithMissingDomain">There is no domain configured for %0%, please contact an administrator, see
log for more information
</key>
<key alias="publishWithNoUrl">The document does not have a URL, possibly due to a naming collision with another document. More details can be found under Info.</key>
<key alias="preventCleanupEnableError">An error occurred while enabling version cleanup for %0%</key>
<key alias="preventCleanupDisableError">An error occurred while disabling version cleanup for %0%</key>
<key alias="copySuccessMessage">Your system information has successfully been copied to the clipboard</key>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+92 -3
View File
@@ -4,9 +4,6 @@
<name>The Umbraco community</name>
<link>https://docs.umbraco.com/umbraco-cms/extending/language-files</link>
</creator>
<area alias="apps">
<key alias="umbContent">Innehåll</key>
</area>
<area alias="actions">
<key alias="assigndomain">Hantera domännamn</key>
<key alias="auditTrail">Hantera versioner</key>
@@ -14,13 +11,16 @@
<key alias="changeDocType">Ändra dokumenttyp</key>
<key alias="copy">Kopiera</key>
<key alias="create">Skapa</key>
<key alias="export">Exportera</key>
<key alias="createGroup">Skapa grupp</key>
<key alias="createPackage">Skapa paket</key>
<key alias="createblueprint">Skapa innehållsmall</key>
<key alias="defaultValue">Standardvärde</key>
<key alias="delete">Ta bort</key>
<key alias="disable">Avaktivera</key>
<key alias="editSettings">Ändra inställningar</key>
<key alias="emptyrecyclebin">Töm papperskorgen</key>
<key alias="enable">Aktivera</key>
<key alias="exportDocumentType">Exportera dokumenttyp</key>
<key alias="importdocumenttype">Importera dokumenttyp</key>
<key alias="importPackage">Importera paket</key>
@@ -40,6 +40,13 @@
<key alias="translate">Översätt</key>
<key alias="unpublish">Avpublicera</key>
<key alias="update">Uppdatera</key>
<key alias="setPermissions">Ändra rättigheter</key>
<key alias="unlock">Lås upp</key>
<key alias="createblueprint">Skapa innehållsmall</key>
<key alias="resendInvite">Skicka inbjudan igen</key>
<key alias="toggleHideUnavailable">Dölj otillgängliga alternativ</key>
<key alias="changeDataType">Ändra datatyp</key>
<key alias="editContent">Redigera innehåll</key>
</area>
<area alias="actionCategories">
<key alias="administration">Administration</key>
@@ -47,6 +54,10 @@
<key alias="other">Övrigt</key>
<key alias="content">Innehåll</key>
</area>
<area alias="apps">
<key alias="umbContent">Innehåll</key>
<key alias="umbInfo">Info</key>
</area>
<area alias="assignDomain">
<key alias="addNew">Lägg till nytt domännamn</key>
<key alias="domain">Domännamn</key>
@@ -113,8 +124,21 @@
<key alias="areaValidationEntriesExceed"><![CDATA[<strong>%0%</strong> måste maximalt finnas <strong>%3%</strong> time(s).]]></key>
</area>
<area alias="validation">
<key alias="invalidEmail">Ogiltig e-postadress</key>
<key alias="invalidNull">Värde får inte vara 'null'</key>
<key alias="invalidEmpty">Värde får inte vara tomt</key>
<key alias="invalidPattern">Värdet är ogiltigt och matchar inte det korrekta formatet</key>
<key alias="entriesShort"><![CDATA[Minsta %0% poster, kräver <strong>%1%</strong> mer.]]></key>
<key alias="entriesExceed"><![CDATA[Max %0% poster, <strong>%1%</strong> för många.]]></key>
<key alias="entriesAreasMismatch">Innehållskraven har ej godkänts på ett eller flera områden.</key>
<key alias="invalidMemberGroupName">Ogiltigt namn för medlemsgrupp</key>
<key alias="invalidUserGroupName">Ogiltigt namn för användargrupp</key>
<key alias="invalidToken">Ogiltig token</key>
<key alias="invalidUsername">Ogiltigt användarnamn</key>
<key alias="duplicateEmail">E-postadressen '%0%' är redan tagen</key>
<key alias="duplicateUserGroupName">Namnet '%0%' är redan taget</key>
<key alias="duplicateMemberGroupName">Namnet '%0%' är redan taget</key>
<key alias="duplicateUsername">Användarnamnet '%0%' är redan taget</key>
</area>
<area alias="blueprints">
<key alias="createBlueprintFrom"><![CDATA[Skapa en ny innehållsmall för <em>%0%</em>]]></key>
@@ -176,6 +200,7 @@
<key alias="clickToEdit">Klicka för att redigera detta objekt</key>
<key alias="createBy">Skapad av</key>
<key alias="createByDesc">Ursprunglig författare</key>
<key alias="updatedBy" version="7.0">Uppdaterad av</key>
<key alias="createDate">Skapad</key>
<key alias="createDateDesc">Datum/tid som dokumentet skapades</key>
<key alias="documentType">Dokumenttyp</key>
@@ -559,6 +584,29 @@
<key alias="retrieve">Hämta</key>
<key alias="selected">valda</key>
</area>
<area alias="colors">
<key alias="blue">Blå</key>
</area>
<area alias="shortcuts">
<key alias="addGroup">Lägg till grupp</key>
<key alias="addProperty">Lägg till egenskap</key>
<key alias="addEditor">Lägg till redigerare</key>
<key alias="addTemplate">Lägg till mall</key>
<key alias="editDataType">Ändra datatyp</key>
<key alias="shortcut">Genvägar</key>
<key alias="showShortcuts">visa genvägar</key>
<key alias="toggleListView">Toggle list view</key>
<key alias="toggleAllowAsRoot">Toggle allow as root</key>
<key alias="commentLine">Kommentera bort/återställ rader</key>
<key alias="removeLine">Radera rad</key>
<key alias="copyLineUp">Kopiera upp rader</key>
<key alias="copyLineDown">Kopiera ned rader</key>
<key alias="moveLineUp">Flytta upp rader</key>
<key alias="moveLineDown">Flytta ned rader</key>
<key alias="generalHeader">Allmänt</key>
<key alias="editorHeader">Redigerare</key>
<key alias="addTab">Lägg till tabb</key>
</area>
<area alias="graphicheadline">
<key alias="backgroundcolor">Bakgrundsfärg</key>
<key alias="bold">Fetstil</key>
@@ -661,6 +709,11 @@
<key alias="createNewMember">Skapa en ny medlem</key>
<key alias="allMembers">Alla medlemmar</key>
<key alias="memberGroupNoProperties">Medlemsgrupper har inga extra egenskaper för redigering.</key>
<key alias="duplicateMemberLogin">En medlem med detta inlogg existerar redan</key>
<key alias="memberHasGroup">Medlem finns redan i gruppen '%0%'</key>
<key alias="memberHasPassword">Medlem har redan ett lösenord</key>
<key alias="memberLockoutNotEnabled">Avstängning är inte aktiverat för denna medlem</key>
<key alias="memberNotInGroup">Medlem är inte med i gruppen '%0%'</key>
</area>
<area alias="moveOrCopy">
<key alias="choose">Välj sida ovan...</key>
@@ -1087,9 +1140,45 @@
<key alias="sortCreateDateAscending">Äldst</key>
<key alias="sortCreateDateDescending">Nyast</key>
<key alias="sortLastLoginDateDescending">Senaste login</key>
<key alias="duplicateLogin">En användare med detta inlogg existerar redan</key>
<key alias="passwordRequiresDigit">Lösenordet måste innehålla minst en siffra ('0'-'9')</key>
<key alias="passwordRequiresLower">Lösenordet måste innehålla minst en liten bokstav ('a'-'z')</key>
<key alias="passwordRequiresNonAlphanumeric">Lösenordet måste innehålla minst ett specialtecken</key>
<key alias="passwordRequiresUniqueChars">Lösenordet måste innehålla minst %0% unika tecken</key>
<key alias="passwordRequiresUpper">Lösenordet måste innehålla minst en stor bokstav ('A'-'Z')</key>
<key alias="passwordTooShort">Lösenordet måste vara minst %0% tecken</key>
<key alias="userHasPassword">Användaren har redan ett lösenord</key>
<key alias="userHasGroup">Användaren finns redan i gruppen '%0%'</key>
<key alias="userLockoutNotEnabled">Avstängning är inte aktiverat för denna användaren</key>
<key alias="userNotInGroup">Användaren finns inte i gruppen '%0%'</key>
</area>
<area alias="logViewer">
<key alias="selectAllLogLevelFilters">Välj alla</key>
<key alias="deselectAllLogLevelFilters">Avmarkera alla</key>
</area>
<area alias="preview">
<key alias="endLabel">Avsluta</key>
<key alias="endTitle">Avsluta förhandsvisning</key>
<key alias="openWebsiteLabel">Förhandsvisa webbplats</key>
<key alias="openWebsiteTitle">Öppna webbplats i förhandsvisningsläge</key>
<key alias="returnToPreviewHeadline">Förhandsvisa webbplats?</key>
<key alias="returnToPreviewDescription">Du har avslutat förhandsvisningsläge, vill du aktivera det igen för att se senast sparade version av webbplatsen?</key>
<key alias="returnToPreviewAcceptButton">Förhandsvisa senaste version</key>
<key alias="returnToPreviewDeclineButton">Visa publicerad version</key>
<key alias="viewPublishedContentHeadline">Visa publicerad version?</key>
<key alias="viewPublishedContentDescription">Du visar webbplats i förhandsvisningsläge, vill du avsluta förhandsvisning och visa senaste publicerad version av webbplatsen?
</key>
<key alias="viewPublishedContentAcceptButton">Visa publicerad version</key>
<key alias="viewPublishedContentDeclineButton">Fortsätt förhandsvisning</key>
</area>
<area alias="permissions">
<key alias="FolderCreation">Skapa kataloger</key>
<key alias="FileWritingForPackages">Skriva filer till paket</key>
<key alias="FileWriting">Skriva filer</key>
<key alias="MediaFolderCreation">Skapa media-kataloger</key>
</area>
<area alias="analytics">
<key alias="minimalLevelDescription">Vi kommer endast att skicka ett anonymt Id så att vi informeras om att webbplatsens finns.</key>
<key alias="basicLevelDescription">Vi kommer att skicka ett anonymt Id, Umbraco-version och installerade paket</key>
</area>
</language>
@@ -5,7 +5,7 @@
@using Umbraco.Extensions
@{
var isLoggedIn = Context.User?.Identity?.IsAuthenticated ?? false;
var isLoggedIn = Context.User.GetMemberIdentity()?.IsAuthenticated ?? false;
var logoutModel = new PostRedirectModel();
// You can modify this to redirect to a different URL instead of the current one
logoutModel.RedirectUrl = null;
@@ -15,7 +15,7 @@
{
<div class="login-status">
<p>Welcome back <strong>@Context?.User?.Identity?.Name</strong>!</p>
<p>Welcome back <strong>@Context.User?.GetMemberIdentity()?.Name</strong>!</p>
@using (Html.BeginUmbracoForm<UmbLoginStatusController>("HandleLogout", new { RedirectUrl = logoutModel.RedirectUrl }))
{
@@ -330,7 +330,10 @@ public static class ClaimsIdentityExtensions
var firstValue = identity.FindFirstValue(ClaimTypes.NameIdentifier);
if (firstValue is not null)
{
return int.Parse(firstValue, CultureInfo.InvariantCulture);
if (int.TryParse(firstValue, CultureInfo.InvariantCulture, out var id))
{
return id;
}
}
return null;
+32 -6
View File
@@ -1,15 +1,17 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Diagnostics.CodeAnalysis;
namespace Umbraco.Extensions;
public static class IntExtensions
{
/// <summary>
/// Does something 'x' amount of times
/// Does something 'x' amount of times.
/// </summary>
/// <param name="n"></param>
/// <param name="action"></param>
/// <param name="n">Number of times to execute the action.</param>
/// <param name="action">The action to execute.</param>
public static void Times(this int n, Action<int> action)
{
for (var i = 0; i < n; i++)
@@ -19,11 +21,11 @@ public static class IntExtensions
}
/// <summary>
/// Creates a Guid based on an integer value
/// Creates a Guid based on an integer value.
/// </summary>
/// <param name="value"><see cref="int" /> value to convert</param>
/// <param name="value">The <see cref="int" /> value to convert.</param>
/// <returns>
/// <see cref="Guid" />
/// The converted <see cref="Guid" />.
/// </returns>
public static Guid ToGuid(this int value)
{
@@ -31,4 +33,28 @@ public static class IntExtensions
BitConverter.GetBytes(value).CopyTo(bytes, 0);
return new Guid(bytes);
}
/// <summary>
/// Restores a GUID previously created from an integer value using <see cref="ToGuid" />.
/// </summary>
/// <param name="value">The <see cref="Guid" /> value to convert.</param>
/// <param name="result">The converted <see cref="int" />.</param>
/// <returns>
/// True if the <see cref="int" /> value could be created, otherwise false.
/// </returns>
/// <remarks>
/// This is used with Umbraco entities that only have integer references in the database (e.g. users).
/// </remarks>
public static bool TryParseFromGuid(Guid value, [NotNullWhen(true)] out int? result)
{
if (value.ToString().EndsWith("-0000-0000-0000-000000000000") is false)
{
// We have a proper GUID, not one converted from an integer.
result = null;
return false;
}
result = BitConverter.ToInt32(value.ToByteArray());
return true;
}
}
@@ -826,7 +826,7 @@ public static class PublishedContentExtensions
/// </param>
/// <returns></returns>
/// <remarks>
/// This can be useful in order to return all nodes in an entire site by a type when combined with TypedContentAtRoot
/// This can be useful in order to return all nodes in an entire site by a type when combined with ContentAtRoot
/// </remarks>
public static IEnumerable<IPublishedContent> DescendantsOrSelfOfType(
this IEnumerable<IPublishedContent> parentNodes, IVariationContextAccessor variationContextAccessor, string docTypeAlias, string? culture = null) => parentNodes.SelectMany(x =>
@@ -843,7 +843,7 @@ public static class PublishedContentExtensions
/// </param>
/// <returns></returns>
/// <remarks>
/// This can be useful in order to return all nodes in an entire site by a type when combined with TypedContentAtRoot
/// This can be useful in order to return all nodes in an entire site by a type when combined with ContentAtRoot
/// </remarks>
public static IEnumerable<T> DescendantsOrSelf<T>(this IEnumerable<IPublishedContent> parentNodes, IVariationContextAccessor variationContextAccessor, string? culture = null)
where T : class, IPublishedContent =>
@@ -21,16 +21,17 @@ public sealed class PublicAccessHandler :
private void Handle(IEnumerable<IMemberGroup> affectedEntities)
{
var keyName = Constants.Entities.AdditionalDataKeys.MemberGroupPreviousName;
foreach (IMemberGroup grp in affectedEntities)
{
// check if the name has changed
if ((grp.AdditionalData?.ContainsKey("previousName") ?? false)
&& grp.AdditionalData["previousName"] != null
&& grp.AdditionalData["previousName"]?.ToString().IsNullOrWhiteSpace() == false
&& grp.AdditionalData["previousName"]?.ToString() != grp.Name)
if ((grp.AdditionalData?.ContainsKey(keyName) ?? false)
&& grp.AdditionalData[keyName] != null
&& grp.AdditionalData[keyName]?.ToString().IsNullOrWhiteSpace() == false
&& grp.AdditionalData[keyName]?.ToString() != grp.Name)
{
_publicAccessService.RenameMemberGroupRoleRules(
grp.AdditionalData["previousName"]?.ToString(),
grp.AdditionalData[keyName]?.ToString(),
grp.Name);
}
}
+1 -8
View File
@@ -346,19 +346,12 @@ namespace Umbraco.Cms.Core.IO
// our root path, due to relative segments, so better check
if (_ioHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar))
{
// this says that 4.7.2 supports long paths - but Windows does not
// https://docs.microsoft.com/en-us/dotnet/api/system.io.pathtoolongexception?view=netframework-4.7.2
if (path.Length > 260)
{
throw new PathTooLongException($"Path {path} is too long.");
}
return path;
}
// nothing prevents us to reach the file, security-wise, yet it is outside
// this filesystem's root - throw
throw new UnauthorizedAccessException($"File original: [{originalPath}] full: [{path}] is outside this filesystem's root.");
throw new UnauthorizedAccessException($"Requested path {originalPath} is outside this filesystem's root.");
}
/// <summary>
+15
View File
@@ -7,6 +7,21 @@ public sealed class AuditItem : EntityBase, IAuditItem
/// <summary>
/// Initializes a new instance of the <see cref="AuditItem" /> class.
/// </summary>
public AuditItem(int objectId, AuditType type, int userId, string? entityType, DateTime createDate, string? comment = null, string? parameters = null)
{
DisableChangeTracking();
Id = objectId;
Comment = comment;
AuditType = type;
UserId = userId;
EntityType = entityType;
Parameters = parameters;
CreateDate = createDate;
EnableChangeTracking();
}
public AuditItem(int objectId, AuditType type, int userId, string? entityType, string? comment = null, string? parameters = null)
{
DisableChangeTracking();
@@ -1,69 +1,69 @@
namespace Umbraco.Cms.Core.Models.ContentEditing;
/// <summary>
/// The action associated with saving a content item
/// The action associated with saving a content item.
/// </summary>
public enum ContentSaveAction
{
/// <summary>
/// Saves the content item, no publish
/// Saves the content item, no publish.
/// </summary>
Save = 0,
/// <summary>
/// Creates a new content item
/// Creates a new content item.
/// </summary>
SaveNew = 1,
/// <summary>
/// Saves and publishes the content item
/// Saves and publishes the content item.
/// </summary>
Publish = 2,
/// <summary>
/// Creates and publishes a new content item
/// Creates and publishes a new content item.
/// </summary>
PublishNew = 3,
/// <summary>
/// Saves and sends publish notification
/// Saves and sends publish notification.
/// </summary>
SendPublish = 4,
/// <summary>
/// Creates and sends publish notification
/// Creates and sends publish notification.
/// </summary>
SendPublishNew = 5,
/// <summary>
/// Saves and schedules publishing
/// Saves and schedules publishing.
/// </summary>
Schedule = 6,
/// <summary>
/// Creates and schedules publishing
/// Creates and schedules publishing.
/// </summary>
ScheduleNew = 7,
/// <summary>
/// Saves and publishes the content item including all descendants that have a published version
/// Saves and publishes the content item including all descendants that have a published version.
/// </summary>
PublishWithDescendants = 8,
/// <summary>
/// Creates and publishes the content item including all descendants that have a published version
/// Creates and publishes the new content item including all descendants that have a published version.
/// </summary>
PublishWithDescendantsNew = 9,
/// <summary>
/// Saves and publishes the content item including all descendants regardless of whether they have a published version
/// or not
/// or not.
/// </summary>
PublishWithDescendantsForce = 10,
/// <summary>
/// Creates and publishes the content item including all descendants regardless of whether they have a published
/// version or not
/// Creates and publishes the new content item including all descendants regardless of whether they have a published
/// version or not.
/// </summary>
PublishWithDescendantsForceNew = 11,
}
@@ -25,6 +25,7 @@ public class PropertyTypeBasic
[Required]
[RegularExpression(@"^([a-zA-Z]\w.*)$", ErrorMessage = "Invalid alias")]
[MaxLength(255, ErrorMessage = "Alias is too long")]
[DataMember(Name = "alias")]
public string Alias { get; set; } = null!;
+30 -5
View File
@@ -132,6 +132,30 @@ public abstract class ContentTypeBase : TreeEntityBase, IContentTypeBase
}
}
/// <summary>
/// Detects if any persisted property types have been removed by comparing old and new collections,
/// and sets <see cref="HasPropertyTypeBeenRemoved"/> accordingly.
/// </summary>
private void DetectPropertyTypeRemovals(IEnumerable<IPropertyType> oldPropertyTypes, IEnumerable<IPropertyType> newPropertyTypes)
{
if (HasPropertyTypeBeenRemoved)
{
return;
}
var oldIds = new HashSet<int>(oldPropertyTypes.Select(pt => pt.Id).Where(id => id > 0));
if (oldIds.Count == 0)
{
return;
}
var newIds = new HashSet<int>(newPropertyTypes.Select(pt => pt.Id).Where(id => id > 0));
if (oldIds.Any(id => !newIds.Contains(id)))
{
HasPropertyTypeBeenRemoved = true;
}
}
/// <summary>
/// PropertyTypes that are not part of a PropertyGroup
/// </summary>
@@ -273,6 +297,7 @@ public abstract class ContentTypeBase : TreeEntityBase, IContentTypeBase
{
if (PropertyTypeCollection != null)
{
DetectPropertyTypeRemovals(PropertyTypeCollection, value);
PropertyTypeCollection.ClearCollectionChangedEvents();
}
@@ -443,21 +468,21 @@ public abstract class ContentTypeBase : TreeEntityBase, IContentTypeBase
/// Please note that resetting the dirty properties could potentially
/// obstruct the saving of a new or updated entity.
/// </remarks>
public override void ResetDirtyProperties()
public override void ResetDirtyProperties(bool rememberDirty)
{
base.ResetDirtyProperties();
base.ResetDirtyProperties(rememberDirty);
// loop through each property group to reset the property types
var propertiesReset = new List<int>();
foreach (PropertyGroup propertyGroup in PropertyGroups)
{
propertyGroup.ResetDirtyProperties();
propertyGroup.ResetDirtyProperties(rememberDirty);
if (propertyGroup.PropertyTypes is not null)
{
foreach (IPropertyType propertyType in propertyGroup.PropertyTypes)
{
propertyType.ResetDirtyProperties();
propertyType.ResetDirtyProperties(rememberDirty);
propertiesReset.Add(propertyType.Id);
}
}
@@ -467,7 +492,7 @@ public abstract class ContentTypeBase : TreeEntityBase, IContentTypeBase
// but don't re-reset ones we've already done.
foreach (IPropertyType propertyType in PropertyTypes.Where(x => propertiesReset.Contains(x.Id) == false))
{
propertyType.ResetDirtyProperties();
propertyType.ResetDirtyProperties(rememberDirty);
}
}
@@ -13,6 +13,7 @@ public abstract class ContentTypeCompositionBase : ContentTypeBase, IContentType
{
private List<IContentTypeComposition> _contentTypeComposition = new();
private List<int> _removedContentTypeKeyTracker = new();
private bool _hasCompositionBeenRemoved;
protected ContentTypeCompositionBase(IShortStringHelper shortStringHelper, int parentId)
: base(shortStringHelper, parentId)
@@ -104,6 +105,24 @@ public abstract class ContentTypeCompositionBase : ContentTypeBase, IContentType
}
}
/// <summary>
/// A boolean flag indicating if a composition has been removed from this instance.
/// </summary>
/// <remarks>
/// This is currently (specifically) used in order to know that we need to refresh the content cache which
/// needs to occur when a composition has been removed from a content type
/// </remarks>
[IgnoreDataMember]
internal bool HasCompositionTypeBeenRemoved
{
get => _hasCompositionBeenRemoved;
private set
{
_hasCompositionBeenRemoved = value;
OnPropertyChanged(nameof(HasCompositionTypeBeenRemoved));
}
}
/// <inheritdoc />
public IEnumerable<IPropertyType> GetOriginalComposedPropertyTypes() => GetRawComposedPropertyTypes();
@@ -179,6 +198,8 @@ public abstract class ContentTypeCompositionBase : ContentTypeBase, IContentType
_removedContentTypeKeyTracker.AddRange(compositionIdsToRemove);
}
HasCompositionTypeBeenRemoved = true;
OnPropertyChanged(nameof(ContentTypeComposition));
return _contentTypeComposition.Remove(contentTypeComposition);
@@ -10,5 +10,7 @@ public sealed class ApiContentRoute : IApiContentRoute
public string Path { get; }
public string? QueryString { get; set; }
public IApiContentStartItem StartItem { get; }
}
@@ -4,5 +4,10 @@ public interface IApiContentRoute
{
string Path { get; }
public string? QueryString
{
get => null; set { }
}
IApiContentStartItem StartItem { get; }
}
+1 -1
View File
@@ -35,7 +35,7 @@ _additionalData ??= new Dictionary<string, object?>();
// if the name has changed, add the value to the additional data,
// this is required purely for event handlers to know the previous name of the group
// so we can keep the public access up to date.
AdditionalData["previousName"] = _name;
AdditionalData[Constants.Entities.AdditionalDataKeys.MemberGroupPreviousName] = _name;
}
SetPropertyValueAndDetectChanges(value, ref _name, nameof(Name));
+39
View File
@@ -20,6 +20,7 @@ public class PropertyGroup : EntityBase, IEquatable<PropertyGroup>
private string _alias;
private string? _name;
private bool _hasPropertyTypeBeenRemoved;
private PropertyTypeCollection? _propertyTypes;
private int _sortOrder;
@@ -94,6 +95,20 @@ public class PropertyGroup : EntityBase, IEquatable<PropertyGroup>
set => SetPropertyValueAndDetectChanges(value, ref _sortOrder, nameof(SortOrder));
}
/// <summary>
/// A boolean flag indicating if a property type has been removed from this group.
/// </summary>
[IgnoreDataMember]
internal bool HasPropertyTypeBeenRemoved
{
get => _hasPropertyTypeBeenRemoved;
private set
{
_hasPropertyTypeBeenRemoved = value;
OnPropertyChanged(nameof(HasPropertyTypeBeenRemoved));
}
}
/// <summary>
/// Gets or sets a collection of property types for the group.
/// </summary>
@@ -112,6 +127,7 @@ public class PropertyGroup : EntityBase, IEquatable<PropertyGroup>
{
if (_propertyTypes != null)
{
DetectPropertyTypeRemovals(_propertyTypes, value);
_propertyTypes.ClearCollectionChangedEvents();
}
@@ -155,4 +171,27 @@ public class PropertyGroup : EntityBase, IEquatable<PropertyGroup>
private void PropertyTypesChanged(object? sender, NotifyCollectionChangedEventArgs e) =>
OnPropertyChanged(nameof(PropertyTypes));
private void DetectPropertyTypeRemovals(PropertyTypeCollection oldPropertyTypes, PropertyTypeCollection? newPropertyTypes)
{
if (HasPropertyTypeBeenRemoved)
{
return;
}
var oldIds = new HashSet<int>(oldPropertyTypes.Select(pt => pt.Id).Where(id => id > 0));
if (oldIds.Count == 0)
{
return;
}
var newIds = newPropertyTypes != null
? new HashSet<int>(newPropertyTypes.Select(pt => pt.Id).Where(id => id > 0))
: new HashSet<int>();
if (oldIds.Any(id => !newIds.Contains(id)))
{
HasPropertyTypeBeenRemoved = true;
}
}
}
@@ -0,0 +1,28 @@
namespace Umbraco.Cms.Core.Models;
/// <summary>
/// Describes the options available with publishing a content branch for force publishing.
/// </summary>
[Flags]
public enum PublishBranchFilter
{
/// <summary>
/// The default behavior is to publish only the published content that has changed.
/// </summary>
Default = 0,
/// <summary>
/// For publishing a branch, publish all changed content, including content that is not published.
/// </summary>
IncludeUnpublished = 1,
/// <summary>
/// For publishing a branch, force republishing of all published content, including content that has not changed.
/// </summary>
ForceRepublish = 2,
/// <summary>
/// For publishing a branch, publish all content, including content that is not published and content that has not changed.
/// </summary>
All = IncludeUnpublished | ForceRepublish,
}
@@ -0,0 +1,28 @@
namespace Umbraco.Cms.Core.Models;
/// <summary>
/// Specifies options for publishing notifcations when saving.
/// </summary>
[Flags]
public enum PublishNotificationSaveOptions
{
/// <summary>
/// Do not publish any notifications.
/// </summary>
None = 0,
/// <summary>
/// Only publish the saving notification.
/// </summary>
Saving = 1,
/// <summary>
/// Only publish the saved notification.
/// </summary>
Saved = 2,
/// <summary>
/// Publish all the notifications.
/// </summary>
All = Saving | Saved,
}
@@ -30,7 +30,7 @@ public class PublishedValueFallback : IPublishedValueFallback
/// <inheritdoc />
public bool TryGetValue<T>(IPublishedProperty property, string? culture, string? segment, Fallback fallback, T? defaultValue, out T? value)
{
_variationContextAccessor.ContextualizeVariation(property.PropertyType.Variations, ref culture, ref segment);
_variationContextAccessor.ContextualizeVariation(property.PropertyType.Variations, property.Alias, ref culture, ref segment);
foreach (var f in fallback)
{
@@ -78,7 +78,7 @@ public class PublishedValueFallback : IPublishedValueFallback
return false;
}
_variationContextAccessor.ContextualizeVariation(propertyType.Variations, ref culture, ref segment);
_variationContextAccessor.ContextualizeVariation(propertyType.Variations, alias, ref culture, ref segment);
foreach (var f in fallback)
{
@@ -124,7 +124,7 @@ public class PublishedValueFallback : IPublishedValueFallback
IPublishedPropertyType? propertyType = content.ContentType.GetPropertyType(alias);
if (propertyType != null)
{
_variationContextAccessor.ContextualizeVariation(propertyType.Variations, content.Id, ref culture, ref segment);
_variationContextAccessor.ContextualizeVariation(propertyType.Variations, content.Id, alias, ref culture, ref segment);
noValueProperty = content.GetProperty(alias);
}
@@ -195,7 +195,7 @@ public class PublishedValueFallback : IPublishedValueFallback
{
culture = null;
segment = null;
_variationContextAccessor.ContextualizeVariation(propertyType.Variations, content.Id, ref culture, ref segment);
_variationContextAccessor.ContextualizeVariation(propertyType.Variations, content.Id, alias, ref culture, ref segment);
}
property = content?.GetProperty(alias);
@@ -25,9 +25,15 @@ public class VariationContext
public string Segment { get; }
/// <summary>
/// Gets the segment for the content item
/// Gets the segment for the content item.
/// </summary>
/// <param name="contentId"></param>
/// <returns></returns>
/// <param name="contentId">The content Id.</param>
public virtual string GetSegment(int contentId) => Segment;
/// <summary>
/// Gets the segment for the content item and property alias.
/// </summary>
/// <param name="contentId">The content Id.</param>
/// <param name="propertyAlias">The property alias.</param>
public virtual string GetSegment(int contentId, string propertyAlias) => Segment;
}
@@ -8,25 +8,45 @@ namespace Umbraco.Extensions;
public static class VariationContextAccessorExtensions
{
[Obsolete("Please use the method overload that accepts all parameters. Scheduled for removal in Umbraco 18.")]
public static void ContextualizeVariation(
this IVariationContextAccessor variationContextAccessor,
ContentVariation variations,
ref string? culture,
ref string? segment)
=> variationContextAccessor.ContextualizeVariation(variations, null, ref culture, ref segment);
=> variationContextAccessor.ContextualizeVariation(variations, null, null, ref culture, ref segment);
public static void ContextualizeVariation(
this IVariationContextAccessor variationContextAccessor,
ContentVariation variations,
string? propertyAlias,
ref string? culture,
ref string? segment)
=> variationContextAccessor.ContextualizeVariation(variations, null, propertyAlias, ref culture, ref segment);
[Obsolete("Please use the method overload that accepts all parameters. Scheduled for removal in Umbraco 18.")]
public static void ContextualizeVariation(
this IVariationContextAccessor variationContextAccessor,
ContentVariation variations,
int contentId,
ref string? culture,
ref string? segment)
=> variationContextAccessor.ContextualizeVariation(variations, (int?)contentId, ref culture, ref segment);
=> variationContextAccessor.ContextualizeVariation(variations, (int?)contentId, null, ref culture, ref segment);
public static void ContextualizeVariation(
this IVariationContextAccessor variationContextAccessor,
ContentVariation variations,
int contentId,
string? propertyAlias,
ref string? culture,
ref string? segment)
=> variationContextAccessor.ContextualizeVariation(variations, (int?)contentId, propertyAlias, ref culture, ref segment);
private static void ContextualizeVariation(
this IVariationContextAccessor variationContextAccessor,
ContentVariation variations,
int? contentId,
string? propertyAlias,
ref string? culture,
ref string? segment)
{
@@ -37,18 +57,22 @@ public static class VariationContextAccessorExtensions
// use context values
VariationContext? publishedVariationContext = variationContextAccessor?.VariationContext;
if (culture == null)
{
culture = variations.VariesByCulture() ? publishedVariationContext?.Culture : string.Empty;
}
culture ??= variations.VariesByCulture() ? publishedVariationContext?.Culture : string.Empty;
if (segment == null)
{
if (variations.VariesBySegment())
{
segment = contentId == null
? publishedVariationContext?.Segment
: publishedVariationContext?.GetSegment(contentId.Value);
if (contentId == null)
{
segment = publishedVariationContext?.Segment;
}
else
{
segment = propertyAlias == null ?
publishedVariationContext?.GetSegment(contentId.Value) :
publishedVariationContext?.GetSegment(contentId.Value, propertyAlias);
}
}
else
{
+1
View File
@@ -4,6 +4,7 @@ namespace Umbraco.Cms.Core;
/// Provides an equivalent to the c# lock statement, to be used in a using block.
/// </summary>
/// <remarks>Ie replace <c>lock (o) {...}</c> by <c>using (new MonitorLock(o)) { ... }</c></remarks>
[Obsolete("Use System.Threading.Lock instead. This will be removed in a future version.")]
public class MonitorLock : IDisposable
{
private readonly bool _entered;
@@ -32,4 +32,14 @@ public class ContentTreeChangeNotification : TreeChangeNotification<IContent>
: base(new TreeChange<IContent>(target, changeTypes), messages)
{
}
public ContentTreeChangeNotification(
IContent target,
TreeChangeTypes changeTypes,
IEnumerable<string>? publishedCultures,
IEnumerable<string>? unpublishedCultures,
EventMessages messages)
: base(new TreeChange<IContent>(target, changeTypes, publishedCultures, unpublishedCultures), messages)
{
}
}
@@ -3,23 +3,29 @@ using Umbraco.Cms.Core.Security;
namespace Umbraco.Cms.Core.Persistence.Repositories;
/// <summary>
/// Repository for external logins with Guid as key, so it can be shared for members and users
/// Repository for external logins with Guid as key, so it can be shared for members and users.
/// </summary>
public interface IExternalLoginWithKeyRepository : IReadWriteQueryRepository<int, IIdentityUserLogin>,
IQueryRepository<IIdentityUserToken>
{
/// <summary>
/// Replaces all external login providers for the user/member key
/// Replaces all external login providers for the user/member key.
/// </summary>
void Save(Guid userOrMemberKey, IEnumerable<IExternalLogin> logins);
/// <summary>
/// Replaces all external login provider tokens for the providers specified for the user/member key
/// Replaces all external login provider tokens for the providers specified for the user/member key.
/// </summary>
void Save(Guid userOrMemberKey, IEnumerable<IExternalLoginToken> tokens);
/// <summary>
/// Deletes all external logins for the specified the user/member key
/// Deletes all external logins for the specified the user/member key.
/// </summary>
void DeleteUserLogins(Guid userOrMemberKey);
/// <summary>
/// Deletes external logins that aren't associated with the current collection of providers.
/// </summary>
/// <param name="currentLoginProviders">The names of the currently configured providers.</param>
void DeleteUserLoginsForRemovedProviders(IEnumerable<string> currentLoginProviders) { }
}
@@ -38,4 +38,11 @@ public interface IMemberRepository : IContentRepository<int, IMember>
/// <param name="query"></param>
/// <returns></returns>
int GetCountByQuery(IQuery<IMember>? query);
/// <summary>
/// Saves only the properties related to login for the member, using an optimized, non-locking update.
/// </summary>
/// <param name="member">The member to update.</param>
/// <returns>Used to avoid the full save of the member object after a login operation.</returns>
Task UpdateLoginPropertiesAsync(IMember member) => Task.CompletedTask;
}
@@ -1,4 +1,4 @@
using System.Linq.Expressions;
using System.Linq.Expressions;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Persistence.Querying;
@@ -109,5 +109,26 @@ public interface IUserRepository : IReadWriteQueryRepository<int, IUser>
void ClearLoginSession(Guid sessionId);
/// <summary>
/// Gets a page of users, ordered by Id and starting from the provided Id.
/// </summary>
/// <param name="id">The user Id to start retrieving users from.</param>
/// <param name="count">The number of users to return.</param>
/// <returns>A page of <see cref="IUser"/> instances.</returns>
[Obsolete("No longer used in Umbraco. Scheduled for removal in Umbraco 18.")]
IEnumerable<IUser> GetNextUsers(int id, int count);
/// <summary>
/// Gets a page of approved users, ordered by Id and starting from the provided Id.
/// </summary>
/// <param name="id">The user Id to start retrieving users from.</param>
/// <param name="count">The number of users to return.</param>
/// <returns>A page of <see cref="IUser"/> instances.</returns>
IEnumerable<IUser> GetNextApprovedUsers(int id, int count) => Enumerable.Empty<IUser>();
/// <summary>
/// Invalidates sessions for users that aren't associated with the current collection of providers.
/// </summary>
/// <param name="currentProviderKeys">The keys for the currently configured providers.</param>
void InvalidateSessionsForRemovedProviders(IEnumerable<string> currentProviderKeys) { }
}
@@ -7,7 +7,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.Validators;
/// <summary>
/// A validator that validates that the value is not null or empty (if it is a string)
/// </summary>
public sealed class RequiredValidator : IValueRequiredValidator, IManifestValueValidator
public class RequiredValidator : IValueRequiredValidator, IManifestValueValidator
{
private const string ValueCannotBeNull = "Value cannot be null";
private const string ValueCannotBeEmpty = "Value cannot be empty";
@@ -23,7 +23,7 @@ public sealed class RequiredValidator : IValueRequiredValidator, IManifestValueV
ValidateRequired(value, valueType);
/// <inheritdoc cref="IValueRequiredValidator.ValidateRequired" />
public IEnumerable<ValidationResult> ValidateRequired(object? value, string? valueType)
public virtual IEnumerable<ValidationResult> ValidateRequired(object? value, string? valueType)
{
if (value == null)
{
@@ -31,6 +31,12 @@ public interface IPublishedSnapshotService : IDisposable
/// </remarks>
IPublishedSnapshot CreatePublishedSnapshot(string? previewToken);
/// <summary>
/// Indicates if the database cache is in the process of being rebuilt.
/// </summary>
/// <returns></returns>
bool IsRebuilding() => false;
/// <summary>
/// Rebuilds internal database caches (but does not reload).
/// </summary>
@@ -61,6 +67,38 @@ public interface IPublishedSnapshotService : IDisposable
IReadOnlyCollection<int>? mediaTypeIds = null,
IReadOnlyCollection<int>? memberTypeIds = null);
/// <summary>
/// Rebuilds internal database caches (but does not reload).
/// </summary>
/// <param name="contentTypeIds">
/// If not null will process content for the matching content types, if empty will process all
/// content
/// </param>
/// <param name="mediaTypeIds">
/// If not null will process content for the matching media types, if empty will process all
/// media
/// </param>
/// <param name="memberTypeIds">
/// If not null will process content for the matching members types, if empty will process all
/// members
/// </param>
/// <param name="useBackgroundThread">Flag indicating whether to use a background thread for the operation and immediately return to the caller.</param>
/// <remarks>
/// <para>
/// Forces the snapshot service to rebuild its internal database caches. For instance, some caches
/// may rely on a database table to store pre-serialized version of documents.
/// </para>
/// <para>
/// This does *not* reload the caches. Caches need to be reloaded, for instance via
/// <see cref="DistributedCache" /> RefreshAllPublishedSnapshot method.
/// </para>
/// </remarks>
void Rebuild(
bool useBackgroundThread,
IReadOnlyCollection<int>? contentTypeIds = null,
IReadOnlyCollection<int>? mediaTypeIds = null,
IReadOnlyCollection<int>? memberTypeIds = null) => Rebuild(contentTypeIds, mediaTypeIds, memberTypeIds);
/// <summary>
/// Rebuilds all internal database caches (but does not reload).
@@ -77,6 +115,22 @@ public interface IPublishedSnapshotService : IDisposable
/// </remarks>
void RebuildAll() => Rebuild(Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>());
/// <summary>
/// Rebuilds all internal database caches (but does not reload).
/// </summary>
/// <param name="useBackgroundThread">Flag indicating whether to use a background thread for the operation and immediately return to the caller.</param>
/// <remarks>
/// <para>
/// Forces the snapshot service to rebuild its internal database caches. For instance, some caches
/// may rely on a database table to store pre-serialized version of documents.
/// </para>
/// <para>
/// This does *not* reload the caches. Caches need to be reloaded, for instance via
/// <see cref="DistributedCache" /> RefreshAllPublishedSnapshot method.
/// </para>
/// </remarks>
void RebuildAll(bool useBackgroundThread) => Rebuild(useBackgroundThread, Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>());
/* An IPublishedCachesService implementation can rely on transaction-level events to update
* its internal, database-level data, as these events are purely internal. However, it cannot
* rely on cache refreshers CacheUpdated events to update itself, as these events are external
+1 -1
View File
@@ -33,7 +33,7 @@ public interface ITagQuery
/// <summary>
/// Gets all document tags.
/// </summary>
/// /// <remarks>
/// <remarks>
/// If no culture is specified, it retrieves tags with an invariant culture.
/// If a culture is specified, it only retrieves tags for that culture.
/// Use "*" to retrieve tags for all cultures.

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