示例#1
0
        /// <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 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());
    }
        /// <summary>
        /// Tries to find an Umbraco document for a <c>PublishedRequest</c> and a route.
        /// </summary>
        /// <returns>The document node, or null.</returns>
        protected IPublishedContent FindContent(IPublishedRequestBuilder docreq, string route)
        {
            if (!UmbracoContextAccessor.TryGetUmbracoContext(out var umbracoContext))
            {
                return(null);
            }

            if (docreq == null)
            {
                throw new System.ArgumentNullException(nameof(docreq));
            }

            _logger.LogDebug("Test route {Route}", route);

            IPublishedContent node = umbracoContext.Content.GetByRoute(umbracoContext.InPreviewMode, route, culture: docreq.Culture);

            if (node != null)
            {
                docreq.SetPublishedContent(node);
                _logger.LogDebug("Got content, id={NodeId}", node.Id);
            }
            else
            {
                _logger.LogDebug("No match.");
            }

            return(node);
        }
示例#4
0
        /// <summary>
        /// Follows external redirection through <c>umbracoRedirect</c> document property.
        /// </summary>
        /// <remarks>As per legacy, if the redirect does not work, we just ignore it.</remarks>
        private void FollowExternalRedirect(IPublishedRequestBuilder request)
        {
            if (request.PublishedContent == null)
            {
                return;
            }

            // don't try to find a redirect if the property doesn't exist
            if (request.PublishedContent.HasProperty(Constants.Conventions.Content.Redirect) == false)
            {
                return;
            }

            var redirectId  = request.PublishedContent.Value(_publishedValueFallback, Constants.Conventions.Content.Redirect, defaultValue: -1);
            var redirectUrl = "#";

            if (redirectId > 0)
            {
                redirectUrl = _publishedUrlProvider.GetUrl(redirectId);
            }
            else
            {
                // might be a UDI instead of an int Id
                GuidUdi?redirectUdi = request.PublishedContent.Value <GuidUdi>(_publishedValueFallback, Constants.Conventions.Content.Redirect);
                if (redirectUdi is not null)
                {
                    redirectUrl = _publishedUrlProvider.GetUrl(redirectUdi.Guid);
                }
            }

            if (redirectUrl != "#")
            {
                request.SetRedirect(redirectUrl);
            }
        }
        private async Task SetUmbracoRouteValues(ActionExecutingContext context, IPublishedContent content)
        {
            if (content != null)
            {
                IUmbracoContextAccessor umbracoContextAccessor = context.HttpContext.RequestServices.GetRequiredService <IUmbracoContextAccessor>();
                IPublishedRouter        router = context.HttpContext.RequestServices.GetRequiredService <IPublishedRouter>();

                var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext();

                IPublishedRequestBuilder requestBuilder = await router.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl);

                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();
            }
        }
示例#6
0
    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();
        }
    }
示例#7
0
        /// <summary>
        /// Tries to find the document matching the request, by running the IPublishedContentFinder instances.
        /// </summary>
        /// <exception cref="InvalidOperationException">There is no finder collection.</exception>
        internal bool FindPublishedContent(IPublishedRequestBuilder request)
        {
            const string tracePrefix = "FindPublishedContent: ";

            // look for the document
            // the first successful finder, if any, will set this.PublishedContent, and may also set this.Template
            // some finders may implement caching
            using (_profilingLogger.DebugDuration <PublishedRouter>(
                       $"{tracePrefix}Begin finders",
                       $"{tracePrefix}End finders"))
            {
                // iterate but return on first one that finds it
                var found = _contentFinders.Any(finder =>
                {
                    _logger.LogDebug("Finder {ContentFinderType}", finder.GetType().FullName);
                    return(finder.TryFindContent(request));
                });

                _logger.LogDebug(
                    "Found? {Found}, Content: {PublishedContentId}, Template: {TemplateAlias}, Domain: {Domain}, Culture: {Culture}, StatusCode: {StatusCode}",
                    found,
                    request.HasPublishedContent() ? request.PublishedContent.Id : "NULL",
                    request.HasTemplate() ? request.Template?.Alias : "NULL",
                    request.HasDomain() ? request.Domain.ToString() : "NULL",
                    request.Culture ?? "NULL",
                    request.ResponseStatusCode);

                return(found);
            }
        }
