/// <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); } }
/// <summary> /// Adds a predefined schema /// </summary> /// <param name="schema">Schema</param> public void AddPredefinedSchema(SemanticSchema schema) { var existing = GetSemanticSchema(schema.Id.ToString()); if (existing != null) { return; } List <SemanticSchema> newSchemas = new List <SemanticSchema>(_semanticSchemas) { schema }; schema.Initialize(_localization); }
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); }
/// <summary> /// Class constructor /// </summary> static DocsLocalization() { const string rootElementName = "Topic"; const string topicBodyFieldName = "topicBody"; const string topicTitleFieldName = "topicTitle"; const string coreVocabularyPrefix = "tri"; using (new Tracer()) { // Predefined Topic schema that always has an ID of 1 (deployer adds this) SemanticSchema topicSchema = new SemanticSchema { Id = 1, RootElement = rootElementName, Fields = new List <SemanticSchemaField> { new SemanticSchemaField { Name = topicBodyFieldName, Path = $"/{rootElementName}/{topicBodyFieldName}", IsMultiValue = false, Semantics = new List <FieldSemantics> { new FieldSemantics { Prefix = coreVocabularyPrefix, Entity = rootElementName, Property = topicBodyFieldName } }, Fields = new List <SemanticSchemaField>() }, new SemanticSchemaField { Name = topicTitleFieldName, Path = $"/{rootElementName}/{topicTitleFieldName}", IsMultiValue = false, Semantics = new List <FieldSemantics> { new FieldSemantics { Prefix = coreVocabularyPrefix, Entity = rootElementName, Property = topicTitleFieldName } }, Fields = new List <SemanticSchemaField>() } }, Semantics = new List <SchemaSemantics> { new FieldSemantics { Prefix = coreVocabularyPrefix, Entity = rootElementName } } }; _semanticSchemas = new List <SemanticSchema> { topicSchema }; _semanticVocabs = new List <SemanticVocabulary> { new SemanticVocabulary { Prefix = coreVocabularyPrefix, Vocab = Models.ViewModel.CoreVocabulary } }; List <string> mediaPatterns = new List <string> { "^/favicon.ico", $"^/{SiteConfiguration.SystemFolder}/assets/.*", $"^/{SiteConfiguration.SystemFolder}/.*\\.json$" }; _staticContentUrlPattern = string.Join("|", mediaPatterns); } }
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); }
/// <summary> /// Adds a predefined schema /// </summary> /// <param name="schema">Schema</param> public void AddPredefinedSchema(SemanticSchema schema) => _mappingsManager.AddPredefinedSchema(schema);
/// <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, Localization 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 { // Custom Page Model List <SemanticSchema> inheritedSemanticSchemas = GetInheritedSemanticSchemas(pageModelData, localization); SemanticSchema mainSemanticSchema = (pageModelData.SchemaId != null) ? SemanticMapping.GetSchema(pageModelData.SchemaId, localization) : inheritedSemanticSchemas.FirstOrDefault(); if (mainSemanticSchema == 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 = mainSemanticSchema, InheritedSchemas = inheritedSemanticSchemas }, 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; string pageContextId = pageModel.Id; if (pageModelData.Regions != null) { IEnumerable <RegionModelData> regions = includePageRegions ? pageModelData.Regions : pageModelData.Regions.Where(r => r.IncludePageId == null); pageModel.Regions.UnionWith(regions.Select(data => { SetPageContextId(data, pageContextId); return(CreateRegionModel(data, localization)); })); pageModel.IsVolatile |= pageModel.Regions.Any(region => region.IsVolatile); } } }