using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Core; using Umbraco.Core.Configuration.Models; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; namespace Umbraco.Web.Routing { /// /// Provides an implementation of that handles page nice URLs and a template. /// /// /// This finder allows for an odd routing pattern similar to altTemplate, probably only use case is if there is an alternative mime type template and it should be routable by something like "/hello/world/json" where the JSON template is to be used for the "world" page /// Handles /foo/bar/template where /foo/bar is the nice URL of a document, and template a template alias. /// If successful, then the template of the document request is also assigned. /// public class ContentFinderByUrlAndTemplate : ContentFinderByUrl { private readonly ILogger _logger; private readonly IFileService _fileService; private readonly IContentTypeService _contentTypeService; private readonly WebRoutingSettings _webRoutingSettings; /// /// Initializes a new instance of the class. /// public ContentFinderByUrlAndTemplate( ILogger logger, IFileService fileService, IContentTypeService contentTypeService, IUmbracoContextAccessor umbracoContextAccessor, IOptions webRoutingSettings) : base(logger, umbracoContextAccessor) { _logger = logger; _fileService = fileService; _contentTypeService = contentTypeService; _webRoutingSettings = webRoutingSettings.Value; } /// /// Tries to find and assign an Umbraco document to a PublishedRequest. /// /// The PublishedRequest. /// A value indicating whether an Umbraco document was found and assigned. /// If successful, also assigns the template. public override bool TryFindContent(IPublishedRequestBuilder frequest) { var path = frequest.Uri.GetAbsolutePathDecoded(); 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; } } }