示例#8
0
        /// <summary>
        /// Looks for wildcard domains in the path and updates <c>Culture</c> accordingly.
        /// </summary>
        internal void HandleWildcardDomains(IPublishedRequestBuilder request)
        {
            const string tracePrefix = "HandleWildcardDomains: ";

            if (request.PublishedContent == null)
            {
                return;
            }

            var nodePath = request.PublishedContent.Path;

            _logger.LogDebug("{TracePrefix}Path={NodePath}", tracePrefix, nodePath);
            var    rootNodeId     = request.Domain != null ? request.Domain.ContentId : (int?)null;
            var    umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();
            Domain domain         = DomainUtilities.FindWildcardDomainInPath(umbracoContext.PublishedSnapshot.Domains.GetAll(true), nodePath, rootNodeId);

            // always has a contentId and a culture
            if (domain != null)
            {
                request.SetCulture(domain.Culture);
                _logger.LogDebug("{TracePrefix}Got domain on node {DomainContentId}, set culture to {CultureName}", tracePrefix, domain.ContentId, request.Culture);
            }
            else
            {
                _logger.LogDebug("{TracePrefix}No match.", tracePrefix);
            }
        }
示例#9
0
        /// <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 bool TryFindContent(IPublishedRequestBuilder frequest)
        {
            if (!_umbracoContextAccessor.TryGetUmbracoContext(out var umbracoContext))
            {
                return(false);
            }
            if (umbracoContext == null || (umbracoContext != null && umbracoContext.InPreviewMode == false && _webRoutingSettings.DisableFindContentByIdPath))
            {
                return(false);
            }

            IPublishedContent node = null;
            var path = frequest.AbsolutePathDecoded;

            var nodeId = -1;

            // no id if "/"
            if (path != "/")
            {
                var noSlashPath = path.Substring(1);

                if (int.TryParse(noSlashPath, NumberStyles.Integer, CultureInfo.InvariantCulture, out nodeId) == false)
                {
                    nodeId = -1;
                }

                if (nodeId > 0)
                {
                    _logger.LogDebug("Id={NodeId}", nodeId);
                    node = umbracoContext.Content.GetById(nodeId);

                    if (node != null)
                    {
                        var cultureFromQuerystring = _requestAccessor.GetQueryStringValue("culture");

                        // if we have a node, check if we have a culture in the query string
                        if (!string.IsNullOrEmpty(cultureFromQuerystring))
                        {
                            // we're assuming it will match a culture, if an invalid one is passed in, an exception will throw (there is no TryGetCultureInfo method), i think this is ok though
                            frequest.SetCulture(cultureFromQuerystring);
                        }

                        frequest.SetPublishedContent(node);
                        _logger.LogDebug("Found node with id={PublishedContentId}", node.Id);
                    }
                    else
                    {
                        nodeId = -1; // trigger message below
                    }
                }
            }

            if (nodeId == -1)
            {
                _logger.LogDebug("Not a node id");
            }

            return(node != null);
        }
示例#10
0
        /// <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);
        }
示例#11
0
    private async Task RouteRequestInternalAsync(IPublishedRequestBuilder builder, bool skipContentFinders = false)
    {
        // if request builder was already flagged to redirect then return
        // whoever called us is in charge of actually redirecting
        if (builder.IsRedirect())
        {
            return;
        }

        // set the culture
        SetVariationContext(builder.Culture);

        var foundContentByFinders = false;

        // Find the published content if it's not assigned.
        // This could be manually assigned with a custom route handler, etc...
        // which in turn could call this method
        // to setup the rest of the pipeline but we don't want to run the finders since there's one assigned.
        if (!builder.HasPublishedContent() && !skipContentFinders)
        {
            if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug("FindPublishedContentAndTemplate: Path={UriAbsolutePath}", builder.Uri.AbsolutePath);
            }

            // run the document finders
            foundContentByFinders = await FindPublishedContent(builder);
        }

        // if we are not a redirect
        if (!builder.IsRedirect())
        {
            // handle not-found, redirects, access...
            await HandlePublishedContent(builder);

            // find a template
            FindTemplate(builder, foundContentByFinders);

            // handle umbracoRedirect
            FollowExternalRedirect(builder);

            // handle wildcard domains
            HandleWildcardDomains(builder);

            // set the culture  -- again, 'cos it might have changed due to a finder or wildcard domain
            SetVariationContext(builder.Culture);
        }

        // trigger the routing request (used to be called Prepared) event - at that point it is still possible to change about anything
        // even though the request might be flagged for redirection - we'll redirect _after_ the event
        var routingRequest = new RoutingRequestNotification(builder);
        await _eventAggregator.PublishAsync(routingRequest);

        // we don't take care of anything so if the content has changed, it's up to the user
        // to find out the appropriate template
    }
