コード例 #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="RedirectToUmbracoPageResult"/> class.
 /// </summary>
 public RedirectToUmbracoPageResult(IPublishedContent?publishedContent, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor)
 {
     _publishedContent = publishedContent;
     Key = publishedContent?.Key ?? Guid.Empty;
     _publishedUrlProvider   = publishedUrlProvider;
     _umbracoContextAccessor = umbracoContextAccessor;
 }
コード例 #2
0
ファイル: UrlProvider.cs プロジェクト: robertjf/Umbraco-CMS
        /// <summary>
        /// Gets the URL of a published content.
        /// </summary>
        /// <param name="content">The published content.</param>
        /// <param name="mode">The URL mode.</param>
        /// <param name="culture">A culture.</param>
        /// <param name="current">The current absolute URL.</param>
        /// <returns>The URL for the published content.</returns>
        /// <remarks>
        /// <para>The URL is absolute or relative depending on <c>mode</c> and on <c>current</c>.</para>
        /// <para>If the published content is multi-lingual, gets the URL for the specified culture or,
        /// when no culture is specified, the current culture.</para>
        /// <para>If the provider is unable to provide a URL, it returns "#".</para>
        /// </remarks>
        public string GetUrl(IPublishedContent?content, UrlMode mode = UrlMode.Default, string?culture = null, Uri?current = null)
        {
            if (content == null || content.ContentType.ItemType == PublishedItemType.Element)
            {
                return("#");
            }

            if (mode == UrlMode.Default)
            {
                mode = Mode;
            }

            // this the ONLY place where we deal with default culture - IUrlProvider always receive a culture
            // be nice with tests, assume things can be null, ultimately fall back to invariant
            // (but only for variant content of course)
            // We need to check all ancestors because urls are variant even for invariant content, if an ancestor is variant.
            if (culture == null && content.AncestorsOrSelf().Any(x => x.ContentType.VariesByCulture()))
            {
                culture = _variationContextAccessor?.VariationContext?.Culture ?? string.Empty;
            }

            if (current == null)
            {
                IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();
                current = umbracoContext.CleanedUmbracoUrl;
            }


            UrlInfo?url = _urlProviders.Select(provider => provider.GetUrl(content, mode, culture, current))
                          .FirstOrDefault(u => u is not null);

            return(url?.Text ?? "#"); // legacy wants this
        }
コード例 #3
0
        // tries to get a value, recursing the tree
        // because we recurse, content may not even have the a property with the specified alias (but only some ancestor)
        // in case no value was found, noValueProperty contains the first property that was found (which does not have a value)
        private bool TryGetValueWithAncestorsFallback <T>(IPublishedContent?content, string alias, string?culture, string?segment, out T?value, ref IPublishedProperty?noValueProperty)
        {
            IPublishedProperty?property;  // if we are here, content's property has no value

            do
            {
                content = content?.Parent;

                var propertyType = content?.ContentType.GetPropertyType(alias);

                if (propertyType != null && content is not null)
                {
                    culture = null;
                    segment = null;
                    _variationContextAccessor.ContextualizeVariation(propertyType.Variations, content.Id, ref culture, ref segment);
                }

                property = content?.GetProperty(alias);
                if (property != null && noValueProperty == null)
                {
                    noValueProperty = property;
                }
            }while (content != null && (property == null || property.HasValue(culture, segment) == false));

            // if we found a content with the property having a value, return that property value
            if (property != null && property.HasValue(culture, segment))
            {
                value = property.Value <T>(this, culture, segment);
                return(true);
            }

            value = default;
            return(false);
        }
