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 15f95b2a60..6fb6f0156d 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 @@ -21,7 +21,6 @@ $scope.page.listViewPath = null; $scope.page.isNew = $scope.isNew ? true : false; $scope.page.buttonGroupState = "init"; - $scope.page.culture = $scope.culture; $scope.page.hideActionsMenu = infiniteMode ? true : false; $scope.page.hideChangeVariant = infiniteMode ? true : false; $scope.allowOpen = true; @@ -56,39 +55,68 @@ bindEvents(); // set first app to active + // TODO: We need to track active $scope.content.apps[0].active = true; + setActiveCulture(); + } + + /** + * The content item(s) are loaded into an array and this will set the active content item based on the current culture. + * If the content item is invariant, then only one item exists in the array. + */ + function setActiveCulture() { // set the active variant - var activeCultureSet = false; + var activeVariant = null; _.each($scope.content.variants, function (v) { - if (!activeCultureSet) { - if (v.language.culture === $scope.page.culture) { - v.active = true; - $scope.content.currentVariant = v; - activeCultureSet = true; - } + if (v.language.culture === $scope.culture) { + v.active = true; + activeVariant = v; + } + else { + v.active = false; } }); - if (!activeCultureSet) { + if (!activeVariant) { // set the first variant to active $scope.content.variants[0].active = true; - $scope.content.currentVariant = $scope.content.variants[0]; - } + activeVariant = $scope.content.variants[0]; + } + + //If there are no editors yet then create one with the current content. + //if there's already a main editor then update it with the current content. + //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 + //content apps section and the content apps contains the view for editing content itself + //and we need to assign a view model to the subView so that it is scoped to the current + //editor so that split views work. This is a bit hacky but it's required because the content + //app stuff isn't built to have a scoped model to an editor, it's built to have a single global + //model but that doesn't work for having split view. + + if (!activeVariant.apps) { + //copy from the main model + activeVariant.apps = angular.copy($scope.content.apps); + } + + //the assign the activeVariant to a view model to the content app + var contentApp = _.find(activeVariant.apps, function (a) { + return a.alias === "content"; + }); + contentApp.viewModel = activeVariant; - // create new editor for split view if ($scope.editors.length === 0) { var editor = { - content: $scope.content + content: activeVariant }; $scope.editors.push(editor); } else if ($scope.editors.length === 1) { - $scope.editors[0].content = $scope.content; + $scope.editors[0].content = activeVariant; } else { //fixme - need to fix something here if we are re-loading a content item that is in a split view } - } + } function bindEvents() { //bindEvents can be called more than once and we don't want to have multiple bound events @@ -487,7 +515,21 @@ $scope.openInSplitView = function (selectedVariant) { - console.log(selectedVariant); + //only the content app can be selected since no other apps are shown, and because we copy all of these apps + //to the "editors" we need to update this across all editors + for (var e = 0; e < $scope.editors.length; e++) { + var editor = $scope.editors[e]; + for (var i = 0; i < editor.content.apps.length; i++) { + var app = editor.content.apps[i]; + if (app.alias === "content") { + app.active = true; + } + else { + app.active = false; + } + } + } + var editor = {}; // hacking animation states - these should hopefully be easier to do when we upgrade angular @@ -575,6 +617,12 @@ } }; + $scope.$watch('culture', function (newVal, oldVal) { + if (newVal !== oldVal) { + setActiveCulture(); + } + }); + //ensure to unregister from all events! $scope.$on('$destroy', function () { for (var e in evts) { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js index a0d38667bf..3e318982f1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js @@ -201,7 +201,7 @@ Use this directive to construct a header inside the main editor window. **/ -(function() { +(function () { 'use strict'; function EditorHeaderDirective(iconHelper, $location, editorService) { @@ -213,17 +213,17 @@ Use this directive to construct a header inside the main editor window. scope.vm.currentVariant = ""; function onInit() { - setCurrentVariant(scope.variants); + setCurrentVariant(); } - function setCurrentVariant(variants) { - angular.forEach(variants, function (variant) { - if(variant.active) { + function setCurrentVariant() { + angular.forEach(scope.variants, function (variant) { + if (variant.active) { scope.vm.currentVariant = variant; } }); } - + scope.goBack = function () { if (scope.onBack) { scope.onBack(); @@ -235,11 +235,11 @@ Use this directive to construct a header inside the main editor window. $location.search("cculture", variant.language.culture); }; - scope.openIconPicker = function() { + scope.openIconPicker = function () { var iconPicker = { icon: scope.icon.split(' ')[0], color: scope.icon.split(' ')[1], - submit: function(model) { + submit: function (model) { if (model.icon) { if (model.color) { scope.icon = model.icon + " " + model.color; @@ -251,35 +251,44 @@ Use this directive to construct a header inside the main editor window. } editorService.close(); }, - close: function() { + close: function () { editorService.close(); } }; editorService.iconPicker(iconPicker); }; - scope.closeSplitView = function() { - if(scope.onCloseSplitView) { + scope.closeSplitView = function () { + if (scope.onCloseSplitView) { scope.onCloseSplitView(); } }; - scope.openInSplitView = function(event, variant) { - if(scope.onOpenInSplitView) { + scope.openInSplitView = function (event, variant) { + if (scope.onOpenInSplitView) { scope.vm.dropdownOpen = false; - scope.onOpenInSplitView({"variant": variant}); + scope.onOpenInSplitView({ "variant": variant }); } }; - //TODO: Change this, we cannot watch the whole model - //scope.$watch('variants', function(newValue, oldValue){ - // if(!newValue) return; - // if(newValue === oldValue) return; - // setCurrentVariant(newValue); - //}, true); - onInit(); + //watch for the active culture changing, if it changes, update the current variant + if (scope.variants) { + scope.$watch(function () { + for (var i = 0; i < scope.variants.length; i++) { + var v = scope.variants[i]; + if (v.active) { + return v.language.culture; + } + } + return scope.vm.currentVariant.language.culture; //should never get here + }, function (newValue, oldValue) { + if (newValue !== scope.vm.currentVariant.language.culture) { + setCurrentVariant(); + } + }); + } } var directive = { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubviews.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubviews.directive.js index db0dad8de7..ea3c4799cd 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubviews.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorsubviews.directive.js @@ -1,48 +1,26 @@ -(function() { - 'use strict'; +(function () { + 'use strict'; - function EditorSubViewsDirective() { + function EditorSubViewsDirective() { - function link(scope, el, attr, ctrl) { + function link(scope, el, attr, ctrl) { - scope.activeView = {}; + } - // set toolbar from selected navigation item - function setActiveView(items) { + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-sub-views.html', + scope: { + subViews: "=", + model: "=" + }, + link: link + }; - for (var index = 0; index < items.length; index++) { + return directive; + } - var item = items[index]; - - if (item.active && item.view) { - scope.activeView = item; - } - } - } - - // watch for navigation changes - scope.$watch('subViews', function(newValue, oldValue) { - if (newValue) { - setActiveView(newValue); - } - }, true); - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/umb-editor-sub-views.html', - scope: { - subViews: "=", - model: "=" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEditorSubViews', EditorSubViewsDirective); + angular.module('umbraco.directives').directive('umbEditorSubViews', EditorSubViewsDirective); })(); diff --git a/src/Umbraco.Web.UI.Client/src/init.js b/src/Umbraco.Web.UI.Client/src/init.js index 6e444cbf84..fb6fadb444 100644 --- a/src/Umbraco.Web.UI.Client/src/init.js +++ b/src/Umbraco.Web.UI.Client/src/init.js @@ -43,7 +43,9 @@ app.run(['userService', '$q', '$log', '$rootScope', '$route', '$location', 'urlH } var currentRouteParams = null; - var globalQueryStrings = ["mculture"]; + + //A list of query strings defined that when changed will not cause a reload of the route + var nonRoutingQueryStrings = ["mculture", "cculture"]; /** execute code on each successful route */ $rootScope.$on('$routeChangeSuccess', function (event, current, previous) { @@ -131,15 +133,17 @@ app.run(['userService', '$q', '$log', '$rootScope', '$route', '$location', 'urlH var allowRoute = true; - //the only time that we want to cancel is if any of the globalQueryStrings have changed - //in which case the number of parts need to be equal before comparing values - if (_.keys(currUrlParts).length == _.keys(nextUrlParts).length) { + //The only time that we want to cancel is if only any of the nonRoutingQueryStrings have changed/added. + //If any of the other parts have changed we do not cancel + var currRoutingKeys = _.difference(_.keys(currUrlParts), nonRoutingQueryStrings); + var nextRoutingKeys = _.difference(_.keys(nextUrlParts), nonRoutingQueryStrings); + var diff = _.difference(currRoutingKeys, nextRoutingKeys); + //if the routing parameter keys are the same, we'll compare their values to see if any have changed and if so then the routing will be allowed. + if (diff.length == 0) { var partsChanged = 0; - _.each(currUrlParts, function (value, key) { - if (globalQueryStrings.indexOf(key) === -1) { - if (value.toLowerCase() !== nextUrlParts[key].toLowerCase()) { - partsChanged++; - } + _.each(currRoutingKeys, function (k) { + if (currUrlParts[k] != nextUrlParts[k]) { + partsChanged++; } }); if (partsChanged === 0) { diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js index d41427c203..be65dffd48 100644 --- a/src/Umbraco.Web.UI.Client/src/routes.js +++ b/src/Umbraco.Web.UI.Client/src/routes.js @@ -161,7 +161,7 @@ app.config(function ($routeProvider) { .when('/:section/:tree/:method?/:id', { //This allows us to dynamically change the template for this route since you cannot inject services into the templateUrl method. template: "
", - //This controller will execute for this route, then we replace the template dynamnically based on the current tree. + //This controller will execute for this route, then we replace the template dynamically based on the current tree. controller: function ($scope, $route, $routeParams, treeService) { if (!$routeParams.tree || !$routeParams.method) { 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 bf89bf2fe9..a437f3b6a7 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 @@ -17,12 +17,12 @@ + model="content"> diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html index 9d259d2c70..6a545dad5e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html @@ -67,10 +67,9 @@ {{variant.state}} - - + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html index 76d3104924..4e4a7229cc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html @@ -3,12 +3,12 @@
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 26ceaaa351..32842fb94b 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 @@ -1,24 +1,42 @@ (function () { "use strict"; - function ContentAppContentController($scope) { + function ContentAppContentController($scope, $timeout) { var vm = this; + vm.loading = true; function onInit() { - //select the first one in the list - //TODO: We need to track the active one - vm.content = $scope.model.variants[0]; - vm.content.active = true; + //TODO: This is pretty ugly since this component inherits the scope from umbEditorSubViews which is supplied a + //'model' which is the entire content object passed from the server which we can't use because this 'app' needs to be + //in the context of the current culture within a split view. So the content controller will assign a special 'viewModel' to the + //subView so that we have a model in the context of the editor. + //Ideally this would be a directive and we can just pass a model in but because contentApps currently are + //rendered purely based on views that won't work. Perhaps we can consider dynamically compiling directives like + // https://www.codelord.net/2015/05/19/angularjs-dynamically-loading-directives/ - angular.forEach(vm.content.tabs, function(group){ + vm.content = $scope.subView.viewModel; + + angular.forEach(vm.content.tabs, function (group) { group.open = true; }); + + vm.loading = false; } onInit(); + $scope.$watch(function () { + return $scope.subView.viewModel.language.culture; + }, function (newVal, oldVal) { + if (newVal !== oldVal) { + vm.loading = true; + $timeout(function () { + onInit(); + }); + } + }); } angular.module("umbraco").controller("Umbraco.Editors.Content.Apps.ContentController", ContentAppContentController); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.html b/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.html index 7cb3db570d..056933a85c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/apps/content/content.html @@ -1,6 +1,6 @@
-
+
{{ group.label }}
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 d3086a5b34..bb7f198a36 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 @@ -6,7 +6,7 @@ * @description * The controller for the content editor */ -function ContentEditController($scope, $routeParams, contentResource) { +function ContentEditController($scope, $rootScope, $routeParams, contentResource) { var infiniteMode = $scope.model && $scope.model.infiniteMode; @@ -23,7 +23,15 @@ function ContentEditController($scope, $routeParams, contentResource) { $scope.getScaffoldMethod = $routeParams.blueprintId ? scaffoldBlueprint : scaffoldEmpty; $scope.page = $routeParams.page; $scope.isNew = infiniteMode ? $scope.model.create : $routeParams.create; - $scope.culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; //load the default culture selected in the main tree if any + //load the default culture selected in the main tree if any + $scope.culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; + + //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 + // and then we can pass in the updated culture to the editor + $scope.$on('$routeUpdate', function (event, next) { + $scope.culture = next.params.cculture; + }); } angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController);