diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js index 7d6d5dc131..4902d943a7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js @@ -179,6 +179,15 @@ variant.variants[i].active = false; } } + + // keep track of the open variants across the different split views + // push the first variant then update the variant index based on the editor index + if(vm.openVariants && vm.openVariants.length === 0) { + vm.openVariants.push(variant.language.culture); + } else { + vm.openVariants[editorIndex] = variant.language.culture; + } + } //then assign the variant to a view model to the content app @@ -186,14 +195,6 @@ return a.alias === "content"; }); contentApp.viewModel = variant; - - // keep track of the open variants across the different split views - // push the first variant then update the variant index based on the editor index - if(vm.openVariants && vm.openVariants.length === 0) { - vm.openVariants.push(variant.language.culture); - } else { - vm.openVariants[editorIndex] = variant.language.culture; - } // make sure the same app it set to active in the new variant if(activeAppAlias) { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index 3ad2864fa8..ffcf9f2eef 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -204,6 +204,29 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q }); }, + /** + * @ngdoc method + * @name umbraco.services.navigationService#retainQueryStrings + * @methodOf umbraco.services.navigationService + * + * @description + * Will check the next route parameters to see if any of the query strings that should be retained from the previous route are missing, + * if they are they will be merged and an object containing all route parameters is returned. If nothing should be changed, then null is returned. + * @param {Object} currRouteParams The current route parameters + * @param {Object} nextRouteParams The next route parameters + */ + retainQueryStrings: function (currRouteParams, nextRouteParams) { + var toRetain = angular.copy(nextRouteParams); + var updated = false; + _.each(retainedQueryStrings, function (r) { + if (currRouteParams[r] && !nextRouteParams[r]) { + toRetain[r] = currRouteParams[r]; + updated = true; + } + }); + return updated ? toRetain : null; + }, + /** * @ngdoc method * @name umbraco.services.navigationService#load diff --git a/src/Umbraco.Web.UI.Client/src/init.js b/src/Umbraco.Web.UI.Client/src/init.js index 50e5c21203..3f44638096 100644 --- a/src/Umbraco.Web.UI.Client/src/init.js +++ b/src/Umbraco.Web.UI.Client/src/init.js @@ -122,12 +122,25 @@ app.run(['userService', '$q', '$log', '$rootScope', '$route', '$location', 'urlH $route.reload(); } else { - //check if the location being changed is only the mculture query string, if so, cancel the routing since this is just - //used as a global persistent query string that does not change routes. - + + //check if the location being changed is only due to global/state query strings which means the location change + //isn't actually going to cause a route change. if (navigationService.isRouteChangingNavigation(currentRouteParams, next.params)) { - //continue the route - $route.reload(); + //The location change will cause a route change. We need to ensure that the global/state + //query strings have not been stripped out. If they have, we'll re-add them and re-route. + + var toRetain = navigationService.retainQueryStrings(currentRouteParams, next.params); + if (toRetain) { + $route.updateParams(toRetain); + } + else { + //continue the route + $route.reload(); + } + } + else { + //navigation is not changing but we should update the currentRouteParams to include all current parameters + currentRouteParams = angular.copy(next.params); } } }); diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less index 15a319b520..b0c90483f7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less @@ -36,7 +36,7 @@ } .umb-content-grid__icon.-light { - color: @gray-8; + color: @gray-5; } @@ -58,7 +58,7 @@ } .umb-content-grid__item-name.-light { - color: @gray-8; + color: @gray-5; } .umb-content-grid__details-list { @@ -69,7 +69,7 @@ } .umb-content-grid__details-list.-light { - color: @gray-8; + color: @gray-5; } .umb-content-grid__details-label { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html index 33be361206..760597fcab 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html @@ -15,12 +15,16 @@ {{ item.name }} - + @@ -32,4 +36,4 @@ There are no items to show - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html index 5245384aac..91c0af5005 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html @@ -15,6 +15,9 @@ +
+ Status +
+
+ + +
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 5f38f7daae..e1a1299c55 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,167 +1,175 @@ -function listViewController($scope, $routeParams, $injector, $location, $timeout, currentUserResource, notificationsService, iconHelper, editorState, localizationService, appState, mediaResource, listViewHelper, navigationService, editorService) { +function listViewController($scope, $routeParams, $injector, $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 - // we want. NOTE: This is just a safety check since when we scaffold an empty model on the server we remove - // the list view tab entirely when it's new. - if ($routeParams.create) { - $scope.isNew = true; - return; - } + //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 + // we want. NOTE: This is just a safety check since when we scaffold an empty model on the server we remove + // the list view tab entirely when it's new. + if ($routeParams.create) { + $scope.isNew = true; + return; + } - //Now we need to check if this is for media, members or content because that will depend on the resources we use - var contentResource, getContentTypesCallback, getListResultsCallback, deleteItemCallback, getIdCallback, createEditUrlCallback; + //Now we need to check if this is for media, members or content because that will depend on the resources we use + var contentResource, getContentTypesCallback, getListResultsCallback, deleteItemCallback, getIdCallback, createEditUrlCallback; - //check the config for the entity type, or the current section name (since the config is only set in c#, not in pre-vals) - if (($scope.model.config.entityType && $scope.model.config.entityType === "member") || (appState.getSectionState("currentSection") === "member")) { - $scope.entityType = "member"; - contentResource = $injector.get('memberResource'); - getContentTypesCallback = $injector.get('memberTypeResource').getTypes; - getListResultsCallback = contentResource.getPagedResults; - deleteItemCallback = contentResource.deleteByKey; - getIdCallback = function (selected) { - return selected.key; - }; - createEditUrlCallback = function (item) { - return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.key + "?page=" + $scope.options.pageNumber + "&listName=" + $scope.contentId; - }; - } - else { - //check the config for the entity type, or the current section name (since the config is only set in c#, not in pre-vals) - if (($scope.model.config.entityType && $scope.model.config.entityType === "media") || (appState.getSectionState("currentSection") === "media")) { - $scope.entityType = "media"; - contentResource = $injector.get('mediaResource'); - getContentTypesCallback = $injector.get('mediaTypeResource').getAllowedTypes; - } - else { - $scope.entityType = "content"; - contentResource = $injector.get('contentResource'); - getContentTypesCallback = $injector.get('contentTypeResource').getAllowedTypes; - } - getListResultsCallback = contentResource.getChildren; - deleteItemCallback = contentResource.deleteById; - getIdCallback = function (selected) { - return selected.id; - }; - createEditUrlCallback = function (item) { - return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.id + "?page=" + $scope.options.pageNumber; - }; - } + //check the config for the entity type, or the current section name (since the config is only set in c#, not in pre-vals) + if (($scope.model.config.entityType && $scope.model.config.entityType === "member") || (appState.getSectionState("currentSection") === "member")) { + $scope.entityType = "member"; + contentResource = $injector.get('memberResource'); + getContentTypesCallback = $injector.get('memberTypeResource').getTypes; + getListResultsCallback = contentResource.getPagedResults; + deleteItemCallback = contentResource.deleteByKey; + getIdCallback = function (selected) { + return selected.key; + }; + createEditUrlCallback = function (item) { + return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.key + "?page=" + $scope.options.pageNumber + "&listName=" + $scope.contentId; + }; + } + else { + //check the config for the entity type, or the current section name (since the config is only set in c#, not in pre-vals) + if (($scope.model.config.entityType && $scope.model.config.entityType === "media") || (appState.getSectionState("currentSection") === "media")) { + $scope.entityType = "media"; + contentResource = $injector.get('mediaResource'); + getContentTypesCallback = $injector.get('mediaTypeResource').getAllowedTypes; + } + else { + $scope.entityType = "content"; + contentResource = $injector.get('contentResource'); + getContentTypesCallback = $injector.get('contentTypeResource').getAllowedTypes; + } + getListResultsCallback = contentResource.getChildren; + deleteItemCallback = contentResource.deleteById; + getIdCallback = function (selected) { + return selected.id; + }; + createEditUrlCallback = function (item) { + return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.id + "?page=" + $scope.options.pageNumber; + }; + } - $scope.pagination = []; - $scope.isNew = false; - $scope.actionInProgress = false; - $scope.selection = []; - $scope.folders = []; - $scope.page = {}; - $scope.listViewResultSet = { - totalPages: 0, - items: [] - }; + $scope.pagination = []; + $scope.isNew = false; + $scope.actionInProgress = false; + $scope.selection = []; + $scope.folders = []; + $scope.page = {}; + $scope.listViewResultSet = { + totalPages: 0, + items: [] + }; - //when this is null, we don't check permissions - $scope.currentNodePermissions = null; + //when this is null, we don't check permissions + $scope.currentNodePermissions = null; - if ($scope.entityType === "content") { - //Just ensure we do have an editorState - if (editorState.current) { - //Fetch current node allowed actions for the current user - //This is the current node & not each individual child node in the list - var currentUserPermissions = editorState.current.allowedActions; + if ($scope.entityType === "content") { + //Just ensure we do have an editorState + if (editorState.current) { + //Fetch current node allowed actions for the current user + //This is the current node & not each individual child node in the list + var currentUserPermissions = editorState.current.allowedActions; - //Create a nicer model rather than the funky & hard to remember permissions strings - $scope.currentNodePermissions = { - "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 - "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) - }; - } - } + //Create a nicer model rather than the funky & hard to remember permissions strings + $scope.currentNodePermissions = { + "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 + "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) + }; + } + } - //when this is null, we don't check permissions - $scope.buttonPermissions = null; + //when this is null, we don't check permissions + $scope.buttonPermissions = null; - //When we are dealing with 'content', we need to deal with permissions on child nodes. - // Currently there is no real good way to - if ($scope.entityType === "content") { + //When we are dealing with 'content', we need to deal with permissions on child nodes. + // Currently there is no real good way to + if ($scope.entityType === "content") { - var idsWithPermissions = null; + var idsWithPermissions = null; - $scope.buttonPermissions = { - canCopy: true, - canCreate: true, - canDelete: true, - canMove: true, - canPublish: true, - canUnpublish: true - }; + $scope.buttonPermissions = { + canCopy: true, + canCreate: true, + canDelete: true, + canMove: true, + canPublish: true, + canUnpublish: true + }; - $scope.$watch("selection.length", function (newVal, oldVal) { + $scope.$watch("selection.length", function (newVal, oldVal) { - if ((idsWithPermissions == null && newVal > 0) || (idsWithPermissions != null)) { + if ((idsWithPermissions == null && newVal > 0) || (idsWithPermissions != null)) { - //get all of the selected ids - var ids = _.map($scope.selection, function (i) { - return i.id.toString(); - }); + //get all of the selected ids + var ids = _.map($scope.selection, function (i) { + return i.id.toString(); + }); - //remove the dictionary items that don't have matching ids - var filtered = {}; - _.each(idsWithPermissions, function (value, key, list) { - if (_.contains(ids, key)) { - filtered[key] = value; - } - }); - idsWithPermissions = filtered; + //remove the dictionary items that don't have matching ids + var filtered = {}; + _.each(idsWithPermissions, function (value, key, list) { + if (_.contains(ids, key)) { + filtered[key] = value; + } + }); + idsWithPermissions = filtered; - //find all ids that we haven't looked up permissions for - var existingIds = _.keys(idsWithPermissions); - var missingLookup = _.map(_.difference(ids, existingIds), function (i) { - return Number(i); - }); + //find all ids that we haven't looked up permissions for + var existingIds = _.keys(idsWithPermissions); + var missingLookup = _.map(_.difference(ids, existingIds), function (i) { + return Number(i); + }); - if (missingLookup.length > 0) { - currentUserResource.getPermissions(missingLookup).then(function (p) { - $scope.buttonPermissions = listViewHelper.getButtonPermissions(p, idsWithPermissions); - }); + if (missingLookup.length > 0) { + currentUserResource.getPermissions(missingLookup).then(function (p) { + $scope.buttonPermissions = listViewHelper.getButtonPermissions(p, idsWithPermissions); + }); + } + else { + $scope.buttonPermissions = listViewHelper.getButtonPermissions({}, idsWithPermissions); + } } - else { - $scope.buttonPermissions = listViewHelper.getButtonPermissions({}, idsWithPermissions); - } - } - }); + }); - } + } - //Get the current culturename from the QueryString - to pass into the WebAPI call - var cultureNameQs = $location.search().cculture ? $location.search().cculture : $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, + pageNumber: ($routeParams.page && Number($routeParams.page) != NaN && Number($routeParams.page) > 0) ? $routeParams.page : 1, + filter: '', + orderBy: ($scope.model.config.orderBy ? $scope.model.config.orderBy : 'VersionDate').trim(), + orderDirection: $scope.model.config.orderDirection ? $scope.model.config.orderDirection.trim() : "desc", + orderBySystemField: true, + includeProperties: $scope.model.config.includeProperties ? $scope.model.config.includeProperties : [ + { alias: 'updateDate', header: 'Last edited', isSystem: 1 }, + { alias: 'updater', header: 'Last edited by', isSystem: 1 } + ], + layout: { + layouts: $scope.model.config.layouts, + activeLayout: listViewHelper.getLayout($routeParams.id, $scope.model.config.layouts) + }, + allowBulkPublish: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkPublish, + 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, + cultureName: $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture + }; - $scope.options = { - displayAtTabNumber: $scope.model.config.displayAtTabNumber ? $scope.model.config.displayAtTabNumber : 1, - pageSize: $scope.model.config.pageSize ? $scope.model.config.pageSize : 10, - pageNumber: ($routeParams.page && Number($routeParams.page) != NaN && Number($routeParams.page) > 0) ? $routeParams.page : 1, - filter: '', - orderBy: ($scope.model.config.orderBy ? $scope.model.config.orderBy : 'VersionDate').trim(), - orderDirection: $scope.model.config.orderDirection ? $scope.model.config.orderDirection.trim() : "desc", - orderBySystemField: true, - includeProperties: $scope.model.config.includeProperties ? $scope.model.config.includeProperties : [ - { alias: 'updateDate', header: 'Last edited', isSystem: 1 }, - { alias: 'updater', header: 'Last edited by', isSystem: 1 } - ], - layout: { - layouts: $scope.model.config.layouts, - activeLayout: listViewHelper.getLayout($routeParams.id, $scope.model.config.layouts) - }, - allowBulkPublish: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkPublish, - 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, - cultureName: cultureNameQs - }; + //watch for culture changes in the query strings and update accordingly + $scope.$watch(function () { + return $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; + }, function (newVal, oldVal) { + if (newVal && newVal !== oldVal) { + //update the options + $scope.options.cultureName = newVal; + $scope.reloadView($scope.contentId); + } + }); // Check if selected order by field is actually custom field for (var j = 0; j < $scope.options.includeProperties.length; j++) { @@ -172,8 +180,8 @@ function listViewController($scope, $routeParams, $injector, $location, $timeout } } - //update all of the system includeProperties to enable sorting - _.each($scope.options.includeProperties, function (e, i) { + //update all of the system includeProperties to enable sorting + _.each($scope.options.includeProperties, function (e, i) { //NOTE: special case for contentTypeAlias, it's a system property that cannot be sorted // to do that, we'd need to update the base query for content to include the content type alias column @@ -194,11 +202,11 @@ function listViewController($scope, $routeParams, $injector, $location, $timeout e.header = v; }); } - }); + }); - $scope.selectLayout = function (selectedLayout) { - $scope.options.layout.activeLayout = listViewHelper.setLayout($routeParams.id, selectedLayout, $scope.model.config.layouts); - }; + $scope.selectLayout = function (selectedLayout) { + $scope.options.layout.activeLayout = listViewHelper.setLayout($routeParams.id, selectedLayout, $scope.model.config.layouts); + }; function showNotificationsAndReset(err, reload, successMsg) { @@ -213,10 +221,10 @@ function listViewController($scope, $routeParams, $injector, $location, $timeout }; } - $timeout(function() { - $scope.bulkStatus = ""; - $scope.actionInProgress = false; - }, + $timeout(function () { + $scope.bulkStatus = ""; + $scope.actionInProgress = false; + }, 500); if (reload === true) { @@ -225,177 +233,177 @@ function listViewController($scope, $routeParams, $injector, $location, $timeout if (successMsg) { localizationService.localize("bulk_done") - .then(function(v) { + .then(function (v) { notificationsService.success(v, successMsg); }); } } $scope.next = function (pageNumber) { - $scope.options.pageNumber = pageNumber; - $scope.reloadView($scope.contentId); - }; + $scope.options.pageNumber = pageNumber; + $scope.reloadView($scope.contentId); + }; - $scope.goToPage = function (pageNumber) { - $scope.options.pageNumber = pageNumber; - $scope.reloadView($scope.contentId); - }; + $scope.goToPage = function (pageNumber) { + $scope.options.pageNumber = pageNumber; + $scope.reloadView($scope.contentId); + }; - $scope.prev = function (pageNumber) { - $scope.options.pageNumber = pageNumber; - $scope.reloadView($scope.contentId); - }; + $scope.prev = function (pageNumber) { + $scope.options.pageNumber = pageNumber; + $scope.reloadView($scope.contentId); + }; - /*Loads the search results, based on parameters set in prev,next,sort and so on*/ - /*Pagination is done by an array of objects, due angularJS's funky way of monitoring state - with simple values */ + /*Loads the search results, based on parameters set in prev,next,sort and so on*/ + /*Pagination is done by an array of objects, due angularJS's funky way of monitoring state + with simple values */ - $scope.getContent = function() { - $scope.reloadView($scope.contentId, true); - } + $scope.getContent = function () { + $scope.reloadView($scope.contentId, true); + } - $scope.reloadView = function (id, reloadFolders) { + $scope.reloadView = function (id, reloadFolders) { - $scope.viewLoaded = false; + $scope.viewLoaded = false; - listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); + listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); - getListResultsCallback(id, $scope.options).then(function (data) { + getListResultsCallback(id, $scope.options).then(function (data) { - $scope.actionInProgress = false; - $scope.listViewResultSet = data; + $scope.actionInProgress = false; + $scope.listViewResultSet = data; - //update all values for display - if ($scope.listViewResultSet.items) { - _.each($scope.listViewResultSet.items, function (e, index) { - setPropertyValues(e); - }); - } + //update all values for display + if ($scope.listViewResultSet.items) { + _.each($scope.listViewResultSet.items, function (e, index) { + setPropertyValues(e); + }); + } - if (reloadFolders && $scope.entityType === 'media') { - //The folders aren't loaded - we only need to do this once since we're never changing node ids - mediaResource.getChildFolders($scope.contentId) + if (reloadFolders && $scope.entityType === 'media') { + //The folders aren't loaded - we only need to do this once since we're never changing node ids + mediaResource.getChildFolders($scope.contentId) .then(function (page) { - $scope.folders = page.items; - $scope.viewLoaded = true; + $scope.folders = page.items; + $scope.viewLoaded = true; }); - } else { - $scope.viewLoaded = true; - } + } else { + $scope.viewLoaded = true; + } - //NOTE: This might occur if we are requesting a higher page number than what is actually available, for example - // if you have more than one page and you delete all items on the last page. In this case, we need to reset to the last - // available page and then re-load again - if ($scope.options.pageNumber > $scope.listViewResultSet.totalPages) { - $scope.options.pageNumber = $scope.listViewResultSet.totalPages; + //NOTE: This might occur if we are requesting a higher page number than what is actually available, for example + // if you have more than one page and you delete all items on the last page. In this case, we need to reset to the last + // available page and then re-load again + if ($scope.options.pageNumber > $scope.listViewResultSet.totalPages) { + $scope.options.pageNumber = $scope.listViewResultSet.totalPages; - //reload! - $scope.reloadView(id); - } + //reload! + $scope.reloadView(id); + } - }); - }; + }); + }; - var searchListView = _.debounce(function () { - $scope.$apply(function () { - makeSearch(); - }); - }, 500); - - $scope.forceSearch = function (ev) { - //13: enter - switch (ev.keyCode) { - case 13: + var searchListView = _.debounce(function () { + $scope.$apply(function () { makeSearch(); - break; - } - }; + }); + }, 500); - $scope.enterSearch = function () { - $scope.viewLoaded = false; - searchListView(); - }; + $scope.forceSearch = function (ev) { + //13: enter + switch (ev.keyCode) { + case 13: + makeSearch(); + break; + } + }; - function makeSearch() { - if ($scope.options.filter !== null && $scope.options.filter !== undefined) { - $scope.options.pageNumber = 1; - $scope.reloadView($scope.contentId); - } - } + $scope.enterSearch = function () { + $scope.viewLoaded = false; + searchListView(); + }; - $scope.isAnythingSelected = function () { - if ($scope.selection.length === 0) { - return false; - } else { - return true; - } - }; + function makeSearch() { + if ($scope.options.filter !== null && $scope.options.filter !== undefined) { + $scope.options.pageNumber = 1; + $scope.reloadView($scope.contentId); + } + } - $scope.selectedItemsCount = function () { - return $scope.selection.length; - }; + $scope.isAnythingSelected = function () { + if ($scope.selection.length === 0) { + return false; + } else { + return true; + } + }; - $scope.clearSelection = function () { - listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); - }; + $scope.selectedItemsCount = function () { + return $scope.selection.length; + }; - $scope.getIcon = function (entry) { - return iconHelper.convertFromLegacyIcon(entry.icon); - }; + $scope.clearSelection = function () { + listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); + }; - function serial(selected, fn, getStatusMsg, index) { - return fn(selected, index).then(function (content) { - index++; - $scope.bulkStatus = getStatusMsg(index, selected.length); - return index < selected.length ? serial(selected, fn, getStatusMsg, index) : content; - }, function (err) { - var reload = index > 0; - showNotificationsAndReset(err, reload); - return err; - }); - } + $scope.getIcon = function (entry) { + return iconHelper.convertFromLegacyIcon(entry.icon); + }; - function applySelected(fn, getStatusMsg, getSuccessMsg, confirmMsg) { - var selected = $scope.selection; - if (selected.length === 0) - return; - if (confirmMsg && !confirm(confirmMsg)) - return; + function serial(selected, fn, getStatusMsg, index) { + return fn(selected, index).then(function (content) { + index++; + $scope.bulkStatus = getStatusMsg(index, selected.length); + return index < selected.length ? serial(selected, fn, getStatusMsg, index) : content; + }, function (err) { + var reload = index > 0; + showNotificationsAndReset(err, reload); + return err; + }); + } - $scope.actionInProgress = true; - $scope.bulkStatus = getStatusMsg(0, selected.length); + function applySelected(fn, getStatusMsg, getSuccessMsg, confirmMsg) { + var selected = $scope.selection; + if (selected.length === 0) + return; + if (confirmMsg && !confirm(confirmMsg)) + return; - return serial(selected, fn, getStatusMsg, 0).then(function (result) { - // executes once the whole selection has been processed - // in case of an error (caught by serial), result will be the error - if (!(result.data && angular.isArray(result.data.notifications))) - showNotificationsAndReset(result, true, getSuccessMsg(selected.length)); - }); - } + $scope.actionInProgress = true; + $scope.bulkStatus = getStatusMsg(0, selected.length); - $scope.delete = function() { + return serial(selected, fn, getStatusMsg, 0).then(function (result) { + // executes once the whole selection has been processed + // in case of an error (caught by serial), result will be the error + if (!(result.data && angular.isArray(result.data.notifications))) + showNotificationsAndReset(result, true, getSuccessMsg(selected.length)); + }); + } + + $scope.delete = function () { var confirmDeleteText = ""; localizationService.localize("defaultdialogs_confirmdelete") - .then(function(value) { + .then(function (value) { confirmDeleteText = value; var attempt = applySelected( - function(selected, index) { return deleteItemCallback(getIdCallback(selected[index])); }, - function(count, total) { + function (selected, index) { return deleteItemCallback(getIdCallback(selected[index])); }, + function (count, total) { var key = (total === 1 ? "bulk_deletedItemOfItem" : "bulk_deletedItemOfItems"); return localizationService.localize(key, [count, total]); }, - function(total) { + function (total) { var key = (total === 1 ? "bulk_deletedItem" : "bulk_deletedItems"); return localizationService.localize(key, [total]); }, confirmDeleteText + "?"); if (attempt) { - attempt.then(function() { + attempt.then(function () { //executes if all is successful, let's sync the tree var activeNode = appState.getTreeState("selectedNode"); if (activeNode) { @@ -406,43 +414,43 @@ function listViewController($scope, $routeParams, $injector, $location, $timeout }); }; - $scope.publish = function () { + $scope.publish = function () { applySelected( - function (selected, index) { return contentResource.publishById(getIdCallback(selected[index])); }, - function (count, total) { - var key = (total === 1 ? "bulk_publishedItemOfItem" : "bulk_publishedItemOfItems"); - return localizationService.localize(key, [count, total]); - }, - function (total) { - var key = (total === 1 ? "bulk_publishedItem" : "bulk_publishedItems"); - return localizationService.localize(key, [total]); - }); - }; + function (selected, index) { return contentResource.publishById(getIdCallback(selected[index])); }, + function (count, total) { + var key = (total === 1 ? "bulk_publishedItemOfItem" : "bulk_publishedItemOfItems"); + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = (total === 1 ? "bulk_publishedItem" : "bulk_publishedItems"); + return localizationService.localize(key, [total]); + }); + }; - $scope.unpublish = function() { + $scope.unpublish = function () { applySelected( - function(selected, index) { return contentResource.unPublish(getIdCallback(selected[index])); }, - function(count, total) { + function (selected, index) { return contentResource.unPublish(getIdCallback(selected[index])); }, + function (count, total) { var key = (total === 1 ? "bulk_unpublishedItemOfItem" : "bulk_unpublishedItemOfItems"); return localizationService.localize(key, [count, total]); }, - function(total) { + function (total) { var key = (total === 1 ? "bulk_unpublishedItem" : "bulk_unpublishedItems"); return localizationService.localize(key, [total]); }); }; - $scope.move = function() { + $scope.move = function () { var move = { section: $scope.entityType, currentNode: $scope.contentId, - submit: function(model) { + submit: function (model) { if (model.target) { performMove(model.target); } editorService.close(); }, - close: function() { + close: function () { editorService.close(); } } @@ -450,205 +458,205 @@ function listViewController($scope, $routeParams, $injector, $location, $timeout }; - function performMove(target) { + function performMove(target) { - //NOTE: With the way this applySelected/serial works, I'm not sure there's a better way currently to return - // a specific value from one of the methods, so we'll have to try this way. Even though the first method - // will fire once per every node moved, the destination path will be the same and we need to use that to sync. - var newPath = null; - applySelected( - function(selected, index) { - return contentResource.move({ parentId: target.id, id: getIdCallback(selected[index]) }) - .then(function(path) { - newPath = path; - return path; - }); - }, - function(count, total) { - var key = (total === 1 ? "bulk_movedItemOfItem" : "bulk_movedItemOfItems"); - return localizationService.localize(key, [count, total]); - }, - function(total) { - var key = (total === 1 ? "bulk_movedItem" : "bulk_movedItems"); - return localizationService.localize(key, [total]); - }) - .then(function() { - //executes if all is successful, let's sync the tree - if (newPath) { + //NOTE: With the way this applySelected/serial works, I'm not sure there's a better way currently to return + // a specific value from one of the methods, so we'll have to try this way. Even though the first method + // will fire once per every node moved, the destination path will be the same and we need to use that to sync. + var newPath = null; + applySelected( + function (selected, index) { + return contentResource.move({ parentId: target.id, id: getIdCallback(selected[index]) }) + .then(function (path) { + newPath = path; + return path; + }); + }, + function (count, total) { + var key = (total === 1 ? "bulk_movedItemOfItem" : "bulk_movedItemOfItems"); + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = (total === 1 ? "bulk_movedItem" : "bulk_movedItems"); + return localizationService.localize(key, [total]); + }) + .then(function () { + //executes if all is successful, let's sync the tree + if (newPath) { - //we need to do a double sync here: first refresh the node where the content was moved, - // then refresh the node where the content was moved from - navigationService.syncTree({ - tree: target.nodeType ? target.nodeType : (target.metaData.treeAlias), - path: newPath, - forceReload: true, - activate: false - }) - .then(function(args) { - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - if (activeNode) { - navigationService.reloadNode(activeNode); - } - }); - } - }); - } + //we need to do a double sync here: first refresh the node where the content was moved, + // then refresh the node where the content was moved from + navigationService.syncTree({ + tree: target.nodeType ? target.nodeType : (target.metaData.treeAlias), + path: newPath, + forceReload: true, + activate: false + }) + .then(function (args) { + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + if (activeNode) { + navigationService.reloadNode(activeNode); + } + }); + } + }); + } $scope.copy = function () { var copyEditor = { section: $scope.entityType, currentNode: $scope.contentId, - submit: function(model) { + submit: function (model) { if (model.target) { performCopy(model.target, model.relateToOriginal); } editorService.close(); }, - close: function() { + close: function () { editorService.close(); } }; editorService.copy(copyEditor); }; - function performCopy(target, relateToOriginal) { - applySelected( - function (selected, index) { return contentResource.copy({ parentId: target.id, id: getIdCallback(selected[index]), relateToOriginal: relateToOriginal }); }, - function (count, total) { - var key = (total === 1 ? "bulk_copiedItemOfItem" : "bulk_copiedItemOfItems"); - return localizationService.localize(key, [count, total]); - }, - function (total) { - var key = (total === 1 ? "bulk_copiedItem" : "bulk_copiedItems"); - return localizationService.localize(key, [total]); - }); - } + function performCopy(target, relateToOriginal) { + applySelected( + function (selected, index) { return contentResource.copy({ parentId: target.id, id: getIdCallback(selected[index]), relateToOriginal: relateToOriginal }); }, + function (count, total) { + var key = (total === 1 ? "bulk_copiedItemOfItem" : "bulk_copiedItemOfItems"); + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = (total === 1 ? "bulk_copiedItem" : "bulk_copiedItems"); + return localizationService.localize(key, [total]); + }); + } - function getCustomPropertyValue(alias, properties) { - var value = ''; - var index = 0; - var foundAlias = false; - for (var i = 0; i < properties.length; i++) { - if (properties[i].alias == alias) { - foundAlias = true; - break; - } - index++; - } + function getCustomPropertyValue(alias, properties) { + var value = ''; + var index = 0; + var foundAlias = false; + for (var i = 0; i < properties.length; i++) { + if (properties[i].alias == alias) { + foundAlias = true; + break; + } + index++; + } - if (foundAlias) { - value = properties[index].value; - } + if (foundAlias) { + value = properties[index].value; + } - return value; - } + return value; + } - /** This ensures that the correct value is set for each item in a row, we don't want to call a function during interpolation or ng-bind as performance is really bad that way */ - function setPropertyValues(result) { + /** This ensures that the correct value is set for each item in a row, we don't want to call a function during interpolation or ng-bind as performance is really bad that way */ + function setPropertyValues(result) { - //set the edit url - result.editPath = createEditUrlCallback(result); + //set the edit url + result.editPath = createEditUrlCallback(result); - _.each($scope.options.includeProperties, function (e, i) { + _.each($scope.options.includeProperties, function (e, i) { - var alias = e.alias; + var alias = e.alias; - // First try to pull the value directly from the alias (e.g. updatedBy) - var value = result[alias]; + // First try to pull the value directly from the alias (e.g. updatedBy) + var value = result[alias]; - // If this returns an object, look for the name property of that (e.g. owner.name) - if (value === Object(value)) { - value = value['name']; - } + // If this returns an object, look for the name property of that (e.g. owner.name) + if (value === Object(value)) { + value = value['name']; + } - // If we've got nothing yet, look at a user defined property - if (typeof value === 'undefined') { - value = getCustomPropertyValue(alias, result.properties); - } + // If we've got nothing yet, look at a user defined property + if (typeof value === 'undefined') { + value = getCustomPropertyValue(alias, result.properties); + } - // If we have a date, format it - if (isDate(value)) { - value = value.substring(0, value.length - 3); - } + // If we have a date, format it + if (isDate(value)) { + value = value.substring(0, value.length - 3); + } - // set what we've got on the result - result[alias] = value; - }); + // set what we've got on the result + result[alias] = value; + }); - } + } - function isDate(val) { - if (angular.isString(val)) { - return val.match(/^(\d{4})\-(\d{2})\-(\d{2})\ (\d{2})\:(\d{2})\:(\d{2})$/); - } - return false; - } + function isDate(val) { + if (angular.isString(val)) { + return val.match(/^(\d{4})\-(\d{2})\-(\d{2})\ (\d{2})\:(\d{2})\:(\d{2})$/); + } + return false; + } - function initView() { - //default to root id if the id is undefined - var id = $routeParams.id; - if (id === undefined) { - id = -1; - } + function initView() { + //default to root id if the id is undefined + var id = $routeParams.id; + if (id === undefined) { + id = -1; + } - getContentTypesCallback(id).then(function (items) { - $scope.listViewAllowedTypes = items; - }); + getContentTypesCallback(id).then(function (items) { + $scope.listViewAllowedTypes = items; + }); - $scope.contentId = id; - $scope.isTrashed = id === "-20" || id === "-21"; + $scope.contentId = id; + $scope.isTrashed = id === "-20" || id === "-21"; - $scope.options.allowBulkPublish = $scope.options.allowBulkPublish && !$scope.isTrashed; - $scope.options.allowBulkUnpublish = $scope.options.allowBulkUnpublish && !$scope.isTrashed; + $scope.options.allowBulkPublish = $scope.options.allowBulkPublish && !$scope.isTrashed; + $scope.options.allowBulkUnpublish = $scope.options.allowBulkUnpublish && !$scope.isTrashed; - $scope.options.bulkActionsAllowed = $scope.options.allowBulkPublish || - $scope.options.allowBulkUnpublish || - $scope.options.allowBulkCopy || - $scope.options.allowBulkMove || - $scope.options.allowBulkDelete; + $scope.options.bulkActionsAllowed = $scope.options.allowBulkPublish || + $scope.options.allowBulkUnpublish || + $scope.options.allowBulkCopy || + $scope.options.allowBulkMove || + $scope.options.allowBulkDelete; - $scope.reloadView($scope.contentId, true); - } + $scope.reloadView($scope.contentId, true); + } - function getLocalizedKey(alias) { + function getLocalizedKey(alias) { - switch (alias) { - case "sortOrder": - return "general_sort"; - case "updateDate": - return "content_updateDate"; - case "updater": - return "content_updatedBy"; - case "createDate": - return "content_createDate"; - case "owner": - return "content_createBy"; - case "published": - return "content_isPublished"; - case "contentTypeAlias": - //TODO: Check for members - return $scope.entityType === "content" ? "content_documentType" : "content_mediatype"; - case "email": - return "general_email"; - case "username": - return "general_username"; - } - return alias; - } + switch (alias) { + case "sortOrder": + return "general_sort"; + case "updateDate": + return "content_updateDate"; + case "updater": + return "content_updatedBy"; + case "createDate": + return "content_createDate"; + case "owner": + return "content_createBy"; + case "published": + return "content_isPublished"; + case "contentTypeAlias": + //TODO: Check for members + return $scope.entityType === "content" ? "content_documentType" : "content_mediatype"; + case "email": + return "general_email"; + case "username": + return "general_username"; + } + return alias; + } - function getItemKey(itemId) { - for (var i = 0; i < $scope.listViewResultSet.items.length; i++) { - var item = $scope.listViewResultSet.items[i]; - if (item.id === itemId) { - return item.key; - } - } - } + function getItemKey(itemId) { + for (var i = 0; i < $scope.listViewResultSet.items.length; i++) { + var item = $scope.listViewResultSet.items[i]; + if (item.id === itemId) { + return item.key; + } + } + } - //GO! - initView(); + //GO! + initView(); } diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs index 427dadb2c9..16eb3b9e87 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs @@ -4,9 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; using Newtonsoft.Json; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Validation; -using Umbraco.Web.WebApi; +using Newtonsoft.Json.Converters; namespace Umbraco.Web.Models.ContentEditing { @@ -22,8 +20,11 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "createDate")] public DateTime CreateDate { get; set; } + /// + /// Boolean indicating if this item is published or not based on it's + /// [DataMember(Name = "published")] - public bool Published { get; set; } + public bool Published => State == ContentSavedState.Published || State == ContentSavedState.PublishedPendingChanges; /// /// Determines if the content item is a draft @@ -44,6 +45,16 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "sortOrder")] public int SortOrder { get; set; } + /// + /// The saved/published state of an item + /// + /// + /// This is nullable since it's only relevant for content (non-content like media + members will be null) + /// + [DataMember(Name = "state")] + [JsonConverter(typeof(StringEnumConverter))] + public ContentSavedState? State { get; set; } + protected bool Equals(ContentItemBasic other) { return Id == other.Id; diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs b/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs index f433af58d6..2ceb682a13 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs @@ -8,21 +8,21 @@ /// /// The item isn't created yet /// - NotCreated, + NotCreated = 1, /// /// The item is saved but isn't published /// - Draft, + Draft = 2, /// /// The item is published and there are no pending changes /// - Published, + Published = 3, /// /// The item is published and there are pending changes /// - PublishedPendingChanges + PublishedPendingChanges = 4 } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index 1334cb1572..1da5bbbb44 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -32,7 +32,6 @@ namespace Umbraco.Web.Models.Mapping var contentTypeBasicResolver = new ContentTypeBasicResolver(); var defaultTemplateResolver = new DefaultTemplateResolver(); var variantResolver = new ContentVariantResolver(localizationService); - var contentSavedStateResolver = new ContentSavedStateResolver(); //FROM IContent TO ContentItemDisplay CreateMap() @@ -67,7 +66,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Segment, opt => opt.Ignore()) .ForMember(dest => dest.Language, opt => opt.Ignore()) .ForMember(dest => dest.Notifications, opt => opt.Ignore()) - .ForMember(dest => dest.State, opt => opt.ResolveUsing(contentSavedStateResolver)) + .ForMember(dest => dest.State, opt => opt.ResolveUsing>()) .ForMember(dest => dest.Tabs, opt => opt.ResolveUsing(tabsAndPropertiesResolver)); //FROM IContent TO ContentItemBasic @@ -81,63 +80,76 @@ namespace Umbraco.Web.Models.Mapping .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.UpdateDate, opt => opt.ResolveUsing()) - .ForMember(dest => dest.Published, opt => opt.ResolveUsing()) - .ForMember(dest => dest.Name, opt => opt.ResolveUsing()); + .ForMember(dest => dest.UpdateDate, opt => opt.ResolveUsing()) + .ForMember(dest => dest.Name, opt => opt.ResolveUsing()) + .ForMember(dest => dest.State, 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 CultureUpdateDateResolver : IValueResolver, DateTime> - { - public DateTime Resolve(IContent source, ContentItemBasic destination, DateTime destMember, ResolutionContext context) + /// + /// Resolves the update date for a content item/content variant + /// + private class UpdateDateResolver : IValueResolver, DateTime> { - var culture = context.GetCulture(); - - //a culture needs to be in the context for a variant content item - if (culture == null || source.ContentType.VariesByCulture() == false) - return source.UpdateDate; - - var pubDate = source.GetPublishDate(culture); - return pubDate.HasValue ? pubDate.Value : source.UpdateDate; - } - } - - internal class CulturePublishedResolver : IValueResolver, bool> - { - public bool Resolve(IContent source, ContentItemBasic destination, bool destMember, ResolutionContext context) - { - var culture = context.GetCulture(); - - //a culture needs to be in the context for a variant content item - if (culture == null || source.ContentType.VariesByCulture() == false) - return source.Published; - - return source.IsCulturePublished(culture); - } - } - - internal class CultureNameResolver : IValueResolver, string> - { - public string Resolve(IContent source, ContentItemBasic destination, string destMember, ResolutionContext context) - { - var culture = context.GetCulture(); - - //a culture needs to be in the context for a variant content item - if (culture == null || source.ContentType.VariesByCulture() == false) - return source.Name; - - if (source.CultureNames.TryGetValue(culture, out var name) && !string.IsNullOrWhiteSpace(name)) + public DateTime Resolve(IContent source, ContentItemBasic destination, DateTime destMember, ResolutionContext context) { - return name; - } - else - { - return $"({ source.Name })"; + var culture = context.GetCulture(); + + //a culture needs to be in the context for a variant content item + if (culture == null || source.ContentType.VariesByCulture() == false) + return source.UpdateDate; + + var pubDate = source.GetPublishDate(culture); + return pubDate.HasValue ? pubDate.Value : source.UpdateDate; } } + + /// + /// Resolves the published flag for a content item/content variant + /// + private class PublishedResolver : IValueResolver, bool> + { + public bool Resolve(IContent source, ContentItemBasic destination, bool destMember, ResolutionContext context) + { + var culture = context.GetCulture(); + + //a culture needs to be in the context for a variant content item + if (culture == null || source.ContentType.VariesByCulture() == false) + return source.Published; + + return source.IsCulturePublished(culture); + } + } + + /// + /// Resolves the name for a content item/content variant + /// + private class NameResolver : IValueResolver, string> + { + public string Resolve(IContent source, ContentItemBasic destination, string destMember, ResolutionContext context) + { + var culture = context.GetCulture(); + + //a culture needs to be in the context for a variant content item + if (culture == null || source.ContentType.VariesByCulture() == false) + return source.Name; + + if (source.CultureNames.TryGetValue(culture, out var name) && !string.IsNullOrWhiteSpace(name)) + { + return name; + } + else + { + return $"({ source.Name })"; + } + } + } + } + + + } diff --git a/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs index 4fb4d3370a..b2b4408110 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs @@ -6,9 +6,30 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - internal class ContentSavedStateResolver : IValueResolver + + /// + /// Returns the for an item + /// + /// + internal class ContentBasicSavedStateResolver : IValueResolver, ContentSavedState?> + where T : ContentPropertyBasic { - public ContentSavedState Resolve(IContent source, ContentVariantDisplay destination, ContentSavedState destMember, ResolutionContext context) + private readonly ContentSavedStateResolver _inner = new ContentSavedStateResolver(); + + public ContentSavedState? Resolve(IContent source, IContentProperties destination, ContentSavedState? destMember, ResolutionContext context) + { + return _inner.Resolve(source, destination, default, context); + } + } + + /// + /// Returns the for an item + /// + /// + internal class ContentSavedStateResolver : IValueResolver, ContentSavedState> + where T : ContentPropertyBasic + { + public ContentSavedState Resolve(IContent source, IContentProperties destination, ContentSavedState destMember, ResolutionContext context) { PublishedState publishedState; bool isEdited; diff --git a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs index caa9f29e70..c07d66b6e2 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs @@ -44,7 +44,7 @@ namespace Umbraco.Web.Models.Mapping .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.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.UseValue(null)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) @@ -62,7 +62,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon)) .ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed)) .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.UseValue(null)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) diff --git a/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs index 22d499f3d6..f64d8dc529 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs @@ -73,7 +73,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.MembershipScenario, opt => opt.ResolveUsing(src => membershipScenarioMappingResolver.Resolve(src))) .ForMember(dest => dest.Notifications, opt => opt.Ignore()) .ForMember(dest => dest.Errors, opt => opt.Ignore()) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.UseValue(null)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) @@ -91,7 +91,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email)) .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.Username)) .ForMember(dest => dest.Trashed, opt => opt.Ignore()) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.UseValue(null)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) @@ -115,7 +115,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Path, opt => opt.Ignore()) .ForMember(dest => dest.SortOrder, opt => opt.Ignore()) .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.UseValue(ContentSavedState.Draft)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Trashed, opt => opt.Ignore())