コード例 #4
0
    /// <summary>
    ///     Creates an <see cref="IEnumerable{PublishedSearchResult}" /> containing all content from the
    ///     <paramref name="cache" />.
    /// </summary>
    /// <param name="results">The search results.</param>
    /// <param name="cache">The cache to fetch the content from.</param>
    /// <returns>
    ///     An <see cref="IEnumerable{PublishedSearchResult}" /> containing all content.
    /// </returns>
    /// <exception cref="ArgumentNullException">cache</exception>
    /// <remarks>
    ///     Search results are skipped if it can't be fetched from the <paramref name="cache" /> by its integer id.
    /// </remarks>
    public static IEnumerable <PublishedSearchResult> ToPublishedSearchResults(
        this IEnumerable <ISearchResult> results,
        IPublishedCache?cache)
    {
        if (cache == null)
        {
            throw new ArgumentNullException(nameof(cache));
        }

        var publishedSearchResults = new List <PublishedSearchResult>();

        foreach (ISearchResult result in results)
        {
            if (int.TryParse(result.Id, NumberStyles.Integer, CultureInfo.InvariantCulture, out var contentId))
            {
                IPublishedContent?content = cache.GetById(contentId);
                if (content is not null)
                {
                    publishedSearchResults.Add(new PublishedSearchResult(content, result.Score));
                }
            }
        }

        return(publishedSearchResults);
    }
コード例 #5
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);
        }
コード例 #6
0
        private void ApplyHideTopLevelNodeFromPath(IPublishedContent content, IList <string> segments, bool preview)
        {
            // in theory if hideTopLevelNodeFromPath is true, then there should be only one
            // top-level node, or else domains should be assigned. but for backward compatibility
            // we add this check - we look for the document matching "/" and if it's not us, then
            // we do not hide the top level path
            // it has to be taken care of in GetByRoute too so if
            // "/foo" fails (looking for "/*/foo") we try also "/foo".
            // this does not make much sense anyway esp. if both "/foo/" and "/bar/foo" exist, but
            // that's the way it works pre-4.10 and we try to be backward compat for the time being
            if (content.Parent == null)
            {
                IPublishedContent?rootNode = GetByRoute(preview, "/", true);
                if (rootNode == null)
                {
                    throw new Exception("Failed to get node at /.");
                }

                if (rootNode.Id == content.Id) // remove only if we're the default node
                {
                    segments.RemoveAt(segments.Count - 1);
                }
            }
            else
            {
                segments.RemoveAt(segments.Count - 1);
            }
        }
コード例 #7
0
    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());
    }
コード例 #8
0
        /// <inheritdoc />
        public async Task <IPublishedRequest> UpdateRequestAsync(IPublishedRequest request, IPublishedContent?publishedContent)
        {
            // store the original (if any)
            IPublishedContent?content = request.PublishedContent;

            IPublishedRequestBuilder builder = new PublishedRequestBuilder(request.Uri, _fileService);

            // set to the new content (or null if specified)
            builder.SetPublishedContent(publishedContent);

            // re-route
            await RouteRequestInternalAsync(builder);

            // return if we are redirect
            if (builder.IsRedirect())
            {
                return(BuildRequest(builder));
            }

            // this will occur if publishedContent is null and the last chance finders also don't assign content
            if (!builder.HasPublishedContent())
            {
                // means the engine could not find a proper document to handle 404
                // restore the saved content so we know it exists
                builder.SetPublishedContent(content);
            }

            if (!builder.HasDomain())
            {
                FindDomain(builder);
            }

            return(BuildRequest(builder));
        }
コード例 #9
0
ファイル: ContentNode.cs プロジェクト: robertjf/Umbraco-CMS
    private IPublishedContent?GetModel(ref IPublishedContent?model, ContentData?contentData)
    {
        if (model != null)
        {
            return(model);
        }

        if (contentData == null)
        {
            return(null);
        }

        // create the model - we want to be fast, so no lock here: we may create
        // more than 1 instance, but the lock below ensures we only ever return
        // 1 unique instance - and locking is a nice explicit way to ensure this
        IPublishedContent?m =
            new PublishedContent(this, contentData, _publishedSnapshotAccessor, _variationContextAccessor, _publishedModelFactory).CreateModel(_publishedModelFactory);

        // locking 'this' is not a best-practice but ContentNode is internal and
        // we know what we do, so it is fine here and avoids allocating an object
        lock (this)
        {
            return(model ??= m);
        }
    }
