Represents a pipeline/chain of configured Strongly Typed View Model Builders (DD4T-based).
Each Model Builder in the pipeline is invoked and has the possibility to modify the resulting Page/Entity Model. The first Model Builder has to construct the View Models (it will get in null). Normally, the DefaultModelBuilder will be the first and only one. NOTE: The Model Builder pipeline is not a public extension point; it should only be used for advanced (SDL-owned) modules like the SmartTarget module.
コード例 #1
0
        /// <summary>
        /// Populates a Dynamic List by executing the query it specifies.
        /// </summary>
        /// <param name="dynamicList">The Dynamic List which specifies the query and is to be populated.</param>
        /// <param name="localization">The context Localization.</param>
        public void PopulateDynamicList(DynamicList dynamicList, Localization localization)
        {
            using (new Tracer(dynamicList, localization))
            {
                SimpleBrokerQuery simpleBrokerQuery = dynamicList.GetQuery(localization) as SimpleBrokerQuery;
                if (simpleBrokerQuery == null)
                {
                    throw new DxaException($"Unexpected result from {dynamicList.GetType().Name}.GetQuery: {dynamicList.GetQuery(localization)}");
                }

                BrokerQuery brokerQuery   = new BrokerQuery(simpleBrokerQuery);
                string[]    componentUris = brokerQuery.ExecuteQuery().ToArray();
                Log.Debug($"Broker Query returned {componentUris.Length} results. HasMore={brokerQuery.HasMore}");

                if (componentUris.Length > 0)
                {
                    Type resultType = dynamicList.ResultType;
                    ComponentMetaFactory componentMetaFactory = new ComponentMetaFactory(localization.GetCmUri());
                    dynamicList.QueryResults = componentUris
                                               .Select(c => ModelBuilderPipeline.CreateEntityModel(CreateEntityModelData(componentMetaFactory.GetMeta(c)), resultType, localization))
                                               .ToList();
                }

                dynamicList.HasMore = brokerQuery.HasMore;
            }
        }
コード例 #2
0
        protected virtual object MapRichText(RichTextData richTextData, Type targetType, ILocalization localization)
        {
            IList <IRichTextFragment> fragments = new List <IRichTextFragment>();

            foreach (object fragment in richTextData.Fragments)
            {
                string htmlFragment = fragment as string;
                if (htmlFragment == null)
                {
                    // Embedded Entity Model (for Media Items)
                    MediaItem mediaItem = (MediaItem)ModelBuilderPipeline.CreateEntityModel((EntityModelData)fragment, typeof(MediaItem), localization);
                    mediaItem.IsEmbedded = true;
                    if (mediaItem.MvcData == null)
                    {
                        mediaItem.MvcData = mediaItem.GetDefaultView(localization);
                    }
                    fragments.Add(mediaItem);
                }
                else
                {
                    // HTML fragment.
                    fragments.Add(new RichTextFragment(htmlFragment));
                }
            }
            RichText richText = new RichText(fragments);

            if (targetType == typeof(RichText))
            {
                return(richText);
            }

            return(richText.ToString());
        }
コード例 #3
0
        /// <summary>
        /// Gets an Entity Model for a given Entity Identifier.
        /// </summary>
        /// <param name="id">The Entity Identifier in format ComponentID-TemplateID.</param>
        /// <param name="localization">The context Localization.</param>
        /// <returns>The Entity Model.</returns>
        /// <exception cref="DxaItemNotFoundException">If no Entity Model exists for the given URL.</exception>
        /// <remarks>
        /// Since we can't obtain CT metadata for DCPs, we obtain the View Name from the CT Title.
        /// </remarks>
        public virtual EntityModel GetEntityModel(string id, Localization localization)
        {
            using (new Tracer(id, localization))
            {
                string[] idParts = id.Split('-');
                if (idParts.Length != 2)
                {
                    throw new DxaException(String.Format("Invalid Entity Identifier '{0}'. Must be in format ComponentID-TemplateID.", id));
                }

                string componentUri = string.Format("tcm:{0}-{1}", localization.LocalizationId, idParts[0]);
                string templateUri  = string.Format("tcm:{0}-{1}-32", localization.LocalizationId, idParts[1]);

                IComponentPresentationFactory componentPresentationFactory = DD4TFactoryCache.GetComponentPresentationFactory(localization);
                IComponentPresentation        dcp;
                if (!componentPresentationFactory.TryGetComponentPresentation(out dcp, componentUri, templateUri))
                {
                    throw new DxaItemNotFoundException(id, localization.LocalizationId);
                }

                EntityModel result = ModelBuilderPipeline.CreateEntityModel(dcp, localization);
                if (result.XpmMetadata != null)
                {
                    // Entity Models requested through this method are per definition "query based" in XPM terminology.
                    result.XpmMetadata["IsQueryBased"] = true;
                }
                return(result);
            }
        }
