diff --git a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs index 8119753c8b..b7fa65131f 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs @@ -121,8 +121,7 @@ namespace Umbraco.Tests.Models.Mapping { AssertDisplayProperty(result, p, ApplicationContext); } - Assert.AreEqual(content.PropertyGroups.Count(), result.Tabs.Count() - 1); - Assert.IsTrue(result.Tabs.Any(x => x.Label == ui.Text("general", "properties"))); + Assert.AreEqual(content.PropertyGroups.Count(), result.Tabs.Count()); Assert.IsTrue(result.Tabs.First().IsActive); Assert.IsTrue(result.Tabs.Except(new[] {result.Tabs.First()}).All(x => x.IsActive == false)); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js index 28de977cc9..3aca83b44b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js @@ -27,10 +27,10 @@ }; // get available templates - scope.availableTemplates = getAvailableTemplates(scope.node); + scope.availableTemplates = scope.node.allowedTemplates; // get document type details - scope.documentType = getDocumentType(scope.node); + scope.documentType = scope.node.documentType; loadAuditTrail(); @@ -41,9 +41,8 @@ loadAuditTrail(); }; - scope.openDocumentType = function (documentType) { - // remove first "#" from url if it is prefixed else the path won't work - var url = documentType.url.replace(/^#/, ""); + scope.openDocumentType = function (documentType) { + var url = "/settings/documenttypes/edit/" + documentType.id; $location.path(url); }; @@ -52,15 +51,6 @@ // update template value scope.node.template = templateAlias; - // update template value on the correct tab - angular.forEach(scope.node.tabs, function (tab) { - angular.forEach(tab.properties, function (property) { - if (property.alias === "_umb_template") { - property.value = templateAlias; - } - }); - }); - }; scope.datePickerChange = function (event, type) { @@ -113,54 +103,11 @@ }); } - function getAvailableTemplates(node) { - - var availableTemplates = {}; - - // find the templates in the properties array - angular.forEach(node.properties, function (property) { - if (property.alias === "_umb_template") { - if (property.config && property.config.items) { - availableTemplates = property.config.items; - } - } - }); - - return availableTemplates; - - } - - function getDocumentType(node) { - - var documentType = {}; - - // find the document type in the properties array - angular.forEach(node.properties, function (property) { - if (property.alias === "_umb_doctype") { - if (property.value && property.value.length > 0) { - documentType = property.value[0]; - } - } - }); - - return documentType; - - } - function setPublishDate(date) { // update publish value scope.node.releaseDate = date; - // update template value on the correct tab - angular.forEach(scope.node.tabs, function (tab) { - angular.forEach(tab.properties, function (property) { - if (property.alias === "_umb_releasedate") { - property.value = date; - } - }); - }); - // emit event var args = { node: scope.node, date: date }; eventsService.emit("editors.content.changePublishDate", args); @@ -172,15 +119,6 @@ // update publish value scope.node.releaseDate = null; - // update template value on the correct tab - angular.forEach(scope.node.tabs, function (tab) { - angular.forEach(tab.properties, function (property) { - if (property.alias === "_umb_releasedate") { - property.value = null; - } - }); - }); - // emit event var args = { node: scope.node, date: null }; eventsService.emit("editors.content.changePublishDate", args); @@ -192,15 +130,6 @@ // update publish value scope.node.removeDate = date; - // update template value on the correct tab - angular.forEach(scope.node.tabs, function (tab) { - angular.forEach(tab.properties, function (property) { - if (property.alias === "_umb_expiredate") { - property.value = date; - } - }); - }); - // emit event var args = { node: scope.node, date: date }; eventsService.emit("editors.content.changeUnpublishDate", args); @@ -212,15 +141,6 @@ // update publish value scope.node.removeDate = null; - // update template value on the correct tab - angular.forEach(scope.node.tabs, function (tab) { - angular.forEach(tab.properties, function (property) { - if (property.alias === "_umb_expiredate") { - property.value = null; - } - }); - }); - // emit event var args = { node: scope.node, date: null }; eventsService.emit("editors.content.changeUnpublishDate", args); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js index 2b5fa22d4d..2dd287e81a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -324,22 +324,13 @@ //this is basically the same as for media but we need to explicitly add some extra properties var saveModel = this.formatMediaPostData(displayModel, action); - var genericTab = _.find(displayModel.tabs, function (item) { - return item.id === 0; - }); - - var propExpireDate = _.find(genericTab.properties, function (item) { - return item.alias === "_umb_expiredate"; - }); - var propReleaseDate = _.find(genericTab.properties, function (item) { - return item.alias === "_umb_releasedate"; - }); - var propTemplate = _.find(genericTab.properties, function (item) { - return item.alias === "_umb_template"; - }); - saveModel.expireDate = propExpireDate ? propExpireDate.value : null; - saveModel.releaseDate = propReleaseDate ? propReleaseDate.value : null; - saveModel.templateAlias = propTemplate ? propTemplate.value : null; + var propExpireDate = displayModel.removeDate; + var propReleaseDate = displayModel.releaseDate; + var propTemplate = displayModel.template; + + saveModel.expireDate = propExpireDate ? propExpireDate : null; + saveModel.releaseDate = propReleaseDate ? propReleaseDate : null; + saveModel.templateAlias = propTemplate ? propTemplate : null; return saveModel; } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html index d5c39883df..240d9bb03d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html @@ -165,7 +165,7 @@ @@ -177,6 +177,7 @@ ng-model="node.template" ng-options="key as value for (key, value) in availableTemplates" ng-change="updateTemplate(node.template)"> + diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs index 5d424e4dd3..d0194ac009 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs @@ -5,7 +5,6 @@ using Umbraco.Core.Models; namespace Umbraco.Web.Models.ContentEditing { - /// /// A model representing a content item to be displayed in the back office /// @@ -28,6 +27,12 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "template")] public string TemplateAlias { get; set; } + + [DataMember(Name = "allowedTemplates")] + public IDictionary AllowedTemplates { get; set; } + + [DataMember(Name = "documentType")] + public ContentTypeBasic DocumentType { get; set; } [DataMember(Name = "urls")] public string[] Urls { get; set; } diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index 1809bffd3d..9741a2cff4 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -14,6 +14,7 @@ using Umbraco.Web.Trees; using Umbraco.Web.Routing; using umbraco.BusinessLogic.Actions; using Umbraco.Core.PropertyEditors; +using Content = Umbraco.Core.Models.Content; namespace Umbraco.Web.Models.Mapping { @@ -50,6 +51,11 @@ namespace Umbraco.Web.Models.Mapping .ForMember(display => display.Notifications, expression => expression.Ignore()) .ForMember(display => display.Errors, expression => expression.Ignore()) .ForMember(display => display.Alias, expression => expression.Ignore()) + .ForMember(display => display.DocumentType, expression => expression.ResolveUsing()) + .ForMember(display => display.AllowedTemplates, expression => + expression.MapFrom(content => content.ContentType.AllowedTemplates + .Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false) + .ToDictionary(t => t.Alias, t => t.Name))) .ForMember(display => display.Tabs, expression => expression.ResolveUsing(new TabsAndPropertiesResolver(applicationContext.Services.TextService))) .ForMember(display => display.AllowedActions, expression => expression.ResolveUsing( new ActionButtonsResolver(new Lazy(() => applicationContext.Services.UserService)))) @@ -105,111 +111,36 @@ namespace Umbraco.Web.Models.Mapping var url = urlHelper.GetUmbracoApiService(controller => controller.GetTreeNode(display.Id.ToString(), null)); display.TreeNodeUrl = url; } - - //fill in the template config to be passed to the template drop down. - var templateItemConfig = new Dictionary {{"", localizedText.Localize("general/choose")}}; - foreach (var t in content.ContentType.AllowedTemplates - .Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false)) - { - templateItemConfig.Add(t.Alias, t.Name); - } - + if (content.ContentType.IsContainer) { TabsAndPropertiesResolver.AddListView(display, "content", dataTypeService, localizedText); - } + } - var properties = new List - { - new ContentPropertyDisplay - { - Alias = string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("content/documentType"), - Value = localizedText.UmbracoDictionaryTranslate(display.ContentTypeName), - View = PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View - }, - new ContentPropertyDisplay - { - Alias = string.Format("{0}releasedate", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("content/releaseDate"), - Value = display.ReleaseDate.HasValue ? display.ReleaseDate.Value.ToIsoString() : null, - //Not editible for people without publish permission (U4-287) - View = display.AllowedActions.Contains(ActionPublish.Instance.Letter.ToString(CultureInfo.InvariantCulture)) ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View, - Config = new Dictionary - { - {"offsetTime", "1"} - } - //TODO: Fix up hard coded datepicker - }, - new ContentPropertyDisplay - { - Alias = string.Format("{0}expiredate", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("content/unpublishDate"), - Value = display.ExpireDate.HasValue ? display.ExpireDate.Value.ToIsoString() : null, - //Not editible for people without publish permission (U4-287) - View = display.AllowedActions.Contains(ActionPublish.Instance.Letter.ToString(CultureInfo.InvariantCulture)) ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View, - Config = new Dictionary - { - {"offsetTime", "1"} - } - //TODO: Fix up hard coded datepicker - }, - new ContentPropertyDisplay - { - Alias = string.Format("{0}template", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("template/template"), - Value = display.TemplateAlias, - View = "dropdown", //TODO: Hard coding until we make a real dropdown property editor to lookup - Config = new Dictionary - { - {"items", templateItemConfig} - } - } - }; - - - TabsAndPropertiesResolver.MapGenericProperties(content, display, localizedText, properties.ToArray(), - genericProperties => - { - //TODO: This would be much nicer with the IUmbracoContextAccessor so we don't use singletons - //If this is a web request and there's a user signed in and the - // user has access to the settings section, we will - if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null - && UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) - { - var currentDocumentType = contentTypeService.GetContentType(display.ContentTypeAlias); - var currentDocumentTypeName = currentDocumentType == null ? string.Empty : localizedText.UmbracoDictionaryTranslate(currentDocumentType.Name); - - var currentDocumentTypeId = currentDocumentType == null ? string.Empty : currentDocumentType.Id.ToString(CultureInfo.InvariantCulture); - //TODO: Hard coding this is not good - var docTypeLink = string.Format("#/settings/documenttypes/edit/{0}", currentDocumentTypeId); - - //Replace the doc type property - var docTypeProperty = genericProperties.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); - docTypeProperty.Value = new List - { - new - { - linkText = currentDocumentTypeName, - url = docTypeLink, - target = "_self", - icon = "icon-item-arrangement" - } - }; - //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor - docTypeProperty.View = "urllist"; - } - - // inject 'Link to document' as the first generic property - genericProperties.Insert(0, new ContentPropertyDisplay - { - Alias = string.Format("{0}urls", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("content/urls"), - Value = string.Join(",", display.Urls), - View = "urllist" //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor - }); - }); + TabsAndPropertiesResolver.MapGenericProperties(content, display, localizedText); } + + /// + /// Resolves a from the item and checks if the current user + /// has access to see this data + /// + private class ContentTypeBasicResolver : ValueResolver + { + protected override ContentTypeBasic ResolveCore(IContent source) + { + //TODO: This would be much nicer with the IUmbracoContextAccessor so we don't use singletons + //If this is a web request and there's a user signed in and the + // user has access to the settings section, we will + if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null + && UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) + { + var contentTypeBasic = Mapper.Map(source.ContentType); + return contentTypeBasic; + } + //no access + return null; + } + } /// /// Creates the list of action buttons allowed for this user - Publish, Send to publish, save, unpublish returned as the button's 'letter' diff --git a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs index 1722eecf79..f49c2893b8 100644 --- a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using AutoMapper; using Umbraco.Core; @@ -28,7 +29,7 @@ namespace Umbraco.Web.Models.Mapping public TabsAndPropertiesResolver(ILocalizedTextService localizedTextService, IEnumerable ignoreProperties) : this(localizedTextService) - { + { if (ignoreProperties == null) throw new ArgumentNullException("ignoreProperties"); IgnoreProperties = ignoreProperties; } @@ -60,42 +61,7 @@ namespace Umbraco.Web.Models.Mapping //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 - } - }; + var contentProps = new List(); if (customProperties != null) { @@ -111,9 +77,15 @@ namespace Umbraco.Web.Models.Mapping { onGenericPropertiesMapped(contentProps); } - - //re-assign + + //re-assign genericProps.Properties = contentProps; + + //Show or hide properties tab based on wether it has or not any properties + if (genericProps.Properties.Any() == false) + { + display.Tabs = display.Tabs.Where(x => x.Id != 0); + } } /// @@ -132,8 +104,8 @@ namespace Umbraco.Web.Models.Mapping switch (entityType) { case "content": - dtdId = Constants.System.DefaultContentListViewDataTypeId; - + dtdId = Constants.System.DefaultContentListViewDataTypeId; + break; case "media": dtdId = Constants.System.DefaultMediaListViewDataTypeId; @@ -146,7 +118,7 @@ namespace Umbraco.Web.Models.Mapping } //first try to get the custom one if there is one - var dt = dataTypeService.GetDataTypeDefinitionByName(customDtdName) + var dt = dataTypeService.GetDataTypeDefinitionByName(customDtdName) ?? dataTypeService.GetDataTypeDefinitionById(dtdId); if (dt == null) @@ -195,9 +167,9 @@ namespace Umbraco.Web.Models.Mapping SetChildItemsTabPosition(display, listViewConfig, listViewTab); } - private static void SetChildItemsTabPosition(TabbedContentItem display, + private static void SetChildItemsTabPosition(TabbedContentItem display, IDictionary listViewConfig, - Tab listViewTab) + Tab listViewTab) where TPersisted : IContentBase { // Find position of tab from config @@ -237,9 +209,9 @@ namespace Umbraco.Web.Models.Mapping var groupsGroupsByName = content.PropertyGroups.OrderBy(x => x.SortOrder).GroupBy(x => x.Name); foreach (var groupsByName in groupsGroupsByName) { - var properties = new List(); - - // merge properties for groups with the same name + var properties = new List(); + + // merge properties for groups with the same name foreach (var group in groupsByName) { var groupProperties = content.GetPropertiesForGroup(group) @@ -281,8 +253,8 @@ namespace Umbraco.Web.Models.Mapping tabs.Add(new Tab { - Id = 0, - Label = _localizedTextService.Localize("general/properties"), + Id = 0, + Label = _localizedTextService.Localize("general/properties"), Alias = "Generic properties", Properties = genericproperties });