コード例 #10
0
        /// <summary>
        /// Gets the URL of a published content.
        /// </summary>
        /// <param name="content">The published content.</param>
        /// <param name="mode">The URL mode.</param>
        /// <param name="culture">A culture.</param>
        /// <param name="current">The current absolute URL.</param>
        /// <returns>The URL for the published content.</returns>
        /// <remarks>
        /// <para>The URL is absolute or relative depending on <c>mode</c> and on <c>current</c>.</para>
        /// <para>If the published content is multi-lingual, gets the URL for the specified culture or,
        /// when no culture is specified, the current culture.</para>
        /// <para>If the provider is unable to provide a URL, it returns "#".</para>
        /// </remarks>
        public string GetUrl(IPublishedContent?content, UrlMode mode = UrlMode.Default, string?culture = null, Uri?current = null)
        {
            if (content == null || content.ContentType.ItemType == PublishedItemType.Element)
            {
                return("#");
            }

            if (mode == UrlMode.Default)
            {
                mode = Mode;
            }

            // this the ONLY place where we deal with default culture - IUrlProvider always receive a culture
            // be nice with tests, assume things can be null, ultimately fall back to invariant
            // (but only for variant content of course)
            if (content.ContentType.VariesByCulture())
            {
                if (culture == null)
                {
                    culture = _variationContextAccessor?.VariationContext?.Culture ?? "";
                }
            }

            if (current == null)
            {
                var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();
                current = umbracoContext.CleanedUmbracoUrl;
            }


            var url = _urlProviders.Select(provider => provider.GetUrl(content, mode, culture, current))
                      .FirstOrDefault(u => u is not null);

            return(url?.Text ?? "#"); // legacy wants this
        }
コード例 #11
0
ファイル: Source.cs プロジェクト: robertjf/Umbraco-CMS
    public INavigableContent?Get(int id)
    {
        // wrap in a navigable content
        IPublishedContent?content = _data.GetById(_preview, id);

        return(content == null ? null : new NavigableContent(content));
    }
コード例 #12
0
    /// <summary>
    ///     Creates a strongly typed published content model for an internal published content.
    /// </summary>
    /// <param name="content">The internal published content.</param>
    /// <param name="publishedModelFactory">The published model factory</param>
    /// <returns>The strongly typed published content model.</returns>
    public static IPublishedContent?CreateModel(
        this IPublishedContent?content,
        IPublishedModelFactory?publishedModelFactory)
    {
        if (publishedModelFactory == null)
        {
            throw new ArgumentNullException(nameof(publishedModelFactory));
        }

        if (content == null)
        {
            return(null);
        }

        // get model
        // if factory returns nothing, throw
        IPublishedElement model = publishedModelFactory.CreateModel(content);

        if (model == null)
        {
            throw new InvalidOperationException("Factory returned null.");
        }

        // if factory returns a different type, throw
        if (!(model is IPublishedContent publishedContent))
        {
            throw new InvalidOperationException(
                      $"Factory returned model of type {model.GetType().FullName} which does not implement IPublishedContent.");
        }

        return(publishedContent);
    }
コード例 #13
0
 /// <summary>
 ///     Initializes a new instance of the <see cref="PublishedRequest" /> class.
 /// </summary>
 public PublishedRequest(
     Uri uri,
     string absolutePathDecoded,
     IPublishedContent?publishedContent,
     bool isInternalRedirect,
     ITemplate?template,
     DomainAndUri?domain,
     string?culture,
     string?redirectUrl,
     int?responseStatusCode,
     IReadOnlyList <string>?cacheExtensions,
     IReadOnlyDictionary <string, string>?headers,
     bool setNoCacheHeader,
     bool ignorePublishedContentCollisions)
 {
     Uri = uri ?? throw new ArgumentNullException(nameof(uri));
     AbsolutePathDecoded = absolutePathDecoded ?? throw new ArgumentNullException(nameof(absolutePathDecoded));
     PublishedContent    = publishedContent;
     IsInternalRedirect  = isInternalRedirect;
     Template            = template;
     Domain             = domain;
     Culture            = culture;
     RedirectUrl        = redirectUrl;
     ResponseStatusCode = responseStatusCode;
     CacheExtensions    = cacheExtensions;
     Headers            = headers;
     SetNoCacheHeader   = setNoCacheHeader;
     IgnorePublishedContentCollisions = ignorePublishedContentCollisions;
 }
コード例 #14
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();
        }
    }