示例#12
0
        /// <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>If successful, also assigns the template.</remarks>
        public override bool TryFindContent(IPublishedRequestBuilder frequest)
        {
            var path = frequest.AbsolutePathDecoded;

            if (frequest.Domain != null)
            {
                path = DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, path);
            }

            // no template if "/"
            if (path == "/")
            {
                _logger.LogDebug("No template in path '/'");
                return(false);
            }

            // look for template in last position
            var pos           = path.LastIndexOf('/');
            var templateAlias = path.Substring(pos + 1);

            path = pos == 0 ? "/" : path.Substring(0, pos);

            ITemplate template = _fileService.GetTemplate(templateAlias);

            if (template == null)
            {
                _logger.LogDebug("Not a valid template: '{TemplateAlias}'", templateAlias);
                return(false);
            }

            _logger.LogDebug("Valid template: '{TemplateAlias}'", templateAlias);

            // look for node corresponding to the rest of the route
            var route = frequest.Domain != null ? (frequest.Domain.ContentId + path) : path;
            IPublishedContent node = FindContent(frequest, route);

            if (node == null)
            {
                _logger.LogDebug("Not a valid route to node: '{Route}'", route);
                return(false);
            }

            // IsAllowedTemplate deals both with DisableAlternativeTemplates and ValidateAlternativeTemplates settings
            if (!node.IsAllowedTemplate(_contentTypeService, _webRoutingSettings, template.Id))
            {
                _logger.LogWarning("Alternative template '{TemplateAlias}' is not allowed on node {NodeId}.", template.Alias, node.Id);
                frequest.SetPublishedContent(null); // clear
                return(false);
            }

            // got it
            frequest.SetTemplate(template);
            return(true);
        }
示例#13
0
    /// <summary>
    ///     Tries to find the document matching the request, by running the IPublishedContentFinder instances.
    /// </summary>
    /// <exception cref="InvalidOperationException">There is no finder collection.</exception>
    internal async Task <bool> FindPublishedContent(IPublishedRequestBuilder request)
    {
        const string tracePrefix = "FindPublishedContent: ";

        // look for the document
        // the first successful finder, if any, will set this.PublishedContent, and may also set this.Template
        // some finders may implement caching
        DisposableTimer?profilingScope = null;

        try
        {
            if (_logger.IsEnabled(LogLevel.Debug))
            {
                profilingScope = _profilingLogger.DebugDuration <PublishedRouter>(
                    $"{tracePrefix}Begin finders",
                    $"{tracePrefix}End finders");
            }

            // iterate but return on first one that finds it
            var found = false;
            foreach (IContentFinder contentFinder in _contentFinders)
            {
                if (_logger.IsEnabled(LogLevel.Debug))
                {
                    _logger.LogDebug("Finder {ContentFinderType}", contentFinder.GetType().FullName);
                }

                found = await contentFinder.TryFindContent(request);

                if (found)
                {
                    break;
                }
            }

            if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug(
                    "Found? {Found}, Content: {PublishedContentId}, Template: {TemplateAlias}, Domain: {Domain}, Culture: {Culture}, StatusCode: {StatusCode}",
                    found,
                    request.HasPublishedContent() ? request.PublishedContent?.Id : "NULL",
                    request.HasTemplate() ? request.Template?.Alias : "NULL",
                    request.HasDomain() ? request.Domain?.ToString() : "NULL",
                    request.Culture ?? "NULL",
                    request.ResponseStatusCode);
            }

            return(found);
        }
        finally
        {
            profilingScope?.Dispose();
        }
    }
        public void Setting_Domain_Also_Sets_Culture()
        {
            IPublishedRequestBuilder sut = GetBuilder();

            Assert.IsNull(sut.Culture);

            sut.SetDomain(
                new DomainAndUri(
                    new Domain(1, "test", 2, "en-AU", false), new Uri("https://example.com/en-au")));

            Assert.IsNotNull(sut.Domain);
            Assert.IsNotNull(sut.Culture);
        }
