From c8e65f085b62a6687728a9e7cdfdb127fa6a1466 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Jul 2018 22:32:46 +1000 Subject: [PATCH] Gets content apps working nicer so we can have a proper view model that is discoverable, updates list views to use the new way, removes strange hack of copying a list view from a tab that doesn't exist. --- .../components/content/edit.controller.js | 6 +- .../components/editor/umbeditorsubview.js | 33 +++++ .../editor/umbeditorsubviews.directive.js | 5 + .../validation/valsubview.directive.js | 8 +- .../src/views/components/content/edit.html | 10 +- .../editor/umb-editor-sub-view.html | 10 ++ .../apps/content/content.controller.js | 14 +-- .../views/content/apps/content/content.html | 2 +- .../src/views/content/apps/info/info.html | 6 +- .../apps/listview/listview.controller.js | 28 ----- .../views/content/apps/listview/listview.html | 4 +- .../media/apps/content/content.controller.js | 2 +- .../src/views/media/apps/content/content.html | 6 +- .../apps/listview/listview.controller.js | 23 ---- .../views/media/apps/listview/listview.html | 5 +- .../src/views/media/edit.html | 113 ++++++++---------- .../src/views/mediatypes/edit.controller.js | 3 + src/Umbraco.Web/Editors/ContentController.cs | 1 + src/Umbraco.Web/Editors/MediaController.cs | 3 +- src/Umbraco.Web/Editors/MemberController.cs | 1 + .../Models/ContentEditing/ContentApp.cs | 6 + .../Models/Mapping/ContentAppResolver.cs | 21 ++-- .../Mapping/ContentAppResolverExtensions.cs | 81 +++++++++++++ .../Models/Mapping/ContentMapperProfile.cs | 32 ++--- .../Models/Mapping/MediaAppResolver.cs | 20 ++-- .../Models/Mapping/MediaMapperProfile.cs | 12 +- .../Mapping/TabsAndPropertiesResolver.cs | 41 +------ .../PropertyEditors/ListViewConfiguration.cs | 4 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 29 files changed, 272 insertions(+), 229 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubview.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-view.html delete mode 100644 src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.controller.js delete mode 100644 src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.controller.js create mode 100644 src/Umbraco.Web/Models/Mapping/ContentAppResolverExtensions.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 76274a4901..ddd98d4b8a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -104,9 +104,7 @@ //with a copy of the contentApps. This is required because each editor renders it's own //header and content apps section and the content apps contains the view for editing content itself //and we need to assign a view model to the subView so that it is scoped to the current - //editor so that split views work. This is a bit hacky but it's required because the content - //app stuff isn't built to have a scoped model to an editor, it's built to have a single global - //model but that doesn't work for having split view. + //editor so that split views work. //copy the apps from the main model if not assigned yet to the variant if (!variant.apps) { @@ -132,7 +130,7 @@ } } - //the assign the variant to a view model to the content app + //then assign the variant to a view model to the content app var contentApp = _.find(variant.apps, function (a) { return a.alias === "content"; }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubview.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubview.js new file mode 100644 index 0000000000..e8b41529ae --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubview.js @@ -0,0 +1,33 @@ +(function () { + 'use strict'; + + /** + * A directive that renders a defined view with a view model and a the whole content model. + **/ + function EditorSubViewDirective() { + + function link(scope, el, attr, ctrl) { + //The model can contain: view, viewModel, name, alias, icon + + if (!scope.model.view) { + throw "No view defined for the content app"; + } + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-sub-view.html', + scope: { + model: "=", + content: "=" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorSubView', EditorSubViewDirective); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubviews.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubviews.directive.js index ea3c4799cd..7033a03943 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubviews.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubviews.directive.js @@ -1,6 +1,11 @@ (function () { 'use strict'; + /** + * A directive that just repeats over a list of defined views which are all able to access the same common model. + * This is only used in simple cases, whereas media and content use umbEditorSubView (singular) which allows + * passing in a view model specific to the view and the entire content model for support if required. + **/ function EditorSubViewsDirective() { function link(scope, el, attr, ctrl) { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js index ca571d3954..a65a08d17e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js @@ -18,7 +18,7 @@ } var valFormManager = ctrl[1]; - scope.subView.hasError = false; + scope.model.hasError = false; //listen for form validation changes valFormManager.onValidationStatusChanged(function (evt, args) { @@ -27,14 +27,14 @@ var subViewContent = el.find(".ng-invalid"); if (subViewContent.length > 0) { - scope.subView.hasError = true; + scope.model.hasError = true; } else { - scope.subView.hasError = false; + scope.model.hasError = false; } } else { - scope.subView.hasError = false; + scope.model.hasError = false; } }); diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html b/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html index f8ce725f18..88b5343c1f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html @@ -33,11 +33,11 @@ - - +
+
+ +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-view.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-view.html new file mode 100644 index 0000000000..068ba1a1b5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-view.html @@ -0,0 +1,10 @@ +
+ +
+
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.controller.js index 7c4ed5aaa4..3e30b25a94 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.controller.js @@ -8,15 +8,7 @@ function onInit() { - //TODO: This is pretty ugly since this component inherits the scope from umbEditorSubViews which is supplied a - //'model' which is the entire content object passed from the server which we can't use because this 'app' needs to be - //in the context of the current culture within a split view. So the content controller will assign a special 'viewModel' to the - //subView so that we have a model in the context of the editor. - //Ideally this would be a directive and we can just pass a model in but because contentApps currently are - //rendered purely based on views that won't work. Perhaps we can consider dynamically compiling directives like - // https://www.codelord.net/2015/05/19/angularjs-dynamically-loading-directives/ - - vm.content = $scope.subView.viewModel; + vm.content = $scope.model.viewModel; angular.forEach(vm.content.tabs, function (group) { group.open = true; @@ -29,9 +21,9 @@ //if this variant has a culture/language assigned, then we need to watch it since it will change //if the language drop down changes and we need to re-init - if ($scope.subView.viewModel.language) { + if ($scope.model.viewModel.language) { $scope.$watch(function () { - return $scope.subView.viewModel.language.culture; + return $scope.model.viewModel.language.culture; }, function (newVal, oldVal) { if (newVal !== oldVal) { vm.loading = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.html b/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.html index 056933a85c..db71539c59 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.html @@ -4,7 +4,7 @@
{{ group.label }}
-   +  
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/apps/info/info.html b/src/Umbraco.Web.UI.Client/src/views/content/apps/info/info.html index 197aeb35b9..c40486012c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/apps/info/info.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/apps/info/info.html @@ -1,4 +1,4 @@ - \ No newline at end of file + ng-if="content" + node="content"> + diff --git a/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.controller.js deleted file mode 100644 index 2733b56690..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.controller.js +++ /dev/null @@ -1,28 +0,0 @@ -(function () { - "use strict"; - - function ContentAppListViewController($scope) { - - var vm = this; - - vm.listViewGroup = {}; - - //TODO: We need to fix this, there is no umbContainerView anymore since this worked as a hack by copying - // across a tab/property editor to a content App, instead we are going to need to hack the list view a different - // way since currently it still requires us to use umb-property-editor so we'll either need to construct the model - // here ourselves or somehow allow the server to pass in a model for a content app. - - function onInit() { - angular.forEach($scope.model.tabs, function(group){ - if(group.alias === "umbContainerView") { - vm.listViewGroup = group; - } - }); - } - - onInit(); - - } - - angular.module("umbraco").controller("Umbraco.Editors.Content.Apps.ListViewController", ContentAppListViewController); -})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.html index 74d690c671..a19d7ea4d9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.html @@ -1,6 +1,6 @@ -
+
- + diff --git a/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.controller.js index 3349bfbcba..6dcf1211c2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.controller.js @@ -6,7 +6,7 @@ var vm = this; function onInit() { - angular.forEach($scope.model.tabs, function(group){ + angular.forEach($scope.content.tabs, function(group){ group.open = true; }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.html b/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.html index a865e2ff5d..8c16a451d9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.html @@ -1,9 +1,9 @@
-
+
{{ group.label }}
-   +  
@@ -14,4 +14,4 @@
-
\ No newline at end of file +
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.controller.js deleted file mode 100644 index 7ad5724e75..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.controller.js +++ /dev/null @@ -1,23 +0,0 @@ -(function () { - "use strict"; - - function MediaAppListViewController($scope) { - - var vm = this; - - vm.listViewGroup = {}; - - function onInit() { - angular.forEach($scope.model.tabs, function(group){ - if(group.alias === "Contents") { - vm.listViewGroup = group; - } - }); - } - - onInit(); - - } - - angular.module("umbraco").controller("Umbraco.Editors.Media.Apps.ListViewController", MediaAppListViewController); -})(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.html index e463680ae6..6c558301c7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/apps/listview/listview.html @@ -1,8 +1,7 @@ -
+
- +
- \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/media/edit.html b/src/Umbraco.Web.UI.Client/src/views/media/edit.html index 3de3758af2..a9afde36ac 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/edit.html @@ -1,81 +1,74 @@
- + -
+ - + - - + + - + +
+
+ +
+
+
- - + -
+ - + + - - - - - - + - + - - + + - - + + - - - + + + - + - + -
+
-
-
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.controller.js index 14b3a56c9b..c8aba53cf5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.controller.js @@ -76,17 +76,20 @@ vm.page.navigation = [ { "name": vm.labels.design, + "alias": "design", "icon": "icon-document-dashed-line", "view": "views/mediatypes/views/design/design.html", "active": true }, { "name": vm.labels.listview, + "alias": "listView", "icon": "icon-list", "view": "views/mediatypes/views/listview/listview.html" }, { "name": vm.labels.permissions, + "alias": "permissions", "icon": "icon-keychain", "view": "views/mediatypes/views/permissions/permissions.html" } diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index fb19131e96..0af61e9a0e 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -235,6 +235,7 @@ namespace Umbraco.Web.Editors } }; + //TODO: Change this over to use "Content Apps" TabsAndPropertiesResolver.AddListView(display.ContentVariants.First(), "content", "recycleBin", Services.DataTypeService, Services.TextService); return display; diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 94b88459f7..4482df0ec7 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -101,6 +101,7 @@ namespace Umbraco.Web.Editors Path = "-1," + Constants.System.RecycleBinMedia }; + //TODO: Change this over to use "Content Apps" TabsAndPropertiesResolver.AddListView(display, "media", "recycleBin", Services.DataTypeService, Services.TextService); return display; @@ -182,7 +183,7 @@ namespace Umbraco.Web.Editors /// /// /// - public PagedResult> GetChildFolders(int id, int pageNumber, int pageSize) + public PagedResult> GetChildFolders(int id, int pageNumber = 1, int pageSize = 1000) { //Suggested convention for folder mediatypes - we can make this more or less complicated as long as we document it... //if you create a media type, which has an alias that ends with ...Folder then its a folder: ex: "secureFolder", "bannerFolder", "Folder" diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index 5d5367b3dc..f235f69020 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -138,6 +138,7 @@ namespace Umbraco.Web.Editors ParentId = -1 }; + //TODO: Change this over to use "Content Apps" TabsAndPropertiesResolver.AddListView(display, "member", listName, Services.DataTypeService, Services.TextService); return display; diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentApp.cs b/src/Umbraco.Web/Models/ContentEditing/ContentApp.cs index 3477b200cb..75b5a973fe 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentApp.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentApp.cs @@ -19,6 +19,12 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "view")] public string View { get; set; } + + /// + /// The view model specific to this app + /// + [DataMember(Name = "viewModel")] + public object ViewModel { get; set; } } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentAppResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentAppResolver.cs index ca58f81b91..2c7502151d 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentAppResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentAppResolver.cs @@ -1,6 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using AutoMapper; using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping @@ -24,13 +27,14 @@ namespace Umbraco.Web.Models.Mapping View = "views/content/apps/info/info.html" }; - private static readonly ContentApp _listViewApp = new ContentApp + private readonly IDataTypeService _dataTypeService; + private readonly PropertyEditorCollection _propertyEditorCollection; + + public ContentAppResolver(IDataTypeService dataTypeService, PropertyEditorCollection propertyEditorCollection) { - Alias = "childItems", - Name = "Child items", - Icon = "icon-list", - View = "views/content/apps/listview/listview.html" - }; + _dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); + _propertyEditorCollection = propertyEditorCollection ?? throw new ArgumentNullException(nameof(propertyEditorCollection)); + } public IEnumerable Resolve(IContent source, ContentItemDisplay destination, IEnumerable destMember, ResolutionContext context) { @@ -42,7 +46,8 @@ namespace Umbraco.Web.Models.Mapping if (source.ContentType.IsContainer) { - apps.Add(_listViewApp); + //If it's a container then add the list view app and view model + apps.Add(this.CreateListViewApp(_dataTypeService, _propertyEditorCollection, source.ContentType.Alias, "content")); } return apps; diff --git a/src/Umbraco.Web/Models/Mapping/ContentAppResolverExtensions.cs b/src/Umbraco.Web/Models/Mapping/ContentAppResolverExtensions.cs new file mode 100644 index 0000000000..7e2ee0301c --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/ContentAppResolverExtensions.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using AutoMapper; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + internal static class ContentAppResolverExtensions + { + /// + /// Extension method to create a list view app for the content app collection + /// + /// + /// + /// + public static ContentApp CreateListViewApp( + this IValueResolver> resolver, + IDataTypeService dataTypeService, PropertyEditorCollection propertyEditors, + string contentTypeAlias, + string entityType) + where TContent: IContentBase + { + var listViewApp = new ContentApp + { + Alias = "childItems", + Name = "Child items", + Icon = "icon-list", + View = "views/content/apps/listview/listview.html" + }; + + var customDtdName = Core.Constants.Conventions.DataTypes.ListViewPrefix + contentTypeAlias; + var dtdId = Core.Constants.DataTypes.DefaultContentListView; + //first try to get the custom one if there is one + var dt = dataTypeService.GetDataType(customDtdName) + ?? dataTypeService.GetDataType(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 editor = propertyEditors[dt.EditorAlias]; + if (editor == null) + { + throw new NullReferenceException("The property editor with alias " + dt.EditorAlias + " does not exist"); + } + + var listViewConfig = editor.GetConfigurationEditor().ToConfigurationEditor(dt.Configuration); + //add the entity type to the config + listViewConfig["entityType"] = entityType; + + //Override Tab Label if tabName is provided + if (listViewConfig.ContainsKey("tabName")) + { + var configTabName = listViewConfig["tabName"]; + if (configTabName != null && string.IsNullOrWhiteSpace(configTabName.ToString()) == false) + listViewApp.Name = configTabName.ToString(); + } + + //This is the view model used for the list view app + listViewApp.ViewModel = new List + { + new ContentPropertyDisplay + { + Alias = $"{Core.Constants.PropertyEditors.InternalGenericPropertiesPrefix}containerView", + Label = "", + Value = null, + View = editor.GetValueEditor().View, + HideLabel = true, + Config = listViewConfig + } + }; + + return listViewApp; + } + } + +} diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index b50df61eb5..622220ab78 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -3,6 +3,7 @@ using AutoMapper; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Trees; @@ -14,7 +15,15 @@ namespace Umbraco.Web.Models.Mapping /// internal class ContentMapperProfile : Profile { - public ContentMapperProfile(IUserService userService, ILocalizedTextService textService, IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, ILocalizationService localizationService, ILogger logger) + public ContentMapperProfile( + IUserService userService, + ILocalizedTextService textService, + IContentService contentService, + IContentTypeService contentTypeService, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + PropertyEditorCollection propertyEditors, + ILogger logger) { // create, capture, cache var contentOwnerResolver = new OwnerResolver(userService); @@ -27,17 +36,15 @@ namespace Umbraco.Web.Models.Mapping var defaultTemplateResolver = new DefaultTemplateResolver(); var contentUrlResolver = new ContentUrlResolver(textService, contentService, logger); var variantResolver = new ContentVariantResolver(localizationService, textService); - var contentAppResolver = new ContentAppResolver(); + var contentAppResolver = new ContentAppResolver(dataTypeService, propertyEditors); //FROM IContent TO ContentItemDisplay CreateMap() .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(src.Blueprint ? Constants.UdiEntityType.DocumentBlueprint : Constants.UdiEntityType.Document, src.Key))) .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => contentOwnerResolver.Resolve(src))) .ForMember(dest => dest.Updater, opt => opt.ResolveUsing(src => creatorResolver.Resolve(src))) - //.ForMember(dest => dest.Name, opt => opt.ResolveUsing()) .ForMember(dest => dest.ContentVariants, opt => opt.ResolveUsing(variantResolver)) .ForMember(dest => dest.ContentApps, opt => opt.ResolveUsing(contentAppResolver)) - //.ForMember(dest => dest.Variants, opt => opt.ResolveUsing(variantResolver)) .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon)) .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) .ForMember(dest => dest.ContentTypeName, opt => opt.MapFrom(src => src.ContentType.Name)) @@ -45,29 +52,19 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.IsBlueprint, opt => opt.MapFrom(src => src.Blueprint)) .ForMember(dest => dest.IsChildOfListView, opt => opt.ResolveUsing(childOfListViewResolver)) .ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed)) - //TODO: The publish date needs to be per variant - in our display models - //.ForMember(dest => dest.PublishDate, opt => opt.MapFrom(src => src.PublishDate)) .ForMember(dest => dest.TemplateAlias, opt => opt.ResolveUsing(defaultTemplateResolver)) .ForMember(dest => dest.Urls, opt => opt.ResolveUsing(contentUrlResolver)) - //.ForMember(dest => dest.Properties, opt => opt.Ignore()) .ForMember(dest => dest.AllowPreview, opt => opt.Ignore()) .ForMember(dest => dest.TreeNodeUrl, opt => opt.ResolveUsing(contentTreeNodeUrlResolver)) .ForMember(dest => dest.Notifications, opt => opt.Ignore()) .ForMember(dest => dest.Errors, opt => opt.Ignore()) - //.ForMember(dest => dest.Alias, opt => opt.Ignore()) .ForMember(dest => dest.DocumentType, opt => opt.ResolveUsing(contentTypeBasicResolver)) .ForMember(dest => dest.AllowedTemplates, opt => opt.MapFrom(content => content.ContentType.AllowedTemplates .Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false) .ToDictionary(t => t.Alias, t => t.Name))) - //.ForMember(dest => dest.Tabs, opt => opt.ResolveUsing(tabsAndPropertiesResolver)) .ForMember(dest => dest.AllowedActions, opt => opt.ResolveUsing(src => actionButtonsResolver.Resolve(src))) .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); - //.AfterMap((content, display) => - //{ - // if (content.ContentType.IsContainer) - // TabsAndPropertiesResolver.AddListView(display, "content", dataTypeService, textService); - //}); CreateMap() .ForMember(dest => dest.PublishDate, opt => opt.MapFrom(src => src.PublishDate)) @@ -76,12 +73,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Segment, opt => opt.Ignore()) .ForMember(dest => dest.Language, opt => opt.Ignore()) .ForMember(dest => dest.IsEdited, opt => opt.Ignore()) - .ForMember(dest => dest.Tabs, opt => opt.ResolveUsing(tabsAndPropertiesResolver)) - .AfterMap((content, display) => - { - if (content.ContentType.IsContainer) - TabsAndPropertiesResolver.AddListView(display, content.ContentType.Alias, "content", dataTypeService, textService); - }); + .ForMember(dest => dest.Tabs, opt => opt.ResolveUsing(tabsAndPropertiesResolver)); //FROM IContent TO ContentItemBasic CreateMap>() diff --git a/src/Umbraco.Web/Models/Mapping/MediaAppResolver.cs b/src/Umbraco.Web/Models/Mapping/MediaAppResolver.cs index cbf8cd3aa2..0455c320c7 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaAppResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaAppResolver.cs @@ -1,6 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using AutoMapper; using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping @@ -23,13 +26,14 @@ namespace Umbraco.Web.Models.Mapping View = "views/media/apps/info/info.html" }; - private static readonly ContentApp _listViewApp = new ContentApp + private readonly IDataTypeService _dataTypeService; + private readonly PropertyEditorCollection _propertyEditorCollection; + + public MediaAppResolver(IDataTypeService dataTypeService, PropertyEditorCollection propertyEditorCollection) { - Alias = "childItems", - Name = "Child items", - Icon = "icon-list", - View = "views/media/apps/listview/listview.html" - }; + _dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); + _propertyEditorCollection = propertyEditorCollection ?? throw new ArgumentNullException(nameof(propertyEditorCollection)); + } public IEnumerable Resolve(IMedia source, MediaItemDisplay destination, IEnumerable destMember, ResolutionContext context) { @@ -37,7 +41,7 @@ namespace Umbraco.Web.Models.Mapping if (source.ContentType.IsContainer || source.ContentType.Alias == Umbraco.Core.Constants.Conventions.MediaTypes.Folder) { - apps.Add(_listViewApp); + apps.Add(this.CreateListViewApp(_dataTypeService, _propertyEditorCollection, source.ContentType.Alias, "media")); } else { diff --git a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs index 4a5c5386f8..65fe8cf6a9 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs @@ -3,6 +3,7 @@ using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Trees; @@ -14,7 +15,14 @@ namespace Umbraco.Web.Models.Mapping /// internal class MediaMapperProfile : Profile { - public MediaMapperProfile(IUserService userService, ILocalizedTextService textService, IDataTypeService dataTypeService, IMediaService mediaService, IMediaTypeService mediaTypeService, ILogger logger) + public MediaMapperProfile( + IUserService userService, + ILocalizedTextService textService, + IDataTypeService dataTypeService, + IMediaService mediaService, + IMediaTypeService mediaTypeService, + PropertyEditorCollection propertyEditors, + ILogger logger) { // create, capture, cache var mediaOwnerResolver = new OwnerResolver(userService); @@ -22,7 +30,7 @@ namespace Umbraco.Web.Models.Mapping var childOfListViewResolver = new MediaChildOfListViewResolver(mediaService, mediaTypeService); var contentTreeNodeUrlResolver = new ContentTreeNodeUrlResolver(); var mediaTypeBasicResolver = new ContentTypeBasicResolver(); - var mediaAppResolver = new MediaAppResolver(); + var mediaAppResolver = new MediaAppResolver(dataTypeService, propertyEditors); //FROM IMedia TO MediaItemDisplay CreateMap() diff --git a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs index 1b5da1a398..aaa4239f8d 100644 --- a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs @@ -27,14 +27,7 @@ namespace Umbraco.Web.Models.Mapping IgnoreProperties = ignoreProperties ?? throw new ArgumentNullException(nameof(ignoreProperties)); } - /// - /// Adds the container (listview) tab to the document - /// - /// - /// - /// This must be either 'content' or 'media' - /// - /// + //TODO: Get rid of this it should not be needed anymore since list views are dynamically added to "content apps" internal static void AddListView( ITabbedContent display, string contentTypeAlias, string entityType, @@ -46,7 +39,6 @@ namespace Umbraco.Web.Models.Mapping { case "content": dtdId = Constants.DataTypes.DefaultContentListView; - break; case "media": dtdId = Constants.DataTypes.DefaultMediaListView; @@ -126,37 +118,6 @@ namespace Umbraco.Web.Models.Mapping return -1; } - //private static void SetChildItemsTabPosition(ITabbedContentItem display, - // IDictionary listViewConfig, - // Tab listViewTab) - //{ - // // Find position of tab from config - // var tabIndexForChildItems = GetTabNumberFromConfig(listViewConfig); - // if (tabIndexForChildItems != -1) - // { - // // Tab position is recorded 1-based but we insert into collection 0-based - // tabIndexForChildItems--; - - // // Ensure within bounds - // if (tabIndexForChildItems < 0) - // { - // tabIndexForChildItems = 0; - // } - - // if (tabIndexForChildItems > display.Tabs.Count()) - // { - // tabIndexForChildItems = display.Tabs.Count(); - // } - // } - // else tabIndexForChildItems = 0; - - // // Recreate tab list with child items tab at configured position - // var tabs = new List>(); - // tabs.AddRange(display.Tabs); - // tabs.Insert(tabIndexForChildItems, listViewTab); - // display.Tabs = tabs; - //} - /// /// Returns a collection of custom generic properties that exist on the generic properties tab /// diff --git a/src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs b/src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs index cec8f232d3..b8811ec69c 100644 --- a/src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs @@ -33,7 +33,7 @@ namespace Umbraco.Web.PropertyEditors public BulkActionPermissionSettings BulkActionPermissions { get; set; } = new BulkActionPermissionSettings(); // fixme managing defaults? [ConfigurationField("tabName", "Tab Name", "textstring", Description = "The name of the listview tab (default if empty: 'Child Items')")] - public int TabName { get; set; } + public string TabName { get; set; } public class Property { @@ -83,4 +83,4 @@ namespace Umbraco.Web.PropertyEditors public bool AllowBulkDelete { get; set; } = true; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 158137724f..b754d7ed26 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -240,6 +240,7 @@ +