コード例 #15
0
        private string?GetRouteByIdInternal(bool preview, int contentId, bool?hideTopLevelNode, string?culture)
        {
            IPublishedContent?node = GetById(preview, contentId);

            if (node == null)
            {
                return(null);
            }

            hideTopLevelNode = hideTopLevelNode ?? HideTopLevelNodeFromPath; // default = settings

            // walk up from that node until we hit a node with a domain,
            // or we reach the content root, collecting URLs in the way
            var pathParts       = new List <string>();
            IPublishedContent?n = node;
            var urlSegment      = n.UrlSegment(_variationContextAccessor, culture);
            var hasDomains      = _domainCache.GetAssignedWithCulture(culture, n.Id);

            while (hasDomains == false && n != null) // n is null at root
            {
                // no segment indicates this is not published when this is a variant
                if (urlSegment.IsNullOrWhiteSpace())
                {
                    return(null);
                }

                pathParts.Add(urlSegment !);

                // move to parent node
                n = n.Parent;
                if (n != null)
                {
                    urlSegment = n.UrlSegment(_variationContextAccessor, culture);
                }

                hasDomains = n != null && _domainCache.GetAssignedWithCulture(culture, n.Id);
            }

            // at this point this will be the urlSegment of the root, no segment indicates this is not published when this is a variant
            if (urlSegment.IsNullOrWhiteSpace())
            {
                return(null);
            }

            // no domain, respect HideTopLevelNodeFromPath for legacy purposes
            if (hasDomains == false && hideTopLevelNode.Value)
            {
                ApplyHideTopLevelNodeFromPath(node, pathParts, preview);
            }

            // assemble the route
            pathParts.Reverse();
            var path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc
            //prefix the root node id containing the domain if it exists (this is a standard way of creating route paths)
            //and is done so that we know the ID of the domain node for the path
            var route = (n?.Id.ToString(CultureInfo.InvariantCulture) ?? "") + path;

            return(route);
        }
コード例 #16
0
        /// <summary>
        /// Indicates that the current PublishedContent is the initial one.
        /// </summary>
        public void SetIsInitialPublishedContent()
        {
            EnsureWriteable();

            // note: it can very well be null if the initial content was not found
            _initialPublishedContent           = _publishedContent;
            IsInternalRedirectPublishedContent = false;
        }
コード例 #17
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);
        }
コード例 #18
0
        /// <summary>
        /// Returns the content id based on the configured ContentErrorPage section.
        /// </summary>
        internal static int?GetContentIdFromErrorPageConfig(
            ContentErrorPage errorPage,
            IEntityService entityService,
            IPublishedContentQuery publishedContentQuery,
            int?domainContentId)
        {
            if (errorPage.HasContentId)
            {
                return(errorPage.ContentId);
            }

            if (errorPage.HasContentKey)
            {
                // need to get the Id for the GUID
                // TODO: When we start storing GUIDs into the IPublishedContent, then we won't have to look this up
                // but until then we need to look it up in the db. For now we've implemented a cached service for
                // converting Int -> Guid and vice versa.
                Attempt <int> found = entityService.GetId(errorPage.ContentKey, UmbracoObjectTypes.Document);
                if (found.Success)
                {
                    return(found.Result);
                }

                return(null);
            }

            if (errorPage.ContentXPath.IsNullOrWhiteSpace() == false)
            {
                try
                {
                    // we have an xpath statement to execute
                    var xpathResult = UmbracoXPathPathSyntaxParser.ParseXPathQuery(
                        xpathExpression: errorPage.ContentXPath !,
                        nodeContextId: domainContentId,
                        getPath: nodeid =>
                    {
                        IEntitySlim?ent = entityService.Get(nodeid);
                        return(ent?.Path.Split(',').Reverse());
                    },
                        publishedContentExists: i => publishedContentQuery.Content(i) != null);

                    // now we'll try to execute the expression
                    IPublishedContent?nodeResult = publishedContentQuery.ContentSingleAtXPath(xpathResult);
                    if (nodeResult != null)
                    {
                        return(nodeResult.Id);
                    }
                }
                catch (Exception ex)
                {
                    StaticApplicationLogging.Logger.LogError(ex, "Could not parse xpath expression: {ContentXPath}", errorPage.ContentXPath);
                    return(null);
                }
            }

            return(null);
        }
