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 cab71842b1..07c1913773 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 @@ -1026,6 +1026,7 @@ getMethod: "&", getScaffoldMethod: "&?", culture: "=?", + segment: "=?", infiniteModel: "=?" } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js index 5dc8903e65..24bcefa6b8 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js @@ -12,7 +12,6 @@ editor: "<", editorIndex: "<", editorCount: "<", - openVariants: "<", onCloseSplitView: "&", onSelectVariant: "&", onOpenSplitView: "&", 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 1808d1bffa..17bf716e39 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 @@ -9,7 +9,8 @@ bindings: { page: "<", content: "<", // TODO: Not sure if this should be = since we are changing the 'active' property of a variant - variantId: "<", + culture: "<", + segment: "<", onSelectApp: "&?", onSelectAppAnchor: "&?", onBack: "&?", @@ -42,7 +43,6 @@ //Used to track the open variants across the split views // The values are the variant ids of the currently open variants. // See variantHelper.getId() for the current format. - vm.openVariants = []; /** Called when the component initializes */ function onInit() { @@ -61,7 +61,10 @@ */ function onChanges(changes) { - if (changes.variantId && !changes.variantId.isFirstChange() && changes.variantId.currentValue !== changes.variantId.previousValue) { + if (changes.culture && !changes.culture.isFirstChange() && changes.culture.currentValue !== changes.culture.previousValue) { + setActiveVariant(); + } + if (changes.segment && !changes.segment.isFirstChange() && changes.segment.currentValue !== changes.segment.previousValue) { setActiveVariant(); } } @@ -81,13 +84,17 @@ } /** - * Set the active variant based on the current culture + segment (query string) + * Set the active variant based on the current culture or segment (query string) */ function setActiveVariant() { // set the active variant var activeVariant = null; _.each(vm.content.variants, function (v) { - if (variantHelper.getId(v) === vm.variantId) { + if ( + (!v.language || v.language.culture === vm.culture) + && + (v.segment === vm.segment) + ) { v.active = true; activeVariant = v; } @@ -107,10 +114,9 @@ if (vm.editors.length > 1) { //now re-sync any other editor content (i.e. if split view is open) for (var s = 1; s < vm.editors.length; s++) { - var editorVariantId = variantHelper.getId(vm.editors[s].content); //get the variant from the scope model var variant = _.find(vm.content.variants, function (v) { - return variantHelper.getId(v) === editorVariantId; + return (!v.language || v.language.culture === vm.editors[s].content.language.culture) && v.segment === vm.editors[s].content.segment; }); vm.editors[s].content = initVariant(variant, s); } @@ -125,19 +131,25 @@ */ function insertVariantEditor(index, variant) { - var variantId = variantHelper.getId(variant); - //check if the variant at the index is the same, if it's null an editor will be added - var currentVariantId = vm.editors.length === 0 || vm.editors.length <= index ? null : vm.editors[index].variantId; + var variantCulture = variant.language ? variant.language.culture : "invariant"; + var variantSegment = variant.segment; + + //check if the culture at the index is the same, if it's null an editor will be added + var currentCulture = vm.editors.length === 0 || vm.editors.length <= index ? null : vm.editors[index].culture; + //check if the segment at the index is the same, if it's null an editor will be added + var currentSegment = vm.editors.length === 0 || vm.editors.length <= index ? null : vm.editors[index].segment; + + if (currentCulture !== variantCulture || currentSegment !== variantSegment) { - if (currentVariantId !== variantId) { //Not the current culture which means we need to modify the array. //NOTE: It is not good enough to just replace the `content` object at a given index in the array // since that would mean that directives are not re-initialized. vm.editors.splice(index, 1, { content: variant, //used for "track-by" ng-repeat - variantId: variantId + culture: variantCulture, + segment: variantSegment }); } else { @@ -147,6 +159,7 @@ } function initVariant(variant, editorIndex) { + //The model that is assigned to the editor contains the current content variant along //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 @@ -158,8 +171,8 @@ variant.apps = angular.copy(vm.content.apps); } - //if this is a variant has a culture/language than we need to assign the language drop down info - if (variant.language) { + //if this is a variant it has a culture/language or segment than we need to assign the variant drop down + if (variant.language || variant.segment !== null) { //if the variant list that defines the header drop down isn't assigned to the variant then assign it now if (!variant.variants) { variant.variants = _.map(vm.content.variants, @@ -178,8 +191,11 @@ //ensure the current culture is set as the active one for (var i = 0; i < variant.variants.length; i++) { - if (variant.variants[i].language.culture === variant.language.culture && - variant.variants[i].segment === variant.segment) { + if ( + (!variant.variants[i].language || variant.variants[i].language.culture === variant.language.culture) + && + variant.variants[i].segment === variant.segment + ) { variant.variants[i].active = true; } else { @@ -187,6 +203,8 @@ } } + /* + //SEGMENTS_TODO: Remove this part if not used. var variantId = variantHelper.getId(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 @@ -195,7 +213,7 @@ } else { vm.openVariants[editorIndex] = variantId; } - + */ } //then assign the variant to a view model to the content app @@ -221,16 +239,19 @@ return variant; } + + function getCultureFromVariant(variant) { + return variant.language ? variant.language.culture : null; + } /** * Adds a new editor to the editors array to show content in a split view * @param {any} selectedVariant */ function openSplitView(selectedVariant) { - var variant = variantHelper.getId(selectedVariant); - + //Find the whole variant model based on the culture that was chosen var variant = _.find(vm.content.variants, function (v) { - return variantHelper.getId(v) === variant; + return getCultureFromVariant(v) === getCultureFromVariant(selectedVariant) && v.segment === selectedVariant.segment; }); insertVariantEditor(vm.editors.length, initVariant(variant, vm.editors.length)); @@ -273,9 +294,12 @@ $timeout(function () { vm.editors.splice(editorIndex, 1); //remove variant from open variants - vm.openVariants.splice(editorIndex, 1); + + // SEGMENTS_TODO: Test this scenario. + //update the current culture to reflect the last open variant (closing the split view corresponds to selecting the other variant) - $location.search("cculture", vm.openVariants[0]); + //$location.search({"cculture": vm.openVariants[0].language, "csegment": vm.openVariants[0]}); + $location.search({"cculture": vm.editors[0].content.language ? vm.editors[0].content.language.culture : null, "csegment": vm.editors[0].content.segment}); splitViewChanged(); }, 400); } @@ -287,10 +311,10 @@ */ function selectVariant(variant, editorIndex) { - var variantId = variantHelper.getId(variant); - - // prevent variants already open in a split view to be opened - if (vm.openVariants.indexOf(variantId) !== -1) { + var variantCulture = variant.language ? variant.language.culture : "invariant"; + var variantSegment = variant.segment || null; + + if (vm.editors.find((editor) => (!editor.content.language || editor.content.language.culture === variantCulture) && editor.content.segment === variantSegment)) { return; } @@ -299,7 +323,7 @@ if (editorIndex === 0) { //If we've made it this far, then update the query string. //The editor will respond to this query string changing. - $location.search("cculture", variantId); + $location.search({"cculture": variantCulture, "csegment": variant.segment}); } else { @@ -311,12 +335,14 @@ } variant.active = true; + //get the variant content model and initialize the editor with that var contentVariant = _.find(vm.content.variants, function (v) { - return variantHelper.getId(v) === variantId; + return (!v.language || v.language.culture === variantCulture) && v.segment === variantSegment; }); editor.content = initVariant(contentVariant, editorIndex); + //update the editors collection insertVariantEditor(editorIndex, contentVariant); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbbreadcrumbs.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbbreadcrumbs.directive.js index 7755d9d63b..f80b3ceb3e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbbreadcrumbs.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbbreadcrumbs.directive.js @@ -73,7 +73,7 @@ Use this directive to generate a list of breadcrumbs. var path = scope.pathTo(ancestor); $location.path(path); - navigationService.clearSearch(["cculture"]); + navigationService.clearSearch(["cculture", "csegment"]); } scope.pathTo = function (ancestor) { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontentheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontentheader.directive.js index 86a5fdbd79..c52e309580 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontentheader.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontentheader.directive.js @@ -3,7 +3,7 @@ function EditorContentHeader(serverValidationManager, localizationService, editorState, variantHelper) { - function link(scope, el, attr, ctrl) { + function link(scope) { var unsubscribe = []; @@ -42,22 +42,24 @@ function checkErrorsOnOtherVariants() { var check = false; angular.forEach(scope.content.variants, function (variant) { - if (scope.openVariants.indexOf(variant.language.culture) === -1 && scope.variantHasError(variant.language.culture)) { + // SEGMENTS_TODO: Check that this correction is okay, can we even see the active var here? + if (variant.active !== true && scope.variantHasError(variant)) { check = true; } }); scope.vm.errorsOnOtherVariants = check; } - function onCultureValidation(valid, errors, allErrors, culture) { - var index = scope.vm.variantsWithError.indexOf(culture); + function onVariantValidation(valid, errors, allErrors, culture, segment) { + // SEGMENTS_TODO: See wether we can use errors, allErrors? + var index = scope.vm.variantsWithError.findIndex((item) => item.culture === culture && item.segment === segment) if(valid === true) { if (index !== -1) { scope.vm.variantsWithError.splice(index, 1); } } else { if (index === -1) { - scope.vm.variantsWithError.push(culture); + scope.vm.variantsWithError.push({"culture": culture, "segment": segment}); } } checkErrorsOnOtherVariants(); @@ -82,10 +84,10 @@ angular.forEach(scope.content.variants, function (variant) { - unsubscribe.push(serverValidationManager.subscribe(null, variant.language !== null ? variant.language.culture : null, variant.segment, null, onCultureValidation)); + unsubscribe.push(serverValidationManager.subscribe(null, variant.language !== null ? variant.language.culture : null, variant.segment, null, onVariantValidation)); }); - unsubscribe.push(serverValidationManager.subscribe(null, null, null, null, onCultureValidation)); + unsubscribe.push(serverValidationManager.subscribe(null, null, null, null, onVariantValidation)); @@ -100,6 +102,10 @@ }); } + function getCultureFromVariant(variant) { + return variant.language ? variant.language.culture : null; + } + scope.getVariantDisplayName = variantHelper.getDisplayName; scope.goBack = function () { @@ -143,25 +149,30 @@ /** * keep track of open variants - this is used to prevent the same variant to be open in more than one split view - * @param {any} culture + * @param {any} variant */ scope.variantIsOpen = function (variant) { - var variantId = variantHelper.getId(variant); - return (scope.openVariants.indexOf(variantId) !== -1); + + // SEGMENTS_TODO: ... does this work? + if (scope.content.variants.find((v) => variant.active === true && (getCultureFromVariant(v) === getCultureFromVariant(variant)) && variant.segment === variant.segment)) { + console.log("VARIANT IS OPEN") + return; + } + console.log("VARIANT IS closed", scope.content.variants) } /** * Check whether a variant has a error, used to display errors in variant switcher. * @param {any} culture */ - scope.variantHasError = function(culture) { + scope.variantHasError = function(variant) { // if we are looking for the default language we also want to check for invariant. - if (culture === scope.vm.defaultVariant.language.culture) { - if(scope.vm.variantsWithError.indexOf("invariant") !== -1) { + if (variant.language.culture === scope.vm.defaultVariant.language.culture && variant.segment === null) { + if(scope.vm.variantsWithError.find((item) => item.culture === "invariant" && item.segment === null) !== undefined) { return true; } } - if(scope.vm.variantsWithError.indexOf(culture) !== -1) { + if(scope.vm.variantsWithError.find((item) => item.culture === variant.language.culture && item.segment === variant.segment) !== undefined) { return true; } return false; @@ -205,7 +216,6 @@ menu: "=", hideActionsMenu: " 3) { htmlFieldReference = parts[3] || ""; } 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 8d1caab850..a3554e59ec 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 @@ -29,7 +29,7 @@ function navigationService($routeParams, $location, $q, $injector, eventsService //A list of query strings defined that when changed will not cause a reload of the route - var nonRoutingQueryStrings = ["mculture", "cculture", "lq", "sr"]; + var nonRoutingQueryStrings = ["mculture", "cculture", "csegment", "lq", "sr"]; var retainedQueryStrings = ["mculture"]; function setMode(mode) { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/varianthelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/varianthelper.service.js index d1eb6e8ad9..996e2afc9e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/varianthelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/varianthelper.service.js @@ -8,27 +8,6 @@ function variantHelper() { * Returns the id for this variant * @param {any} variant */ - function getId(variant) { - var hasLanguage = variant.language && !!variant.language.culture; - var hasSegment = !!variant.segment; - - var sep = ";"; - - if (!hasLanguage && !hasSegment) { - // Invariant - return ""; - } else if (hasLanguage && !hasSegment) { - // Culture only - return variant.language.culture; - } else if (!hasLanguage && hasSegment) { - // Segment only - return sep + variant.segment; - } else { - // Culture and Segment - return variant.language.culture + sep + variant.segment; - } - } - function getDisplayName(variant) { if (variant == null) { return ""; @@ -54,7 +33,6 @@ function variantHelper() { } return { - getId, getDisplayName } } 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 da91e0bee5..55f702d1c1 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 @@ -9,7 +9,8 @@ - -
- +   - + - + - + - +
Open in split view
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 2a3f67a7e3..572a083cbe 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 @@ -7,6 +7,10 @@ //if we make the viewModel the variant itself, we end up with a circular reference in the models which isn't ideal // (i.e. variant.apps[contentApp].viewModel = variant) //so instead since we already have access to the content, we can just get the variant directly by the index. + + var unbindLanguageWatcher; + var unbindSegmentWatcher; + var timeout = null; var vm = this; vm.loading = true; @@ -16,26 +20,50 @@ vm.content = $scope.content.variants[$scope.model.viewModel]; serverValidationManager.notify(); vm.loading = false; + timeout = null;// ensure timeout is set to null, so we know that its not running anymore. //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 (vm.content.language) { - $scope.$watch(function () { + unbindLanguageWatcher = $scope.$watch(function () { return vm.content.language.culture; }, function (newVal, oldVal) { if (newVal !== oldVal) { - vm.loading = true; - - // TODO: Can we minimize the flicker? - $timeout(function () { - onInit(); - }, 100); + requestUpdate(); } }); + } else { + unbindLanguageWatcher = function() {} + } + + unbindSegmentWatcher = $scope.$watch(function () { + return vm.content.segment; + }, function (newVal, oldVal) { + if (newVal !== oldVal) { + requestUpdate(); + } + }); + + } + + function requestUpdate() { + if (timeout === null) { + vm.loading = true; + + // TODO: Can we minimize the flicker? + timeout = $timeout(function () { + onInit(); + }, 100); } } onInit(); + + $scope.$on("$destroy", function() { + unbindLanguageWatcher(); + unbindSegmentWatcher(); + $timeout.cancel(timeout); + }); } angular.module("umbraco").controller("Umbraco.Editors.Content.Apps.ContentController", ContentAppContentController); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js index 64f601f8b7..db91f62a9b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js @@ -60,6 +60,9 @@ function contentCreateController($scope, language as what is selected in the tree */ .search("cculture", mainCulture) /* when we create a new node we must make sure that any previously + opened segments is reset */ + .search("csegment", null) + /* when we create a new node we must make sure that any previously used blueprint is reset */ .search("blueprintId", null); close(); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js index ad79bf01d1..2c7c53e31f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js @@ -28,6 +28,7 @@ function ContentEditController($scope, $routeParams, contentResource) { $scope.isNew = infiniteMode ? $scope.model.create : $routeParams.create; //load the default culture selected in the main tree if any $scope.culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; + $scope.segment = $routeParams.csegment ? $routeParams.csegment : null; //Bind to $routeUpdate which will execute anytime a location changes but the route is not triggered. //This is so we can listen to changes on the cculture parameter since that will not cause a route change @@ -36,6 +37,7 @@ function ContentEditController($scope, $routeParams, contentResource) { //will not cause a route change and so we can update the isNew and contentId flags accordingly. $scope.$on('$routeUpdate', function (event, next) { $scope.culture = next.params.cculture ? next.params.cculture : $routeParams.mculture; + $scope.segment = next.params.csegment ? next.params.csegment : null; $scope.isNew = next.params.create === "true"; $scope.contentId = infiniteMode ? $scope.model.id : $routeParams.id; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/edit.html b/src/Umbraco.Web.UI.Client/src/views/content/edit.html index ddd9dd35ec..94e8c25b89 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/edit.html @@ -8,6 +8,7 @@ tree-alias="content" is-new="isNew" culture="culture" + segment="segment" infinite-model="model">
diff --git a/src/Umbraco.Web.UI.Client/src/views/contentblueprints/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/contentblueprints/edit.controller.js index 982af76d69..15112650c6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/contentblueprints/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/contentblueprints/edit.controller.js @@ -44,6 +44,7 @@ function ContentBlueprintEditController($scope, $routeParams, contentResource) { //load the default culture selected in the main tree if any $scope.culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; + $scope.segment = $routeParams.csegment ? $routeParams.csegment : null; //Bind to $routeUpdate which will execute anytime a location changes but the route is not triggered. //This is so we can listen to changes on the cculture parameter since that will not cause a route change @@ -52,6 +53,7 @@ function ContentBlueprintEditController($scope, $routeParams, contentResource) { //will not cause a route change and so we can update the isNew and contentId flags accordingly. $scope.$on('$routeUpdate', function (event, next) { $scope.culture = next.params.cculture ? next.params.cculture : $routeParams.mculture; + $scope.segment = next.params.csegment ? next.params.csegment : null; $scope.isNew = $routeParams.id === "-1"; $scope.contentId = $routeParams.id; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/contentblueprints/edit.html b/src/Umbraco.Web.UI.Client/src/views/contentblueprints/edit.html index 191dd4db5c..751bbe5970 100644 --- a/src/Umbraco.Web.UI.Client/src/views/contentblueprints/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/contentblueprints/edit.html @@ -5,6 +5,7 @@ get-scaffold-method="getScaffoldMethod" is-new="isNew" tree-alias="contentblueprints" - culture="culture"> + culture="culture" + segment="segment">