/// <summary> /// Builds a strongly typed Entity Model based on a given DXA R2 Data Model. /// </summary> /// <param name="entityModel">The strongly typed Entity Model to build. Is <c>null</c> for the first Entity Model Builder in the pipeline.</param> /// <param name="entityModelData">The DXA R2 Data Model.</param> /// <param name="baseModelType">The base type for the Entity Model to build.</param> /// <param name="localization">The context <see cref="ILocalization"/>.</param> public virtual void BuildEntityModel(ref EntityModel entityModel, EntityModelData entityModelData, Type baseModelType, ILocalization localization) { using (new Tracer(entityModel, entityModelData, baseModelType, localization)) { MvcData mvcData = CreateMvcData(entityModelData.MvcData, "Entity"); SemanticSchema semanticSchema = SemanticMapping.GetSchema(entityModelData.SchemaId, localization); Type modelType = (baseModelType == null) ? ModelTypeRegistry.GetViewModelType(mvcData) : semanticSchema.GetModelTypeFromSemanticMapping(baseModelType); MappingData mappingData = new MappingData { SourceViewModel = entityModelData, ModelType = modelType, PropertyValidation = new Validation { MainSchema = semanticSchema, InheritedSchemas = GetInheritedSemanticSchemas(entityModelData, localization) }, Fields = entityModelData.Content, MetadataFields = entityModelData.Metadata, Localization = localization }; entityModel = (EntityModel)CreateViewModel(mappingData); entityModel.Id = entityModelData.Id; entityModel.MvcData = mvcData ?? entityModel.GetDefaultView(localization); } }
protected virtual object MapKeyword(KeywordModelData keywordModelData, Type targetType, ILocalization localization) { if (typeof(KeywordModel).IsAssignableFrom(targetType)) { KeywordModel result; if (keywordModelData.SchemaId == null) { result = new KeywordModel { ExtensionData = keywordModelData.ExtensionData }; } else { MappingData keywordMappingData = new MappingData { SourceViewModel = keywordModelData, ModelType = targetType, PropertyValidation = new Validation { MainSchema = SemanticMapping.GetSchema(keywordModelData.SchemaId, localization), InheritedSchemas = GetInheritedSemanticSchemas(keywordModelData as ViewModelData, localization) }, MetadataFields = keywordModelData.Metadata, Localization = localization }; result = (KeywordModel)CreateViewModel(keywordMappingData); } result.Id = keywordModelData.Id; result.Title = keywordModelData.Title; result.Description = keywordModelData.Description ?? string.Empty; result.Key = keywordModelData.Key ?? string.Empty; result.TaxonomyId = keywordModelData.TaxonomyId; return(result); } if (targetType == typeof(Tag)) { return(new Tag { DisplayText = GetKeywordDisplayText(keywordModelData), Key = keywordModelData.Key, TagCategory = localization.GetCmUri(keywordModelData.TaxonomyId, (int)ItemType.Category) }); } if (targetType == typeof(bool)) { string key = string.IsNullOrEmpty(keywordModelData.Key) ? keywordModelData.Title : keywordModelData.Key; return(Convert.ToBoolean(key)); } return(GetKeywordDisplayText(keywordModelData)); }
/// <summary> /// Builds a strongly typed Page Model from a given DXA R2 Data Model. /// </summary> /// <param name="pageModel">The strongly typed Page Model to build. Is <c>null</c> for the first Page Model Builder in the pipeline.</param> /// <param name="pageModelData">The DXA R2 Data Model.</param> /// <param name="includePageRegions">Indicates whether Include Page Regions should be included.</param> /// <param name="localization">The context <see cref="ILocalization"/>.</param> public virtual void BuildPageModel(ref PageModel pageModel, PageModelData pageModelData, bool includePageRegions, ILocalization localization) { using (new Tracer(pageModel, pageModelData, includePageRegions, localization)) { MvcData mvcData = CreateMvcData(pageModelData.MvcData, "Page"); Type modelType = ModelTypeRegistry.GetViewModelType(mvcData); if (modelType == typeof(PageModel)) { // Standard Page Model. pageModel = new PageModel(pageModelData.Id) { ExtensionData = pageModelData.ExtensionData, HtmlClasses = pageModelData.HtmlClasses, XpmMetadata = localization.IsXpmEnabled ? pageModelData.XpmMetadata : null, }; } else if (pageModelData.SchemaId == null) { // Custom Page Model, but no custom metadata. pageModel = (PageModel)modelType.CreateInstance(pageModelData.Id); pageModel.ExtensionData = pageModelData.ExtensionData; pageModel.HtmlClasses = pageModelData.HtmlClasses; pageModel.XpmMetadata = localization.IsXpmEnabled ? pageModelData.XpmMetadata : null; } else { // Custom Page Model with custom metadata; do full-blown model mapping. MappingData mappingData = new MappingData { SourceViewModel = pageModelData, ModelId = pageModelData.Id, ModelType = modelType, PropertyValidation = new Validation { MainSchema = SemanticMapping.GetSchema(pageModelData.SchemaId, localization), InheritedSchemas = GetInheritedSemanticSchemas(pageModelData, localization) }, MetadataFields = pageModelData.Metadata, Localization = localization }; pageModel = (PageModel)CreateViewModel(mappingData); } pageModel.MvcData = mvcData; pageModel.Meta = pageModelData.Meta ?? new Dictionary <string, string>(); pageModel.Title = PostProcessPageTitle(pageModelData, localization); // TODO TSI-2210: This should eventually be done in Model Service. pageModel.Url = pageModelData.UrlPath; if (pageModelData.Regions != null) { IEnumerable <RegionModelData> regions = includePageRegions ? pageModelData.Regions : pageModelData.Regions.Where(r => r.IncludePageId == null); pageModel.Regions.UnionWith(regions.Select(data => CreateRegionModel(data, localization))); pageModel.IsVolatile |= pageModel.Regions.Any(region => region.IsVolatile); } } }
protected virtual List <SemanticSchema> GetInheritedSemanticSchemas(ViewModelData pageModelData, ILocalization localization) { List <SemanticSchema> schemas = new List <SemanticSchema>(); if (pageModelData.ExtensionData != null && pageModelData.ExtensionData.ContainsKey("Schemas")) { string[] ids = pageModelData.ExtensionData["Schemas"] as string[]; if (ids != null && ids.Length > 0) { foreach (string inheritedSchemaId in ids) { SemanticSchema schema = SemanticMapping.GetSchema(inheritedSchemaId, localization); if (schema == null) { continue; } schemas.Add(schema); } } } return(schemas); }
private RichText ResolveRichText(XmlDocument doc, Localization localization) { const string xlinkNamespaceUri = "http://www.w3.org/1999/xlink"; XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); nsmgr.AddNamespace("xhtml", "http://www.w3.org/1999/xhtml"); nsmgr.AddNamespace("xlink", xlinkNamespaceUri); // Process/resolve hyperlinks with XLink attributes ILinkResolver linkResolver = SiteConfiguration.LinkResolver; foreach (XmlElement linkElement in doc.SelectNodes("//a[@xlink:href]", nsmgr)) { // DD4T BinaryPublisher may have resolved a href attribute already (for links to MM Components) string linkUrl = linkElement.GetAttribute("href"); if (string.IsNullOrEmpty(linkUrl)) { // No href attribute found. Apparently the XLink refers to a regular Component; we resolve it to a URL here. string tcmUri = linkElement.GetAttribute("href", xlinkNamespaceUri); if (!string.IsNullOrEmpty(tcmUri)) { // Try to resolve directly to Binary content of MM Component. linkUrl = linkResolver.ResolveLink(tcmUri, resolveToBinary: true); } } if (!string.IsNullOrEmpty(linkUrl)) { // The link was resolved; set HTML href attribute linkElement.SetAttribute("href", linkUrl); ApplyHashIfApplicable(linkElement, localization); // Remove all XLink and data- attributes IEnumerable <XmlAttribute> attributesToRemove = linkElement.Attributes.Cast <XmlAttribute>() .Where(a => a.NamespaceURI == xlinkNamespaceUri || a.LocalName == "xlink" || a.LocalName.StartsWith("data-")).ToArray(); foreach (XmlAttribute attr in attributesToRemove) { linkElement.Attributes.Remove(attr); } } else { // The link was not resolved; remove the hyperlink. XmlNode parentNode = linkElement.ParentNode; foreach (XmlNode childNode in linkElement.ChildNodes) { parentNode.InsertBefore(childNode, linkElement); } parentNode.RemoveChild(linkElement); } } // Resolve embedded media items List <EntityModel> embeddedEntities = new List <EntityModel>(); foreach (XmlElement imgElement in doc.SelectNodes("//img[@data-schemaUri]", nsmgr)) { string[] schemaTcmUriParts = imgElement.GetAttribute("data-schemaUri").Split('-'); SemanticSchema semanticSchema = SemanticMapping.GetSchema(schemaTcmUriParts[1], localization); // The semantic mapping may resolve to a more specific model type than specified here (e.g. YouTubeVideo instead of just MediaItem) Type modelType = semanticSchema.GetModelTypeFromSemanticMapping(typeof(MediaItem)); MediaItem mediaItem = (MediaItem)modelType.CreateInstance(); mediaItem.ReadFromXhtmlElement(imgElement); if (mediaItem.MvcData == null) { // In DXA 1.1 MediaItem.ReadFromXhtmlElement was expected to set MvcData. // In DXA 1.2 this should be done in a GetDefaultView override (which is also used for other embedded Entities) mediaItem.MvcData = mediaItem.GetDefaultView(localization); } embeddedEntities.Add(mediaItem); // Replace img element with marker XML processing instruction imgElement.ParentNode.ReplaceChild( doc.CreateProcessingInstruction(EmbeddedEntityProcessingInstructionName, String.Empty), imgElement ); } // Split the XHTML into fragments based on marker XML processing instructions. string xhtml = doc.DocumentElement.InnerXml; IList <IRichTextFragment> richTextFragments = new List <IRichTextFragment>(); int lastFragmentIndex = 0; int i = 0; foreach (Match embeddedEntityMatch in EmbeddedEntityProcessingInstructionRegex.Matches(xhtml)) { int embeddedEntityIndex = embeddedEntityMatch.Index; if (embeddedEntityIndex > lastFragmentIndex) { richTextFragments.Add(new RichTextFragment(xhtml.Substring(lastFragmentIndex, embeddedEntityIndex - lastFragmentIndex))); } richTextFragments.Add(embeddedEntities[i++]); lastFragmentIndex = embeddedEntityIndex + embeddedEntityMatch.Length; } if (lastFragmentIndex < xhtml.Length) { // Final text fragment richTextFragments.Add(new RichTextFragment(xhtml.Substring(lastFragmentIndex))); } return(new RichText(richTextFragments)); }
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); }