コード例 #19
0
        private async Task <MacroContent> RenderAsync(MacroModel macro, IPublishedContent?content)
        {
            if (content == null)
            {
                throw new ArgumentNullException(nameof(content));
            }

            var macroInfo = $"Render Macro: {macro.Name}, cache: {macro.CacheDuration}";

            using (_profilingLogger.DebugDuration <MacroRenderer>(macroInfo, "Rendered Macro."))
            {
                // parse macro parameters ie replace the special [#key], [$key], etc. syntaxes
                foreach (var prop in macro.Properties)
                {
                    prop.Value = ParseAttribute(prop.Value);
                }

                var cultureName = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
                macro.CacheIdentifier = await GetContentCacheIdentifier(macro, content.Id, cultureName);

                // get the macro from cache if it is there
                var macroContent = GetMacroContentFromCache(macro);

                // macroContent.IsEmpty may be true, meaning the macro produces no output,
                // but still can be cached because its execution did not trigger any error.
                // so we need to actually render, only if macroContent is null
                if (macroContent != null)
                {
                    return(macroContent);
                }

                // this will take care of errors
                // it may throw, if we actually want to throw, so better not
                // catch anything here and let the exception be thrown
                var attempt = ExecuteMacroOfType(macro, content);

                // by convention ExecuteMacroByType must either throw or return a result
                // just check to avoid internal errors
                macroContent = attempt.Result;
                if (macroContent == null)
                {
                    throw new Exception("Internal error, ExecuteMacroOfType returned no content.");
                }

                // add to cache if render is successful
                // content may be empty but that's not an issue
                if (attempt.Success)
                {
                    // write to cache (if appropriate)
                    await AddMacroContentToCacheAsync(macro, macroContent);
                }

                return(macroContent);
            }
        }
コード例 #20
0
    public static IHtmlContent GetCropUrl(this IUrlHelper urlHelper, IPublishedContent?mediaItem, string propertyAlias, string cropAlias, bool htmlEncode = true, UrlMode urlMode = UrlMode.Default)
    {
        if (mediaItem == null)
        {
            return(HtmlString.Empty);
        }

        var url = mediaItem.GetCropUrl(propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true, urlMode: urlMode);

        return(CreateHtmlString(url, htmlEncode));
    }
コード例 #21
0
    private async Task <IActionResult> GetMacroResultAsHtml(string?macroAlias, int pageId,
                                                            IDictionary <string, object>?macroParams)
    {
        IMacro?m = macroAlias is null ? null : _macroService.GetByAlias(macroAlias);

        if (m == null)
        {
            return(NotFound());
        }

        IUmbracoContext   umbracoContext   = _umbracoContextAccessor.GetRequiredUmbracoContext();
        IPublishedContent?publishedContent = umbracoContext.Content?.GetById(true, pageId);

        //if it isn't supposed to be rendered in the editor then return an empty string
        //currently we cannot render a macro if the page doesn't yet exist
        if (pageId == -1 || publishedContent == null || m.DontRender)
        {
            //need to create a specific content result formatted as HTML since this controller has been configured
            //with only json formatters.
            return(Content(string.Empty, "text/html", Encoding.UTF8));
        }


        // When rendering the macro in the backoffice the default setting would be to use the Culture of the logged in user.
        // Since a Macro might contain thing thats related to the culture of the "IPublishedContent" (ie Dictionary keys) we want
        // to set the current culture to the culture related to the content item. This is hacky but it works.

        // fixme
        // in a 1:1 situation we do not handle the language being edited
        // so the macro renders in the wrong language

        var culture = DomainUtilities.GetCultureFromDomains(publishedContent.Id, publishedContent.Path, null,
                                                            umbracoContext, _siteDomainHelper);

        if (culture != null)
        {
            Thread.CurrentThread.CurrentCulture       =
                Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture);
        }

        // must have an active variation context!
        _variationContextAccessor.VariationContext = new VariationContext(culture);

        using (umbracoContext.ForcedPreview(true))
        {
            //need to create a specific content result formatted as HTML since this controller has been configured
            //with only json formatters.
            return(Content(
                       (await _componentRenderer.RenderMacroForContent(publishedContent, m.Alias, macroParams)).ToString() ??
                       string.Empty, "text/html",
                       Encoding.UTF8));
        }
    }
コード例 #22
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));
            }
        }