コード例 #4
0
        protected virtual object MapRichText(RichTextData richTextData, Type targetType, Localization localization)
        {
            IList <IRichTextFragment> fragments = new List <IRichTextFragment>();

            foreach (object fragment in richTextData.Fragments)
            {
                string htmlFragment = fragment as string;
                if (htmlFragment == null)
                {
                    var         entityModelData = (EntityModelData)fragment;
                    EntityModel embeddedItem    = ModelBuilderPipeline.CreateEntityModel(entityModelData,
                                                                                         entityModelData.BinaryContent != null ? typeof(MediaItem) : typeof(EntityModel), localization);
                    if (embeddedItem.MvcData == null)
                    {
                        embeddedItem.MvcData = embeddedItem.GetDefaultView(localization);
                    }
                    embeddedItem.IsEmbedded = true;
                    fragments.Add(embeddedItem);
                }
                else
                {
                    // HTML fragment.
                    fragments.Add(new RichTextFragment(htmlFragment));
                }
            }
            RichText richText = new RichText(fragments);

            if (targetType == typeof(RichText))
            {
                return(richText);
            }

            return(richText.ToString());
        }
コード例 #5
0
        protected virtual object MapComponentLink(EntityModelData entityModelData, Type targetType, Localization localization)
        {
            if (targetType == typeof(Link))
            {
                return(new Link
                {
                    Id = entityModelData.Id,
                    Url = GetLinkUrl(entityModelData, localization)
                });
            }

            if (targetType == typeof(string))
            {
                return(GetLinkUrl(entityModelData, localization));
            }

            if (!typeof(EntityModel).IsAssignableFrom(targetType))
            {
                throw new DxaException($"Cannot map Component Link to property of type '{targetType.Name}'.");
            }

            if (string.IsNullOrWhiteSpace(entityModelData.SchemaId))
            {
                return(null);
            }

            return(ModelBuilderPipeline.CreateEntityModel(entityModelData, targetType, localization));
        }
コード例 #6
0
        /// <summary>
        /// Populates a Dynamic List by executing the query it specifies.
        /// </summary>
        /// <param name="dynamicList">The Dynamic List which specifies the query and is to be populated.</param>
        /// <param name="localization">The context Localization.</param>
        public override void PopulateDynamicList(DynamicList dynamicList, Localization localization)
        {
            using (new Tracer(dynamicList, localization))
            {
                SimpleBrokerQuery simpleBrokerQuery = dynamicList.GetQuery(localization) as SimpleBrokerQuery;
                if (simpleBrokerQuery == null)
                {
                    throw new DxaException($"Unexpected result from {dynamicList.GetType().Name}.GetQuery: {dynamicList.GetQuery(localization)}");
                }

                // get our cursor indexer for this list
                var cursors = CursorIndexer.GetCursorIndexer(dynamicList.Id);

                // given our start index into the paged list we need to translate that to a cursor
                int start = simpleBrokerQuery.Start;
                simpleBrokerQuery.Cursor = cursors[start];

                // the cursor retrieved may of came from a different start index so we update start
                int startIndex = cursors.StartIndex;
                simpleBrokerQuery.Start = startIndex;
                dynamicList.Start       = startIndex;

                var cachedDynamicList = SiteConfiguration.CacheProvider.GetOrAdd(
                    $"PopulateDynamicList-{dynamicList.Id}-{simpleBrokerQuery.GetHashCode()}", // key
                    CacheRegions.BrokerQuery,
                    () =>
                {
                    var brokerQuery = new GraphQLQueryProvider();

                    var components = brokerQuery.ExecuteQueryItems(simpleBrokerQuery).ToList();
                    Log.Debug($"Broker Query returned {components.Count} results. HasMore={brokerQuery.HasMore}");

                    if (components.Count > 0)
                    {
                        Type resultType          = dynamicList.ResultType;
                        dynamicList.QueryResults = components
                                                   .Select(
                            c =>
                            ModelBuilderPipeline.CreateEntityModel(
                                CreateEntityModelData((Component)c), resultType,
                                localization))
                                                   .ToList();
                    }

                    dynamicList.HasMore = brokerQuery.HasMore;

                    if (brokerQuery.HasMore)
                    {
                        // update cursor
                        cursors[simpleBrokerQuery.Start + simpleBrokerQuery.PageSize] = brokerQuery.Cursor;
                    }

                    return(dynamicList);
                });

                dynamicList.QueryResults = cachedDynamicList.QueryResults;
                dynamicList.HasMore      = cachedDynamicList.HasMore;
            }
        }