示例#15
0
        /// <summary>
        /// Handles the published content (if any).
        /// </summary>
        /// <param name="request">The request builder.</param>
        /// <remarks>
        /// Handles "not found", internal redirects ...
        /// things that must be handled in one place because they can create loops
        /// </remarks>
        private void HandlePublishedContent(IPublishedRequestBuilder request)
        {
            // because these might loop, we have to have some sort of infinite loop detection
            int       i = 0, j = 0;
            const int maxLoop = 8;

            do
            {
                _logger.LogDebug("HandlePublishedContent: Loop {LoopCounter}", i);

                // handle not found
                if (request.PublishedContent == null)
                {
                    request.SetIs404();
                    _logger.LogDebug("HandlePublishedContent: No document, try last chance lookup");

                    // if it fails then give up, there isn't much more that we can do
                    if (_contentLastChanceFinder.TryFindContent(request) == false)
                    {
                        _logger.LogDebug("HandlePublishedContent: Failed to find a document, give up");
                        break;
                    }

                    _logger.LogDebug("HandlePublishedContent: Found a document");
                }

                // follow internal redirects as long as it's not running out of control ie infinite loop of some sort
                j = 0;
                while (FollowInternalRedirects(request) && j++ < maxLoop)
                {
                }

                // we're running out of control
                if (j == maxLoop)
                {
                    break;
                }

                // loop while we don't have page, ie the redirect or access
                // got us to nowhere and now we need to run the notFoundLookup again
                // as long as it's not running out of control ie infinite loop of some sort
            } while (request.PublishedContent == null && i++ < maxLoop);

            if (i == maxLoop || j == maxLoop)
            {
                _logger.LogDebug("HandlePublishedContent: Looks like we are running into an infinite loop, abort");
                request.SetPublishedContent(null);
            }

            _logger.LogDebug("HandlePublishedContent: End");
        }
示例#16
0
    /// <summary>
    ///     This method finalizes/builds the PCR with the values assigned.
    /// </summary>
    /// <returns>
    ///     Returns false if the request was not successfully configured
    /// </returns>
    /// <remarks>
    ///     This method logic has been put into it's own method in case developers have created a custom PCR or are assigning
    ///     their own values
    ///     but need to finalize it themselves.
    /// </remarks>
    internal IPublishedRequest BuildRequest(IPublishedRequestBuilder builder)
    {
        IPublishedRequest result = builder.Build();

        if (!builder.HasPublishedContent())
        {
            return(result);
        }

        // set the culture -- again, 'cos it might have changed in the event handler
        SetVariationContext(result.Culture);

        return(result);
    }
示例#17
0
    private async Task <IPublishedRequest> TryRouteRequest(IPublishedRequestBuilder request)
    {
        FindDomain(request);

        if (request.IsRedirect())
        {
            return(request.Build());
        }

        if (request.HasPublishedContent())
        {
            return(request.Build());
        }

        await FindPublishedContent(request);

        return(request.Build());
    }
示例#18
0
        private IPublishedRequest TryRouteRequest(IPublishedRequestBuilder request)
        {
            FindDomain(request);

            if (request.IsRedirect())
            {
                return(request.Build());
            }

            if (request.HasPublishedContent())
            {
                return(request.Build());
            }

            FindPublishedContent(request);

            return(request.Build());
        }
示例#19
0
        private async Task <IPublishedRequest> RouteRequestAsync(IUmbracoContext umbracoContext)
        {
            // ok, process

            // instantiate, prepare and process the published content request
            // important to use CleanedUmbracoUrl - lowercase path-only version of the current url
            IPublishedRequestBuilder requestBuilder = await _publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl);

            // TODO: This is ugly with the re-assignment to umbraco context but at least its now
            // an immutable object. The only way to make this better would be to have a RouteRequest
            // as part of UmbracoContext but then it will require a PublishedRouter dependency so not sure that's worth it.
            // Maybe could be a one-time Set method instead?
            IPublishedRequest routedRequest = await _publishedRouter.RouteRequestAsync(requestBuilder, new RouteRequestOptions(RouteDirection.Inbound));

            umbracoContext.PublishedRequest = routedRequest;

            return(routedRequest);
        }
示例#20
0
        /// <inheritdoc />
        public async Task <IPublishedRequest> RouteRequestAsync(IPublishedRequestBuilder builder, RouteRequestOptions options)
        {
            // outbound routing performs different/simpler logic
            if (options.RouteDirection == RouteDirection.Outbound)
            {
                return(await TryRouteRequest(builder));
            }

            // find domain
            if (builder.Domain == null)
            {
                FindDomain(builder);
            }

            await RouteRequestInternalAsync(builder);

            // complete the PCR and assign the remaining values
            return(BuildRequest(builder));
        }
