2024-09-27 09:12:19 +02:00
|
|
|
using System.Globalization;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using Microsoft.Extensions.Options;
|
|
|
|
|
using Umbraco.Cms.Core.Configuration.Models;
|
|
|
|
|
using Umbraco.Cms.Core.Models;
|
|
|
|
|
using Umbraco.Cms.Core.Models.PublishedContent;
|
|
|
|
|
using Umbraco.Cms.Core.PublishedCache;
|
|
|
|
|
using Umbraco.Cms.Core.Services;
|
2024-10-01 15:03:02 +02:00
|
|
|
using Umbraco.Cms.Core.Services.Navigation;
|
2024-09-27 09:12:19 +02:00
|
|
|
using Umbraco.Cms.Core.Web;
|
|
|
|
|
using Umbraco.Extensions;
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Cms.Core.Routing;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-05-26 07:11:53 +02:00
|
|
|
/// Provides URLs.
|
2024-09-27 09:12:19 +02:00
|
|
|
/// </summary>
|
|
|
|
|
public class NewDefaultUrlProvider : IUrlProvider
|
|
|
|
|
{
|
|
|
|
|
private readonly IPublishedContentCache _publishedContentCache;
|
|
|
|
|
private readonly IDomainCache _domainCache;
|
|
|
|
|
private readonly IIdKeyMap _idKeyMap;
|
|
|
|
|
private readonly IDocumentUrlService _documentUrlService;
|
2024-10-01 15:03:02 +02:00
|
|
|
private readonly IDocumentNavigationQueryService _navigationQueryService;
|
2025-02-25 13:25:33 +01:00
|
|
|
private readonly IPublishedContentStatusFilteringService _publishedContentStatusFilteringService;
|
2025-10-08 08:27:01 +02:00
|
|
|
private readonly ILogger<NewDefaultUrlProvider> _logger;
|
2024-09-27 09:12:19 +02:00
|
|
|
private readonly ISiteDomainMapper _siteDomainMapper;
|
|
|
|
|
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
|
|
|
|
private readonly UriUtility _uriUtility;
|
|
|
|
|
private RequestHandlerSettings _requestSettings;
|
2025-05-26 07:11:53 +02:00
|
|
|
private readonly ILanguageService _languageService;
|
2024-09-27 09:12:19 +02:00
|
|
|
|
2025-08-28 12:09:59 +02:00
|
|
|
// TODO See if we can make GetUrlFromRoute asynchronous and avoid the GetAwaiter().GetResult() in when using ILanguageService.
|
2025-05-26 07:11:53 +02:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes a new instance of the <see cref="NewDefaultUrlProvider"/> class.
|
|
|
|
|
/// </summary>
|
2024-10-01 15:03:02 +02:00
|
|
|
public NewDefaultUrlProvider(
|
|
|
|
|
IOptionsMonitor<RequestHandlerSettings> requestSettings,
|
2025-10-08 08:27:01 +02:00
|
|
|
ILogger<NewDefaultUrlProvider> logger,
|
2024-10-01 15:03:02 +02:00
|
|
|
ISiteDomainMapper siteDomainMapper,
|
|
|
|
|
IUmbracoContextAccessor umbracoContextAccessor,
|
|
|
|
|
UriUtility uriUtility,
|
|
|
|
|
IPublishedContentCache publishedContentCache,
|
|
|
|
|
IDomainCache domainCache,
|
|
|
|
|
IIdKeyMap idKeyMap,
|
|
|
|
|
IDocumentUrlService documentUrlService,
|
2025-02-25 13:25:33 +01:00
|
|
|
IDocumentNavigationQueryService navigationQueryService,
|
2025-05-26 07:11:53 +02:00
|
|
|
IPublishedContentStatusFilteringService publishedContentStatusFilteringService,
|
|
|
|
|
ILanguageService languageService)
|
2024-10-01 15:03:02 +02:00
|
|
|
{
|
|
|
|
|
_requestSettings = requestSettings.CurrentValue;
|
|
|
|
|
_logger = logger;
|
|
|
|
|
_siteDomainMapper = siteDomainMapper;
|
|
|
|
|
_umbracoContextAccessor = umbracoContextAccessor;
|
|
|
|
|
_uriUtility = uriUtility;
|
|
|
|
|
_publishedContentCache = publishedContentCache;
|
|
|
|
|
_domainCache = domainCache;
|
|
|
|
|
_idKeyMap = idKeyMap;
|
|
|
|
|
_documentUrlService = documentUrlService;
|
|
|
|
|
_navigationQueryService = navigationQueryService;
|
2025-02-25 13:25:33 +01:00
|
|
|
_publishedContentStatusFilteringService = publishedContentStatusFilteringService;
|
2025-05-26 07:11:53 +02:00
|
|
|
_languageService = languageService;
|
2024-10-01 15:03:02 +02:00
|
|
|
|
|
|
|
|
requestSettings.OnChange(x => _requestSettings = x);
|
2024-09-27 09:12:19 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-08 08:27:01 +02:00
|
|
|
/// <inheritdoc />
|
|
|
|
|
public string Alias => Constants.UrlProviders.Content;
|
|
|
|
|
|
2024-09-27 09:12:19 +02:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the other URLs of a published content.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id">The published content id.</param>
|
|
|
|
|
/// <param name="current">The current absolute URL.</param>
|
|
|
|
|
/// <returns>The other URLs for the published content.</returns>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// <para>
|
|
|
|
|
/// Other URLs are those that <c>GetUrl</c> would not return in the current context, but would be valid
|
|
|
|
|
/// URLs for the node in other contexts (different domain for current request, umbracoUrlAlias...).
|
|
|
|
|
/// </para>
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public virtual IEnumerable<UrlInfo> GetOtherUrls(int id, Uri current)
|
|
|
|
|
{
|
2025-05-26 07:11:53 +02:00
|
|
|
Attempt<Guid> keyAttempt = _idKeyMap.GetKeyForId(id, UmbracoObjectTypes.Document);
|
2024-09-27 09:12:19 +02:00
|
|
|
|
|
|
|
|
if (keyAttempt.Success is false)
|
|
|
|
|
{
|
|
|
|
|
yield break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-26 07:11:53 +02:00
|
|
|
Guid key = keyAttempt.Result;
|
2024-09-27 09:12:19 +02:00
|
|
|
|
|
|
|
|
IPublishedContent? node = _publishedContentCache.GetById(key);
|
|
|
|
|
if (node == null)
|
|
|
|
|
{
|
|
|
|
|
yield break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// look for domains, walking up the tree
|
|
|
|
|
IPublishedContent? n = node;
|
|
|
|
|
IEnumerable<DomainAndUri>? domainUris =
|
|
|
|
|
DomainUtilities.DomainsForNode(_domainCache, _siteDomainMapper, n.Id, current, false);
|
|
|
|
|
|
|
|
|
|
// n is null at root
|
|
|
|
|
while (domainUris == null && n != null)
|
|
|
|
|
{
|
2025-02-25 13:25:33 +01:00
|
|
|
n = n.Parent<IPublishedContent>(_navigationQueryService, _publishedContentStatusFilteringService); // move to parent node
|
2024-09-27 09:12:19 +02:00
|
|
|
domainUris = n == null
|
|
|
|
|
? null
|
|
|
|
|
: DomainUtilities.DomainsForNode(_domainCache, _siteDomainMapper, n.Id, current);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no domains = exit
|
|
|
|
|
if (domainUris == null)
|
|
|
|
|
{
|
|
|
|
|
yield break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (DomainAndUri d in domainUris)
|
|
|
|
|
{
|
|
|
|
|
var culture = d.Culture;
|
|
|
|
|
|
|
|
|
|
// although we are passing in culture here, if any node in this path is invariant, it ignores the culture anyways so this is ok
|
|
|
|
|
var route = GetLegacyRouteFormatById(key, culture);
|
2025-01-28 10:10:08 +01:00
|
|
|
if (route == null || route == "#")
|
2024-09-27 09:12:19 +02:00
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// need to strip off the leading ID for the route if it exists (occurs if the route is for a node with a domain assigned)
|
2025-04-01 11:39:22 +02:00
|
|
|
var pos = route.IndexOf('/', StringComparison.Ordinal);
|
2025-05-26 07:11:53 +02:00
|
|
|
var path = pos == 0 ? route : route[pos..];
|
2024-09-27 09:12:19 +02:00
|
|
|
|
|
|
|
|
var uri = new Uri(CombinePaths(d.Uri.GetLeftPart(UriPartial.Path), path));
|
|
|
|
|
uri = _uriUtility.UriFromUmbraco(uri, _requestSettings);
|
2025-10-08 08:27:01 +02:00
|
|
|
yield return UrlInfo.FromUri(uri, Alias, culture);
|
2024-09-27 09:12:19 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-08 08:27:01 +02:00
|
|
|
#region GetPreviewUrl
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public Task<UrlInfo?> GetPreviewUrlAsync(IContent content, string? culture, string? segment)
|
|
|
|
|
=> Task.FromResult<UrlInfo?>(
|
|
|
|
|
UrlInfo.AsUrl(
|
2025-10-22 09:48:55 +02:00
|
|
|
$"preview?id={content.Key}&culture={culture}&segment={segment}",
|
2025-10-08 08:27:01 +02:00
|
|
|
Alias,
|
|
|
|
|
culture,
|
|
|
|
|
isExternal: false));
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2024-09-27 09:12:19 +02:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the legacy route format by id
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="key"></param>
|
|
|
|
|
/// <param name="culture"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// When no domain is set the route can be something like /child/grandchild
|
|
|
|
|
/// When a domain is set, the route can be something like 1234/grandchild
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
|
|
|
|
private string GetLegacyRouteFormatById(Guid key, string? culture)
|
|
|
|
|
{
|
2025-01-08 09:14:14 +01:00
|
|
|
var isDraft = _umbracoContextAccessor.GetRequiredUmbracoContext().InPreviewMode;
|
|
|
|
|
return _documentUrlService.GetLegacyRouteFormat(key, culture, isDraft);
|
2024-09-27 09:12:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public virtual UrlInfo? GetUrl(IPublishedContent content, UrlMode mode, string? culture, Uri current)
|
|
|
|
|
{
|
|
|
|
|
if (!current.IsAbsoluteUri)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Current URL must be absolute.", nameof(current));
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-08 09:14:14 +01:00
|
|
|
// This might seem to be some code duplication, as we do the same check in GetLegacyRouteFormat
|
|
|
|
|
// but this is strictly neccesary, as if we're coming from a published notification
|
|
|
|
|
// this document will still not always be in the memory cache. And thus we have to hit the DB
|
|
|
|
|
// We have the published content now, so we can check if the culture is published, and thus avoid the DB hit.
|
|
|
|
|
string route;
|
|
|
|
|
var isDraft = _umbracoContextAccessor.GetRequiredUmbracoContext().InPreviewMode;
|
2025-10-22 09:48:55 +02:00
|
|
|
if (isDraft is false && string.IsNullOrWhiteSpace(culture) is false && content.Cultures.Any() && content.IsInvariantOrHasCulture(culture) is false)
|
2025-01-08 09:14:14 +01:00
|
|
|
{
|
|
|
|
|
route = "#";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
route = GetLegacyRouteFormatById(content.Key, culture);
|
|
|
|
|
}
|
2024-09-27 09:12:19 +02:00
|
|
|
|
|
|
|
|
// will not use cache if previewing
|
|
|
|
|
|
|
|
|
|
return GetUrlFromRoute(route, content.Id, current, mode, culture);
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-26 07:11:53 +02:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the URL from the provided route.
|
|
|
|
|
/// </summary>
|
2024-09-27 09:12:19 +02:00
|
|
|
internal UrlInfo? GetUrlFromRoute(
|
|
|
|
|
string? route,
|
|
|
|
|
int id,
|
|
|
|
|
Uri current,
|
|
|
|
|
UrlMode mode,
|
|
|
|
|
string? culture)
|
|
|
|
|
{
|
2024-11-04 12:27:47 +01:00
|
|
|
if (string.IsNullOrWhiteSpace(route) || route.Equals("#"))
|
2024-09-27 09:12:19 +02:00
|
|
|
{
|
2025-05-26 07:11:53 +02:00
|
|
|
if (_logger.IsEnabled(LogLevel.Debug))
|
2024-09-27 09:12:19 +02:00
|
|
|
{
|
|
|
|
|
_logger.LogDebug(
|
|
|
|
|
"Couldn't find any page with nodeId={NodeId}. This is most likely caused by the page not being published.",
|
|
|
|
|
id);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// extract domainUri and path
|
|
|
|
|
// route is /<path> or <domainRootId>/<path>
|
2025-04-01 11:39:22 +02:00
|
|
|
var pos = route.IndexOf('/', StringComparison.Ordinal);
|
2024-09-27 09:12:19 +02:00
|
|
|
var path = pos == 0 ? route : route[pos..];
|
|
|
|
|
DomainAndUri? domainUri = pos == 0
|
|
|
|
|
? null
|
|
|
|
|
: DomainUtilities.DomainForNode(
|
|
|
|
|
_domainCache,
|
|
|
|
|
_siteDomainMapper,
|
|
|
|
|
int.Parse(route[..pos], CultureInfo.InvariantCulture),
|
|
|
|
|
current,
|
|
|
|
|
culture);
|
|
|
|
|
|
2025-05-26 07:11:53 +02:00
|
|
|
var defaultCulture = _languageService.GetDefaultIsoCodeAsync().GetAwaiter().GetResult();
|
Merge branch 'v15/dev' into v16/dev (#18971)
* Only prevent the unpublish or delete of a related item when configured to do so if it is related as a child, not as a parent (#18886)
* Only prevent the unpubkish or delete of a related item when configured to do so if it is related as a child, not as a parent.
* Fixed incorect parameter names.
* Fixed failing integration tests.
* Use using variable instead to reduce nesting
* Applied suggestions from code review.
* Used simple using statement throughout RelationService for consistency.
* Applied XML header comments consistently.
---------
Co-authored-by: mole <nikolajlauridsen@protonmail.ch>
* Feature: highlight invariant doc with variant blocks is unsupported (#18806)
* mark variant blocks in invariant docs as invalid
* implement RTE Blocks
* Fix pagination for users restricted by start nodes (#18907)
* Fix pagination for users restricted by start nodes
* Default implementation to avoid breakage
* Review comments
* Fix failing test
* Add media start node tests
* Fix issue preventing blueprint derived values from being scaffolded (#18917)
* Fix issue preventing blueprint derived values from being scaffolded.
* fix manipulating frooen array
* compare with variantId as well
---------
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
* ci: add Azure Static Web Apps workflow file
on-behalf-of: @Azure opensource@microsoft.com
* ci: add Azure Static Web Apps workflow file
on-behalf-of: @Azure opensource@microsoft.com
* ci: add Azure Static Web Apps workflow file
on-behalf-of: @Azure opensource@microsoft.com
* Remove admin permission on user configuration, allowing users with user section access only to manaage users and groups. (#18848)
* Tiptap RTE: Style Menu extension kind (#18918)
* Adds 'styleMenu' Tiptap toolbar extension kind
* Adds icons for `<h4>` and `<p>` tags
* Adds commands to HTML Global Attributes extension
for setting the `class` and `id` attributes.
* Renamed "default-tiptap-toolbar-element.api.ts" file
The "element" part was confusing.
* Toolbar Menu: uses correct `item` value
* Cascading Menu: adds localization for the label
* Adds `label` attribute to UUI components
for accessibility.
* Toolbar Menu: uses correct `appearance` value
* Removed unrequired `api` from Style Select
* Destructs the `item.data` object
* Ensure has children reflects only items with folder children when folders only are queried. (#18790)
* Ensure has children reflects only items with folder children when folders only are queried.
* Added supression for change to integration test public code.
---------
Co-authored-by: Migaroez <geusens@gmail.com>
* Only apply validation on content update to variant cultures where the editor has permission for the culture (#18778)
* Only apply validation on content update to variant cultures where the editor has permission for the culture.
* Remove inadvertent comment updates.
* Fixed failing integration test.
* Adds ancestor ID details on document tree and collection responses (#18909)
* Populate ancestor keys on document tree response items.
* Populate ancestor keys on document collection response items.
* Update OpenApi.json
* Use array of objects rather than Ids for the ancestor collection.
* Update OpenApi.json.
* Move publish with descendants to a background task with polling (#18497)
* Use background queue for database cache rebuild and track rebuilding status.
* Updated OpenApi.json and client-side types.
* Updated client to poll for completion of database rebuild.
* Move IBackgroundTaskQueue to core and prepare publish branch to run as background task.
* Endpoints for retrieval of status and result from branch publish operations.
* Poll and retrieve result for publish with descendants.
* Handled issues from testing.
* Rework to single controller for status and result.
* Updated client side sdk.
* OpenApi post dev merge gen
---------
Co-authored-by: Migaroez <geusens@gmail.com>
* Clear roots before rebuilding navigation dictionary (#18766)
* Clear roots before rebuilding navigation dictionary.
* Added tests to verify fix.
* Correct test implementation.
* Convert integration tests with method overloads into test cases.
* Integration test compatibility supressions.
* Fixes save of empty, invariant block list on variant content. (#18932)
* remove unnecessary code (#18927)
* V15/bugfix/fix route issue from 18859 (#18931)
* unique check
* unique for workspace empty path
* more unique routes
* Bump vite from 6.2.3 to 6.2.4 in /src/Umbraco.Web.UI.Client
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.2.3 to 6.2.4.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.2.4/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.2.4/packages/vite)
---
updated-dependencies:
- dependency-name: vite
dependency-version: 6.2.4
dependency-type: direct:development
...
Signed-off-by: dependabot[bot] <support@github.com>
* removes autogenerated workflows
* make getHasUnpersistedChanges public (#18929)
* Added management API endpoint, service and repository for retrieval of references from the recycle bin (#18882)
* Added management API endpoint, service and repository for retrieval of references from the recycle bin.
* Update src/Umbraco.Cms.Api.Management/Controllers/Document/RecycleBin/ReferencedByDocumentRecycleBinController.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Removed unused code.
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Updated management API endpoint and model for data type references to align with that used for documents, media etc. (#18905)
* Updated management API endpoint and model for data type references to align with that used for documents, media etc.
* Refactoring.
* Update src/Umbraco.Core/Constants-ReferenceTypes.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Fixed typos.
* Added id to tracked reference content type response.
* Updated OpenApi.json.
* Added missing updates.
* Renamed model and constants from code review feedback.
* Fix typo
* Fix multiple enumeration
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: mole <nikolajlauridsen@protonmail.ch>
* Skip lock tests
* Look-up redirect in content finder for multi-lingual sites using path and legacy route prefixed with the integer ID of the node with domains defined (#18763)
* Look-up redirect in content finder for multi-lingual sites using path and legacy route prefixed with the integer ID of the node with domains defined.
* Added tests to verify functionality.
* Added reference to previous PR.
* Referenced second PR.
* Assemble URLs for all cultures, not just the default.
* Revert previous update.
* Display an original URL if we have one.
* Bump vite from 6.2.4 to 6.2.5 in /src/Umbraco.Web.UI.Client
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.2.4 to 6.2.5.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.2.5/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.2.5/packages/vite)
---
updated-dependencies:
- dependency-name: vite
dependency-version: 6.2.5
dependency-type: direct:development
...
Signed-off-by: dependabot[bot] <support@github.com>
* Add raw value validation to multiple text strings property editor (#18936)
* Add raw value validation to multiple text strings property editor
* Added additional assert on unit test and comment on validation logic.
* Don't remove items to obtain a valid value
---------
Co-authored-by: Andy Butland <abutland73@gmail.com>
* Integration tests for content publishing with ancestor unpublished (#18941)
* Resolved warnings in test class.
* Refactor regions into partial classes.
* Aligned test names.
* Variable name refactoring.
* Added tests for unpublished paths.
* Adjust tests to verify current behaviour.
* Cleaned up project file.
* fix circular icon import (#18952)
* remove segment toggle for elements (#18949)
* Fix modal route registration circular import (#18953)
* fix modal route registration circular import
* Update modal-route-registration.controller.ts
* V15/fix/18595 (#18925)
* fix for #18595
* updates the en.ts
* Avoid unneeded Dictionary operations (#18890)
* Avoid some heap allocations
* Remove unneeded double seek
* Avoid allocating new empty arrays, reuse existing empty array
* Avoid allocating strings for parsing comma separated int values (#18199)
* Data type References UI: Workspace + Delete (#18914)
* Updated management API endpoint and model for data type references to align with that used for documents, media etc.
* Refactoring.
* Update src/Umbraco.Core/Constants-ReferenceTypes.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Fixed typos.
* generate server models
* add extension slot
* register data type reference info app
* add reference data mappers
* Added id to tracked reference content type response.
* Updated OpenApi.json.
* Added missing updates.
* generate new models
* update models
* register ref item
* remove debugger
* render types
* register member type property type ref
* register media type property type ref
* Renamed model and constants from code review feedback.
* register reference workspace info app kind
* use kind for document references
* use kind for media references
* use kind for member references
* use deleteWithRelation kind when deleting data types
* fix manifest types
* fix types
* Update types.gen.ts
* update code to fit new server models
---------
Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Feature: discard changes for block workspace (#18930)
* make getHasUnpersistedChanges public
* Discard changes impl for Block Workspace
* fix 18367 (#18956)
* Merge commit from fork
* Prevent path traveral vulnerability with upload of temporary files.
* Used BadRequest instead of NotFound for invalid file name response.
* V15 QA Fixing the failing media acceptance tests (#18881)
* Fixed the function name due to test helper changes
* Updated assertion steps due to UI changes
* Added more waits
* Bumped version
* Increase timeout
* Reverted
---------
Co-authored-by: Andreas Zerbst <73799582+andr317c@users.noreply.github.com>
* V15 QA added clipboard test for not being able to copy to root when block is not allowed at root (#18937)
* Added clipboard test
* Bumped version
* Updated to use the name
* Run all tests on the pipeline
* Reverted command
* build: adjusts circular ref number to 4
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: mole <nikolajlauridsen@protonmail.ch>
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
Co-authored-by: Lee Kelleher <leekelleher@users.noreply.github.com>
Co-authored-by: Migaroez <geusens@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
Co-authored-by: Mads Rasmussen <madsr@hey.com>
Co-authored-by: Jacob Welander Jensen <64834767+Welander1994@users.noreply.github.com>
Co-authored-by: Henrik <hg@impact.dk>
Co-authored-by: Sebastiaan Janssen <sebastiaan@umbraco.com>
Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com>
Co-authored-by: Andreas Zerbst <73799582+andr317c@users.noreply.github.com>
2025-04-09 09:58:01 +02:00
|
|
|
if (domainUri is not null ||
|
|
|
|
|
string.IsNullOrEmpty(culture) ||
|
2024-09-27 09:12:19 +02:00
|
|
|
culture.Equals(defaultCulture, StringComparison.InvariantCultureIgnoreCase))
|
|
|
|
|
{
|
2025-10-08 08:27:01 +02:00
|
|
|
Uri url = AssembleUrl(domainUri, path, current, mode);
|
|
|
|
|
return UrlInfo.FromUri(url, Alias, culture);
|
2024-09-27 09:12:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Uri AssembleUrl(DomainAndUri? domainUri, string path, Uri current, UrlMode mode)
|
|
|
|
|
{
|
|
|
|
|
Uri uri;
|
|
|
|
|
|
|
|
|
|
// ignore vdir at that point, UriFromUmbraco will do it
|
|
|
|
|
// no domain was found
|
|
|
|
|
if (domainUri == null)
|
|
|
|
|
{
|
|
|
|
|
if (current == null)
|
|
|
|
|
{
|
|
|
|
|
mode = UrlMode.Relative; // best we can do
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (mode)
|
|
|
|
|
{
|
|
|
|
|
case UrlMode.Absolute:
|
|
|
|
|
uri = new Uri(current!.GetLeftPart(UriPartial.Authority) + path);
|
|
|
|
|
break;
|
|
|
|
|
case UrlMode.Relative:
|
|
|
|
|
case UrlMode.Auto:
|
|
|
|
|
uri = new Uri(path, UriKind.Relative);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(mode));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// a domain was found
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (mode == UrlMode.Auto)
|
|
|
|
|
{
|
|
|
|
|
// this check is a little tricky, we can't just compare domains
|
|
|
|
|
if (current != null && domainUri.Uri.GetLeftPart(UriPartial.Authority) ==
|
|
|
|
|
current.GetLeftPart(UriPartial.Authority))
|
|
|
|
|
{
|
|
|
|
|
mode = UrlMode.Relative;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mode = UrlMode.Absolute;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (mode)
|
|
|
|
|
{
|
|
|
|
|
case UrlMode.Absolute:
|
|
|
|
|
uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path));
|
|
|
|
|
break;
|
|
|
|
|
case UrlMode.Relative:
|
|
|
|
|
uri = new Uri(CombinePaths(domainUri.Uri.AbsolutePath, path), UriKind.Relative);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(mode));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UriFromUmbraco will handle vdir
|
|
|
|
|
// meaning it will add vdir into domain URLs too!
|
|
|
|
|
return _uriUtility.UriFromUmbraco(uri, _requestSettings);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string CombinePaths(string path1, string path2)
|
|
|
|
|
{
|
|
|
|
|
var path = path1.TrimEnd(Constants.CharArrays.ForwardSlash) + path2;
|
|
|
|
|
return path == "/" ? path : path.TrimEnd(Constants.CharArrays.ForwardSlash);
|
|
|
|
|
}
|
|
|
|
|
}
|