예제 #1
0
        internal UrlInfo GetUrlFromRoute(
            string route,
            IUmbracoContext umbracoContext,
            int id,
            Uri current,
            UrlMode mode,
            string culture)
        {
            if (string.IsNullOrWhiteSpace(route))
            {
                _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>
            var          pos       = route.IndexOf('/');
            var          path      = pos == 0 ? route : route.Substring(pos);
            DomainAndUri domainUri = pos == 0
                ? null
                : DomainUtilities.DomainForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, int.Parse(route.Substring(0, pos), CultureInfo.InvariantCulture), current, culture);

            var defaultCulture = _localizationService.GetDefaultLanguageIsoCode();

            if (domainUri is not null || string.IsNullOrEmpty(culture) || culture.Equals(defaultCulture, StringComparison.InvariantCultureIgnoreCase))
            {
                var url = AssembleUrl(domainUri, path, current, mode).ToString();
                return(UrlInfo.Url(url, culture));
            }

            return(null);
        }
예제 #2
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);
            }
        }
예제 #3
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);
        }
예제 #4
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);
        }
예제 #5
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DomainAndUri"/> class.
 /// </summary>
 /// <param name="domain">The original domain.</param>
 /// <param name="currentUri">The context current Uri.</param>
 public DomainAndUri(Domain domain, Uri currentUri)
     : base(domain)
 {
     try
     {
         Uri = DomainUtilities.ParseUriFromDomainName(Name, currentUri);
     }
     catch (UriFormatException)
     {
         throw new ArgumentException($"Failed to parse invalid domain: node id={domain.ContentId}, hostname=\"{Name.ToCSharpString()}\"."
                                     + " Hostname should be a valid uri.", nameof(domain));
     }
 }
예제 #6
0
        /// <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));
            }
        }
예제 #7
0
        internal UrlInfo GetUrlFromRoute(string route, IUmbracoContext umbracoContext, int id, Uri current, UrlMode mode, string culture)
        {
            if (string.IsNullOrWhiteSpace(route))
            {
                _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>
            var pos       = route.IndexOf('/');
            var path      = pos == 0 ? route : route.Substring(pos);
            var domainUri = pos == 0
                ? null
                : DomainUtilities.DomainForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, int.Parse(route.Substring(0, pos), CultureInfo.InvariantCulture), current, culture);

            // assemble the URL from domainUri (maybe null) and path
            var url = AssembleUrl(domainUri, path, current, mode).ToString();

            return(UrlInfo.Url(url, culture));
        }
예제 #8
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);
        }
예제 #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);
            }
            _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);
        }
예제 #10
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(Microsoft.Extensions.Logging.LogLevel.Debug))
            {
                _logger.LogDebug("{TracePrefix}Uri={RequestUri}", tracePrefix, request.Uri);
            }
            var          umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();
            IDomainCache?domainsCache   = umbracoContext.PublishedSnapshot.Domains;
            var          domains        = domainsCache?.GetAll(includeWildcards: 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(Microsoft.Extensions.Logging.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(Microsoft.Extensions.Logging.LogLevel.Debug))
                {
                    _logger.LogDebug("{TracePrefix}Matches no domain", tracePrefix);
                }

                request.SetCulture(defaultCulture ?? CultureInfo.CurrentUICulture.Name);
            }
            if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
            {
                _logger.LogDebug("{TracePrefix}Culture={CultureName}", tracePrefix, request.Culture);
            }

            return(request.Domain != null);
        }
예제 #11
0
        /// <summary>
        /// Gets the other URLs of a published content.
        /// </summary>
        /// <param name="umbracoContext">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 IEnumerable <UrlInfo> GetOtherUrls(int id, Uri current)
        {
            var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();
            var node           = umbracoContext.Content.GetById(id);

            if (node == null)
            {
                yield break;
            }

            if (!node.HasProperty(Constants.Conventions.Content.UrlAlias))
            {
                yield break;
            }

            // look for domains, walking up the tree
            var n          = node;
            var domainUris = DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, n.Id, current, false);

            while (domainUris == null && n != null) // n is null at root
            {
                // move to parent node
                n          = n.Parent;
                domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, n.Id, current, excludeDefault: false);
            }

            // determine whether the alias property varies
            var varies = node.GetProperty(Constants.Conventions.Content.UrlAlias).PropertyType.VariesByCulture();

            if (domainUris == null)
            {
                // no domain
                // if the property is invariant, then URL "/<alias>" is ok
                // if the property varies, then what are we supposed to do?
                //  the content finder may work, depending on the 'current' culture,
                //  but there's no way we can return something meaningful here
                if (varies)
                {
                    yield break;
                }

                var umbracoUrlName = node.Value <string>(_publishedValueFallback, Constants.Conventions.Content.UrlAlias);
                var aliases        = umbracoUrlName?.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries);

                if (aliases == null || aliases.Any() == false)
                {
                    yield break;
                }

                foreach (var alias in aliases.Distinct())
                {
                    var path = "/" + alias;
                    var uri  = new Uri(path, UriKind.Relative);
                    yield return(UrlInfo.Url(_uriUtility.UriFromUmbraco(uri, _requestConfig).ToString()));
                }
            }
            else
            {
                // some domains: one URL per domain, which is "<domain>/<alias>"
                foreach (var domainUri in domainUris)
                {
                    // if the property is invariant, get the invariant value, URL is "<domain>/<invariant-alias>"
                    // if the property varies, get the variant value, URL is "<domain>/<variant-alias>"

                    // but! only if the culture is published, else ignore
                    if (varies && !node.HasCulture(domainUri.Culture))
                    {
                        continue;
                    }

                    var umbracoUrlName = varies
                        ? node.Value <string>(_publishedValueFallback, Constants.Conventions.Content.UrlAlias, culture: domainUri.Culture)
                        : node.Value <string>(_publishedValueFallback, Constants.Conventions.Content.UrlAlias);

                    var aliases = umbracoUrlName?.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries);

                    if (aliases == null || aliases.Any() == false)
                    {
                        continue;
                    }

                    foreach (var alias in aliases.Distinct())
                    {
                        var path = "/" + alias;
                        var uri  = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path));
                        yield return(UrlInfo.Url(_uriUtility.UriFromUmbraco(uri, _requestConfig).ToString(), domainUri.Culture));
                    }
                }
            }
        }