示例#21
0
        /// <inheritdoc/>
        public bool TryFindContent(IPublishedRequestBuilder frequest)
        {
            if (!_umbracoContextAccessor.TryGetUmbracoContext(out var umbracoContext))
            {
                return(false);
            }
            if (int.TryParse(_requestAccessor.GetRequestValue("umbPageID"), NumberStyles.Integer, CultureInfo.InvariantCulture, out int pageId))
            {
                IPublishedContent doc = umbracoContext.Content.GetById(pageId);

                if (doc != null)
                {
                    frequest.SetPublishedContent(doc);
                    return(true);
                }
            }

            return(false);
        }
        public void Setting_Published_Content_Clears_Template_And_Redirect()
        {
            IPublishedRequestBuilder sut = GetBuilder();

            sut.SetTemplate(Mock.Of <ITemplate>());

            Assert.IsNotNull(sut.Template);

            sut.SetInternalRedirect(Mock.Of <IPublishedContent>());

            Assert.IsNull(sut.Template);
            Assert.IsTrue(sut.IsInternalRedirect);

            sut.SetTemplate(Mock.Of <ITemplate>());
            sut.SetPublishedContent(Mock.Of <IPublishedContent>());

            Assert.IsNull(sut.Template);
            Assert.IsFalse(sut.IsInternalRedirect);
        }
        public void Builds_All_Values()
        {
            IPublishedRequestBuilder sut = GetBuilder();

            IPublishedContent content  = Mock.Of <IPublishedContent>(x => x.Id == 1);
            ITemplate         template = Mock.Of <ITemplate>(x => x.Id == 1);

            string[] cacheExt  = new[] { "must-revalidate" };
            var      auCulture = "en-AU";
            var      usCulture = "en-US";
            var      domain    = new DomainAndUri(
                new Domain(1, "test", 2, auCulture, false), new Uri("https://example.com/en-au"));
            IReadOnlyDictionary <string, string> headers = new Dictionary <string, string> {
                ["Hello"] = "world"
            };
            var redirect = "https://test.com";

            sut
            .SetNoCacheHeader(true)
            .SetCacheExtensions(cacheExt)
            .SetDomain(domain)
            .SetCulture(usCulture)
            .SetHeaders(headers)
            .SetInternalRedirect(content)
            .SetRedirect(redirect)
            .SetTemplate(template);

            IPublishedRequest request = sut.Build();

            Assert.AreEqual(true, request.SetNoCacheHeader);
            Assert.AreEqual(cacheExt, request.CacheExtensions);
            Assert.AreEqual(usCulture, request.Culture);
            Assert.AreEqual(domain, request.Domain);
            Assert.AreEqual(headers, request.Headers);
            Assert.AreEqual(true, request.IsInternalRedirect);
            Assert.AreEqual(content, request.PublishedContent);
            Assert.AreEqual(redirect, request.RedirectUrl);
            Assert.AreEqual(302, request.ResponseStatusCode);
            Assert.AreEqual(template, request.Template);
            Assert.AreEqual(_baseUri, request.Uri);
        }
示例#24
0
        /// <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 virtual async Task <bool> TryFindContent(IPublishedRequestBuilder frequest)
        {
            if (!UmbracoContextAccessor.TryGetUmbracoContext(out var umbracoContext))
            {
                return(false);
            }

            string route;

            if (frequest.Domain != null)
            {
                route = frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.AbsolutePathDecoded);
            }
            else
            {
                route = frequest.AbsolutePathDecoded;
            }

            IPublishedContent?node = FindContent(frequest, route);

            return(node != null);
        }
示例#25
0
    /// <summary>
    ///     Tries to find an Umbraco document for a <c>PublishedRequest</c> and a route.
    /// </summary>
    /// <returns>The document node, or null.</returns>
    protected IPublishedContent?FindContent(IPublishedRequestBuilder docreq, string route)
    {
        if (!UmbracoContextAccessor.TryGetUmbracoContext(out IUmbracoContext? umbracoContext))
        {
            return(null);
        }

        if (docreq == null)
        {
            throw new ArgumentNullException(nameof(docreq));
        }

        if (_logger.IsEnabled(LogLevel.Debug))
        {
            _logger.LogDebug("Test route {Route}", route);
        }

        IPublishedContent?node =
            umbracoContext.Content?.GetByRoute(umbracoContext.InPreviewMode, route, culture: docreq.Culture);

        if (node != null)
        {
            docreq.SetPublishedContent(node);
            if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug("Got content, id={NodeId}", node.Id);
            }
        }
        else
        {
            if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug("No match.");
            }
        }

        return(node);
    }