コード例 #7
0
        /// <summary>
        /// Gets an Entity Model for a given Entity Identifier.
        /// </summary>
        /// <param name="id">The Entity Identifier in format ComponentID-TemplateID.</param>
        /// <param name="localization">The context Localization.</param>
        /// <returns>The Entity Model.</returns>
        /// <exception cref="DxaItemNotFoundException">If no Entity Model exists for the given URL.</exception>
        /// <remarks>
        /// Since we can't obtain CT metadata for DCPs, we obtain the View Name from the CT Title.
        /// </remarks>
        public virtual EntityModel GetEntityModel(string id, Localization localization)
        {
            using (new Tracer(id, localization))
            {
                string[] idParts = id.Split('-');
                if (idParts.Length != 2)
                {
                    throw new DxaException(String.Format("Invalid Entity Identifier '{0}'. Must be in format ComponentID-TemplateID.", id));
                }

                string componentUri = localization.GetCmUri(idParts[0]);
                string templateUri  = localization.GetCmUri(idParts[1], (int)ItemType.ComponentTemplate);

                IComponentPresentationFactory componentPresentationFactory = DD4TFactoryCache.GetComponentPresentationFactory(localization);
                IComponentPresentation        dcp;
                if (!componentPresentationFactory.TryGetComponentPresentation(out dcp, componentUri, templateUri))
                {
                    throw new DxaItemNotFoundException(id, localization.Id);
                }

                EntityModel result;
                if (CacheRegions.IsViewModelCachingEnabled)
                {
                    EntityModel cachedEntityModel = SiteConfiguration.CacheProvider.GetOrAdd(
                        string.Format("{0}-{1}", id, localization.Id), // key
                        CacheRegions.EntityModel,
                        () => ModelBuilderPipeline.CreateEntityModel(dcp, localization),
                        dependencies: new[] { componentUri }
                        );

                    // Don't return the cached Entity Model itself, because we don't want dynamic logic to modify the cached state.
                    result = (EntityModel)cachedEntityModel.DeepCopy();
                }
                else
                {
                    result = ModelBuilderPipeline.CreateEntityModel(dcp, localization);
                }

                if (result.XpmMetadata != null)
                {
                    // Entity Models requested through this method are per definition "query based" in XPM terminology.
                    result.XpmMetadata["IsQueryBased"] = true;
                }
                return(result);
            }
        }
コード例 #8
0
        protected virtual PageModel LoadPageModel(int pageId, bool addIncludes, Localization localization)
        {
            using (new Tracer(pageId, addIncludes, localization))
            {
                PageModelData pageModelData = SiteConfiguration.ModelServiceProvider.GetPageModelData(pageId, localization, addIncludes);

                if (pageModelData == null)
                {
                    throw new DxaItemNotFoundException($"Page not found for publication id {localization.Id} and page id {pageId}");
                }

                if (pageModelData.MvcData == null)
                {
                    throw new DxaException($"Data Model for Page '{pageModelData.Title}' ({pageModelData.Id}) contains no MVC data. Ensure that the Page is published using the DXA R2 TBBs.");
                }

                return(ModelBuilderPipeline.CreatePageModel(pageModelData, addIncludes, localization));
            }
        }