コード例 #23
0
ファイル: ContentCache.cs プロジェクト: robertjf/Umbraco-CMS
    // gets a published content as a previewing draft, if preview is true
    // this is for published content when previewing
    private static IPublishedContent?GetPublishedContentAsDraft(IPublishedContent?content /*, bool preview*/)
    {
        if (content == null /*|| preview == false*/)
        {
            return(null); // content;
        }

        // an object in the cache is either an IPublishedContentOrMedia,
        // or a model inheriting from PublishedContentExtended - in which
        // case we need to unwrap to get to the original IPublishedContentOrMedia.
        var inner = PublishedContent.UnwrapIPublishedContent(content);

        return(inner.AsDraft());
    }
コード例 #24
0
        public async Task <MacroContent> RenderAsync(string macroAlias, IPublishedContent?content, IDictionary <string, object?>?macroParams)
        {
            var m = _appCaches.RuntimeCache.GetCacheItem(CacheKeys.MacroFromAliasCacheKey + macroAlias, () => _macroService.GetByAlias(macroAlias));

            if (m == null)
            {
                throw new InvalidOperationException("No macro found by alias " + macroAlias);
            }

            var macro = new MacroModel(m);

            UpdateMacroModelProperties(macro, macroParams);
            return(await RenderAsync(macro, content));
        }
コード例 #25
0
    private void StoreOldRoute(IContent entity, OldRoutesDictionary oldRoutes)
    {
        if (!_publishedSnapshotAccessor.TryGetPublishedSnapshot(out IPublishedSnapshot? publishedSnapshot))
        {
            return;
        }

        IPublishedContentCache?contentCache  = publishedSnapshot?.Content;
        IPublishedContent?     entityContent = contentCache?.GetById(entity.Id);

        if (entityContent is null)
        {
            return;
        }

        // get the default affected cultures by going up the tree until we find the first culture variant entity (default to no cultures)
        var defaultCultures = entityContent.AncestorsOrSelf().FirstOrDefault(a => a.Cultures.Any())?.Cultures.Keys
                              .ToArray()
                              ?? Array.Empty <string>();

        foreach (IPublishedContent publishedContent in entityContent.DescendantsOrSelf(_variationContextAccessor))
        {
            // if this entity defines specific cultures, use those instead of the default ones
            IEnumerable <string> cultures =
                publishedContent.Cultures.Any() ? publishedContent.Cultures.Keys : defaultCultures;

            foreach (var culture in cultures)
            {
                var route = contentCache?.GetRouteById(publishedContent.Id, culture);
                if (!IsNotRoute(route))
                {
                    oldRoutes[new ContentIdAndCulture(publishedContent.Id, culture)] = new ContentKeyAndOldRoute(publishedContent.Key, route !);
                }
                else if (string.IsNullOrEmpty(culture))
                {
                    // Retry using all languages, if this is invariant but has a variant ancestor
                    var languages = _localizationService.GetAllLanguages();
                    foreach (var language in languages)
                    {
                        route = contentCache?.GetRouteById(publishedContent.Id, language.IsoCode);
                        if (!IsNotRoute(route))
                        {
                            oldRoutes[new ContentIdAndCulture(publishedContent.Id, language.IsoCode)] =
                                new ContentKeyAndOldRoute(publishedContent.Key, route !);
                        }
                    }
                }
            }
        }
    }
コード例 #26
0
ファイル: ContentCache.cs プロジェクト: robertjf/Umbraco-CMS
    private IPublishedContent?FollowRoute(IPublishedContent?content, IReadOnlyList <string> parts, int start, string?culture)
    {
        var i = start;

        while (content != null && i < parts.Count)
        {
            var part = parts[i++];
            content = content.Children(_variationContextAccessor, culture)?.FirstOrDefault(x =>
            {
                var urlSegment = x.UrlSegment(_variationContextAccessor, culture);
                return(urlSegment == part);
            });
        }

        return(content);
    }
コード例 #27
0
    /// <inheritdoc />
    public async Task <IHtmlEncodedString> RenderMacroAsync(int contentId, string alias, IDictionary <string, object>?parameters)
    {
        if (contentId == default)
        {
            throw new ArgumentException("Invalid content id " + contentId);
        }

        IUmbracoContext   umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();
        IPublishedContent?content        = umbracoContext.Content?.GetById(contentId);

        if (content == null)
        {
            throw new InvalidOperationException("Cannot render a macro, no content found by id " + contentId);
        }

        return(await RenderMacroAsync(content, alias, parameters));
    }