示例#26
0
        /// <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 bool TryFindContent(IPublishedRequestBuilder frequest)
        {
            if (!_umbracoContextAccessor.TryGetUmbracoContext(out var umbracoContext))
            {
                return(false);
            }
            _logger.LogDebug("Looking for a page to handle 404.");

            int?domainContentId = null;

            // try to find a culture as best as we can
            string errorCulture = CultureInfo.CurrentUICulture.Name;

            if (frequest.Domain != null)
            {
                errorCulture    = frequest.Domain.Culture;
                domainContentId = frequest.Domain.ContentId;
            }
            else
            {
                var route = frequest.AbsolutePathDecoded;
                var pos   = route.LastIndexOf('/');
                IPublishedContent node = null;
                while (pos > 1)
                {
                    route = route.Substring(0, pos);
                    node  = umbracoContext.Content.GetByRoute(route, culture: frequest?.Culture);
                    if (node != null)
                    {
                        break;
                    }

                    pos = route.LastIndexOf('/');
                }

                if (node != null)
                {
                    Domain d = DomainUtilities.FindWildcardDomainInPath(umbracoContext.PublishedSnapshot.Domains.GetAll(true), node.Path, null);
                    if (d != null)
                    {
                        errorCulture = d.Culture;
                    }
                }
            }

            var error404 = NotFoundHandlerHelper.GetCurrentNotFoundPageId(
                _contentSettings.Error404Collection.ToArray(),
                _entityService,
                new PublishedContentQuery(umbracoContext.PublishedSnapshot, _variationContextAccessor, _examineManager),
                errorCulture,
                domainContentId);

            IPublishedContent content = null;

            if (error404.HasValue)
            {
                _logger.LogDebug("Got id={ErrorNodeId}.", error404.Value);

                content = umbracoContext.Content.GetById(error404.Value);

                _logger.LogDebug(content == null
                    ? "Could not find content with that id."
                    : "Found corresponding content.");
            }
            else
            {
                _logger.LogDebug("Got nothing.");
            }

            frequest
            .SetPublishedContent(content)
            .SetIs404();

            return(content != null);
        }