コード例 #9
0
        private PageModel LoadPageModel(ref string urlPath, bool addIncludes, Localization localization)
        {
            using (new Tracer(urlPath, addIncludes, localization))
            {
                PageModelData pageModelData = SiteConfiguration.ModelServiceProvider.GetPageModelData(urlPath, localization, addIncludes);

                if (pageModelData == null)
                {
                    throw new DxaItemNotFoundException(urlPath);
                }

                if (pageModelData.MvcData == null)
                {
                    throw new DxaException($"Data Model for Page '{pageModelData.Title}' ({pageModelData.Id}) contains no MVC data. Ensure that the Page is published using the DXA R2 TBBs.");
                }

                return(ModelBuilderPipeline.CreatePageModel(pageModelData, addIncludes, localization));
            }
        }
コード例 #10
0
        protected virtual RegionModel CreateRegionModel(RegionModelData regionModelData, ILocalization localization)
        {
            MvcData mvcData         = CreateMvcData(regionModelData.MvcData, "Region");
            Type    regionModelType = ModelTypeRegistry.GetViewModelType(mvcData);

            RegionModel result = (RegionModel)regionModelType.CreateInstance(regionModelData.Name);

            result.ExtensionData = regionModelData.ExtensionData;
            result.HtmlClasses   = regionModelData.HtmlClasses;
            result.MvcData       = mvcData;
            result.XpmMetadata   = localization.IsXpmEnabled ? regionModelData.XpmMetadata : null;

            if (regionModelData.Regions != null)
            {
                IEnumerable <RegionModel> nestedRegionModels = regionModelData.Regions.Select(data => CreateRegionModel(data, localization));
                result.Regions.UnionWith(nestedRegionModels);
                result.IsVolatile |= result.Regions.Any(region => region.IsVolatile);
            }

            if (regionModelData.Entities != null)
            {
                foreach (EntityModelData entityModelData in regionModelData.Entities)
                {
                    EntityModel entityModel;
                    try
                    {
                        entityModel = ModelBuilderPipeline.CreateEntityModel(entityModelData, null, localization);
                        // indicate to region model that this region is potentially volatile if it contains a volatile entity
                        result.IsVolatile |= entityModel.IsVolatile;
                        entityModel.MvcData.RegionName = regionModelData.Name;
                    }
                    catch (Exception ex)
                    {
                        // If there is a problem mapping an Entity, we replace it with an ExceptionEntity which holds the error details and carry on.
                        Log.Error(ex);
                        entityModel = new ExceptionEntity(ex);
                    }
                    result.Entities.Add(entityModel);
                }
            }

            return(result);
        }
コード例 #11
0
        private EntityModel LoadEntityModel(string id, Localization localization)
        {
            using (new Tracer(id, localization))
            {
                EntityModelData entityModelData = SiteConfiguration.ModelServiceProvider.GetEntityModelData(id, localization);

                if (entityModelData == null)
                {
                    throw new DxaItemNotFoundException(id);
                }

                EntityModel result = ModelBuilderPipeline.CreateEntityModel(entityModelData, null, localization);

                if (result.XpmMetadata != null)
                {
                    // Entity Models requested through this method are per definition "query based" in XPM terminology.
                    result.XpmMetadata["IsQueryBased"] = true; // TODO TSI-24: Do this in Model Service (or CM-side?)
                }

                return(result);
            }
        }
