Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b686dc6362 | |||
| ab0c4d2700 | |||
| 8b8a8c0ccb | |||
| 2f84e76952 | |||
| 12a30d3ffd | |||
| 7e1f90fb3b | |||
| feb5567621 | |||
| ab3202b560 | |||
| e10ce234db |
@@ -16,31 +16,13 @@ module.exports = {
|
||||
//processed in the js task
|
||||
js: {
|
||||
preview: { files: ["./src/preview/**/*.js"], out: "umbraco.preview.js" },
|
||||
installer: { files: ["./src/installer/**/*.js"], out: "umbraco.installer.js" },
|
||||
installer: { files: ["./src/installer/**/*.js"], out: "umbraco.installer.js" },
|
||||
controllers: { files: ["./src/{views,controllers}/**/*.controller.js"], out: "umbraco.controllers.js" },
|
||||
directives: { files: ["./src/common/directives/**/*.js"], out: "umbraco.directives.js" },
|
||||
filters: { files: ["./src/common/filters/**/*.js"], out: "umbraco.filters.js" },
|
||||
resources: { files: ["./src/common/resources/**/*.js"], out: "umbraco.resources.js" },
|
||||
services: { files: ["./src/common/services/**/*.js"], out: "umbraco.services.js" },
|
||||
security: { files: ["./src/common/interceptors/**/*.js"], out: "umbraco.interceptors.js" },
|
||||
|
||||
//the controllers for views
|
||||
controllers: {
|
||||
files: [
|
||||
"./src/views/**/*.controller.js",
|
||||
"./src/*.controller.js"
|
||||
], out: "umbraco.controllers.js"
|
||||
},
|
||||
|
||||
//directives/components
|
||||
// - any JS file found in common / directives or common/ components
|
||||
// - any JS file found inside views that has the suffix .directive.js or .component.js
|
||||
directives: {
|
||||
files: [
|
||||
"./src/common/directives/_module.js",
|
||||
"./src/{common/directives,common/components}/**/*.js",
|
||||
"./src/{views}/**/*.{directive,component}.js"
|
||||
],
|
||||
out: "umbraco.directives.js"
|
||||
},
|
||||
security: { files: ["./src/common/interceptors/**/*.js"], out: "umbraco.interceptors.js" }
|
||||
},
|
||||
|
||||
//selectors for copying all views into the build
|
||||
@@ -52,7 +34,7 @@ module.exports = {
|
||||
|
||||
//globs for file-watching
|
||||
globs:{
|
||||
views: ["./src/views/**/*.html", "./src/common/directives/**/*.html", "./src/common/components/**/*.html" ],
|
||||
views: "./src/views/**/*.html",
|
||||
less: "./src/less/**/*.less",
|
||||
js: "./src/*.js",
|
||||
lib: "./lib/**/*",
|
||||
|
||||
+10
-2
@@ -1,7 +1,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function ContentNodeInfoDirective($timeout, logResource, eventsService, userService, localizationService, dateHelper, editorService, redirectUrlsResource, overlayService) {
|
||||
function ContentNodeInfoDirective($timeout, logResource, eventsService, userService, localizationService, dateHelper, editorService, redirectUrlsResource, overlayService, entityResource) {
|
||||
|
||||
function link(scope) {
|
||||
|
||||
@@ -16,8 +16,12 @@
|
||||
scope.disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
|
||||
scope.allowChangeDocumentType = false;
|
||||
scope.allowChangeTemplate = false;
|
||||
scope.allTemplates = [];
|
||||
|
||||
function onInit() {
|
||||
entityResource.getAll("Template").then(function (templates) {
|
||||
scope.allTemplates = templates;
|
||||
});
|
||||
|
||||
// set currentVariant
|
||||
scope.currentVariant = _.find(scope.node.variants, (v) => v.active);
|
||||
@@ -158,8 +162,12 @@
|
||||
}
|
||||
|
||||
scope.openTemplate = function () {
|
||||
var template = _.findWhere(scope.allTemplates, {alias: scope.node.template})
|
||||
if (!template) {
|
||||
return;
|
||||
}
|
||||
var templateEditor = {
|
||||
id: scope.node.templateId,
|
||||
id: template.id,
|
||||
submit: function (model) {
|
||||
editorService.close();
|
||||
},
|
||||
|
||||
+19
-2
@@ -1,7 +1,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function EditorContentHeader(serverValidationManager) {
|
||||
function EditorContentHeader(serverValidationManager, localizationService, editorState) {
|
||||
|
||||
function link(scope, el, attr, ctrl) {
|
||||
|
||||
@@ -13,7 +13,24 @@
|
||||
if (!scope.serverValidationAliasField) {
|
||||
scope.serverValidationAliasField = "Alias";
|
||||
}
|
||||
|
||||
|
||||
scope.isNew = scope.content.state == "NotCreated";
|
||||
|
||||
localizationService.localizeMany([
|
||||
scope.isNew ? "placeholders_a11yCreateItem" : "placeholders_a11yEdit",
|
||||
"placeholders_a11yName"]
|
||||
).then(function (data) {
|
||||
scope.a11yMessage = data[0];
|
||||
scope.a11yName = data[1];
|
||||
if (!scope.isNew) {
|
||||
scope.a11yMessage += " " + scope.content.name;
|
||||
|
||||
} else {
|
||||
var name = editorState.current.contentTypeName;
|
||||
scope.a11yMessage += " " + name;
|
||||
scope.a11yName = name + " " + scope.a11yName;
|
||||
}
|
||||
});
|
||||
scope.vm = {};
|
||||
scope.vm.dropdownOpen = false;
|
||||
scope.vm.currentVariant = "";
|
||||
|
||||
+1
-3
@@ -16,9 +16,7 @@
|
||||
editor.moveRight = true;
|
||||
editor.level = 0;
|
||||
editor.styleIndex = 0;
|
||||
|
||||
editor.infinityMode = true;
|
||||
|
||||
|
||||
// push the new editor to the dom
|
||||
scope.editors.push(editor);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
z-index: @zIndexEditor;
|
||||
}
|
||||
|
||||
.umb-editor--infinityMode {
|
||||
.umb-editor--infiniteMode {
|
||||
transform: none;
|
||||
will-change: transform;
|
||||
transition: transform 400ms ease-in-out;
|
||||
|
||||
+1
-1
@@ -268,7 +268,7 @@ angular.module("umbraco")
|
||||
|
||||
// also make sure the node is not trashed
|
||||
if (nodePath.indexOf($scope.startNodeId.toString()) !== -1 && node.trashed === false) {
|
||||
$scope.gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder" });
|
||||
$scope.gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder", path: node.path });
|
||||
return true;
|
||||
} else {
|
||||
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
|
||||
|
||||
+22
-16
@@ -11,24 +11,30 @@
|
||||
<div class="flex items-center" style="flex: 1;">
|
||||
|
||||
<div id="nameField" class="umb-editor-header__name-and-description" style="flex: 1 1 auto;">
|
||||
<div>
|
||||
<p tabindex="0" class="sr-only">
|
||||
{{a11yMessage}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="umb-editor-header__name-wrapper">
|
||||
<label for="headerName" class="sr-only">{{a11yName}}</label>
|
||||
<ng-form name="headerNameForm">
|
||||
<input
|
||||
data-element="editor-name-field"
|
||||
type="text"
|
||||
class="umb-editor-header__name-input"
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_entername"
|
||||
name="headerName"
|
||||
ng-model="name"
|
||||
ng-class="{'name-is-empty': $parent.name===null || $parent.name===''}"
|
||||
ng-disabled="nameDisabled"
|
||||
umb-auto-focus
|
||||
val-server-field="{{serverValidationNameField}}"
|
||||
required
|
||||
aria-required="true"
|
||||
aria-invalid="{{contentForm.headerNameForm.headerName.$invalid ? true : false}}"
|
||||
autocomplete="off" maxlength="255" />
|
||||
<input data-element="editor-name-field"
|
||||
type="text"
|
||||
class="umb-editor-header__name-input"
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_entername"
|
||||
name="headerName"
|
||||
id="headerName"
|
||||
ng-model="name"
|
||||
ng-class="{'name-is-empty': $parent.name===null || $parent.name===''}"
|
||||
ng-disabled="nameDisabled"
|
||||
umb-auto-focus
|
||||
val-server-field="{{serverValidationNameField}}"
|
||||
required
|
||||
aria-required="true"
|
||||
aria-invalid="{{contentForm.headerNameForm.headerName.$invalid ? true : false}}"
|
||||
autocomplete="off" maxlength="255" />
|
||||
</ng-form>
|
||||
|
||||
<a ng-if="content.variants.length > 0 && hideChangeVariant !== true" class="umb-variant-switcher__toggle" href="" ng-click="vm.dropdownOpen = !vm.dropdownOpen" ng-class="{'--error': vm.errorsOnOtherVariants}">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
ng-class="{'umb-editor--small': model.size === 'small',
|
||||
'umb-editor--animating': model.animating,
|
||||
'--notInFront': model.inFront !== true,
|
||||
'umb-editor--infinityMode': model.infinityMode,
|
||||
'umb-editor--infiniteMode': model.infiniteMode,
|
||||
'moveRight': model.moveRight,
|
||||
'umb-editor--n0': model.styleIndex === 0,
|
||||
'umb-editor--n1': model.styleIndex === 1,
|
||||
|
||||
@@ -24,7 +24,7 @@ function ContentEditController($scope, $routeParams, contentResource) {
|
||||
$scope.page = $routeParams.page;
|
||||
$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 === "true");
|
||||
$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
|
||||
|
||||
+14
@@ -15,6 +15,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
$scope.canAdd = function () {
|
||||
return !$scope.model.docTypes || !$scope.model.value || $scope.model.value.length < $scope.model.docTypes.length;
|
||||
}
|
||||
|
||||
$scope.remove = function (index) {
|
||||
$scope.model.value.splice(index, 1);
|
||||
}
|
||||
@@ -57,10 +61,20 @@
|
||||
|
||||
ncResources.getContentTypes().then(function (docTypes) {
|
||||
$scope.model.docTypes = docTypes;
|
||||
|
||||
// Count doctype name occurrences
|
||||
var docTypeNameOccurrences = _.countBy(docTypes, 'name');
|
||||
|
||||
// Populate document type tab dictionary
|
||||
// And append alias to name if multiple doctypes have the same name
|
||||
docTypes.forEach(function (value) {
|
||||
$scope.docTypeTabs[value.alias] = value.tabs;
|
||||
|
||||
value.displayName = value.name;
|
||||
|
||||
if (docTypeNameOccurrences[value.name] > 1) {
|
||||
value.displayName += " (" + value.alias + ")";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+5
-5
@@ -23,7 +23,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<select id="{{model.alias}}_doctype_select"
|
||||
ng-options="dt.alias as dt.name for dt in selectableDocTypesFor(config) | orderBy: 'name'"
|
||||
ng-options="dt.alias as dt.displayName for dt in selectableDocTypesFor(config) | orderBy: 'name'"
|
||||
ng-model="config.ncAlias" required></select>
|
||||
</td>
|
||||
<td>
|
||||
@@ -35,17 +35,17 @@
|
||||
<input type="text" ng-model="config.nameTemplate" />
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn-danger" ng-click="remove($index)">
|
||||
<button type="button" class="btn btn-danger" ng-click="remove($index)">
|
||||
<localize key="general_delete">Delete</localize>
|
||||
</a>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div>
|
||||
<a class="btn" ng-click="add()">
|
||||
<button type="button" class="btn" ng-click="add()" ng-disabled="!canAdd()">
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
</button>
|
||||
<i class="icon icon-help-alt medium umb-nested-content__help-icon" ng-click="showHelpText = !showHelpText"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -527,6 +527,9 @@
|
||||
<key alias="anchor">#value or ?key=value</key>
|
||||
<key alias="enterAlias">Enter alias...</key>
|
||||
<key alias="generatingAlias">Generating alias...</key>
|
||||
<key alias="a11yCreateItem">Create item</key>
|
||||
<key alias="a11yEdit">Edit</key>
|
||||
<key alias="a11yName">Name</key>
|
||||
</area>
|
||||
<area alias="editcontenttype">
|
||||
<key alias="createListView" version="7.2">Create custom list view</key>
|
||||
|
||||
@@ -530,6 +530,9 @@
|
||||
<key alias="anchor">#value or ?key=value</key>
|
||||
<key alias="enterAlias">Enter alias...</key>
|
||||
<key alias="generatingAlias">Generating alias...</key>
|
||||
<key alias="a11yCreateItem">Create item</key>
|
||||
<key alias="a11yEdit">Edit</key>
|
||||
<key alias="a11yName">Name</key>
|
||||
</area>
|
||||
<area alias="editcontenttype">
|
||||
<key alias="createListView" version="7.2">Create custom list view</key>
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name Umbraco.MainController
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The main application controller
|
||||
*
|
||||
*/
|
||||
function MainController($scope, $location, appState, treeService, notificationsService,
|
||||
userService, historyService, updateChecker, navigationService, eventsService,
|
||||
tmhDynamicLocale, localStorageService, editorService, overlayService) {
|
||||
|
||||
//the null is important because we do an explicit bool check on this in the view
|
||||
$scope.authenticated = null;
|
||||
$scope.touchDevice = appState.getGlobalState("touchDevice");
|
||||
$scope.infiniteMode = false;
|
||||
$scope.overlay = {};
|
||||
$scope.drawer = {};
|
||||
$scope.search = {};
|
||||
$scope.login = {};
|
||||
$scope.tabbingActive = false;
|
||||
|
||||
// There are a number of ways to detect when a focus state should be shown when using the tab key and this seems to be the simplest solution.
|
||||
// For more information about this approach, see https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2
|
||||
function handleFirstTab(evt) {
|
||||
if (evt.keyCode === 9) {
|
||||
$scope.tabbingActive = true;
|
||||
$scope.$digest();
|
||||
window.removeEventListener('keydown', handleFirstTab);
|
||||
window.addEventListener('mousedown', disableTabbingActive);
|
||||
}
|
||||
}
|
||||
|
||||
function disableTabbingActive(evt) {
|
||||
$scope.tabbingActive = false;
|
||||
$scope.$digest();
|
||||
window.removeEventListener('mousedown', disableTabbingActive);
|
||||
window.addEventListener("keydown", handleFirstTab);
|
||||
}
|
||||
|
||||
window.addEventListener("keydown", handleFirstTab);
|
||||
|
||||
|
||||
$scope.removeNotification = function (index) {
|
||||
notificationsService.remove(index);
|
||||
};
|
||||
|
||||
$scope.closeSearch = function() {
|
||||
appState.setSearchState("show", false);
|
||||
};
|
||||
|
||||
$scope.showLoginScreen = function(isTimedOut) {
|
||||
$scope.login.isTimedOut = isTimedOut;
|
||||
$scope.login.show = true;
|
||||
};
|
||||
|
||||
$scope.hideLoginScreen = function() {
|
||||
$scope.login.show = false;
|
||||
};
|
||||
|
||||
var evts = [];
|
||||
|
||||
//when a user logs out or timesout
|
||||
evts.push(eventsService.on("app.notAuthenticated", function (evt, data) {
|
||||
$scope.authenticated = null;
|
||||
$scope.user = null;
|
||||
const isTimedOut = data && data.isTimedOut ? true : false;
|
||||
$scope.showLoginScreen(isTimedOut);
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("app.userRefresh", function(evt) {
|
||||
userService.refreshCurrentUser().then(function(data) {
|
||||
$scope.user = data;
|
||||
|
||||
//Load locale file
|
||||
if ($scope.user.locale) {
|
||||
tmhDynamicLocale.set($scope.user.locale);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
//when the app is ready/user is logged in, setup the data
|
||||
evts.push(eventsService.on("app.ready", function (evt, data) {
|
||||
|
||||
$scope.authenticated = data.authenticated;
|
||||
$scope.user = data.user;
|
||||
|
||||
updateChecker.check().then(function (update) {
|
||||
if (update && update !== "null") {
|
||||
if (update.type !== "None") {
|
||||
var notification = {
|
||||
headline: "Update available",
|
||||
message: "Click to download",
|
||||
sticky: true,
|
||||
type: "info",
|
||||
url: update.url
|
||||
};
|
||||
notificationsService.add(notification);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//if the user has changed we need to redirect to the root so they don't try to continue editing the
|
||||
//last item in the URL (NOTE: the user id can equal zero, so we cannot just do !data.lastUserId since that will resolve to true)
|
||||
if (data.lastUserId !== undefined && data.lastUserId !== null && data.lastUserId !== data.user.id) {
|
||||
|
||||
var section = appState.getSectionState("currentSection");
|
||||
if (section) {
|
||||
//if there's a section already assigned, reload it so the tree is cleared
|
||||
navigationService.reloadSection(section);
|
||||
}
|
||||
|
||||
$location.path("/").search("");
|
||||
historyService.removeAll();
|
||||
treeService.clearCache();
|
||||
editorService.closeAll();
|
||||
overlayService.close();
|
||||
|
||||
//if the user changed, clearout local storage too - could contain sensitive data
|
||||
localStorageService.clearAll();
|
||||
}
|
||||
|
||||
//if this is a new login (i.e. the user entered credentials), then clear out local storage - could contain sensitive data
|
||||
if (data.loginType === "credentials") {
|
||||
localStorageService.clearAll();
|
||||
}
|
||||
|
||||
//Load locale file
|
||||
if ($scope.user.locale) {
|
||||
tmhDynamicLocale.set($scope.user.locale);
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
// events for search
|
||||
evts.push(eventsService.on("appState.searchState.changed", function (e, args) {
|
||||
if (args.key === "show") {
|
||||
$scope.search.show = args.value;
|
||||
}
|
||||
}));
|
||||
|
||||
// events for drawer
|
||||
// manage the help dialog by subscribing to the showHelp appState
|
||||
evts.push(eventsService.on("appState.drawerState.changed", function (e, args) {
|
||||
// set view
|
||||
if (args.key === "view") {
|
||||
$scope.drawer.view = args.value;
|
||||
}
|
||||
// set custom model
|
||||
if (args.key === "model") {
|
||||
$scope.drawer.model = args.value;
|
||||
}
|
||||
// show / hide drawer
|
||||
if (args.key === "showDrawer") {
|
||||
$scope.drawer.show = args.value;
|
||||
}
|
||||
}));
|
||||
|
||||
// events for overlays
|
||||
evts.push(eventsService.on("appState.overlay", function (name, args) {
|
||||
$scope.overlay = args;
|
||||
}));
|
||||
|
||||
// events for tours
|
||||
evts.push(eventsService.on("appState.tour.start", function (name, args) {
|
||||
$scope.tour = args;
|
||||
$scope.tour.show = true;
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("appState.tour.end", function () {
|
||||
$scope.tour = null;
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("appState.tour.complete", function () {
|
||||
$scope.tour = null;
|
||||
}));
|
||||
|
||||
// events for backdrop
|
||||
evts.push(eventsService.on("appState.backdrop", function (name, args) {
|
||||
$scope.backdrop = args;
|
||||
}));
|
||||
|
||||
// event for infinite editors
|
||||
evts.push(eventsService.on("appState.editors.open", function (name, args) {
|
||||
$scope.infiniteMode = args && args.editors.length > 0 ? true : false;
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("appState.editors.close", function (name, args) {
|
||||
$scope.infiniteMode = args && args.editors.length > 0 ? true : false;
|
||||
}));
|
||||
|
||||
//ensure to unregister from all events!
|
||||
$scope.$on('$destroy', function () {
|
||||
for (var e in evts) {
|
||||
eventsService.unsubscribe(evts[e]);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
//register it
|
||||
angular.module('umbraco').controller("Umbraco.MainController", MainController).
|
||||
config(function (tmhDynamicLocaleProvider) {
|
||||
//Set url for locale files
|
||||
tmhDynamicLocaleProvider.localeLocationPattern('lib/angular-i18n/angular-locale_{{locale}}.js');
|
||||
});
|
||||
@@ -1,517 +0,0 @@
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name Umbraco.NavigationController
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Handles the section area of the app
|
||||
*
|
||||
* @param {navigationService} navigationService A reference to the navigationService
|
||||
*/
|
||||
function NavigationController($scope, $rootScope, $location, $log, $q, $routeParams, $timeout, $cookies, treeService, appState, navigationService, keyboardService, historyService, eventsService, angularHelper, languageResource, contentResource, editorState) {
|
||||
|
||||
//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
|
||||
$scope.onTreeInit = function () {
|
||||
|
||||
$scope.treeApi.callbacks.treeNodeExpanded(nodeExpandedHandler);
|
||||
|
||||
//when a tree is loaded into a section, we need to put it into appState
|
||||
$scope.treeApi.callbacks.treeLoaded(function (args) {
|
||||
appState.setTreeState("currentRootNode", args.tree);
|
||||
});
|
||||
|
||||
//when a tree node is synced this event will fire, this allows us to set the currentNode
|
||||
$scope.treeApi.callbacks.treeSynced(function (args) {
|
||||
|
||||
if (args.activate === undefined || args.activate === true) {
|
||||
//set the current selected node
|
||||
appState.setTreeState("selectedNode", args.node);
|
||||
//when a node is activated, this is the same as clicking it and we need to set the
|
||||
//current menu item to be this node as well.
|
||||
//appState.setMenuState("currentNode", args.node);// Niels: No, we are setting it from the dialog.
|
||||
}
|
||||
});
|
||||
|
||||
//this reacts to the options item in the tree
|
||||
$scope.treeApi.callbacks.treeOptionsClick(function (args) {
|
||||
args.event.stopPropagation();
|
||||
args.event.preventDefault();
|
||||
|
||||
//Set the current action node (this is not the same as the current selected node!)
|
||||
//appState.setMenuState("currentNode", args.node);// Niels: No, we are setting it from the dialog.
|
||||
|
||||
if (args.event && args.event.altKey) {
|
||||
args.skipDefault = true;
|
||||
}
|
||||
|
||||
navigationService.showMenu(args);
|
||||
});
|
||||
|
||||
$scope.treeApi.callbacks.treeNodeAltSelect(function (args) {
|
||||
args.event.stopPropagation();
|
||||
args.event.preventDefault();
|
||||
|
||||
args.skipDefault = true;
|
||||
navigationService.showMenu(args);
|
||||
});
|
||||
|
||||
//this reacts to tree items themselves being clicked
|
||||
//the tree directive should not contain any handling, simply just bubble events
|
||||
$scope.treeApi.callbacks.treeNodeSelect(function (args) {
|
||||
var n = args.node;
|
||||
args.event.stopPropagation();
|
||||
args.event.preventDefault();
|
||||
|
||||
if (n.metaData && n.metaData["jsClickCallback"] && angular.isString(n.metaData["jsClickCallback"]) && n.metaData["jsClickCallback"] !== "") {
|
||||
//this is a legacy tree node!
|
||||
var jsPrefix = "javascript:";
|
||||
var js;
|
||||
if (n.metaData["jsClickCallback"].startsWith(jsPrefix)) {
|
||||
js = n.metaData["jsClickCallback"].substr(jsPrefix.length);
|
||||
}
|
||||
else {
|
||||
js = n.metaData["jsClickCallback"];
|
||||
}
|
||||
try {
|
||||
var func = eval(js);
|
||||
//this is normally not necessary since the eval above should execute the method and will return nothing.
|
||||
if (func != null && (typeof func === "function")) {
|
||||
func.call();
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
$log.error("Error evaluating js callback from legacy tree node: " + ex);
|
||||
}
|
||||
}
|
||||
else if (n.routePath) {
|
||||
//add action to the history service
|
||||
historyService.add({ name: n.name, link: n.routePath, icon: n.icon });
|
||||
|
||||
//put this node into the tree state
|
||||
appState.setTreeState("selectedNode", args.node);
|
||||
//when a node is clicked we also need to set the active menu node to this node
|
||||
//appState.setMenuState("currentNode", args.node);
|
||||
|
||||
//not legacy, lets just set the route value and clear the query string if there is one.
|
||||
$location.path(n.routePath);
|
||||
navigationService.clearSearch();
|
||||
}
|
||||
else if (n.section) {
|
||||
$location.path(n.section);
|
||||
navigationService.clearSearch();
|
||||
}
|
||||
|
||||
navigationService.hideNavigation();
|
||||
});
|
||||
|
||||
return treeInitPromise.promise;
|
||||
}
|
||||
|
||||
//set up our scope vars
|
||||
$scope.showContextMenuDialog = false;
|
||||
$scope.showContextMenu = false;
|
||||
$scope.showSearchResults = false;
|
||||
$scope.menuDialogTitle = null;
|
||||
$scope.menuActions = [];
|
||||
$scope.menuNode = null;
|
||||
$scope.languages = [];
|
||||
$scope.selectedLanguage = {};
|
||||
$scope.page = {};
|
||||
$scope.page.languageSelectorIsOpen = false;
|
||||
|
||||
$scope.currentSection = null;
|
||||
$scope.customTreeParams = null;
|
||||
$scope.treeCacheKey = "_";
|
||||
$scope.showNavigation = appState.getGlobalState("showNavigation");
|
||||
// tracks all expanded paths so when the language is switched we can resync it with the already loaded paths
|
||||
var expandedPaths = [];
|
||||
|
||||
//trigger search with a hotkey:
|
||||
keyboardService.bind("ctrl+shift+s", function () {
|
||||
navigationService.showSearch();
|
||||
});
|
||||
|
||||
//// TODO: remove this it's not a thing
|
||||
//$scope.selectedId = navigationService.currentId;
|
||||
|
||||
var isInit = false;
|
||||
var evts = [];
|
||||
|
||||
//Listen for global state changes
|
||||
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) {
|
||||
if (args.key === "showMenuDialog") {
|
||||
$scope.showContextMenuDialog = args.value;
|
||||
}
|
||||
if (args.key === "dialogTemplateUrl") {
|
||||
$scope.dialogTemplateUrl = args.value;
|
||||
}
|
||||
if (args.key === "showMenu") {
|
||||
$scope.showContextMenu = args.value;
|
||||
}
|
||||
if (args.key === "dialogTitle") {
|
||||
$scope.menuDialogTitle = args.value;
|
||||
}
|
||||
if (args.key === "menuActions") {
|
||||
$scope.menuActions = args.value;
|
||||
}
|
||||
if (args.key === "currentNode") {
|
||||
$scope.menuNode = args.value;
|
||||
}
|
||||
}));
|
||||
|
||||
//Listen for tree state changes
|
||||
evts.push(eventsService.on("appState.treeState.changed", function (e, args) {
|
||||
if (args.key === "currentRootNode") {
|
||||
|
||||
//if the changed state is the currentRootNode, determine if this is a full screen app
|
||||
if (args.value.root && args.value.root.containsTrees === false) {
|
||||
$rootScope.emptySection = true;
|
||||
}
|
||||
else {
|
||||
$rootScope.emptySection = false;
|
||||
}
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
//Listen for section state changes
|
||||
evts.push(eventsService.on("appState.sectionState.changed", function (e, args) {
|
||||
|
||||
//section changed
|
||||
if (args.key === "currentSection" && $scope.currentSection != args.value) {
|
||||
//before loading the main tree we need to ensure that the nav is ready
|
||||
navigationService.waitForNavReady().then(() => {
|
||||
$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) {
|
||||
loadLanguages().then(function (languages) {
|
||||
$scope.languages = languages;
|
||||
});
|
||||
}));
|
||||
|
||||
//Emitted when a language is created or an existing one saved/edited
|
||||
evts.push(eventsService.on("editors.languages.languageSaved", function (e, args) {
|
||||
if(args.isNew){
|
||||
//A new language has been created - reload languages for tree
|
||||
loadLanguages().then(function (languages) {
|
||||
$scope.languages = languages;
|
||||
});
|
||||
}
|
||||
else if(args.language.isDefault){
|
||||
//A language was saved and was set to be the new default (refresh the tree, so its at the top)
|
||||
loadLanguages().then(function (languages) {
|
||||
$scope.languages = languages;
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
//when a user logs out or timesout
|
||||
evts.push(eventsService.on("app.notAuthenticated", function () {
|
||||
$scope.authenticated = false;
|
||||
}));
|
||||
|
||||
//when the application is ready and the user is authorized, setup the data
|
||||
//this will occur anytime a new user logs in!
|
||||
evts.push(eventsService.on("app.ready", function (evt, data) {
|
||||
$scope.authenticated = true;
|
||||
ensureInit();
|
||||
}));
|
||||
|
||||
// event for infinite editors
|
||||
evts.push(eventsService.on("appState.editors.open", function (name, args) {
|
||||
$scope.infiniteMode = args && args.editors.length > 0 ? true : false;
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("appState.editors.close", function (name, args) {
|
||||
$scope.infiniteMode = args && args.editors.length > 0 ? true : false;
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("treeService.removeNode", function (e, args) {
|
||||
//check to see if the current page has been removed
|
||||
|
||||
var currentEditorState = editorState.getCurrent()
|
||||
if (currentEditorState && currentEditorState.id.toString() === args.node.id.toString()) {
|
||||
//current page is loaded, so navigate to root
|
||||
var section = appState.getSectionState("currentSection");
|
||||
$location.path("/" + section);
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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 (found) {
|
||||
//set the route param
|
||||
found.active = true;
|
||||
$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
|
||||
}
|
||||
|
||||
if (queryString) {
|
||||
$scope.customTreeParams = queryString;
|
||||
$scope.treeCacheKey = queryString; // this tree uses caching but we need to change it's cache key per lang
|
||||
}
|
||||
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 ensureInit() {
|
||||
|
||||
//only run once ever!
|
||||
if (isInit) {
|
||||
return;
|
||||
}
|
||||
|
||||
isInit = 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();
|
||||
}
|
||||
|
||||
//keep track of the current section when it changes
|
||||
if ($scope.currentSection != $routeParams.section) {
|
||||
appState.setSectionState("currentSection", $routeParams.section);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This loads the language data, if the are no variant content types configured this will return no languages
|
||||
*/
|
||||
function loadLanguages() {
|
||||
|
||||
return contentResource.allowsCultureVariation().then(function (b) {
|
||||
if (b === true) {
|
||||
return languageResource.getAll()
|
||||
} else {
|
||||
return $q.when([]); //resolve an empty collection
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once during init to initialize the navigation/tree/languages
|
||||
*/
|
||||
function initNav() {
|
||||
// load languages
|
||||
loadLanguages().then(function (languages) {
|
||||
|
||||
$scope.languages = languages;
|
||||
|
||||
if ($scope.languages.length > 1) {
|
||||
//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) {
|
||||
// no culture in the request, let's look for one in the cookie that's set when changing language
|
||||
var defaultCulture = $cookies.get("UMB_MCULTURE");
|
||||
if (!defaultCulture || !_.find($scope.languages, function (l) {
|
||||
return l.culture.toLowerCase() === defaultCulture.toLowerCase();
|
||||
})) {
|
||||
// no luck either, look for the default language
|
||||
var defaultLang = _.find($scope.languages, function (l) {
|
||||
return l.isDefault;
|
||||
});
|
||||
if (defaultLang) {
|
||||
defaultCulture = defaultLang.culture;
|
||||
}
|
||||
}
|
||||
$location.search("mculture", defaultCulture ? defaultCulture : 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 });
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function nodeExpandedHandler(args) {
|
||||
//store the reference to the expanded node path
|
||||
if (args.node) {
|
||||
treeService._trackExpandedPaths(args.node, expandedPaths);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.selectLanguage = function (language) {
|
||||
|
||||
$location.search("mculture", language.culture);
|
||||
// add the selected culture to a cookie so the user will log back into the same culture later on (cookie lifetime = one year)
|
||||
var expireDate = new Date();
|
||||
expireDate.setDate(expireDate.getDate() + 365);
|
||||
$cookies.put("UMB_MCULTURE", language.culture, {path: "/", expires: expireDate});
|
||||
|
||||
// close the language selector
|
||||
$scope.page.languageSelectorIsOpen = false;
|
||||
|
||||
configureTreeAndLanguages(); //re-bind language to the query string and update the tree params
|
||||
|
||||
//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
|
||||
|
||||
// set selected language to active
|
||||
angular.forEach($scope.languages, function(language){
|
||||
language.active = false;
|
||||
});
|
||||
language.active = true;
|
||||
|
||||
angularHelper.executeSequentialPromises(promises);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
//this reacts to the options item in the tree
|
||||
// TODO: migrate to nav service
|
||||
// TODO: is this used?
|
||||
$scope.searchShowMenu = function (ev, args) {
|
||||
//always skip default
|
||||
args.skipDefault = true;
|
||||
navigationService.showMenu(args);
|
||||
};
|
||||
|
||||
// TODO: migrate to nav service
|
||||
// TODO: is this used?
|
||||
$scope.searchHide = function () {
|
||||
navigationService.hideSearch();
|
||||
};
|
||||
|
||||
//the below assists with hiding/showing the tree
|
||||
var treeActive = false;
|
||||
|
||||
//Sets a service variable as soon as the user hovers the navigation with the mouse
|
||||
//used by the leaveTree method to delay hiding
|
||||
$scope.enterTree = function (event) {
|
||||
treeActive = true;
|
||||
};
|
||||
|
||||
// Hides navigation tree, with a short delay, is cancelled if the user moves the mouse over the tree again
|
||||
$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 () {
|
||||
if (!treeActive) {
|
||||
navigationService.hideTree();
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.toggleLanguageSelector = function () {
|
||||
$scope.page.languageSelectorIsOpen = !$scope.page.languageSelectorIsOpen;
|
||||
};
|
||||
|
||||
//ensure to unregister from all events!
|
||||
$scope.$on('$destroy', function () {
|
||||
for (var e in evts) {
|
||||
eventsService.unsubscribe(evts[e]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//register it
|
||||
angular.module('umbraco').controller("Umbraco.NavigationController", NavigationController);
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.Cache
|
||||
@@ -8,11 +9,13 @@ namespace Umbraco.Web.Cache
|
||||
public sealed class TemplateCacheRefresher : CacheRefresherBase<TemplateCacheRefresher>
|
||||
{
|
||||
private readonly IdkMap _idkMap;
|
||||
private readonly IContentTypeCommonRepository _contentTypeCommonRepository;
|
||||
|
||||
public TemplateCacheRefresher(AppCaches appCaches, IdkMap idkMap)
|
||||
public TemplateCacheRefresher(AppCaches appCaches, IdkMap idkMap, IContentTypeCommonRepository contentTypeCommonRepository)
|
||||
: base(appCaches)
|
||||
{
|
||||
_idkMap = idkMap;
|
||||
_contentTypeCommonRepository = contentTypeCommonRepository;
|
||||
}
|
||||
|
||||
#region Define
|
||||
@@ -45,6 +48,7 @@ namespace Umbraco.Web.Cache
|
||||
// it has an associated template.
|
||||
ClearAllIsolatedCacheByEntityType<IContent>();
|
||||
ClearAllIsolatedCacheByEntityType<IContentType>();
|
||||
_contentTypeCommonRepository.ClearCache();
|
||||
|
||||
base.Remove(id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user