/// <summary> /// Initializes a new instance of the <see cref="RedirectToUmbracoPageResult"/> class. /// </summary> public RedirectToUmbracoPageResult(IPublishedContent?publishedContent, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor) { _publishedContent = publishedContent; Key = publishedContent?.Key ?? Guid.Empty; _publishedUrlProvider = publishedUrlProvider; _umbracoContextAccessor = umbracoContextAccessor; }
/// <summary> /// Gets the URL of a published content. /// </summary> /// <param name="content">The published content.</param> /// <param name="mode">The URL mode.</param> /// <param name="culture">A culture.</param> /// <param name="current">The current absolute URL.</param> /// <returns>The URL for the published content.</returns> /// <remarks> /// <para>The URL is absolute or relative depending on <c>mode</c> and on <c>current</c>.</para> /// <para>If the published content is multi-lingual, gets the URL for the specified culture or, /// when no culture is specified, the current culture.</para> /// <para>If the provider is unable to provide a URL, it returns "#".</para> /// </remarks> public string GetUrl(IPublishedContent?content, UrlMode mode = UrlMode.Default, string?culture = null, Uri?current = null) { if (content == null || content.ContentType.ItemType == PublishedItemType.Element) { return("#"); } if (mode == UrlMode.Default) { mode = Mode; } // this the ONLY place where we deal with default culture - IUrlProvider always receive a culture // be nice with tests, assume things can be null, ultimately fall back to invariant // (but only for variant content of course) // We need to check all ancestors because urls are variant even for invariant content, if an ancestor is variant. if (culture == null && content.AncestorsOrSelf().Any(x => x.ContentType.VariesByCulture())) { culture = _variationContextAccessor?.VariationContext?.Culture ?? string.Empty; } if (current == null) { IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); current = umbracoContext.CleanedUmbracoUrl; } UrlInfo?url = _urlProviders.Select(provider => provider.GetUrl(content, mode, culture, current)) .FirstOrDefault(u => u is not null); return(url?.Text ?? "#"); // legacy wants this }
// tries to get a value, recursing the tree // because we recurse, content may not even have the a property with the specified alias (but only some ancestor) // in case no value was found, noValueProperty contains the first property that was found (which does not have a value) private bool TryGetValueWithAncestorsFallback <T>(IPublishedContent?content, string alias, string?culture, string?segment, out T?value, ref IPublishedProperty?noValueProperty) { IPublishedProperty?property; // if we are here, content's property has no value do { content = content?.Parent; var propertyType = content?.ContentType.GetPropertyType(alias); if (propertyType != null && content is not null) { culture = null; segment = null; _variationContextAccessor.ContextualizeVariation(propertyType.Variations, content.Id, ref culture, ref segment); } property = content?.GetProperty(alias); if (property != null && noValueProperty == null) { noValueProperty = property; } }while (content != null && (property == null || property.HasValue(culture, segment) == false)); // if we found a content with the property having a value, return that property value if (property != null && property.HasValue(culture, segment)) { value = property.Value <T>(this, culture, segment); return(true); } value = default; return(false); }
/// <summary> /// Creates an <see cref="IEnumerable{PublishedSearchResult}" /> containing all content from the /// <paramref name="cache" />. /// </summary> /// <param name="results">The search results.</param> /// <param name="cache">The cache to fetch the content from.</param> /// <returns> /// An <see cref="IEnumerable{PublishedSearchResult}" /> containing all content. /// </returns> /// <exception cref="ArgumentNullException">cache</exception> /// <remarks> /// Search results are skipped if it can't be fetched from the <paramref name="cache" /> by its integer id. /// </remarks> public static IEnumerable <PublishedSearchResult> ToPublishedSearchResults( this IEnumerable <ISearchResult> results, IPublishedCache?cache) { if (cache == null) { throw new ArgumentNullException(nameof(cache)); } var publishedSearchResults = new List <PublishedSearchResult>(); foreach (ISearchResult result in results) { if (int.TryParse(result.Id, NumberStyles.Integer, CultureInfo.InvariantCulture, out var contentId)) { IPublishedContent?content = cache.GetById(contentId); if (content is not null) { publishedSearchResults.Add(new PublishedSearchResult(content, result.Score)); } } } return(publishedSearchResults); }
/// <summary> /// Tries to find and assign an Umbraco document to a <c>PublishedRequest</c>. /// </summary> /// <param name="frequest">The <c>PublishedRequest</c>.</param> /// <returns>A value indicating whether an Umbraco document was found and assigned.</returns> public async Task <bool> TryFindContent(IPublishedRequestBuilder frequest) { if (!_umbracoContextAccessor.TryGetUmbracoContext(out var umbracoContext)) { return(false); } IPublishedContent?node = null; // no alias if "/" if (frequest.Uri.AbsolutePath != "/") { node = FindContentByAlias( umbracoContext !.Content, frequest.Domain != null ? frequest.Domain.ContentId : 0, frequest.Culture, frequest.AbsolutePathDecoded); if (node != null) { frequest.SetPublishedContent(node); if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Path '{UriAbsolutePath}' is an alias for id={PublishedContentId}", frequest.Uri.AbsolutePath, node.Id); } } } return(node != null); }
private void ApplyHideTopLevelNodeFromPath(IPublishedContent content, IList <string> segments, bool preview) { // in theory if hideTopLevelNodeFromPath is true, then there should be only one // top-level node, or else domains should be assigned. but for backward compatibility // we add this check - we look for the document matching "/" and if it's not us, then // we do not hide the top level path // it has to be taken care of in GetByRoute too so if // "/foo" fails (looking for "/*/foo") we try also "/foo". // this does not make much sense anyway esp. if both "/foo/" and "/bar/foo" exist, but // that's the way it works pre-4.10 and we try to be backward compat for the time being if (content.Parent == null) { IPublishedContent?rootNode = GetByRoute(preview, "/", true); if (rootNode == null) { throw new Exception("Failed to get node at /."); } if (rootNode.Id == content.Id) // remove only if we're the default node { segments.RemoveAt(segments.Count - 1); } } else { segments.RemoveAt(segments.Count - 1); } }
private static async Task <Attempt <UrlInfo?> > DetectCollisionAsync( ILogger logger, IContent content, string url, string culture, IUmbracoContext umbracoContext, IPublishedRouter publishedRouter, ILocalizedTextService textService, IVariationContextAccessor variationContextAccessor, UriUtility uriUtility) { // test for collisions on the 'main' URL var uri = new Uri(url.TrimEnd(Constants.CharArrays.ForwardSlash), UriKind.RelativeOrAbsolute); if (uri.IsAbsoluteUri == false) { uri = uri.MakeAbsolute(umbracoContext.CleanedUmbracoUrl); } uri = uriUtility.UriToUmbraco(uri); IPublishedRequestBuilder builder = await publishedRouter.CreateRequestAsync(uri); IPublishedRequest pcr = await publishedRouter.RouteRequestAsync(builder, new RouteRequestOptions(RouteDirection.Outbound)); if (!pcr.HasPublishedContent()) { const string logMsg = nameof(DetectCollisionAsync) + " did not resolve a content item for original url: {Url}, translated to {TranslatedUrl} and culture: {Culture}"; logger.LogDebug(logMsg, url, uri, culture); var urlInfo = UrlInfo.Message(textService.Localize("content", "routeErrorCannotRoute"), culture); return(Attempt.Succeed(urlInfo)); } if (pcr.IgnorePublishedContentCollisions) { return(Attempt <UrlInfo?> .Fail()); } if (pcr.PublishedContent?.Id != content.Id) { IPublishedContent?o = pcr.PublishedContent; var l = new List <string>(); while (o != null) { l.Add(o.Name(variationContextAccessor) !); o = o.Parent; } l.Reverse(); var s = "/" + string.Join("/", l) + " (id=" + pcr.PublishedContent?.Id + ")"; var urlInfo = UrlInfo.Message(textService.Localize("content", "routeError", new[] { s }), culture); return(Attempt.Succeed(urlInfo)); } // no collision return(Attempt <UrlInfo?> .Fail()); }
/// <inheritdoc /> public async Task <IPublishedRequest> UpdateRequestAsync(IPublishedRequest request, IPublishedContent?publishedContent) { // store the original (if any) IPublishedContent?content = request.PublishedContent; IPublishedRequestBuilder builder = new PublishedRequestBuilder(request.Uri, _fileService); // set to the new content (or null if specified) builder.SetPublishedContent(publishedContent); // re-route await RouteRequestInternalAsync(builder); // return if we are redirect if (builder.IsRedirect()) { return(BuildRequest(builder)); } // this will occur if publishedContent is null and the last chance finders also don't assign content if (!builder.HasPublishedContent()) { // means the engine could not find a proper document to handle 404 // restore the saved content so we know it exists builder.SetPublishedContent(content); } if (!builder.HasDomain()) { FindDomain(builder); } return(BuildRequest(builder)); }
private IPublishedContent?GetModel(ref IPublishedContent?model, ContentData?contentData) { if (model != null) { return(model); } if (contentData == null) { return(null); } // create the model - we want to be fast, so no lock here: we may create // more than 1 instance, but the lock below ensures we only ever return // 1 unique instance - and locking is a nice explicit way to ensure this IPublishedContent?m = new PublishedContent(this, contentData, _publishedSnapshotAccessor, _variationContextAccessor, _publishedModelFactory).CreateModel(_publishedModelFactory); // locking 'this' is not a best-practice but ContentNode is internal and // we know what we do, so it is fine here and avoids allocating an object lock (this) { return(model ??= m); } }
/// <summary> /// Gets the URL of a published content. /// </summary> /// <param name="content">The published content.</param> /// <param name="mode">The URL mode.</param> /// <param name="culture">A culture.</param> /// <param name="current">The current absolute URL.</param> /// <returns>The URL for the published content.</returns> /// <remarks> /// <para>The URL is absolute or relative depending on <c>mode</c> and on <c>current</c>.</para> /// <para>If the published content is multi-lingual, gets the URL for the specified culture or, /// when no culture is specified, the current culture.</para> /// <para>If the provider is unable to provide a URL, it returns "#".</para> /// </remarks> public string GetUrl(IPublishedContent?content, UrlMode mode = UrlMode.Default, string?culture = null, Uri?current = null) { if (content == null || content.ContentType.ItemType == PublishedItemType.Element) { return("#"); } if (mode == UrlMode.Default) { mode = Mode; } // this the ONLY place where we deal with default culture - IUrlProvider always receive a culture // be nice with tests, assume things can be null, ultimately fall back to invariant // (but only for variant content of course) if (content.ContentType.VariesByCulture()) { if (culture == null) { culture = _variationContextAccessor?.VariationContext?.Culture ?? ""; } } if (current == null) { var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); current = umbracoContext.CleanedUmbracoUrl; } var url = _urlProviders.Select(provider => provider.GetUrl(content, mode, culture, current)) .FirstOrDefault(u => u is not null); return(url?.Text ?? "#"); // legacy wants this }
public INavigableContent?Get(int id) { // wrap in a navigable content IPublishedContent?content = _data.GetById(_preview, id); return(content == null ? null : new NavigableContent(content)); }
/// <summary> /// Creates a strongly typed published content model for an internal published content. /// </summary> /// <param name="content">The internal published content.</param> /// <param name="publishedModelFactory">The published model factory</param> /// <returns>The strongly typed published content model.</returns> public static IPublishedContent?CreateModel( this IPublishedContent?content, IPublishedModelFactory?publishedModelFactory) { if (publishedModelFactory == null) { throw new ArgumentNullException(nameof(publishedModelFactory)); } if (content == null) { return(null); } // get model // if factory returns nothing, throw IPublishedElement model = publishedModelFactory.CreateModel(content); if (model == null) { throw new InvalidOperationException("Factory returned null."); } // if factory returns a different type, throw if (!(model is IPublishedContent publishedContent)) { throw new InvalidOperationException( $"Factory returned model of type {model.GetType().FullName} which does not implement IPublishedContent."); } return(publishedContent); }
/// <summary> /// Initializes a new instance of the <see cref="PublishedRequest" /> class. /// </summary> public PublishedRequest( Uri uri, string absolutePathDecoded, IPublishedContent?publishedContent, bool isInternalRedirect, ITemplate?template, DomainAndUri?domain, string?culture, string?redirectUrl, int?responseStatusCode, IReadOnlyList <string>?cacheExtensions, IReadOnlyDictionary <string, string>?headers, bool setNoCacheHeader, bool ignorePublishedContentCollisions) { Uri = uri ?? throw new ArgumentNullException(nameof(uri)); AbsolutePathDecoded = absolutePathDecoded ?? throw new ArgumentNullException(nameof(absolutePathDecoded)); PublishedContent = publishedContent; IsInternalRedirect = isInternalRedirect; Template = template; Domain = domain; Culture = culture; RedirectUrl = redirectUrl; ResponseStatusCode = responseStatusCode; CacheExtensions = cacheExtensions; Headers = headers; SetNoCacheHeader = setNoCacheHeader; IgnorePublishedContentCollisions = ignorePublishedContentCollisions; }
private async Task SetUmbracoRouteValues(ActionExecutingContext context, IPublishedContent?content) { if (content != null) { UriUtility uriUtility = context.HttpContext.RequestServices.GetRequiredService <UriUtility>(); var originalRequestUrl = new Uri(context.HttpContext.Request.GetEncodedUrl()); Uri cleanedUrl = uriUtility.UriToUmbraco(originalRequestUrl); IPublishedRouter router = context.HttpContext.RequestServices.GetRequiredService <IPublishedRouter>(); IPublishedRequestBuilder requestBuilder = await router.CreateRequestAsync(cleanedUrl); requestBuilder.SetPublishedContent(content); IPublishedRequest publishedRequest = requestBuilder.Build(); var routeValues = new UmbracoRouteValues( publishedRequest, (ControllerActionDescriptor)context.ActionDescriptor); context.HttpContext.Features.Set(routeValues); } else { // if there is no content then it should be a not found context.Result = new NotFoundResult(); } }
private string?GetRouteByIdInternal(bool preview, int contentId, bool?hideTopLevelNode, string?culture) { IPublishedContent?node = GetById(preview, contentId); if (node == null) { return(null); } hideTopLevelNode = hideTopLevelNode ?? HideTopLevelNodeFromPath; // default = settings // walk up from that node until we hit a node with a domain, // or we reach the content root, collecting URLs in the way var pathParts = new List <string>(); IPublishedContent?n = node; var urlSegment = n.UrlSegment(_variationContextAccessor, culture); var hasDomains = _domainCache.GetAssignedWithCulture(culture, n.Id); while (hasDomains == false && n != null) // n is null at root { // no segment indicates this is not published when this is a variant if (urlSegment.IsNullOrWhiteSpace()) { return(null); } pathParts.Add(urlSegment !); // move to parent node n = n.Parent; if (n != null) { urlSegment = n.UrlSegment(_variationContextAccessor, culture); } hasDomains = n != null && _domainCache.GetAssignedWithCulture(culture, n.Id); } // at this point this will be the urlSegment of the root, no segment indicates this is not published when this is a variant if (urlSegment.IsNullOrWhiteSpace()) { return(null); } // no domain, respect HideTopLevelNodeFromPath for legacy purposes if (hasDomains == false && hideTopLevelNode.Value) { ApplyHideTopLevelNodeFromPath(node, pathParts, preview); } // assemble the route pathParts.Reverse(); var path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc //prefix the root node id containing the domain if it exists (this is a standard way of creating route paths) //and is done so that we know the ID of the domain node for the path var route = (n?.Id.ToString(CultureInfo.InvariantCulture) ?? "") + path; return(route); }
/// <summary> /// Indicates that the current PublishedContent is the initial one. /// </summary> public void SetIsInitialPublishedContent() { EnsureWriteable(); // note: it can very well be null if the initial content was not found _initialPublishedContent = _publishedContent; IsInternalRedirectPublishedContent = false; }
/// <summary> /// Tries to find and assign an Umbraco document to a <c>PublishedRequest</c>. /// </summary> /// <param name="frequest">The <c>PublishedRequest</c>.</param> /// <returns>A value indicating whether an Umbraco document was found and assigned.</returns> /// <remarks>Optionally, can also assign the template or anything else on the document request, although that is not required.</remarks> public async Task <bool> TryFindContent(IPublishedRequestBuilder frequest) { if (!_umbracoContextAccessor.TryGetUmbracoContext(out var umbracoContext)) { return(false); } var route = frequest.Domain != null ? frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.AbsolutePathDecoded) : frequest.AbsolutePathDecoded; IRedirectUrl?redirectUrl = await _redirectUrlService.GetMostRecentRedirectUrlAsync(route, frequest.Culture); if (redirectUrl == null) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("No match for route: {Route}", route); } return(false); } IPublishedContent?content = umbracoContext.Content?.GetById(redirectUrl.ContentId); var url = content == null ? "#" : content.Url(_publishedUrlProvider, redirectUrl.Culture); if (url.StartsWith("#")) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Route {Route} matches content {ContentId} which has no URL.", route, redirectUrl.ContentId); } return(false); } // Appending any querystring from the incoming request to the redirect URL url = string.IsNullOrEmpty(frequest.Uri.Query) ? url : url + frequest.Uri.Query; if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Route {Route} matches content {ContentId} with URL '{Url}', redirecting.", route, content?.Id, url); } frequest .SetRedirectPermanent(url) // From: http://stackoverflow.com/a/22468386/5018 // See http://issues.umbraco.org/issue/U4-8361#comment=67-30532 // Setting automatic 301 redirects to not be cached because browsers cache these very aggressively which then leads // to problems if you rename a page back to it's original name or create a new page with the original name .SetNoCacheHeader(true) .SetCacheExtensions(new List <string> { "no-store, must-revalidate" }) .SetHeaders(new Dictionary <string, string> { { "Pragma", "no-cache" }, { "Expires", "0" } }); return(true); }
/// <summary> /// Returns the content id based on the configured ContentErrorPage section. /// </summary> internal static int?GetContentIdFromErrorPageConfig( ContentErrorPage errorPage, IEntityService entityService, IPublishedContentQuery publishedContentQuery, int?domainContentId) { if (errorPage.HasContentId) { return(errorPage.ContentId); } if (errorPage.HasContentKey) { // need to get the Id for the GUID // TODO: When we start storing GUIDs into the IPublishedContent, then we won't have to look this up // but until then we need to look it up in the db. For now we've implemented a cached service for // converting Int -> Guid and vice versa. Attempt <int> found = entityService.GetId(errorPage.ContentKey, UmbracoObjectTypes.Document); if (found.Success) { return(found.Result); } return(null); } if (errorPage.ContentXPath.IsNullOrWhiteSpace() == false) { try { // we have an xpath statement to execute var xpathResult = UmbracoXPathPathSyntaxParser.ParseXPathQuery( xpathExpression: errorPage.ContentXPath !, nodeContextId: domainContentId, getPath: nodeid => { IEntitySlim?ent = entityService.Get(nodeid); return(ent?.Path.Split(',').Reverse()); }, publishedContentExists: i => publishedContentQuery.Content(i) != null); // now we'll try to execute the expression IPublishedContent?nodeResult = publishedContentQuery.ContentSingleAtXPath(xpathResult); if (nodeResult != null) { return(nodeResult.Id); } } catch (Exception ex) { StaticApplicationLogging.Logger.LogError(ex, "Could not parse xpath expression: {ContentXPath}", errorPage.ContentXPath); return(null); } } return(null); }
private async Task <MacroContent> RenderAsync(MacroModel macro, IPublishedContent?content) { if (content == null) { throw new ArgumentNullException(nameof(content)); } var macroInfo = $"Render Macro: {macro.Name}, cache: {macro.CacheDuration}"; using (_profilingLogger.DebugDuration <MacroRenderer>(macroInfo, "Rendered Macro.")) { // parse macro parameters ie replace the special [#key], [$key], etc. syntaxes foreach (var prop in macro.Properties) { prop.Value = ParseAttribute(prop.Value); } var cultureName = System.Threading.Thread.CurrentThread.CurrentUICulture.Name; macro.CacheIdentifier = await GetContentCacheIdentifier(macro, content.Id, cultureName); // get the macro from cache if it is there var macroContent = GetMacroContentFromCache(macro); // macroContent.IsEmpty may be true, meaning the macro produces no output, // but still can be cached because its execution did not trigger any error. // so we need to actually render, only if macroContent is null if (macroContent != null) { return(macroContent); } // this will take care of errors // it may throw, if we actually want to throw, so better not // catch anything here and let the exception be thrown var attempt = ExecuteMacroOfType(macro, content); // by convention ExecuteMacroByType must either throw or return a result // just check to avoid internal errors macroContent = attempt.Result; if (macroContent == null) { throw new Exception("Internal error, ExecuteMacroOfType returned no content."); } // add to cache if render is successful // content may be empty but that's not an issue if (attempt.Success) { // write to cache (if appropriate) await AddMacroContentToCacheAsync(macro, macroContent); } return(macroContent); } }
public static IHtmlContent GetCropUrl(this IUrlHelper urlHelper, IPublishedContent?mediaItem, string propertyAlias, string cropAlias, bool htmlEncode = true, UrlMode urlMode = UrlMode.Default) { if (mediaItem == null) { return(HtmlString.Empty); } var url = mediaItem.GetCropUrl(propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true, urlMode: urlMode); return(CreateHtmlString(url, htmlEncode)); }
private async Task <IActionResult> GetMacroResultAsHtml(string?macroAlias, int pageId, IDictionary <string, object>?macroParams) { IMacro?m = macroAlias is null ? null : _macroService.GetByAlias(macroAlias); if (m == null) { return(NotFound()); } IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); IPublishedContent?publishedContent = umbracoContext.Content?.GetById(true, pageId); //if it isn't supposed to be rendered in the editor then return an empty string //currently we cannot render a macro if the page doesn't yet exist if (pageId == -1 || publishedContent == null || m.DontRender) { //need to create a specific content result formatted as HTML since this controller has been configured //with only json formatters. return(Content(string.Empty, "text/html", Encoding.UTF8)); } // When rendering the macro in the backoffice the default setting would be to use the Culture of the logged in user. // Since a Macro might contain thing thats related to the culture of the "IPublishedContent" (ie Dictionary keys) we want // to set the current culture to the culture related to the content item. This is hacky but it works. // fixme // in a 1:1 situation we do not handle the language being edited // so the macro renders in the wrong language var culture = DomainUtilities.GetCultureFromDomains(publishedContent.Id, publishedContent.Path, null, umbracoContext, _siteDomainHelper); if (culture != null) { Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture); } // must have an active variation context! _variationContextAccessor.VariationContext = new VariationContext(culture); using (umbracoContext.ForcedPreview(true)) { //need to create a specific content result formatted as HTML since this controller has been configured //with only json formatters. return(Content( (await _componentRenderer.RenderMacroForContent(publishedContent, m.Alias, macroParams)).ToString() ?? string.Empty, "text/html", Encoding.UTF8)); } }
/// <summary> /// Gets the other URLs of a published content. /// </summary> /// <param name="umbracoContextAccessor">The Umbraco context.</param> /// <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) { IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); IPublishedContent?node = umbracoContext.Content?.GetById(id); if (node == null) { yield break; } // look for domains, walking up the tree IPublishedContent? n = node; IEnumerable <DomainAndUri>?domainUris = DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, n.Id, current, false); while (domainUris == null && n != null) // n is null at root { n = n.Parent; // move to parent node domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _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 = umbracoContext.Content?.GetRouteById(id, culture); if (route == null) { 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) var pos = route.IndexOf('/'); var path = pos == 0 ? route : route.Substring(pos); var uri = new Uri(CombinePaths(d.Uri.GetLeftPart(UriPartial.Path), path)); uri = _uriUtility.UriFromUmbraco(uri, _requestSettings); yield return(UrlInfo.Url(uri.ToString(), culture)); } }
// gets a published content as a previewing draft, if preview is true // this is for published content when previewing private static IPublishedContent?GetPublishedContentAsDraft(IPublishedContent?content /*, bool preview*/) { if (content == null /*|| preview == false*/) { return(null); // content; } // an object in the cache is either an IPublishedContentOrMedia, // or a model inheriting from PublishedContentExtended - in which // case we need to unwrap to get to the original IPublishedContentOrMedia. var inner = PublishedContent.UnwrapIPublishedContent(content); return(inner.AsDraft()); }
public async Task <MacroContent> RenderAsync(string macroAlias, IPublishedContent?content, IDictionary <string, object?>?macroParams) { var m = _appCaches.RuntimeCache.GetCacheItem(CacheKeys.MacroFromAliasCacheKey + macroAlias, () => _macroService.GetByAlias(macroAlias)); if (m == null) { throw new InvalidOperationException("No macro found by alias " + macroAlias); } var macro = new MacroModel(m); UpdateMacroModelProperties(macro, macroParams); return(await RenderAsync(macro, content)); }
private void StoreOldRoute(IContent entity, OldRoutesDictionary oldRoutes) { if (!_publishedSnapshotAccessor.TryGetPublishedSnapshot(out IPublishedSnapshot? publishedSnapshot)) { return; } IPublishedContentCache?contentCache = publishedSnapshot?.Content; IPublishedContent? entityContent = contentCache?.GetById(entity.Id); if (entityContent is null) { return; } // get the default affected cultures by going up the tree until we find the first culture variant entity (default to no cultures) var defaultCultures = entityContent.AncestorsOrSelf().FirstOrDefault(a => a.Cultures.Any())?.Cultures.Keys .ToArray() ?? Array.Empty <string>(); foreach (IPublishedContent publishedContent in entityContent.DescendantsOrSelf(_variationContextAccessor)) { // if this entity defines specific cultures, use those instead of the default ones IEnumerable <string> cultures = publishedContent.Cultures.Any() ? publishedContent.Cultures.Keys : defaultCultures; foreach (var culture in cultures) { var route = contentCache?.GetRouteById(publishedContent.Id, culture); if (!IsNotRoute(route)) { oldRoutes[new ContentIdAndCulture(publishedContent.Id, culture)] = new ContentKeyAndOldRoute(publishedContent.Key, route !); } else if (string.IsNullOrEmpty(culture)) { // Retry using all languages, if this is invariant but has a variant ancestor var languages = _localizationService.GetAllLanguages(); foreach (var language in languages) { route = contentCache?.GetRouteById(publishedContent.Id, language.IsoCode); if (!IsNotRoute(route)) { oldRoutes[new ContentIdAndCulture(publishedContent.Id, language.IsoCode)] = new ContentKeyAndOldRoute(publishedContent.Key, route !); } } } } } }
private IPublishedContent?FollowRoute(IPublishedContent?content, IReadOnlyList <string> parts, int start, string?culture) { var i = start; while (content != null && i < parts.Count) { var part = parts[i++]; content = content.Children(_variationContextAccessor, culture)?.FirstOrDefault(x => { var urlSegment = x.UrlSegment(_variationContextAccessor, culture); return(urlSegment == part); }); } return(content); }
/// <inheritdoc /> public async Task <IHtmlEncodedString> RenderMacroAsync(int contentId, string alias, IDictionary <string, object>?parameters) { if (contentId == default) { throw new ArgumentException("Invalid content id " + contentId); } IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); IPublishedContent?content = umbracoContext.Content?.GetById(contentId); if (content == null) { throw new InvalidOperationException("Cannot render a macro, no content found by id " + contentId); } return(await RenderMacroAsync(content, alias, parameters)); }
public override object?ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object?inter, bool preview) { var isMultiple = IsMultipleDataType(propertyType.DataType); if (string.IsNullOrEmpty(inter?.ToString())) { // Short-circuit on empty value return(isMultiple ? Enumerable.Empty <MediaWithCrops>() : null); } var mediaItems = new List <MediaWithCrops>(); IEnumerable <MediaPicker3PropertyEditor.MediaPicker3PropertyValueEditor.MediaWithCropsDto> dtos = MediaPicker3PropertyEditor.MediaPicker3PropertyValueEditor.Deserialize(_jsonSerializer, inter); MediaPicker3Configuration?configuration = propertyType.DataType.ConfigurationAs <MediaPicker3Configuration>(); IPublishedSnapshot publishedSnapshot = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot(); foreach (MediaPicker3PropertyEditor.MediaPicker3PropertyValueEditor.MediaWithCropsDto dto in dtos) { IPublishedContent?mediaItem = publishedSnapshot.Media?.GetById(preview, dto.MediaKey); if (mediaItem != null) { var localCrops = new ImageCropperValue { Crops = dto.Crops, FocalPoint = dto.FocalPoint, Src = mediaItem.Url(_publishedUrlProvider), }; localCrops.ApplyConfiguration(configuration); // TODO: This should be optimized/cached, as calling Activator.CreateInstance is slow Type mediaWithCropsType = typeof(MediaWithCrops <>).MakeGenericType(mediaItem.GetType()); var mediaWithCrops = (MediaWithCrops)Activator.CreateInstance(mediaWithCropsType, mediaItem, _publishedValueFallback, localCrops) !; mediaItems.Add(mediaWithCrops); if (!isMultiple) { // Short-circuit on single item break; } } } return(isMultiple ? mediaItems : mediaItems.FirstOrDefault()); }
/// <summary> /// Special check for when no template or hijacked route is done which needs to re-run through the routing pipeline /// again for last chance finders /// </summary> private async Task <UmbracoRouteValues> CheckNoTemplateAsync( HttpContext httpContext, UmbracoRouteValues def, bool hasHijackedRoute) { IPublishedRequest request = def.PublishedRequest; // Here we need to check if there is no hijacked route and no template assigned but there is a content item. // If this is the case we want to return a blank page. // We also check if templates have been disabled since if they are then we're allowed to render even though there's no template, // for example for json rendering in headless. if (request.HasPublishedContent() && !request.HasTemplate() && !_umbracoFeatures.Disabled.DisableTemplates && !hasHijackedRoute) { IPublishedContent?content = request.PublishedContent; // This is basically a 404 even if there is content found. // We then need to re-run this through the pipeline for the last // chance finders to work. // Set to null since we are telling it there is no content. request = await _publishedRouter.UpdateRequestAsync(request, null); if (request == null) { throw new InvalidOperationException( $"The call to {nameof(IPublishedRouter.UpdateRequestAsync)} cannot return null"); } string?customActionName = GetTemplateName(request); def = new UmbracoRouteValues( request, def.ControllerActionDescriptor, customActionName); // if the content has changed, we must then again check for hijacked routes if (content != request.PublishedContent) { def = CheckHijackedRoute(httpContext, def, out _); } } return(def); }
public IPublishedProperty?GetProperty(string alias, bool recurse) { IPublishedProperty?property = GetProperty(alias); if (recurse == false) { return(property); } IPublishedContent?content = this; while (content != null && (property == null || property.HasValue() == false)) { content = content.Parent; property = content?.GetProperty(alias); } return(property); }