コード例 #12
0
#pragma warning restore 618


        /// <summary>
        /// Gets a Page Model for a given URL.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <param name="localization">The context Localization.</param>
        /// <param name="addIncludes">Indicates whether include Pages should be expanded.</param>
        /// <returns>The Page Model.</returns>
        /// <exception cref="DxaItemNotFoundException">If no Page Model exists for the given URL.</exception>
        public virtual PageModel GetPageModel(string url, Localization localization, bool addIncludes)
        {
            using (new Tracer(url, localization, addIncludes))
            {
                //We can have a couple of tries to get the page model if there is no file extension on the url request, but it does not end in a slash:
                //1. Try adding the default extension, so /news becomes /news.html
                IPage page = GetPage(url, localization);
                if (page == null && (url == null || (!url.EndsWith("/") && url.LastIndexOf(".", StringComparison.Ordinal) <= url.LastIndexOf("/", StringComparison.Ordinal))))
                {
                    //2. Try adding the default page, so /news becomes /news/index.html
                    page = GetPage(url + "/", localization);
                }
                if (page == null)
                {
                    throw new DxaItemNotFoundException(url);
                }
                FullyLoadDynamicComponentPresentations(page, localization);

                IPage[] includes = addIncludes ? GetIncludesFromModel(page, localization).ToArray() : new IPage[0];

                return(ModelBuilderPipeline.CreatePageModel(page, includes, localization));
            }
        }
コード例 #13
0
        /// <summary>
        /// Gets an Entity Model for a given Entity Identifier.
        /// </summary>
        /// <param name="id">The Entity Identifier in format ComponentID-TemplateID.</param>
        /// <param name="localization">The context Localization.</param>
        /// <returns>The Entity Model.</returns>
        /// <exception cref="DxaItemNotFoundException">If no Entity Model exists for the given URL.</exception>
        /// <remarks>
        /// Since we can't obtain CT metadata for DCPs, we obtain the View Name from the CT Title.
        /// </remarks>
        public virtual EntityModel GetEntityModel(string id, Localization localization)
        {
            using (new Tracer(id, localization))
            {
                string[] idParts = id.Split('-');
                if (idParts.Length != 2)
                {
                    throw new DxaException(String.Format("Invalid Entity Identifier '{0}'. Must be in format ComponentID-TemplateID.", id));
                }

                string componentUri = string.Format("tcm:{0}-{1}", localization.LocalizationId, idParts[0]);
                string templateUri  = string.Format("tcm:{0}-{1}-32", localization.LocalizationId, idParts[1]);

                IComponentPresentationFactory componentPresentationFactory = DD4TFactoryCache.GetComponentPresentationFactory(localization);
                IComponentPresentation        dcp;
                if (!componentPresentationFactory.TryGetComponentPresentation(out dcp, componentUri, templateUri))
                {
                    throw new DxaItemNotFoundException(id);
                }

                return(ModelBuilderPipeline.CreateEntityModel(dcp, localization));
            }
        }
コード例 #14
0
        /// <summary>
        /// Gets a Page Model for a given URL.
        /// </summary>
        /// <param name="urlPath">The URL path (unescaped).</param>
        /// <param name="localization">The context Localization.</param>
        /// <param name="addIncludes">Indicates whether include Pages should be expanded.</param>
        /// <returns>The Page Model.</returns>
        /// <exception cref="DxaItemNotFoundException">If no Page Model exists for the given URL.</exception>
        public virtual PageModel GetPageModel(string urlPath, Localization localization, bool addIncludes)
        {
            using (new Tracer(urlPath, localization, addIncludes))
            {
                if (urlPath == null)
                {
                    urlPath = "/";
                }
                else if (!urlPath.StartsWith("/"))
                {
                    urlPath = "/" + urlPath;
                }

                IPage page = GetPage(urlPath, localization);
                if (page == null && !urlPath.EndsWith("/"))
                {
                    // This may be a SG URL path; try if the index page exists.
                    urlPath += Constants.IndexPageUrlSuffix;
                    page     = GetPage(urlPath, localization);
                }
                else if (urlPath.EndsWith("/"))
                {
                    urlPath += Constants.DefaultExtensionLessPageName;
                }

                if (page == null)
                {
                    throw new DxaItemNotFoundException(urlPath, localization.Id);
                }

                IPage[] includes = addIncludes ? GetIncludesFromModel(page, localization).ToArray() : new IPage[0];

                List <string> dependencies = new List <string>()
                {
                    page.Id
                };
                dependencies.AddRange(includes.Select(p => p.Id));

                PageModel result = null;
                if (CacheRegions.IsViewModelCachingEnabled)
                {
                    PageModel cachedPageModel = SiteConfiguration.CacheProvider.GetOrAdd(
                        string.Format("{0}:{1}", page.Id, addIncludes), // Cache Page Models with and without includes separately
                        CacheRegions.PageModel,
                        () =>
                    {
                        PageModel pageModel = ModelBuilderPipeline.CreatePageModel(page, includes, localization);
                        pageModel.Url       = urlPath;
                        if (pageModel.NoCache)
                        {
                            result = pageModel;
                            return(null);
                        }
                        return(pageModel);
                    },
                        dependencies
                        );

                    if (cachedPageModel != null)
                    {
                        // Don't return the cached Page Model itself, because we don't want dynamic logic to modify the cached state.
                        result = (PageModel)cachedPageModel.DeepCopy();
                    }
                }
                else
                {
                    result     = ModelBuilderPipeline.CreatePageModel(page, includes, localization);
                    result.Url = urlPath;
                }

                if (SiteConfiguration.ConditionalEntityEvaluator != null)
                {
                    result.FilterConditionalEntities(localization);
                }

                return(result);
            }
        }
