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);
        }
示例#2
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);
        }
示例#3
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
        }
    }
示例#4
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);
    }
    public async Task RenderAsync(int pageId, int?altTemplateId, StringWriter writer)
    {
        if (writer == null)
        {
            throw new ArgumentNullException(nameof(writer));
        }

        IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();

        // instantiate a request and process
        // important to use CleanedUmbracoUrl - lowercase path-only version of the current URL, though this isn't going to matter
        // terribly much for this implementation since we are just creating a doc content request to modify it's properties manually.
        IPublishedRequestBuilder requestBuilder =
            await _publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl);

        IPublishedContent?doc = umbracoContext.Content?.GetById(pageId);

        if (doc == null)
        {
            writer.Write("<!-- Could not render template for Id {0}, the document was not found -->", pageId);
            return;
        }

        // in some cases the UmbracoContext will not have a PublishedRequest assigned to it if we are not in the
        // execution of a front-end rendered page. In this case set the culture to the default.
        // set the culture to the same as is currently rendering
        if (umbracoContext.PublishedRequest == null)
        {
            ILanguage?defaultLanguage = _languageService.GetAllLanguages().FirstOrDefault();

            requestBuilder.SetCulture(defaultLanguage == null
                ? CultureInfo.CurrentUICulture.Name
                : defaultLanguage.IsoCode);
        }
        else
        {
            requestBuilder.SetCulture(umbracoContext.PublishedRequest.Culture);
        }

        // set the doc that was found by id
        requestBuilder.SetPublishedContent(doc);

        // set the template, either based on the AltTemplate found or the standard template of the doc
        var templateId = _webRoutingSettings.DisableAlternativeTemplates || !altTemplateId.HasValue
            ? doc.TemplateId
            : altTemplateId.Value;

        if (templateId.HasValue)
        {
            requestBuilder.SetTemplate(_fileService.GetTemplate(templateId.Value));
        }

        // if there is not template then exit
        if (requestBuilder.HasTemplate() == false)
        {
            if (altTemplateId.HasValue == false)
            {
                writer.Write(
                    "<!-- Could not render template for Id {0}, the document's template was not found with id {0}-->",
                    doc.TemplateId);
            }
            else
            {
                writer.Write(
                    "<!-- Could not render template for Id {0}, the altTemplate was not found with id {0}-->",
                    altTemplateId);
            }

            return;
        }

        // First, save all of the items locally that we know are used in the chain of execution, we'll need to restore these
        // after this page has rendered.
        SaveExistingItems(out IPublishedRequest? oldPublishedRequest);

        IPublishedRequest contentRequest = requestBuilder.Build();

        try
        {
            // set the new items on context objects for this templates execution
            SetNewItemsOnContextObjects(contentRequest);

            // Render the template
            ExecuteTemplateRendering(writer, contentRequest);
        }
        finally
        {
            // restore items on context objects to continuing rendering the parent template
            RestoreItems(oldPublishedRequest);
        }
    }