From f368b493dedd03da8db3b3950d2a0e2c33c14659 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Fri, 7 Sep 2018 15:02:44 +0100 Subject: [PATCH 1/4] Updates Content Controller - that uses the Mapping AfterMap to allow us to update the Name of the node based on the culturename passed into the WebAPI --- src/Umbraco.Web/Editors/ContentController.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 09126b80ee..af79e04f2b 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -456,7 +456,8 @@ namespace Umbraco.Web.Editors string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, bool orderBySystemField = true, - string filter = "") + string filter = "", + string cultureName = "") { long totalChildren; IContent[] children; @@ -493,6 +494,11 @@ namespace Umbraco.Web.Editors Mapper.Map>(content, opts => { + if(string.IsNullOrEmpty(cultureName) == false) + { + opts.AfterMap((source, target) => target.Name = source.GetCultureName(cultureName)); + } + // if there's a list of property aliases to map - we will make sure to store this in the mapping context. if (String.IsNullOrWhiteSpace(includeProperties) == false) { @@ -500,6 +506,7 @@ namespace Umbraco.Web.Editors } })); + return pagedResult; } From d61e71aae6485c55fc27c5cc17bb6e502e8dc205 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Fri, 7 Sep 2018 15:09:51 +0100 Subject: [PATCH 2/4] Updates ContentResouce and the ListView Controller to pass along the CultureName --- .../src/common/resources/content.resource.js | 119 +++++++++--------- .../listview/listview.controller.js | 10 +- 2 files changed, 68 insertions(+), 61 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index e879c3aca0..089637521a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -19,8 +19,8 @@ * contentResource.getById(1234) * .then(function(data) { * $scope.content = data; - * }); - * + * }); + * **/ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { @@ -93,7 +93,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * .then(function() { * $scope.complete = true; * }); - * + * * @param {Object} args arguments object * @param {Int} args.parentId the ID of the parent node * @param {Array} options.sortedIds array of node IDs as they should be sorted @@ -134,9 +134,9 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * .then(function() { * alert("node was moved"); * }, function(err){ - * alert("node didnt move:" + err.data.Message); + * alert("node didnt move:" + err.data.Message); * }); - * + * * @param {Object} args arguments object * @param {Int} args.idd the ID of the node to move * @param {Int} args.parentId the ID of the parent node to move to @@ -177,9 +177,9 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * .then(function() { * alert("node was copied"); * }, function(err){ - * alert("node wasnt copy:" + err.data.Message); + * alert("node wasnt copy:" + err.data.Message); * }); - * + * * @param {Object} args arguments object * @param {Int} args.id the ID of the node to copy * @param {Int} args.parentId the ID of the parent node to copy to @@ -218,9 +218,9 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * .then(function() { * alert("node was unpulished"); * }, function(err){ - * alert("node wasnt unpublished:" + err.data.Message); + * alert("node wasnt unpublished:" + err.data.Message); * }); - * + * * @param {Int} id the ID of the node to unpublish * @returns {Promise} resourcePromise object. * @@ -256,8 +256,8 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * .then(function() { * alert('its empty!'); * }); - * - * + * + * * @returns {Promise} resourcePromise object. * */ @@ -284,9 +284,9 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * .then(function() { * alert('its gone!'); * }); - * - * - * @param {Int} id id of content item to delete + * + * + * @param {Int} id id of content item to delete * @returns {Promise} resourcePromise object. * */ @@ -322,13 +322,13 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { *
           * contentResource.getById(1234)
           *    .then(function(content) {
-          *        var myDoc = content; 
+          *        var myDoc = content;
           *        alert('its here!');
           *    });
-          * 
- * + * + * * @param {Int} id id of content item to return - * @param {Int} culture optional culture to retrieve the item in + * @param {Int} culture optional culture to retrieve the item in * @returns {Promise} resourcePromise object containing the content item. * */ @@ -393,12 +393,12 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { *
           * contentResource.getByIds( [1234,2526,28262])
           *    .then(function(contentArray) {
-          *        var myDoc = contentArray; 
+          *        var myDoc = contentArray;
           *        alert('they are here!');
           *    });
-          * 
- * - * @param {Array} ids ids of content items to return as an array + * + * + * @param {Array} ids ids of content items to return as an array * @returns {Promise} resourcePromise object containing the content items array. * */ @@ -433,28 +433,28 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * * @description * Returns a scaffold of an empty content item, given the id of the content item to place it underneath and the content type alias. - * + * * - Parent Id must be provided so umbraco knows where to store the content - * - Content Type alias must be provided so umbraco knows which properties to put on the content scaffold - * + * - Content Type alias must be provided so umbraco knows which properties to put on the content scaffold + * * The scaffold is used to build editors for content that has not yet been populated with data. - * + * * ##usage *
           * contentResource.getScaffold(1234, 'homepage')
           *    .then(function(scaffold) {
           *        var myDoc = scaffold;
-          *        myDoc.name = "My new document"; 
+          *        myDoc.name = "My new document";
           *
           *        contentResource.publish(myDoc, true)
           *            .then(function(content){
           *                alert("Retrieved, updated and published again");
           *            });
           *    });
-          * 
- * + * + * * @param {Int} parentId id of content item to return - * @param {String} alias contenttype alias to base the scaffold on + * @param {String} alias contenttype alias to base the scaffold on * @returns {Promise} resourcePromise object containing the content scaffold. * */ @@ -500,8 +500,8 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * .then(function(url) { * alert('its here!'); * }); - * - * + * + * * @param {Int} id Id of node to return the public url to * @returns {Promise} resourcePromise object containing the url. * @@ -528,11 +528,11 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { *
           * contentResource.getChildren(1234, {pageSize: 10, pageNumber: 2})
           *    .then(function(contentArray) {
-          *        var children = contentArray; 
+          *        var children = contentArray;
           *        alert('they are here!');
           *    });
-          * 
- * + * + * * @param {Int} parentid id of content item to return children of * @param {Object} options optional options object * @param {Int} options.pageSize if paging data, number of nodes per page, default = 0 @@ -540,6 +540,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * @param {String} options.filter if provided, query will only return those with names matching the filter * @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Ascending` * @param {String} options.orderBy property to order items by, default: `SortOrder` + * @param {String} options.cultureName if provided, the results will be for this specific culture/variant * @returns {Promise} resourcePromise object containing an array of content items. * */ @@ -549,10 +550,11 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { includeProperties: [], pageSize: 0, pageNumber: 0, - filter: '', + filter: "", orderDirection: "Ascending", orderBy: "SortOrder", - orderBySystemField: true + orderBySystemField: true, + cultureName: "" }; if (options === undefined) { options = {}; @@ -596,7 +598,8 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { orderBy: options.orderBy, orderDirection: options.orderDirection, orderBySystemField: toBool(options.orderBySystemField), - filter: options.filter + filter: options.filter, + cultureName: options.cultureName })), 'Failed to retrieve children for content item ' + parentId); }, @@ -617,9 +620,9 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * * @description * Saves changes made to a content item to its current version, if the content item is new, the isNew paramater must be passed to force creation - * if the content item needs to have files attached, they must be provided as the files param and passed separately - * - * + * if the content item needs to have files attached, they must be provided as the files param and passed separately + * + * * ##usage *
           * contentResource.getById(1234)
@@ -630,10 +633,10 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
           *                alert("Retrieved, updated and saved again");
           *            });
           *    });
-          * 
- * + * + * * @param {Object} content The content item object with changes applied - * @param {Bool} isNew set to true to create a new item or to update an existing + * @param {Bool} isNew set to true to create a new item or to update an existing * @param {Array} files collection of files for the document * @param {Bool} showNotifications an option to disable/show notifications (default is true) * @returns {Promise} resourcePromise object containing the saved content item. @@ -660,9 +663,9 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * * @description * Saves and publishes changes made to a content item to a new version, if the content item is new, the isNew paramater must be passed to force creation - * if the content item needs to have files attached, they must be provided as the files param and passed separately - * - * + * if the content item needs to have files attached, they must be provided as the files param and passed separately + * + * * ##usage *
           * contentResource.getById(1234)
@@ -673,10 +676,10 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
           *                alert("Retrieved, updated and published again");
           *            });
           *    });
-          * 
- * + * + * * @param {Object} content The content item object with changes applied - * @param {Bool} isNew set to true to create a new item or to update an existing + * @param {Bool} isNew set to true to create a new item or to update an existing * @param {Array} files collection of files for the document * @param {Bool} showNotifications an option to disable/show notifications (default is true) * @returns {Promise} resourcePromise object containing the saved content item. @@ -697,7 +700,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * * @description * Saves changes made to a content item, and notifies any subscribers about a pending publication - * + * * ##usage *
           * contentResource.getById(1234)
@@ -708,11 +711,11 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
           *                alert("Retrieved, updated and notication send off");
           *            });
           *    });
-          * 
- * + * + * * @param {Object} content The content item object with changes applied - * @param {Bool} isNew set to true to create a new item or to update an existing - * @param {Array} files collection of files for the document + * @param {Bool} isNew set to true to create a new item or to update an existing + * @param {Array} files collection of files for the document * @returns {Promise} resourcePromise object containing the saved content item. * */ @@ -730,15 +733,15 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * * @description * Publishes a content item with a given ID - * + * * ##usage *
           * contentResource.publishById(1234)
           *    .then(function(content) {
           *        alert("published");
           *    });
-          * 
- * + * + * * @param {Int} id The ID of the conten to publish * @returns {Promise} resourcePromise object containing the published content item. * diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 47d670e68a..cd835e8d72 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -1,4 +1,4 @@ -function listViewController($scope, $routeParams, $injector, currentUserResource, notificationsService, iconHelper, editorState, localizationService, appState, $timeout, mediaResource, listViewHelper, navigationService, editorService) { +function listViewController($scope, $routeParams, $injector, $location, $timeout, currentUserResource, notificationsService, iconHelper, editorState, localizationService, appState, mediaResource, listViewHelper, navigationService, editorService) { //this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content // that isn't created yet, if we continue this will use the parent id in the route params which isn't what @@ -74,7 +74,7 @@ function listViewController($scope, $routeParams, $injector, currentUserResource "canCopy": _.contains(currentUserPermissions, 'O'), //Magic Char = O "canCreate": _.contains(currentUserPermissions, 'C'), //Magic Char = C "canDelete": _.contains(currentUserPermissions, 'D'), //Magic Char = D - "canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M + "canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M "canPublish": _.contains(currentUserPermissions, 'U'), //Magic Char = U "canUnpublish": _.contains(currentUserPermissions, 'U') //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish) }; @@ -136,6 +136,9 @@ function listViewController($scope, $routeParams, $injector, currentUserResource } + //Get the current culturename from the QueryString - to pass into the WebAPI call + var cultureNameQs = $location.search().mculture; + $scope.options = { displayAtTabNumber: $scope.model.config.displayAtTabNumber ? $scope.model.config.displayAtTabNumber : 1, pageSize: $scope.model.config.pageSize ? $scope.model.config.pageSize : 10, @@ -156,7 +159,8 @@ function listViewController($scope, $routeParams, $injector, currentUserResource allowBulkUnpublish: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkUnpublish, allowBulkCopy: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkCopy, allowBulkMove: $scope.model.config.bulkActionPermissions.allowBulkMove, - allowBulkDelete: $scope.model.config.bulkActionPermissions.allowBulkDelete + allowBulkDelete: $scope.model.config.bulkActionPermissions.allowBulkDelete, + cultureName: cultureNameQs }; // Check if selected order by field is actually custom field From 2fcabdee0ac03497d01924ef3f46661a1aa67647 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Mon, 10 Sep 2018 09:09:24 +0100 Subject: [PATCH 3/4] Use ResolutionContext as opposed to an inline AfterMap function with a CustomResolver class --- src/Umbraco.Web/Editors/ContentController.cs | 4 ++-- .../Models/Mapping/ContentMapperProfile.cs | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index af79e04f2b..d5becce929 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -496,11 +496,11 @@ namespace Umbraco.Web.Editors { if(string.IsNullOrEmpty(cultureName) == false) { - opts.AfterMap((source, target) => target.Name = source.GetCultureName(cultureName)); + opts.Items[ResolutionContextExtensions.CultureKey] = cultureName; } // if there's a list of property aliases to map - we will make sure to store this in the mapping context. - if (String.IsNullOrWhiteSpace(includeProperties) == false) + if (string.IsNullOrWhiteSpace(includeProperties) == false) { opts.Items["IncludeProperties"] = includeProperties.Split(new[] { ", ", "," }, StringSplitOptions.RemoveEmptyEntries); } diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index 3c8a33c625..7d3f3cb63e 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -83,11 +83,20 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed)) .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) .ForMember(dest => dest.Alias, opt => opt.Ignore()) - .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); + .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) + .ForMember(dest => dest.Name, opt => opt.ResolveUsing()); //FROM IContent TO ContentPropertyCollectionDto //NOTE: the property mapping for cultures relies on a culture being set in the mapping context CreateMap(); } } + + internal class CultureNameResolver : IValueResolver, string> + { + public string Resolve(IContent source, ContentItemBasic destination, string destMember, ResolutionContext context) + { + return source.GetCultureName(context.GetCulture()); + } + } } From 94a6639bc70ad3a3a00fc50e91a9f270030331a7 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Mon, 10 Sep 2018 09:29:50 +0100 Subject: [PATCH 4/4] Remove unecessary string null check - the culture ext in mapping code will deal with this --- src/Umbraco.Web/Editors/ContentController.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index d5becce929..b08d8826eb 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -494,10 +494,7 @@ namespace Umbraco.Web.Editors Mapper.Map>(content, opts => { - if(string.IsNullOrEmpty(cultureName) == false) - { - opts.Items[ResolutionContextExtensions.CultureKey] = cultureName; - } + opts.Items[ResolutionContextExtensions.CultureKey] = cultureName; // if there's a list of property aliases to map - we will make sure to store this in the mapping context. if (string.IsNullOrWhiteSpace(includeProperties) == false)