using System; using System.Collections.Generic; using System.Linq; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using umbraco; namespace Umbraco.Web.Models.Mapping { /// /// Creates the tabs collection with properties assigned for display models /// internal class TabsAndPropertiesResolver : ValueResolver>> { private readonly ILocalizedTextService _localizedTextService; protected IEnumerable IgnoreProperties { get; set; } public TabsAndPropertiesResolver(ILocalizedTextService localizedTextService) { if (localizedTextService == null) throw new ArgumentNullException("localizedTextService"); _localizedTextService = localizedTextService; IgnoreProperties = new List(); } public TabsAndPropertiesResolver(ILocalizedTextService localizedTextService, IEnumerable ignoreProperties) : this(localizedTextService) { if (ignoreProperties == null) throw new ArgumentNullException("ignoreProperties"); IgnoreProperties = ignoreProperties; } /// /// Maps properties on to the generic properties tab /// /// /// /// /// /// Any additional custom properties to assign to the generic properties tab. /// /// /// /// The generic properties tab is mapped during AfterMap and is responsible for /// setting up the properties such as Created date, updated date, template selected, etc... /// public static void MapGenericProperties( TPersisted content, ContentItemDisplayBase display, ILocalizedTextService localizedTextService, IEnumerable customProperties = null, Action> onGenericPropertiesMapped = null) where TPersisted : IContentBase { var genericProps = display.Tabs.Single(x => x.Id == 0); //store the current props to append to the newly inserted ones var currProps = genericProps.Properties.ToArray(); var labelEditor = PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View; var contentProps = new List { new ContentPropertyDisplay { Alias = string.Format("{0}id", Constants.PropertyEditors.InternalGenericPropertiesPrefix), Label = "Id", Value = Convert.ToInt32(display.Id).ToInvariantString() + "
" + display.Key + "", View = labelEditor }, new ContentPropertyDisplay { Alias = string.Format("{0}creator", Constants.PropertyEditors.InternalGenericPropertiesPrefix), Label = localizedTextService.Localize("content/createBy"), Description = localizedTextService.Localize("content/createByDesc"), Value = display.Owner.Name, View = labelEditor }, new ContentPropertyDisplay { Alias = string.Format("{0}createdate", Constants.PropertyEditors.InternalGenericPropertiesPrefix), Label = localizedTextService.Localize("content/createDate"), Description = localizedTextService.Localize("content/createDateDesc"), Value = display.CreateDate.ToIsoString(), View = labelEditor }, new ContentPropertyDisplay { Alias = string.Format("{0}updatedate", Constants.PropertyEditors.InternalGenericPropertiesPrefix), Label = localizedTextService.Localize("content/updateDate"), Description = localizedTextService.Localize("content/updateDateDesc"), Value = display.UpdateDate.ToIsoString(), View = labelEditor } }; if (customProperties != null) { //add the custom ones contentProps.AddRange(customProperties); } //now add the user props contentProps.AddRange(currProps); //callback if (onGenericPropertiesMapped != null) { onGenericPropertiesMapped(contentProps); } //re-assign genericProps.Properties = contentProps; } /// /// Adds the container (listview) tab to the document /// /// /// /// This must be either 'content' or 'media' /// internal static void AddListView(TabbedContentItem display, string entityType, IDataTypeService dataTypeService, ILocalizedTextService localizedTextService) where TPersisted : IContentBase { int dtdId; var customDtdName = Constants.Conventions.DataTypes.ListViewPrefix + display.ContentTypeAlias; switch (entityType) { case "content": dtdId = Constants.System.DefaultContentListViewDataTypeId; break; case "media": dtdId = Constants.System.DefaultMediaListViewDataTypeId; break; case "member": dtdId = Constants.System.DefaultMembersListViewDataTypeId; break; default: throw new ArgumentOutOfRangeException("entityType does not match a required value"); } //first try to get the custom one if there is one var dt = dataTypeService.GetDataTypeDefinitionByName(customDtdName) ?? dataTypeService.GetDataTypeDefinitionById(dtdId); if (dt == null) { throw new InvalidOperationException("No list view data type was found for this document type, ensure that the default list view data types exists and/or that your custom list view data type exists"); } var preVals = dataTypeService.GetPreValuesCollectionByDataTypeId(dt.Id); var editor = PropertyEditorResolver.Current.GetByAlias(dt.PropertyEditorAlias); if (editor == null) { throw new NullReferenceException("The property editor with alias " + dt.PropertyEditorAlias + " does not exist"); } var listViewTab = new Tab(); listViewTab.Alias = Constants.Conventions.PropertyGroups.ListViewGroupName; listViewTab.Label = localizedTextService.Localize("content/childItems"); listViewTab.Id = 25; listViewTab.IsActive = true; var listViewConfig = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals); //add the entity type to the config listViewConfig["entityType"] = entityType; var listViewProperties = new List(); listViewProperties.Add(new ContentPropertyDisplay { Alias = string.Format("{0}containerView", Constants.PropertyEditors.InternalGenericPropertiesPrefix), Label = "", Value = null, View = editor.ValueEditor.View, HideLabel = true, Config = listViewConfig }); listViewTab.Properties = listViewProperties; //Is there a better way? var tabs = new List>(); tabs.Add(listViewTab); tabs.AddRange(display.Tabs); display.Tabs = tabs; } protected override IEnumerable> ResolveCore(IContentBase content) { var aggregateTabs = new List>(); //now we need to aggregate the tabs and properties since we might have duplicate tabs (based on aliases) because // of how content composition works. foreach (var propertyGroups in content.PropertyGroups.OrderBy(x => x.SortOrder).GroupBy(x => x.Name)) { var aggregateProperties = new List(); //add the properties from each composite property group foreach (var current in propertyGroups) { var propsForGroup = content.GetPropertiesForGroup(current) .Where(x => IgnoreProperties.Contains(x.Alias) == false); //don't include ignored props aggregateProperties.AddRange( Mapper.Map, IEnumerable>( propsForGroup)); } if (aggregateProperties.Count == 0) continue; TranslateProperties(aggregateProperties); //then we'll just use the root group's data to make the composite tab var rootGroup = propertyGroups.First(x => x.ParentId == null); aggregateTabs.Add(new Tab { Id = rootGroup.Id, Alias = rootGroup.Name, Label = _localizedTextService.UmbracoDictionaryTranslate(rootGroup.Name), Properties = aggregateProperties, IsActive = false }); } //now add the generic properties tab for any properties that don't belong to a tab var orphanProperties = content.GetNonGroupedProperties() .Where(x => IgnoreProperties.Contains(x.Alias) == false); //don't include ignored props //now add the generic properties tab var genericproperties = Mapper.Map, IEnumerable>(orphanProperties).ToList(); TranslateProperties(genericproperties); aggregateTabs.Add(new Tab { Id = 0, Label = _localizedTextService.Localize("general/properties"), Alias = "Generic properties", Properties = genericproperties }); //set the first tab to active aggregateTabs.First().IsActive = true; return aggregateTabs; } private void TranslateProperties(IEnumerable properties) { // Not sure whether it's a good idea to add this to the ContentPropertyDisplay mapper foreach (var prop in properties) { prop.Label = _localizedTextService.UmbracoDictionaryTranslate(prop.Label); prop.Description = _localizedTextService.UmbracoDictionaryTranslate(prop.Description); } } } }