Fixing tree, navs, initializing, sequence of events, oh my. The init logic is much more stable but we have issues with tracking the mculture query string

This commit is contained in:
Shannon
2018-05-04 00:58:18 +10:00
parent 46c7290e4f
commit 912711bfa0
14 changed files with 562 additions and 442 deletions

View File

@@ -124,7 +124,7 @@
* This does the content loading and initializes everything, called on load and changing variants
* @param {any} culture
*/
function getNode(culture) {
function loadContent(culture) {
$scope.page.loading = true;
@@ -258,17 +258,33 @@
else {
//Browse content nodes based on the selected tree language variant
$scope.page.culture ? getNode($scope.page.culture) : getNode();
if ($scope.page.culture) {
loadContent($scope.page.culture);
}
else {
loadContent();
}
}
$scope.unPublish = function () {
//if there's any variants than we need to set the language and include the variants to publish
var culture = null;
if ($scope.content.variants.length > 0) {
_.each($scope.content.variants,
function (d) {
//set the culture if this is current
if (d.current === true) {
culture = d.language.culture;
}
});
}
if (formHelper.submitForm({ scope: $scope, skipValidation: true })) {
$scope.page.buttonGroupState = "busy";
contentResource.unPublish($scope.content.id)
contentResource.unPublish($scope.content.id, culture)
.then(function (data) {
formHelper.resetForm({ scope: $scope, notifications: data.notifications });
@@ -494,7 +510,7 @@
notificationsService.success("Successfully restored " + node.name + " to " + target.name);
// reload the node
getNode();
loadContent();
}, function (err) {
$scope.page.buttonRestore = "error";

View File

@@ -131,8 +131,29 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
treeService.clearCache({ section: section });
}
function load(section) {
$scope.section = section;
/**
* Re-loads the tree with the updated parameters
* @param {any} args either a string representing the 'section' or an object containing: 'section', 'treeAlias', 'customTreeParams', 'cacheKey'
*/
function load(args) {
if (angular.isString(args)) {
$scope.section = args;
}
else if (args) {
if (args.section) {
$scope.section = args.section;
}
if (args.customTreeParams) {
$scope.customtreeparams = args.customTreeParams;
}
if (args.treeAlias) {
$scope.treealias = args.treeAlias;
}
if (args.cacheKey) {
$scope.cachekey = args.cacheKey;
}
}
return loadTree();
}
@@ -399,33 +420,37 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
$scope.altSelect = function (n, ev) {
emitEvent("treeNodeAltSelect", { element: $element, tree: $scope.tree, node: n, event: ev });
};
//watch for section changes
$scope.$watch("section", function (newVal, oldVal) {
if (!$scope.tree) {
loadTree();
}
if (!newVal) {
//store the last section loaded
lastSection = oldVal;
}
else if (newVal !== oldVal && newVal !== lastSection) {
//only reload the tree data and Dom if the newval is different from the old one
// and if the last section loaded is different from the requested one.
loadTree();
//store the new section to be loaded as the last section
//clear any active trees to reset lookups
lastSection = newVal;
}
});
};
loadTree();
$scope.onInit();
//call the onInit method, if the result is a promise then load the tree after that resolves (if it's not a promise this will just resolve automatically).
//NOTE: The promise cannot be rejected, else the tree won't be loaded and we'll get exceptions if some API calls syncTree or similar.
$q.when($scope.onInit(), function (args) {
//the promise resolution can pass in parameters
if (args) {
if (args.section) {
$scope.section = args.section;
}
if (args.customTreeParams) {
$scope.customtreeparams = args.customTreeParams;
}
if (args.treealias) {
$scope.treealias = args.treealias;
}
if (args.cacheKey) {
$scope.cachekey = args.cacheKey;
}
}
//load the tree
loadTree().then(function () {
//because angular doesn't return a promise for the resolve method, we need to resort to some hackery, else
//like normal JS promises we could do resolve(...).then()
if (args.onLoaded && angular.isFunction(args.onLoaded)) {
args.onLoaded();
}
});
});
}
};
}

View File

@@ -215,17 +215,21 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
* @returns {Promise} resourcePromise object.
*
*/
unPublish: function (id) {
unPublish: function (id, culture) {
if (!id) {
throw "id cannot be null";
}
if (!culture) {
culture = null;
}
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"PostUnPublish",
[{ id: id }])),
{ id: id, culture: culture })),
'Failed to publish content with id ' + id);
},
/**

View File

@@ -1,7 +1,12 @@
(function () {
"use strict";
function javascriptLibraryService($q, $http, umbRequestHelper) {
/**
* @ngdoc service
* @name umbraco.resources.javascriptLibraryResource
* @description Handles retrieving data for javascript libraries on the server
**/
function javascriptLibraryResource($q, $http, umbRequestHelper) {
var existingLocales = [];
@@ -32,6 +37,6 @@
return service;
}
angular.module("umbraco.services").factory("javascriptLibraryService", javascriptLibraryService);
angular.module("umbraco.resources").factory("javascriptLibraryResource", javascriptLibraryResource);
})();

View File