コード例 #28
0
    public override object?ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object?inter, bool preview)
    {
        var isMultiple = IsMultipleDataType(propertyType.DataType);

        if (string.IsNullOrEmpty(inter?.ToString()))
        {
            // Short-circuit on empty value
            return(isMultiple ? Enumerable.Empty <MediaWithCrops>() : null);
        }

        var mediaItems = new List <MediaWithCrops>();
        IEnumerable <MediaPicker3PropertyEditor.MediaPicker3PropertyValueEditor.MediaWithCropsDto> dtos =
            MediaPicker3PropertyEditor.MediaPicker3PropertyValueEditor.Deserialize(_jsonSerializer, inter);
        MediaPicker3Configuration?configuration     = propertyType.DataType.ConfigurationAs <MediaPicker3Configuration>();
        IPublishedSnapshot        publishedSnapshot = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot();

        foreach (MediaPicker3PropertyEditor.MediaPicker3PropertyValueEditor.MediaWithCropsDto dto in dtos)
        {
            IPublishedContent?mediaItem = publishedSnapshot.Media?.GetById(preview, dto.MediaKey);
            if (mediaItem != null)
            {
                var localCrops = new ImageCropperValue
                {
                    Crops      = dto.Crops,
                    FocalPoint = dto.FocalPoint,
                    Src        = mediaItem.Url(_publishedUrlProvider),
                };

                localCrops.ApplyConfiguration(configuration);

                // TODO: This should be optimized/cached, as calling Activator.CreateInstance is slow
                Type mediaWithCropsType = typeof(MediaWithCrops <>).MakeGenericType(mediaItem.GetType());
                var  mediaWithCrops     = (MediaWithCrops)Activator.CreateInstance(mediaWithCropsType, mediaItem, _publishedValueFallback, localCrops) !;

                mediaItems.Add(mediaWithCrops);

                if (!isMultiple)
                {
                    // Short-circuit on single item
                    break;
                }
            }
        }

        return(isMultiple ? mediaItems : mediaItems.FirstOrDefault());
    }
コード例 #29
0
    /// <summary>
    ///     Special check for when no template or hijacked route is done which needs to re-run through the routing pipeline
    ///     again for last chance finders
    /// </summary>
    private async Task <UmbracoRouteValues> CheckNoTemplateAsync(
        HttpContext httpContext, UmbracoRouteValues def, bool hasHijackedRoute)
    {
        IPublishedRequest request = def.PublishedRequest;

        // Here we need to check if there is no hijacked route and no template assigned but there is a content item.
        // If this is the case we want to return a blank page.
        // We also check if templates have been disabled since if they are then we're allowed to render even though there's no template,
        // for example for json rendering in headless.
        if (request.HasPublishedContent() &&
            !request.HasTemplate() &&
            !_umbracoFeatures.Disabled.DisableTemplates &&
            !hasHijackedRoute)
        {
            IPublishedContent?content = request.PublishedContent;

            // This is basically a 404 even if there is content found.
            // We then need to re-run this through the pipeline for the last
            // chance finders to work.
            // Set to null since we are telling it there is no content.
            request = await _publishedRouter.UpdateRequestAsync(request, null);

            if (request == null)
            {
                throw new InvalidOperationException(
                          $"The call to {nameof(IPublishedRouter.UpdateRequestAsync)} cannot return null");
            }

            string?customActionName = GetTemplateName(request);

            def = new UmbracoRouteValues(
                request,
                def.ControllerActionDescriptor,
                customActionName);

            // if the content has changed, we must then again check for hijacked routes
            if (content != request.PublishedContent)
            {
                def = CheckHijackedRoute(httpContext, def, out _);
            }
        }

        return(def);
    }
コード例 #30
0
    public IPublishedProperty?GetProperty(string alias, bool recurse)
    {
        IPublishedProperty?property = GetProperty(alias);

        if (recurse == false)
        {
            return(property);
        }

        IPublishedContent?content = this;

        while (content != null && (property == null || property.HasValue() == false))
        {
            content  = content.Parent;
            property = content?.GetProperty(alias);
        }

        return(property);
    }