示例#27
0
    /// <summary>
    ///     Finds a template for the current node, if any.
    /// </summary>
    /// <param name="request">The request builder.</param>
    /// <param name="contentFoundByFinders">
    ///     If the content was found by the finders, before anything such as 404, redirect...
    ///     took place.
    /// </param>
    private void FindTemplate(IPublishedRequestBuilder request, bool contentFoundByFinders)
    {
        // TODO: We've removed the event, might need to re-add?
        // NOTE: at the moment there is only 1 way to find a template, and then ppl must
        // use the Prepared event to change the template if they wish. Should we also
        // implement an ITemplateFinder logic?
        if (request.PublishedContent == null)
        {
            request.SetTemplate(null);
            return;
        }

        // read the alternate template alias, from querystring, form, cookie or server vars,
        // only if the published content is the initial once, else the alternate template
        // does not apply
        // + optionally, apply the alternate template on internal redirects
        var useAltTemplate = contentFoundByFinders ||
                             (_webRoutingSettings.InternalRedirectPreservesTemplate && request.IsInternalRedirect);

        var altTemplate = useAltTemplate
            ? _requestAccessor.GetRequestValue(Constants.Conventions.Url.AltTemplate)
            : null;

        if (string.IsNullOrWhiteSpace(altTemplate))
        {
            // we don't have an alternate template specified. use the current one if there's one already,
            // which can happen if a content lookup also set the template (LookupByNiceUrlAndTemplate...),
            // else lookup the template id on the document then lookup the template with that id.
            if (request.HasTemplate())
            {
                if (_logger.IsEnabled(LogLevel.Debug))
                {
                    _logger.LogDebug("FindTemplate: Has a template already, and no alternate template.");
                }

                return;
            }

            // TODO: We need to limit altTemplate to only allow templates that are assigned to the current document type!
            // if the template isn't assigned to the document type we should log a warning and return 404
            var       templateId = request.PublishedContent.TemplateId;
            ITemplate?template   = GetTemplate(templateId);
            request.SetTemplate(template);
            if (template != null)
            {
                if (_logger.IsEnabled(LogLevel.Debug))
                {
                    _logger.LogDebug(
                        "FindTemplate: Running with template id={TemplateId} alias={TemplateAlias}",
                        template.Id,
                        template.Alias);
                }
            }
            else
            {
                _logger.LogWarning("FindTemplate: Could not find template with id {TemplateId}", templateId);
            }
        }
        else
        {
            // we have an alternate template specified. lookup the template with that alias
            // this means the we override any template that a content lookup might have set
            // so /path/to/page/template1?altTemplate=template2 will use template2

            // ignore if the alias does not match - just trace
            if (request.HasTemplate())
            {
                if (_logger.IsEnabled(LogLevel.Debug))
                {
                    _logger.LogDebug("FindTemplate: Has a template already, but also an alternative template.");
                }
            }

            if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug("FindTemplate: Look for alternative template alias={AltTemplate}", altTemplate);
            }

            // IsAllowedTemplate deals both with DisableAlternativeTemplates and ValidateAlternativeTemplates settings
            if (request.PublishedContent.IsAllowedTemplate(
                    _fileService,
                    _contentTypeService,
                    _webRoutingSettings.DisableAlternativeTemplates,
                    _webRoutingSettings.ValidateAlternativeTemplates,
                    altTemplate))
            {
                // allowed, use
                ITemplate?template = _fileService.GetTemplate(altTemplate);

                if (template != null)
                {
                    request.SetTemplate(template);
                    if (_logger.IsEnabled(LogLevel.Debug))
                    {
                        _logger.LogDebug(
                            "FindTemplate: Got alternative template id={TemplateId} alias={TemplateAlias}",
                            template.Id,
                            template.Alias);
                    }
                }
                else
                {
                    if (_logger.IsEnabled(LogLevel.Debug))
                    {
                        _logger.LogDebug(
                            "FindTemplate: The alternative template with alias={AltTemplate} does not exist, ignoring.",
                            altTemplate);
                    }
                }
            }
            else
            {
                _logger.LogWarning(
                    "FindTemplate: Alternative template {TemplateAlias} is not allowed on node {NodeId}, ignoring.",
                    altTemplate,
                    request.PublishedContent.Id);

                // no allowed, back to default
                var       templateId = request.PublishedContent.TemplateId;
                ITemplate?template   = GetTemplate(templateId);
                request.SetTemplate(template);
                if (_logger.IsEnabled(LogLevel.Debug))
                {
                    _logger.LogDebug(
                        "FindTemplate: Running with template id={TemplateId} alias={TemplateAlias}",
                        template?.Id,
                        template?.Alias);
                }
            }
        }

        if (!request.HasTemplate())
        {
            if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug("FindTemplate: No template was found.");
            }

            // initial idea was: if we're not already 404 and UmbracoSettings.HandleMissingTemplateAs404 is true
            // then reset _pcr.Document to null to force a 404.
            //
            // but: because we want to let MVC hijack routes even though no template is defined, we decide that
            // a missing template is OK but the request will then be forwarded to MVC, which will need to take
            // care of everything.
            //
            // so, don't set _pcr.Document to null here
        }
    }
示例#28
0
    /// <summary>
    ///     Follows internal redirections through the <c>umbracoInternalRedirectId</c> document property.
    /// </summary>
    /// <param name="request">The request builder.</param>
    /// <returns>A value indicating whether redirection took place and led to a new published document.</returns>
    /// <remarks>
    ///     <para>Redirecting to a different site root and/or culture will not pick the new site root nor the new culture.</para>
    ///     <para>As per legacy, if the redirect does not work, we just ignore it.</para>
    /// </remarks>
    private bool FollowInternalRedirects(IPublishedRequestBuilder request)
    {
        if (request.PublishedContent == null)
        {
            throw new InvalidOperationException("There is no PublishedContent.");
        }

        // don't try to find a redirect if the property doesn't exist
        if (request.PublishedContent.HasProperty(Constants.Conventions.Content.InternalRedirectId) == false)
        {
            return(false);
        }

        var redirect = false;
        var valid    = false;
        IPublishedContent?internalRedirectNode = null;
        var internalRedirectId = request.PublishedContent.Value(
            _publishedValueFallback,
            Constants.Conventions.Content.InternalRedirectId,
            defaultValue: -1);
        IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();

        if (internalRedirectId > 0)
        {
            // try and get the redirect node from a legacy integer ID
            valid = true;
            internalRedirectNode = umbracoContext.Content?.GetById(internalRedirectId);
        }
        else
        {
            GuidUdi?udiInternalRedirectId = request.PublishedContent.Value <GuidUdi>(
                _publishedValueFallback,
                Constants.Conventions.Content.InternalRedirectId);
            if (udiInternalRedirectId is not null)
            {
                // try and get the redirect node from a UDI Guid
                valid = true;
                internalRedirectNode = umbracoContext.Content?.GetById(udiInternalRedirectId.Guid);
            }
        }

        if (valid == false)
        {
            // bad redirect - log and display the current page (legacy behavior)
            if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug(
                    "FollowInternalRedirects: Failed to redirect to id={InternalRedirectId}: value is not an int nor a GuidUdi.",
                    request.PublishedContent.GetProperty(Constants.Conventions.Content.InternalRedirectId)?.GetSourceValue());
            }
        }

        if (internalRedirectNode == null)
        {
            if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug(
                    "FollowInternalRedirects: Failed to redirect to id={InternalRedirectId}: no such published document.",
                    request.PublishedContent.GetProperty(Constants.Conventions.Content.InternalRedirectId)?.GetSourceValue());
            }
        }
        else if (internalRedirectId == request.PublishedContent.Id)
        {
            // redirect to self
            if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug("FollowInternalRedirects: Redirecting to self, ignore");
            }
        }
        else
        {
            // save since it will be cleared
            ITemplate?template = request.Template;

            request.SetInternalRedirect(internalRedirectNode); // don't use .PublishedContent here

            // must restore the template if it's an internal redirect & the config option is set
            if (request.IsInternalRedirect && _webRoutingSettings.InternalRedirectPreservesTemplate)
            {
                // restore
                request.SetTemplate(template);
            }

            redirect = true;
            if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug("FollowInternalRedirects: Redirecting to id={InternalRedirectId}", internalRedirectId);
            }
        }

        return(redirect);
    }
示例#29
0
    /// <summary>
    ///     Finds the site root (if any) matching the http request, and updates the PublishedRequest accordingly.
    /// </summary>
    /// <returns>A value indicating whether a domain was found.</returns>
    internal bool FindDomain(IPublishedRequestBuilder request)
    {
        const string tracePrefix = "FindDomain: ";

        // note - we are not handling schemes nor ports here.
        if (_logger.IsEnabled(LogLevel.Debug))
        {
            _logger.LogDebug("{TracePrefix}Uri={RequestUri}", tracePrefix, request.Uri);
        }

        IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();
        IDomainCache?   domainsCache   = umbracoContext.PublishedSnapshot.Domains;
        var             domains        = domainsCache?.GetAll(false).ToList();

        // determines whether a domain corresponds to a published document, since some
        // domains may exist but on a document that has been unpublished - as a whole - or
        // that is not published for the domain's culture - in which case the domain does
        // not apply
        bool IsPublishedContentDomain(Domain domain)
        {
            // just get it from content cache - optimize there, not here
            IPublishedContent?domainDocument = umbracoContext.PublishedSnapshot.Content?.GetById(domain.ContentId);

            // not published - at all
            if (domainDocument == null)
            {
                return(false);
            }

            // invariant - always published
            if (!domainDocument.ContentType.VariesByCulture())
            {
                return(true);
            }

            // variant, ensure that the culture corresponding to the domain's language is published
            return(domain.Culture is not null && domainDocument.Cultures.ContainsKey(domain.Culture));
        }

        domains = domains?.Where(IsPublishedContentDomain).ToList();

        var defaultCulture = domainsCache?.DefaultCulture;

        // try to find a domain matching the current request
        DomainAndUri?domainAndUri = DomainUtilities.SelectDomain(domains, request.Uri, defaultCulture: defaultCulture);

        // handle domain - always has a contentId and a culture
        if (domainAndUri != null)
        {
            // matching an existing domain
            if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug(
                    "{TracePrefix}Matches domain={Domain}, rootId={RootContentId}, culture={Culture}",
                    tracePrefix,
                    domainAndUri.Name,
                    domainAndUri.ContentId,
                    domainAndUri.Culture);
            }

            request.SetDomain(domainAndUri);

            // canonical? not implemented at the moment
            // if (...)
            // {
            //  _pcr.RedirectUrl = "...";
            //  return true;
            // }
        }
        else
        {
            // not matching any existing domain
            if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug("{TracePrefix}Matches no domain", tracePrefix);
            }

            request.SetCulture(defaultCulture ?? CultureInfo.CurrentUICulture.Name);
        }

        if (_logger.IsEnabled(LogLevel.Debug))
        {
            _logger.LogDebug("{TracePrefix}Culture={CultureName}", tracePrefix, request.Culture);
        }

        return(request.Domain != null);
    }
示例#30
0
 public async Task <bool> TryFindContent(IPublishedRequestBuilder frequest) => false;