@@ -41,237 +41,271 @@
* </pre>
*/
angular.module('umbraco.services')
.factory('assetsService', function ($q, $log, angularHelper, umbRequestHelper, $rootScope, $http) {
.factory('assetsService', function ($q, $log, angularHelper, umbRequestHelper, $rootScope, $http, userService, javascriptLibraryResource) {
var initAssetsLoaded = false;
var initAssetsLoaded = false;
function appendRnd (url) {
//if we don't have a global umbraco obj yet, the app is bootstrapping
if (!Umbraco.Sys.ServerVariables.application) {
function appendRnd(url) {
//if we don't have a global umbraco obj yet, the app is bootstrapping
if (!Umbraco.Sys.ServerVariables.application) {
return url;
}
var rnd = Umbraco.Sys.ServerVariables.application.cacheBuster;
var _op = (url.indexOf("?") > 0) ? "&" : "?";
url = url + _op + "umb__rnd=" + rnd;
return url;
};
function convertVirtualPath(path) {
//make this work for virtual paths
if (path.startsWith("~/")) {
path = umbRequestHelper.convertVirtualToAbsolutePath(path);
}
return path;
}
var rnd = Umbraco.Sys.ServerVariables.application.cacheBuster;
var _op = (url.indexOf("?") > 0) ? "&" : "?";
url = url + _op + "umb__rnd=" + rnd;
return url;
};
function convertVirtualPath(path) {
//make this work for virtual paths
if (path.startsWith("~/")) {
path = umbRequestHelper.convertVirtualToAbsolutePath(path);
}
return path;
}
var service = {
loadedAssets: {},
_getAssetPromise: function (path) {
if (this.loadedAssets[path]) {
return this.loadedAssets[path];
} else {
var deferred = $q.defer();
this.loadedAssets[path] = { deferred: deferred, state: "new", path: path };
return this.loadedAssets[path];
}
},
/**
Internal method. This is called when the application is loading and the user is already authenticated, or once the user is authenticated.
There's a few assets the need to be loaded for the application to function but these assets require authentication to load.
*/
_loadInitAssets: function () {
var deferred = $q.defer();
//here we need to ensure the required application assets are loaded
if (initAssetsLoaded === false) {
var self = this;
self.loadJs(umbRequestHelper.getApiUrl("serverVarsJs", "", ""), $rootScope).then(function () {
initAssetsLoaded = true;
//now we need to go get the legacyTreeJs - but this can be done async without waiting.
self.loadJs(umbRequestHelper.getApiUrl("legacyTreeJs", "", ""), $rootScope);
deferred.resolve();
});
}
else {
deferred.resolve();
}
return deferred.promise;
},
/**
* @ngdoc method
* @name umbraco.services.assetsService#loadCss
* @methodOf umbraco.services.assetsService
*
* @description
* Injects a file as a stylesheet into the document head
*
* @param {String} path path to the css file to load
* @param {Scope} scope optional scope to pass into the loader
* @param {Object} keyvalue collection of attributes to pass to the stylesheet element
* @param {Number} timeout in milliseconds
* @returns {Promise} Promise object which resolves when the file has loaded
*/
loadCss: function (path, scope, attributes, timeout) {
path = convertVirtualPath(path);
var asset = this._getAssetPromise(path); // $q.defer();
var t = timeout || 5000;
var a = attributes || undefined;
if (asset.state === "new") {
asset.state = "loading";
LazyLoad.css(appendRnd(path), function () {
if (!scope) {
scope = $rootScope;
* Loads in moment.js requirements during the _loadInitAssets call
*/
function loadMomentLocaleForCurrentUser() {
var self = this;
function loadLocales(currentUser, supportedLocales) {
var locale = currentUser.locale.toLowerCase();
if (locale !== 'en-us') {
var localeUrls = [];
if (supportedLocales.indexOf(locale + '.js') > -1) {
localeUrls.push('lib/moment/' + locale + '.js');
}
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
});
} else if (asset.state === "loaded") {
asset.deferred.resolve(true);
}
return asset.deferred.promise;
},
/**
* @ngdoc method
* @name umbraco.services.assetsService#loadJs
* @methodOf umbraco.services.assetsService
*
* @description
* Injects a file as a javascript into the document
*
* @param {String} path path to the js file to load
* @param {Scope} scope optional scope to pass into the loader
* @param {Object} keyvalue collection of attributes to pass to the script element
* @param {Number} timeout in milliseconds
* @returns {Promise} Promise object which resolves when the file has loaded
*/
loadJs: function (path, scope, attributes, timeout) {
path = convertVirtualPath(path);
var asset = this._getAssetPromise(path); // $q.defer();
var t = timeout || 5000;
var a = attributes || undefined;
if (asset.state === "new") {
asset.state = "loading";
LazyLoad.js(appendRnd(path), function () {
if (!scope) {
scope = $rootScope;
if (locale.indexOf('-') > -1) {
var majorLocale = locale.split('-')[0] + '.js';
if (supportedLocales.indexOf(majorLocale) > -1) {
localeUrls.push('lib/moment/' + majorLocale);
}
}
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
return self.load(localeUrls, $rootScope);
}
else {
$q.when(true);
}
}
userService.getCurrentUser().then(function (currentUser) {
return javascriptLibraryResource.getSupportedLocalesForMoment().then(function (supportedLocales) {
return loadLocales(currentUser, supportedLocales);
});
} else if (asset.state === "loaded") {
asset.deferred.resolve(true);
}
return asset.deferred.promise;
},
/**
* @ngdoc method
* @name umbraco.services.assetsService#load
* @methodOf umbraco.services.assetsService
*
* @description
* Injects a collection of css and js files
*
*
* @param {Array} pathArray string array of paths to the files to load
* @param {Scope} scope optional scope to pass into the loader
* @returns {Promise} Promise object which resolves when all the files has loaded
*/
load: function (pathArray, scope) {
var promise;
if (!angular.isArray(pathArray)) {
throw "pathArray must be an array";
}
// Check to see if there's anything to load, resolve promise if not
var nonEmpty = _.reject(pathArray, function (item) {
return item === undefined || item === "";
});
if (nonEmpty.length === 0) {
var deferred = $q.defer();
promise = deferred.promise;
deferred.resolve(true);
}
var service = {
loadedAssets: {},
_getAssetPromise: function (path) {
if (this.loadedAssets[path]) {
return this.loadedAssets[path];
} else {
var deferred = $q.defer();
this.loadedAssets[path] = { deferred: deferred, state: "new", path: path };
return this.loadedAssets[path];
}
},
/**
Internal method. This is called when the application is loading and the user is already authenticated, or once the user is authenticated.
There's a few assets the need to be loaded for the application to function but these assets require authentication to load.
*/
_loadInitAssets: function () {
//here we need to ensure the required application assets are loaded
if (initAssetsLoaded === false) {
var self = this;
return self.loadJs(umbRequestHelper.getApiUrl("serverVarsJs", "", ""), $rootScope).then(function () {
initAssetsLoaded = true;
//now we need to go get the legacyTreeJs - but this can be done async without waiting.
self.loadJs(umbRequestHelper.getApiUrl("legacyTreeJs", "", ""), $rootScope);
return loadMomentLocaleForCurrentUser();
});
}
else {
return $q.when(true);
}
},
/**
* @ngdoc method
* @name umbraco.services.assetsService#loadCss
* @methodOf umbraco.services.assetsService
*
* @description
* Injects a file as a stylesheet into the document head
*
* @param {String} path path to the css file to load
* @param {Scope} scope optional scope to pass into the loader
* @param {Object} keyvalue collection of attributes to pass to the stylesheet element
* @param {Number} timeout in milliseconds
* @returns {Promise} Promise object which resolves when the file has loaded
*/
loadCss: function (path, scope, attributes, timeout) {
path = convertVirtualPath(path);
var asset = this._getAssetPromise(path); // $q.defer();
var t = timeout || 5000;
var a = attributes || undefined;
if (asset.state === "new") {
asset.state = "loading";
LazyLoad.css(appendRnd(path), function () {
if (!scope) {
scope = $rootScope;
}
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
});
} else if (asset.state === "loaded") {
asset.deferred.resolve(true);
}
return asset.deferred.promise;
},
/**
* @ngdoc method
* @name umbraco.services.assetsService#loadJs
* @methodOf umbraco.services.assetsService
*
* @description
* Injects a file as a javascript into the document
*
* @param {String} path path to the js file to load
* @param {Scope} scope optional scope to pass into the loader
* @param {Object} keyvalue collection of attributes to pass to the script element
* @param {Number} timeout in milliseconds
* @returns {Promise} Promise object which resolves when the file has loaded
*/
loadJs: function (path, scope, attributes, timeout) {
path = convertVirtualPath(path);
var asset = this._getAssetPromise(path); // $q.defer();
var t = timeout || 5000;
var a = attributes || undefined;
if (asset.state === "new") {
asset.state = "loading";
LazyLoad.js(appendRnd(path), function () {
if (!scope) {
scope = $rootScope;
}
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
});
} else if (asset.state === "loaded") {
asset.deferred.resolve(true);
}
return asset.deferred.promise;
},
/**
* @ngdoc method
* @name umbraco.services.assetsService#load
* @methodOf umbraco.services.assetsService
*
* @description
* Injects a collection of css and js files
*
*
* @param {Array} pathArray string array of paths to the files to load
* @param {Scope} scope optional scope to pass into the loader
* @returns {Promise} Promise object which resolves when all the files has loaded
*/
load: function (pathArray, scope) {
var promise;
if (!angular.isArray(pathArray)) {
throw "pathArray must be an array";
}
// Check to see if there's anything to load, resolve promise if not
var nonEmpty = _.reject(pathArray, function (item) {
return item === undefined || item === "";
});
if (nonEmpty.length === 0) {
var deferred = $q.defer();
promise = deferred.promise;
deferred.resolve(true);
return promise;
}
//compile a list of promises
//blocking
var promises = [];
var assets = [];
_.each(nonEmpty, function (path) {
path = convertVirtualPath(path);
var asset = service._getAssetPromise(path);
//if not previously loaded, add to list of promises
if (asset.state !== "loaded") {
if (asset.state === "new") {
asset.state = "loading";
assets.push(asset);
}
//we need to always push to the promises collection to monitor correct execution
promises.push(asset.deferred.promise);
}
});
//gives a central monitoring of all assets to load
promise = $q.all(promises);
// Split into css and js asset arrays, and use LazyLoad on each array
var cssAssets = _.filter(assets,
function (asset) {
return asset.path.match(/(\.css$|\.css\?)/ig);
});
var jsAssets = _.filter(assets,
function (asset) {
return asset.path.match(/(\.js$|\.js\?)/ig);
});
function assetLoaded(asset) {
asset.state = "loaded";
if (!scope) {
scope = $rootScope;
}
angularHelper.safeApply(scope,
function () {
asset.deferred.resolve(true);
});
}
if (cssAssets.length > 0) {
var cssPaths = _.map(cssAssets, function (asset) { return appendRnd(asset.path) });
LazyLoad.css(cssPaths, function () { _.each(cssAssets, assetLoaded); });
}
if (jsAssets.length > 0) {
var jsPaths = _.map(jsAssets, function (asset) { return appendRnd(asset.path) });
LazyLoad.js(jsPaths, function () { _.each(jsAssets, assetLoaded); });
}
return promise;
}
};
//compile a list of promises
//blocking
var promises = [];
var assets = [];
_.each(nonEmpty, function (path) {
path = convertVirtualPath(path);
var asset = service._getAssetPromise(path);
//if not previously loaded, add to list of promises
if (asset.state !== "loaded") {
if (asset.state === "new") {
asset.state = "loading";
assets.push(asset);
}
//we need to always push to the promises collection to monitor correct execution
promises.push(asset.deferred.promise);
}
});
//gives a central monitoring of all assets to load
promise = $q.all(promises);
// Split into css and js asset arrays, and use LazyLoad on each array
var cssAssets = _.filter(assets,
function (asset) {
return asset.path.match(/(\.css$|\.css\?)/ig);
});
var jsAssets = _.filter(assets,
function (asset) {
return asset.path.match(/(\.js$|\.js\?)/ig);
});
function assetLoaded(asset) {
asset.state = "loaded";
if (!scope) {
scope = $rootScope;
}
angularHelper.safeApply(scope,
function () {
asset.deferred.resolve(true);
});
}
if (cssAssets.length > 0) {
var cssPaths = _.map(cssAssets, function (asset) { return appendRnd(asset.path) });
LazyLoad.css(cssPaths, function() { _.each(cssAssets, assetLoaded); });
}
if (jsAssets.length > 0) {
var jsPaths = _.map(jsAssets, function (asset) { return appendRnd(asset.path) });
LazyLoad.js(jsPaths, function () { _.each(jsAssets, assetLoaded); });
}
return promise;
}
};
return service;
});
return service;
});

View File

@@ -17,11 +17,15 @@
*/
function navigationService($rootScope, $routeParams, $log, $location, $q, $timeout, $injector, eventsService, dialogService, umbModelMapper, treeService, notificationsService, historyService, appState, angularHelper) {
//the promise that will be resolved when the navigation is ready
var navReadyPromise = $q.defer();
//the main tree's API reference, this is acquired when the tree has initialized
var mainTreeApi = null;
eventsService.on("app.navigationReady", function (e, args) {
mainTreeApi = args.treeApi;
navReadyPromise.resolve(mainTreeApi);
});
//used to track the current dialog object
@@ -88,18 +92,35 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
var service = {
/** initializes the navigation service */
init: function() {
//keep track of the current section - initially this will always be undefined so
// no point in setting it now until it changes.
$rootScope.$watch(function () {
return $routeParams.section;
}, function (newVal, oldVal) {
appState.setSectionState("currentSection", newVal);
});
/**
* @ngdoc method
* @name umbraco.services.navigationService#waitForNavReady
* @methodOf umbraco.services.navigationService
*
* @description
* returns a promise that will resolve when the navigation is ready
*/
waitForNavReady: function () {
return navReadyPromise.promise;
},
/**
* @ngdoc method
* @name umbraco.services.navigationService#setMainCulture
* @methodOf umbraco.services.navigationService
*
* @description
* Utility to set the mculture query string without changing the route. This is a hack to work around angular's limitations
*/
setMainCulture: function (culture) {
$location.search("mculture", culture);
//fixme: This can work but interferes with our other $locationChangeStart
//var un = $rootScope.$on('$locationChangeStart', function (event) {
// event.preventDefault();
// un();
//});
},
/**
@@ -174,10 +195,12 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
appState.setSectionState("currentSection", sectionAlias);
if (syncArgs) {
this.syncTree(syncArgs);
return this.syncTree(syncArgs);
}
}
setMode("tree");
return $q.when(true);
},
showTray: function () {
@@ -217,13 +240,9 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
throw "args.tree cannot be null";
}
if (mainTreeApi) {
//returns a promise,
return navReadyPromise.promise.then(function () {
return mainTreeApi.syncTree(args);
}
//couldn't sync
return $q.reject();
});
},
/**
@@ -231,28 +250,22 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
have to set an active tree and then sync, the new API does this in one method by using syncTree
*/
_syncPath: function(path, forceReload) {
if (mainTreeApi) {
mainTreeApi.syncTree({ path: path, forceReload: forceReload });
}
return navReadyPromise.promise.then(function () {
return mainTreeApi.syncTree({ path: path, forceReload: forceReload });
});
},
reloadNode: function(node) {
if (mainTreeApi) {
return navReadyPromise.promise.then(function () {
return mainTreeApi.reloadNode(node);
}
else {
return $q.reject();
}
});
},
reloadSection: function(sectionAlias) {
if (mainTreeApi) {
return navReadyPromise.promise.then(function () {
mainTreeApi.clearCache({ section: sectionAlias });
return mainTreeApi.load(sectionAlias);
}
else {
return $q.reject();
}
});
},
/**

View File

@@ -1,5 +1,5 @@
angular.module('umbraco.services')
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, assetsService, dialogService, $timeout, angularHelper, $http, javascriptLibraryService) {
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, dialogService, $timeout, angularHelper, $http) {
var currentUser = null;
var lastUserId = null;
@@ -273,42 +273,6 @@ angular.module('umbraco.services')
}
},
/** Loads the Moment.js Locale for the current user. */
loadMomentLocaleForCurrentUser: function () {
function loadLocales(currentUser, supportedLocales) {
var locale = currentUser.locale.toLowerCase();
if (locale !== 'en-us') {
var localeUrls = [];
if (supportedLocales.indexOf(locale + '.js') > -1) {
localeUrls.push('lib/moment/' + locale + '.js');
}
if (locale.indexOf('-') > -1) {
var majorLocale = locale.split('-')[0] + '.js';
if (supportedLocales.indexOf(majorLocale) > -1) {
localeUrls.push('lib/moment/' + majorLocale);
}
}
return assetsService.load(localeUrls, $rootScope);
}
else {
$q.when(true);
}
}
var promises = {
currentUser: this.getCurrentUser(),
supportedLocales: javascriptLibraryService.getSupportedLocalesForMoment()
}
return $q.all(promises).then(function (values) {
return loadLocales(values.currentUser, values.supportedLocales);
});
},
/** Called whenever a server request is made that contains a x-umb-user-seconds response header for which we can update the user's remaining timeout seconds */
setUserTimeout: function (newTimeout) {
setUserTimeoutInternal(newTimeout);

View File

@@ -14,9 +14,7 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $
$scope.authenticated = null;
$scope.touchDevice = appState.getGlobalState("touchDevice");
$scope.overlay = {};
$scope.navReady = false;
$scope.removeNotification = function (index) {
notificationsService.remove(index);
};
@@ -61,10 +59,6 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $
});
}));
evts.push(eventsService.on('app.navigationReady', function () {
$scope.navReady = true;
}));
//when the app is ready/user is logged in, setup the data
evts.push(eventsService.on("app.ready", function (evt, data) {

View File

@@ -11,6 +11,9 @@
*/
function NavigationController($scope, $rootScope, $location, $log, $q, $routeParams, $timeout, treeService, appState, navigationService, keyboardService, dialogService, historyService, eventsService, sectionResource, angularHelper, languageResource) {
//this is used to trigger the tree to start loading once everything is ready
var treeInitPromise = $q.defer();
$scope.treeApi = {};
//Bind to the main tree events
@@ -107,8 +110,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
navigationService.hideNavigation();
});
//once this is wired up, the nav is ready
eventsService.emit("app.navigationReady", { treeApi: $scope.treeApi});
return treeInitPromise.promise;
}
//TODO: Remove this, this is not healthy
@@ -133,7 +135,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
$scope.page = {};
$scope.page.languageSelectorIsOpen = false;
$scope.currentSection = appState.getSectionState("currentSection");
$scope.currentSection = null;
$scope.customTreeParams = null;
$scope.treeCacheKey = "_";
$scope.showNavigation = appState.getGlobalState("showNavigation");
@@ -155,14 +157,14 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
var evts = [];
//Listen for global state changes
evts.push(eventsService.on("appState.globalState.changed", function(e, args) {
evts.push(eventsService.on("appState.globalState.changed", function (e, args) {
if (args.key === "showNavigation") {
$scope.showNavigation = args.value;
}
}));
//Listen for menu state changes
evts.push(eventsService.on("appState.menuState.changed", function(e, args) {
evts.push(eventsService.on("appState.menuState.changed", function (e, args) {
if (args.key === "showMenuDialog") {
$scope.showContextMenuDialog = args.value;
}
@@ -181,7 +183,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
}));
//Listen for section state changes
evts.push(eventsService.on("appState.treeState.changed", function(e, args) {
evts.push(eventsService.on("appState.treeState.changed", function (e, args) {
var f = args;
if (args.value.root && args.value.root.metaData.containsTrees === false) {
$rootScope.emptySection = true;
@@ -192,32 +194,38 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
}));
//Listen for section state changes
evts.push(eventsService.on("appState.sectionState.changed", function(e, args) {
evts.push(eventsService.on("appState.sectionState.changed", function (e, args) {
//section changed
if (args.key === "currentSection") {
$scope.currentSection = args.value;
//load the tree
configureTreeAndLanguages();
$scope.treeApi.load({ section: $scope.currentSection, customTreeParams: $scope.customTreeParams, cacheKey: $scope.treeCacheKey });
}
//show/hide search results
if (args.key === "showSearchResults") {
$scope.showSearchResults = args.value;
}
}));
// Listen for language updates
evts.push(eventsService.on("editors.languages.languageDeleted", function(e, args) {
languageResource.getAll().then(function(languages) {
evts.push(eventsService.on("editors.languages.languageDeleted", function (e, args) {
languageResource.getAll().then(function (languages) {
$scope.languages = languages;
});
}));
evts.push(eventsService.on("editors.languages.languageCreated", function(e, args) {
languageResource.getAll().then(function(languages) {
evts.push(eventsService.on("editors.languages.languageCreated", function (e, args) {
languageResource.getAll().then(function (languages) {
$scope.languages = languages;
});
}));
//This reacts to clicks passed to the body element which emits a global call to close all dialogs
evts.push(eventsService.on("app.closeDialogs", function(event) {
evts.push(eventsService.on("app.closeDialogs", function (event) {
if (appState.getGlobalState("stickyNavigation")) {
navigationService.hideNavigation();
//TODO: don't know why we need this? - we are inside of an angular event listener.
@@ -226,52 +234,43 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
}));
//when a user logs out or timesout
evts.push(eventsService.on("app.notAuthenticated", function() {
evts.push(eventsService.on("app.notAuthenticated", function () {
$scope.authenticated = false;
}));
//when the application is ready and the user is authorized setup the data
evts.push(eventsService.on("app.ready", function(evt, data) {
$scope.authenticated = true;
// load languages
languageResource.getAll().then(function(languages) {
$scope.languages = languages;
evts.push(eventsService.on("app.ready", function (evt, data) {
init();
}));
if ($scope.languages.length > 1) {
var defaultLang = _.find($scope.languages, function (l) {
return l.isDefault;
/**
* Based on the current state of the application, this configures the scope variables that control the main tree and language drop down
*/
function configureTreeAndLanguages() {
//create the custom query string param for this tree, this is currently only relevant for content
if ($scope.currentSection === "content") {
//must use $location here because $routeParams isn't available until after the route change
var mainCulture = $location.search().mculture;
//select the current language if set in the query string
if (mainCulture && $scope.languages && $scope.languages.length > 1) {
var found = _.find($scope.languages, function (l) {
return l.culture.toLowerCase() === mainCulture.toLowerCase();
});
if (defaultLang) {
if (found) {
//set the route param
$location.search("mculture", defaultLang.culture);
$scope.selectedLanguage = found;
}
}
init();
});
}));
function init() {
//select the current language if set in the query string
var mainCulture = $location.search().mculture;
if (mainCulture && $scope.languages && $scope.languages.length > 1) {
var found = _.find($scope.languages, function (l) {
return l.culture === mainCulture;
});
if (found) {
//set the route param
$scope.selectedLanguage = found;
var queryParams = {};
if ($scope.selectedLanguage && $scope.selectedLanguage.culture) {
queryParams["culture"] = $scope.selectedLanguage.culture;
}
var queryString = $.param(queryParams); //create the query string from the params object
}
//create the custom query string param for this tree
var queryParams = {};
if ($scope.selectedLanguage && $scope.selectedLanguage.culture) {
queryParams["culture"] = $scope.selectedLanguage.culture;
}
var queryString = $.param(queryParams); //create the query string from the params object
if (queryString) {
$scope.customTreeParams = queryString;
$scope.treeCacheKey = queryString; // this tree uses caching but we need to change it's cache key per lang
@@ -279,8 +278,87 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
else {
$scope.treeCacheKey = "_"; // this tree uses caching, there's no lang selected so use the default
}
}
/**
* Called when the app is ready and sets up the navigation (should only be called once)
*/
function init() {
$scope.authenticated = true;
var navInit = false;
//$routeParams will be populated after $routeChangeSuccess since this controller is used outside ng-view,
//* we listen for the first route change with a section to setup the navigation.
//* we listen for all route changes to track the current section.
$rootScope.$on('$routeChangeSuccess', function () {
//only continue if there's a section available
if ($routeParams.section) {
if (!navInit) {
navInit = true;
initNav();
}
else {
//keep track of the current section, when it changes change the state, and we listen for that event change above
if ($scope.currentSection != $routeParams.section) {
appState.setSectionState("currentSection", $routeParams.section);
}
}
}
});
}
/**
* Called once during init to initialize the navigation/tree/languages
*/
function initNav() {
// load languages
languageResource.getAll().then(function (languages) {
$scope.languages = languages;
if ($scope.languages.length > 1) {
var defaultLang = _.find($scope.languages, function (l) {
return l.isDefault;
});
//if there's already one set, check if it exists
var currCulture = null;
var mainCulture = $location.search().mculture;
if (mainCulture) {
currCulture = _.find($scope.languages, function (l) {
return l.culture.toLowerCase() === mainCulture.toLowerCase();
});
}
if (!currCulture) {
navigationService.setMainCulture(defaultLang ? defaultLang.culture : null);
}
}
$scope.currentSection = $routeParams.section;
configureTreeAndLanguages();
//resolve the tree promise, set it's property values for loading the tree which will make the tree load
treeInitPromise.resolve({
section: $scope.currentSection,
customTreeParams: $scope.customTreeParams,
cacheKey: $scope.treeCacheKey,
//because angular doesn't return a promise for the resolve method, we need to resort to some hackery, else
//like normal JS promises we could do resolve(...).then()
onLoaded: function () {
//the nav is ready, let the app know
eventsService.emit("app.navigationReady", { treeApi: $scope.treeApi });
//finally set the section state
appState.setSectionState("currentSection", $routeParams.section);
}
});
});
}
function nodeExpandedHandler(args) {
//store the reference to the expanded node path
@@ -289,40 +367,37 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
}
}
$scope.selectLanguage = function(language) {
$scope.selectLanguage = function (language) {
$location.search("mculture", language.culture);
navigationService.setMainCulture(language.culture);
//$scope.selectedLanguage = language;
// close the language selector
$scope.page.languageSelectorIsOpen = false;
init(); //re-bind language to the query string and update the tree params
configureTreeAndLanguages(); //re-bind language to the query string and update the tree params
//execute after next digest because the internal watch on the customtreeparams needs to be bound now that we've changed it
$timeout(function () {
//reload the tree with it's updated querystring args
$scope.treeApi.load($scope.currentSection).then(function () {
//reload the tree with it's updated querystring args
$scope.treeApi.load({ section: $scope.currentSection, customTreeParams: $scope.customTreeParams, cacheKey: $scope.treeCacheKey }).then(function () {
//re-sync to currently edited node
var currNode = appState.getTreeState("selectedNode");
//create the list of promises
var promises = [];
//starting with syncing to the currently selected node if there is one
if (currNode) {
var path = treeService.getPath(currNode);
promises.push($scope.treeApi.syncTree({ path: path, activate: true }));
}
//TODO: If we want to keep all paths expanded ... but we need more testing since we need to deal with unexpanding
//for (var i = 0; i < expandedPaths.length; i++) {
// promises.push($scope.treeApi.syncTree({ path: expandedPaths[i], activate: false, forceReload: true }));
//}
//execute them sequentially
angularHelper.executeSequentialPromises(promises);
});
//re-sync to currently edited node
var currNode = appState.getTreeState("selectedNode");
//create the list of promises
var promises = [];
//starting with syncing to the currently selected node if there is one
if (currNode) {
var path = treeService.getPath(currNode);
promises.push($scope.treeApi.syncTree({ path: path, activate: true }));
}
//TODO: If we want to keep all paths expanded ... but we need more testing since we need to deal with unexpanding
//for (var i = 0; i < expandedPaths.length; i++) {
// promises.push($scope.treeApi.syncTree({ path: expandedPaths[i], activate: false, forceReload: true }));
//}
//execute them sequentially
angularHelper.executeSequentialPromises(promises);
});
};
//this reacts to the options item in the tree
@@ -350,22 +425,22 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
};
// Hides navigation tree, with a short delay, is cancelled if the user moves the mouse over the tree again
$scope.leaveTree = function(event) {
$scope.leaveTree = function (event) {
//this is a hack to handle IE touch events which freaks out due to no mouse events so the tree instantly shuts down
if (!event) {
return;
}
if (!appState.getGlobalState("touchDevice")) {
treeActive = false;
$timeout(function() {
$timeout(function () {
if (!treeActive) {
navigationService.hideTree();
}
}, 300);
}
};
$scope.toggleLanguageSelector = function() {
$scope.toggleLanguageSelector = function () {
$scope.page.languageSelectorIsOpen = !$scope.page.languageSelectorIsOpen;
};

View File

@@ -1,7 +1,11 @@
/** Executed when the application starts, binds to events and set global state */
app.run(['userService', '$q', '$log', '$rootScope', '$location', 'queryStrings', 'navigationService', 'appState', 'editorState', 'fileManager', 'assetsService', 'eventsService', '$cookies', '$templateCache', 'localStorageService', 'tourService', 'dashboardResource',
function (userService, $q, $log, $rootScope, $location, queryStrings, navigationService, appState, editorState, fileManager, assetsService, eventsService, $cookies, $templateCache, localStorageService, tourService, dashboardResource) {
$rootScope.$on('$locationChangeStart', function (event, next, current, newState, oldState) {
$log.info("location changing to:" + next);
});
//This sets the default jquery ajax headers to include our csrf token, we
// need to user the beforeSend method because our token changes per user/login so
// it cannot be static
@@ -17,27 +21,17 @@ app.run(['userService', '$q', '$log', '$rootScope', '$location', 'queryStrings',
/** Listens for authentication and checks if our required assets are loaded, if/once they are we'll broadcast a ready event */
eventsService.on("app.authenticated", function (evt, data) {
assetsService._loadInitAssets().then(function() {
$q.all([
userService.loadMomentLocaleForCurrentUser(),
tourService.registerAllTours()
]).then(function () {
assetsService._loadInitAssets().then(function () {
//Register all of the tours on the server
tourService.registerAllTours().then(function () {
appReady(data);
appReady(data);
// Auto start intro tour
tourService.getTourByAlias("umbIntroIntroduction").then(function (introTour) {
// start intro tour if it hasn't been completed or disabled
if (introTour && introTour.disabled !== true && introTour.completed !== true) {
tourService.startTour(introTour);
}
});
}, function () {
appAuthenticated = true;
appReady(data);
tourService.registerAllTours().then(function () {
// Auto start intro tour
tourService.getTourByAlias("umbIntroIntroduction").then(function (introTour) {
// start intro tour if it hasn't been completed or disabled
if (introTour && introTour.disabled !== true && introTour.completed !== true) {
tourService.startTour(introTour);
}
});
});
});
@@ -113,9 +107,6 @@ app.run(['userService', '$q', '$log', '$rootScope', '$location', 'queryStrings',
});
/* this will initialize the navigation service once the application has started */
navigationService.init();
//check for touch device, add to global appState
//var touchDevice = ("ontouchstart" in window || window.touch || window.navigator.msMaxTouchPoints === 5 || window.DocumentTouch && document instanceof DocumentTouch);
var touchDevice = /android|webos|iphone|ipad|ipod|blackberry|iemobile|touch/i.test(navigator.userAgent.toLowerCase());

View File

@@ -1,11 +1,10 @@
app.config(function ($routeProvider) {
/**
* This determines if the route can continue depending on authentication and initialization requirements
* @param {boolean} authRequired If true, it first checks if the user is authenticated and will resolve successfully
* @param {boolean} authRequired If true, it checks if the user is authenticated and will resolve successfully
otherwise the route will fail and the $routeChangeError event will execute, in that handler we will redirect to the rejected
path that is resolved from this method and prevent default (prevent the route from executing)
* @param {boolean} navRequired if true, the route can only continue once the main navigation is ready
* @returns {promise}
*/
var canRoute = function(authRequired) {
@@ -13,7 +12,7 @@ app.config(function ($routeProvider) {
return {
/** Checks that the user is authenticated, then ensures that are requires assets are loaded */
isAuthenticatedAndReady: function ($q, userService, $route, assetsService, appState) {
//don't need to check if we've redirected to login and we've already checked auth
if (!$route.current.params.section
&& ($route.current.params.check === false || $route.current.params.check === "false")) {
@@ -22,7 +21,8 @@ app.config(function ($routeProvider) {
return userService.isAuthenticated()
.then(function () {
//before proceeding all initial assets must be loaded
return assetsService._loadInitAssets().then(function () {
//This could be the first time has loaded after the user has logged in, in this case
@@ -42,7 +42,7 @@ app.config(function ($routeProvider) {
// U4-5430, Benjamin Howarth
// We need to change the current route params if the user only has access to a single section
// To do this we need to grab the current user's allowed sections, then reject the promise with the correct path.
if (user.allowedSections.indexOf($route.current.params.section) > -1) {
if (user.allowedSections.indexOf($route.current.params.section) > -1) {
//this will resolve successfully so the route will continue
return $q.when(true);
} else {
@@ -123,7 +123,7 @@ app.config(function ($routeProvider) {
else {
//there's no custom route path so continue as normal
$routeParams.url = "dashboard.aspx?app=" + $routeParams.section;
$scope.templateUrl = 'views/common/dashboard.html';
$scope.templateUrl = 'views/common/dashboard.html';
}
});
},
@@ -185,5 +185,7 @@ app.config(function ($routeProvider) {
}).config(function ($locationProvider) {
//$locationProvider.html5Mode(false).hashPrefix('!'); //turn html5 mode off
// $locationProvider.html5Mode(true); //turn html5 mode on
// $locationProvider.html5Mode(true); //turn html5 mode on
});

View File

@@ -18,11 +18,8 @@
<!-- the tree -->
<div id="tree" ng-show="authenticated">
<umb-tree
cachekey="{{treeCacheKey}}"
api="treeApi"
on-init="onTreeInit()"
section="{{currentSection}}"
customtreeparams="{{customTreeParams}}">
on-init="onTreeInit()" >
</umb-tree>
</div>
</div>

View File

@@ -21,7 +21,7 @@ function ContentEditController($scope, $routeParams, contentResource) {
$scope.getScaffoldMethod = $routeParams.blueprintId ? scaffoldBlueprint : scaffoldEmpty;
$scope.page = $routeParams.page;
$scope.isNew = $routeParams.create;
$scope.culture = $routeParams.cculture;
$scope.culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; //load the default culture selected in the main tree if any
}
angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController);

View File

@@ -64,7 +64,7 @@
<umb-navigation></umb-navigation>
<section id="contentwrapper" ng-if="navReady">
<section id="contentwrapper">
<div id="contentcolumn" ng-view></div>
</section>