コード例 #15
0
 public DefaultContentProvider()
 {
     _modelService = new Providers.ModelService.ModelService();
     ModelBuilderPipeline.Init();
 }
コード例 #16
0
 public DefaultContentProvider()
 {
     ModelBuilderPipeline.Init();
 }
        protected virtual RegionModel CreateRegionModel(RegionModelData regionModelData, ILocalization localization)
        {
            MvcData mvcData         = CreateMvcData(regionModelData.MvcData, "Region");
            Type    regionModelType = ModelTypeRegistry.GetViewModelType(mvcData);

            RegionModel result = (RegionModel)regionModelType.CreateInstance(regionModelData.Name);

            result.ExtensionData = regionModelData.ExtensionData;
            result.HtmlClasses   = regionModelData.HtmlClasses;
            result.MvcData       = mvcData;
            result.XpmMetadata   = localization.IsXpmEnabled ? regionModelData.XpmMetadata : null;
            result.SchemaId      = regionModelData.SchemaId;

            if (!string.IsNullOrEmpty(regionModelData.SchemaId))
            {
                SemanticSchema semanticSchema = SemanticMapping.GetSchema(regionModelData.SchemaId, localization);

                Type modelType = ModelTypeRegistry.GetViewModelType(mvcData);

                MappingData mappingData = new MappingData
                {
                    SourceViewModel    = regionModelData,
                    ModelType          = modelType,
                    PropertyValidation = new Validation
                    {
                        MainSchema       = semanticSchema,
                        InheritedSchemas = GetInheritedSemanticSchemas(regionModelData, localization)
                    },
                    Fields         = null,
                    MetadataFields = regionModelData.Metadata,
                    Localization   = localization
                };
                MapSemanticProperties(result, mappingData);
            }

            if (regionModelData.Regions != null)
            {
                IEnumerable <RegionModel> nestedRegionModels = regionModelData.Regions.Select(data => CreateRegionModel(data, localization));
                result.Regions.UnionWith(nestedRegionModels);
                result.IsVolatile |= result.Regions.Any(region => region.IsVolatile);
            }

            if (regionModelData.Entities != null)
            {
                foreach (EntityModelData entityModelData in regionModelData.Entities)
                {
                    EntityModel entityModel;
                    try
                    {
                        entityModel = ModelBuilderPipeline.CreateEntityModel(entityModelData, null, localization);
                        // indicate to region model that this region is potentially volatile if it contains a volatile entity
                        result.IsVolatile |= entityModel.IsVolatile;
                        entityModel.MvcData.RegionName = regionModelData.Name;
                    }
                    catch (Exception ex)
                    {
                        // If there is a problem mapping an Entity, we replace it with an ExceptionEntity which holds the error details and carry on.
                        Log.Error(ex);
                        entityModel = new ExceptionEntity(ex);
                    }
                    result.Entities.Add(entityModel);
                }
            }

            return(result);
        }