true to hide icon.
@param {string=} alias show and edit the content alias.
+@param {boolean=} aliasLocked Set to true to lock the alias.
@param {boolean=} hideAlias Set to true to hide alias.
@param {string=} description Add a description to the content.
@param {boolean=} hideDescription Set to true to hide description.
@@ -239,12 +240,13 @@ Use this directive to construct a header inside the main editor window.
if (scope.editorfor) {
localizeVars.push(scope.editorfor);
}
- localizationService.localizeMany(localizeVars).then(function (data) {
+ localizationService.localizeMany(localizeVars).then(function(data) {
setAccessibilityForEditor(data);
scope.loading = false;
});
+ } else {
+ scope.loading = false;
}
-
scope.goBack = function () {
if (scope.onBack) {
scope.onBack();
@@ -346,6 +348,7 @@ Use this directive to construct a header inside the main editor window.
icon: "=",
hideIcon: "@",
alias: "=",
+ aliasLocked: "<",
hideAlias: "=",
description: "=",
hideDescription: "@",
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js
index 302378b8c0..31e797c6b4 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js
@@ -4,7 +4,7 @@
* @restrict E
**/
angular.module("umbraco.directives")
- .directive('umbProperty', function (umbPropEditorHelper, userService) {
+ .directive('umbProperty', function (userService) {
return {
scope: {
property: "=",
@@ -16,22 +16,30 @@ angular.module("umbraco.directives")
replace: true,
templateUrl: 'views/components/property/umb-property.html',
link: function (scope) {
+
+ scope.propertyActions = [];
+
userService.getCurrentUser().then(function (u) {
var isAdmin = u.userGroups.indexOf('admin') !== -1;
scope.propertyAlias = (Umbraco.Sys.ServerVariables.isDebuggingEnabled === true || isAdmin) ? scope.property.alias : null;
});
},
//Define a controller for this directive to expose APIs to other directives
- controller: function ($scope, $timeout) {
+ controller: function ($scope) {
var self = this;
-
+
//set the API properties/methods
self.property = $scope.property;
self.setPropertyError = function (errorMsg) {
$scope.property.propertyErrorMessage = errorMsg;
};
+
+ self.setPropertyActions = function(actions) {
+ $scope.propertyActions = actions;
+ };
+
}
};
});
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js
index 2bd93a4b27..3d743c7e9a 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js
@@ -65,6 +65,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use
vm.reloadNode = reloadNode;
vm.syncTree = syncTree;
vm.loadChildren = loadChildren;
+ vm.hasTree = hasTree;
//wire up the exposed api object for hosting controllers
if ($scope.api) {
@@ -72,6 +73,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use
$scope.api.load = vm.load;
$scope.api.reloadNode = vm.reloadNode;
$scope.api.syncTree = vm.syncTree;
+ $scope.api.hasTree = vm.hasTree;
}
//flag to track the last loaded section when the tree 'un-loads'. We use this to determine if we should
@@ -203,6 +205,25 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use
});
}
+ //given a tree alias, this will search the current section tree for the specified tree alias and set the current active tree to it's root node
+ function hasTree(treeAlias) {
+
+ if (!$scope.tree) {
+ throw "Err in umbtree.directive.loadActiveTree, $scope.tree is null";
+ }
+
+ if (!treeAlias) {
+ return false;
+ }
+
+ var treeRoots = getTreeRootNodes();
+ var foundTree = _.find(treeRoots, function (node) {
+ return node.metaData.treeAlias.toUpperCase() === treeAlias.toUpperCase();
+ });
+
+ return foundTree !== undefined;
+ }
+
//given a tree alias, this will search the current section tree for the specified tree alias and set the current active tree to it's root node
function loadActiveTree(treeAlias) {
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbconfirm.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbconfirm.directive.js
index 1ddd09357a..9114cfb1c1 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbconfirm.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbconfirm.directive.js
@@ -56,6 +56,7 @@ function confirmDirective() {
onCancel: '=',
caption: '@',
confirmButtonStyle: '@',
+ confirmDisabled: '',
confirmLabelKey: '@'
},
link: function (scope, element, attr, ctrl) {
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js
index 07b690ad2b..3c9e300f92 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js
@@ -549,7 +549,9 @@
property.dataTypeIcon = propertyModel.dataTypeIcon;
property.dataTypeName = propertyModel.dataTypeName;
property.validation.mandatory = propertyModel.validation.mandatory;
+ property.validation.mandatoryMessage = propertyModel.validation.mandatoryMessage;
property.validation.pattern = propertyModel.validation.pattern;
+ property.validation.patternMessage = propertyModel.validation.patternMessage;
property.showOnMemberProfile = propertyModel.showOnMemberProfile;
property.memberCanEdit = propertyModel.memberCanEdit;
property.isSensitiveValue = propertyModel.isSensitiveValue;
@@ -632,7 +634,9 @@
propertyState: "init",
validation: {
mandatory: false,
- pattern: null
+ mandatoryMessage: null,
+ pattern: null,
+ patternMessage: null
}
};
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js
index 7aedfccacf..60a5d235fe 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js
@@ -389,7 +389,7 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) {
(umbRequestHelper.getApiUrl(
"dataTypeApiBaseUrl",
"PostRenameContainer",
- { id: id, name: name })),
+ { id: id, name: encodeURIComponent(name) })),
"Failed to rename the folder with id " + id);
}
};
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js b/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js
index 647133f0b7..c3a1ba6432 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js
@@ -1,203 +1,264 @@
-/**
- * @ngdoc service
- * @name umbraco.services.clipboardService
- *
- * @requires notificationsService
- * @requires eventsService
- *
- * @description
- * Service to handle clipboard in general across the application. Responsible for handling the data both storing and retrive.
- * The service has a set way for defining a data-set by a entryType and alias, which later will be used to retrive the posible entries for a paste scenario.
- *
- */
-function clipboardService(notificationsService, eventsService, localStorageService) {
-
-
- var STORAGE_KEY = "umbClipboardService";
-
- var retriveStorage = function() {
- if (localStorageService.isSupported === false) {
- return null;
- }
- var dataJSON;
- var dataString = localStorageService.get(STORAGE_KEY);
- if (dataString != null) {
- dataJSON = JSON.parse(dataString);
- }
-
- if(dataJSON == null) {
- dataJSON = new Object();
- }
-
- if(dataJSON.entries === undefined) {
- dataJSON.entries = [];
- }
-
- return dataJSON;
- }
-
- var saveStorage = function(storage) {
- var storageString = JSON.stringify(storage);
-
- try {
- var storageJSON = JSON.parse(storageString);
- localStorageService.set(STORAGE_KEY, storageString);
-
- eventsService.emit("clipboardService.storageUpdate");
-
- return true;
- } catch(e) {
- return false;
- }
-
- return false;
- }
-
-
- var service = {};
-
- /**
- * @ngdoc method
- * @name umbraco.services.clipboardService#copy
- * @methodOf umbraco.services.clipboardService
- *
- * @param {string} type A string defining the type of data to storing, example: 'elementType', 'contentNode'
- * @param {string} alias A string defining the alias of the data to store, example: 'product'
- * @param {object} data A object containing the properties to be saved.
- *
- * @description
- * Saves a single JS-object with a type and alias to the clipboard.
- */
- service.copy = function(type, alias, data) {
-
- var storage = retriveStorage();
-
- var shallowCloneData = Object.assign({}, data);// Notice only a shallow copy, since we dont need to deep copy. (that will happen when storing the data)
- delete shallowCloneData.key;
- delete shallowCloneData.$$hashKey;
-
- var key = data.key || data.$$hashKey || console.error("missing unique key for this content");
-
- // remove previous copies of this entry:
- storage.entries = storage.entries.filter(
- (entry) => {
- return entry.unique !== key;
- }
- );
-
- var entry = {unique:key, type:type, alias:alias, data:shallowCloneData};
- storage.entries.push(entry);
-
- if (saveStorage(storage) === true) {
- notificationsService.success("Clipboard", "Copied to clipboard.");
- } else {
- notificationsService.success("Clipboard", "Couldnt copy this data to clipboard.");
- }
-
- };
-
-
- /**
- * @ngdoc method
- * @name umbraco.services.supportsCopy#supported
- * @methodOf umbraco.services.clipboardService
- *
- * @description
- * Determins wether the current browser is able to performe its actions.
- */
- service.isSupported = function() {
- return localStorageService.isSupported;
- };
-
- /**
- * @ngdoc method
- * @name umbraco.services.supportsCopy#hasEntriesOfType
- * @methodOf umbraco.services.clipboardService
- *
- * @param {string} type A string defining the type of data test for.
- * @param {string} aliases A array of strings providing the alias of the data you want to test for.
- *
- * @description
- * Determines whether the current clipboard has entries that match a given type and one of the aliases.
- */
- service.hasEntriesOfType = function(type, aliases) {
-
- if(service.retriveEntriesOfType(type, aliases).length > 0) {
- return true;
- }
-
- return false;
- };
-
- /**
- * @ngdoc method
- * @name umbraco.services.supportsCopy#retriveEntriesOfType
- * @methodOf umbraco.services.clipboardService
- *
- * @param {string} type A string defining the type of data to recive.
- * @param {string} aliases A array of strings providing the alias of the data you want to recive.
- *
- * @description
- * Returns an array of entries matching the given type and one of the provided aliases.
- */
- service.retriveEntriesOfType = function(type, aliases) {
-
- var storage = retriveStorage();
-
- // Find entries that are fulfilling the criteria for this nodeType and nodeTypesAliases.
- var filteretEntries = storage.entries.filter(
- (entry) => {
- return (entry.type === type && aliases.filter(alias => alias === entry.alias).length > 0);
- }
- );
-
- return filteretEntries;
- };
-
- /**
- * @ngdoc method
- * @name umbraco.services.supportsCopy#retriveEntriesOfType
- * @methodOf umbraco.services.clipboardService
- *
- * @param {string} type A string defining the type of data to recive.
- * @param {string} aliases A array of strings providing the alias of the data you want to recive.
- *
- * @description
- * Returns an array of data of entries matching the given type and one of the provided aliases.
- */
- service.retriveDataOfType = function(type, aliases) {
- return service.retriveEntriesOfType(type, aliases).map((x) => x.data);
- };
-
- /**
- * @ngdoc method
- * @name umbraco.services.supportsCopy#retriveEntriesOfType
- * @methodOf umbraco.services.clipboardService
- *
- * @param {string} type A string defining the type of data to remove.
- * @param {string} aliases A array of strings providing the alias of the data you want to remove.
- *
- * @description
- * Removes entries matching the given type and one of the provided aliases.
- */
- service.clearEntriesOfType = function(type, aliases) {
-
- var storage = retriveStorage();
-
- // Find entries that are NOT fulfilling the criteria for this nodeType and nodeTypesAliases.
- var filteretEntries = storage.entries.filter(
- (entry) => {
- return !(entry.type === type && aliases.filter(alias => alias === entry.alias).length > 0);
- }
- );
-
- storage.entries = filteretEntries;
-
- saveStorage(storage);
- };
-
-
-
- return service;
-}
-angular.module("umbraco.services").factory("clipboardService", clipboardService);
+/**
+ * @ngdoc service
+ * @name umbraco.services.clipboardService
+ *
+ * @requires notificationsService
+ * @requires eventsService
+ *
+ * @description
+ * Service to handle clipboard in general across the application. Responsible for handling the data both storing and retrive.
+ * The service has a set way for defining a data-set by a entryType and alias, which later will be used to retrive the posible entries for a paste scenario.
+ *
+ */
+function clipboardService(notificationsService, eventsService, localStorageService, iconHelper) {
+
+
+ var STORAGE_KEY = "umbClipboardService";
+
+ var retriveStorage = function() {
+ if (localStorageService.isSupported === false) {
+ return null;
+ }
+ var dataJSON;
+ var dataString = localStorageService.get(STORAGE_KEY);
+ if (dataString != null) {
+ dataJSON = JSON.parse(dataString);
+ }
+
+ if(dataJSON == null) {
+ dataJSON = new Object();
+ }
+
+ if(dataJSON.entries === undefined) {
+ dataJSON.entries = [];
+ }
+
+ return dataJSON;
+ }
+
+ var saveStorage = function(storage) {
+ var storageString = JSON.stringify(storage);
+
+ try {
+ var storageJSON = JSON.parse(storageString);
+ localStorageService.set(STORAGE_KEY, storageString);
+
+ eventsService.emit("clipboardService.storageUpdate");
+
+ return true;
+ } catch(e) {
+ return false;
+ }
+
+ return false;
+ }
+
+ var prepareEntryForStorage = function(entryData) {
+
+ var shallowCloneData = Object.assign({}, entryData);// Notice only a shallow copy, since we dont need to deep copy. (that will happen when storing the data)
+ delete shallowCloneData.key;
+ delete shallowCloneData.$$hashKey;
+
+ return shallowCloneData;
+ }
+
+ var isEntryCompatible = function(entry, type, allowedAliases) {
+ return entry.type === type
+ &&
+ (
+ (entry.alias && allowedAliases.filter(allowedAlias => allowedAlias === entry.alias).length > 0)
+ ||
+ (entry.aliases && entry.aliases.filter(entryAlias => allowedAliases.filter(allowedAlias => allowedAlias === entryAlias).length > 0).length === entry.aliases.length)
+ );
+ }
+
+
+ var service = {};
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.clipboardService#copy
+ * @methodOf umbraco.services.clipboardService
+ *
+ * @param {string} type A string defining the type of data to storing, example: 'elementType', 'contentNode'
+ * @param {string} alias A string defining the alias of the data to store, example: 'product'
+ * @param {object} entry A object containing the properties to be saved, this could be the object of a ElementType, ContentNode, ...
+ * @param {string} displayLabel (optional) A string swetting the label to display when showing paste entries.
+ *
+ * @description
+ * Saves a single JS-object with a type and alias to the clipboard.
+ */
+ service.copy = function(type, alias, data, displayLabel) {
+
+ var storage = retriveStorage();
+
+ var uniqueKey = data.key || data.$$hashKey || console.error("missing unique key for this content");
+
+ // remove previous copies of this entry:
+ storage.entries = storage.entries.filter(
+ (entry) => {
+ return entry.unique !== uniqueKey;
+ }
+ );
+
+ var entry = {unique:uniqueKey, type:type, alias:alias, data:prepareEntryForStorage(data), label:displayLabel || data.name, icon:iconHelper.convertFromLegacyIcon(data.icon)};
+ storage.entries.push(entry);
+
+ if (saveStorage(storage) === true) {
+ notificationsService.success("Clipboard", "Copied to clipboard.");
+ } else {
+ notificationsService.error("Clipboard", "Couldnt copy this data to clipboard.");
+ }
+
+ };
+
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.clipboardService#copyArray
+ * @methodOf umbraco.services.clipboardService
+ *
+ * @param {string} type A string defining the type of data to storing, example: 'elementTypeArray', 'contentNodeArray'
+ * @param {string} aliases An array of strings defining the alias of the data to store, example: ['banana', 'apple']
+ * @param {object} datas An array of objects containing the properties to be saved, example: [ElementType, ElementType, ...]
+ * @param {string} displayLabel A string setting the label to display when showing paste entries.
+ * @param {string} displayIcon A string setting the icon to display when showing paste entries.
+ * @param {string} uniqueKey A string prodiving an identifier for this entry, existing entries with this key will be removed to ensure that you only have the latest copy of this data.
+ *
+ * @description
+ * Saves a single JS-object with a type and alias to the clipboard.
+ */
+ service.copyArray = function(type, aliases, datas, displayLabel, displayIcon, uniqueKey) {
+
+ var storage = retriveStorage();
+
+ // Clean up each entry
+ var copiedDatas = datas.map(data => prepareEntryForStorage(data));
+
+ // remove previous copies of this entry:
+ storage.entries = storage.entries.filter(
+ (entry) => {
+ return entry.unique !== uniqueKey;
+ }
+ );
+
+ var entry = {unique:uniqueKey, type:type, aliases:aliases, data:copiedDatas, label:displayLabel, icon:displayIcon};
+
+ storage.entries.push(entry);
+
+ if (saveStorage(storage) === true) {
+ notificationsService.success("Clipboard", "Copied to clipboard.");
+ } else {
+ notificationsService.error("Clipboard", "Couldnt copy this data to clipboard.");
+ }
+
+ };
+
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.supportsCopy#supported
+ * @methodOf umbraco.services.clipboardService
+ *
+ * @description
+ * Determins wether the current browser is able to performe its actions.
+ */
+ service.isSupported = function() {
+ return localStorageService.isSupported;
+ };
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.supportsCopy#hasEntriesOfType
+ * @methodOf umbraco.services.clipboardService
+ *
+ * @param {string} type A string defining the type of data test for.
+ * @param {string} aliases A array of strings providing the alias of the data you want to test for.
+ *
+ * @description
+ * Determines whether the current clipboard has entries that match a given type and one of the aliases.
+ */
+ service.hasEntriesOfType = function(type, aliases) {
+
+ if(service.retriveEntriesOfType(type, aliases).length > 0) {
+ return true;
+ }
+
+ return false;
+ };
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.supportsCopy#retriveEntriesOfType
+ * @methodOf umbraco.services.clipboardService
+ *
+ * @param {string} type A string defining the type of data to recive.
+ * @param {string} aliases A array of strings providing the alias of the data you want to recive.
+ *
+ * @description
+ * Returns an array of entries matching the given type and one of the provided aliases.
+ */
+ service.retriveEntriesOfType = function(type, allowedAliases) {
+
+ var storage = retriveStorage();
+
+ // Find entries that are fulfilling the criteria for this nodeType and nodeTypesAliases.
+ var filteretEntries = storage.entries.filter(
+ (entry) => {
+ return isEntryCompatible(entry, type, allowedAliases);
+ }
+ );
+
+ return filteretEntries;
+ };
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.supportsCopy#retriveEntriesOfType
+ * @methodOf umbraco.services.clipboardService
+ *
+ * @param {string} type A string defining the type of data to recive.
+ * @param {string} aliases A array of strings providing the alias of the data you want to recive.
+ *
+ * @description
+ * Returns an array of data of entries matching the given type and one of the provided aliases.
+ */
+ service.retriveDataOfType = function(type, aliases) {
+ return service.retriveEntriesOfType(type, aliases).map((x) => x.data);
+ };
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.supportsCopy#retriveEntriesOfType
+ * @methodOf umbraco.services.clipboardService
+ *
+ * @param {string} type A string defining the type of data to remove.
+ * @param {string} aliases A array of strings providing the alias of the data you want to remove.
+ *
+ * @description
+ * Removes entries matching the given type and one of the provided aliases.
+ */
+ service.clearEntriesOfType = function(type, allowedAliases) {
+
+ var storage = retriveStorage();
+
+ // Find entries that are NOT fulfilling the criteria for this nodeType and nodeTypesAliases.
+ var filteretEntries = storage.entries.filter(
+ (entry) => {
+ return !isEntryCompatible(entry, type, allowedAliases);
+ }
+ );
+
+ storage.entries = filteretEntries;
+
+ saveStorage(storage);
+ };
+
+
+
+ return service;
+}
+
+
+angular.module("umbraco.services").factory("clipboardService", clipboardService);
+
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js
index ce70e9f543..8d1caab850 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js
@@ -338,6 +338,22 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
});
},
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#hasTree
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Checks if a tree with the given alias exists.
+ *
+ * @param {String} treeAlias the tree alias to check
+ */
+ hasTree: function (treeAlias) {
+ return navReadyPromise.promise.then(function () {
+ return mainTreeApi.hasTree(treeAlias);
+ });
+ },
+
/**
Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to
have to set an active tree and then sync, the new API does this in one method by using syncTree
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js
index 0b8965e4fe..119f40e114 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js
@@ -89,6 +89,7 @@
}
function confirmDelete(overlay) {
+ overlay.confirmType = "delete";
confirm(overlay);
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js
index 3782128af6..35f0d8a34a 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js
@@ -184,7 +184,13 @@
});
if (cached) return cached[1];
- nodeComputedStyle = nodeComputedStyle || this.doc.defaultView.getComputedStyle(node);
+ if (!nodeComputedStyle) {
+ if (node instanceof DocumentFragment) {
+ return true;// though DocumentFragment doesn't directly have display 'none', we know that it will never be visible, and therefore we return true. (and do not cache this, cause it will change if appended to the DOM)
+ } else {
+ nodeComputedStyle = this.doc.defaultView.getComputedStyle(node);
+ }
+ }
var result = false;
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
index 4d4f8792cf..b0941bd5ad 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
@@ -543,11 +543,11 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
'contenteditable': false
},
embed.preview);
-
- if (activeElement) {
+
+ // Only replace if activeElement is an Embed element.
+ if (activeElement && activeElement.nodeName.toUpperCase() === "DIV" && activeElement.classList.contains("embeditem")){
activeElement.replaceWith(wrapper); // directly replaces the html node
- }
- else {
+ } else {
editor.selection.setNode(wrapper);
}
},
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/validationmessage.service.js b/src/Umbraco.Web.UI.Client/src/common/services/validationmessage.service.js
new file mode 100644
index 0000000000..5e0d8b876b
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/common/services/validationmessage.service.js
@@ -0,0 +1,34 @@
+(function () {
+ 'use strict';
+
+ function validationMessageService($q, localizationService) {
+
+ // Gets the message to use for when a mandatory field isn't completed.
+ // Will either use the one provided on the property type's validation object
+ // or a localised default.
+ function getMandatoryMessage(validation) {
+ if (!validation) {
+ return $q.when("");
+ }
+
+ if (validation.mandatoryMessage) {
+ return $q.when(validation.mandatoryMessage);
+ } else {
+ return localizationService.localize("general_required").then(function (value) {
+ return $q.when(value);
+ });
+ }
+ }
+
+ var service = {
+ getMandatoryMessage: getMandatoryMessage
+ };
+
+ return service;
+
+ }
+
+ angular.module('umbraco.services').factory('validationMessageService', validationMessageService);
+
+
+})();
diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less
index ea889211d4..391fafb3fa 100644
--- a/src/Umbraco.Web.UI.Client/src/less/belle.less
+++ b/src/Umbraco.Web.UI.Client/src/less/belle.less
@@ -130,6 +130,7 @@
@import "components/umb-media-grid.less";
@import "components/umb-folder-grid.less";
@import "components/umb-content-grid.less";
+@import "components/umb-contextmenu.less";
@import "components/umb-layout-selector.less";
@import "components/tooltip/umb-tooltip.less";
@import "components/tooltip/umb-tooltip-list.less";
@@ -138,6 +139,7 @@
@import "components/umb-grid.less";
@import "components/umb-empty-state.less";
@import "components/umb-property-editor.less";
+@import "components/umb-property-actions.less";
@import "components/umb-color-swatches.less";
@import "components/check-circle.less";
@import "components/umb-file-icon.less";
@@ -188,6 +190,8 @@
@import "components/users/umb-user-preview.less";
@import "components/users/umb-user-picker-list.less";
+@import "components/contextdialogs/umb-dialog-datatype-delete.less";
+
// Utilities
@import "utilities/layout/_display.less";
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/contextdialogs/umb-dialog-datatype-delete.less b/src/Umbraco.Web.UI.Client/src/less/components/contextdialogs/umb-dialog-datatype-delete.less
new file mode 100644
index 0000000000..0e0b8f22bd
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/less/components/contextdialogs/umb-dialog-datatype-delete.less
@@ -0,0 +1,33 @@
+.umb-dialog-datatype-delete {
+
+
+ .umb-dialog-datatype-delete__table-head-column-name {
+ width: 140px;
+ }
+
+ .umb-table-body__icon {
+ margin-right: 5px;
+ vertical-align: top;
+ display: inline-block;
+ }
+
+ .table tbody td {
+ vertical-align: top;
+ }
+ .table tbody td > span {
+ margin: 5px 0;
+ vertical-align: middle;
+ }
+ .table tbody p {
+ line-height: 12px;
+ margin: 5px 0;
+ vertical-align: middle;
+ }
+
+ .table tbody .icon {
+ vertical-align: top;
+ margin-right: 5px;
+ display: inline-block;
+ }
+
+}
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-contextmenu.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-contextmenu.less
new file mode 100644
index 0000000000..8512e2020d
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-contextmenu.less
@@ -0,0 +1,75 @@
+.umb-contextmenu {
+ margin: 0;
+ list-style: none;
+ user-select: none;
+
+ overflow: hidden;
+ border-radius: 3px;
+ border: 1px solid @dropdownBorder;
+ .box-shadow(0 5px 20px rgba(0,0,0,.3));
+ border-bottom: 1px solid rgba(0,0,0,.2);
+
+ .sep {
+ display: block;
+ border-top: 1px solid @gray-9;
+
+ &:first-child {
+ border-top: none;
+ }
+ }
+
+}
+
+.umb-contextmenu-item {
+
+ .icon {
+ font-size: 18px;
+ vertical-align: middle;
+ }
+
+ .menu-label {
+ display: inline-block;
+ vertical-align: middle;
+ margin-left: 5px;
+ }
+
+ button {
+
+ position: relative;
+
+ display: block;
+ font-weight: normal;
+ line-height: @baseLineHeight;
+ white-space: nowrap;
+
+ background-color: @ui-option;
+ border: 0;
+ padding: 7px 12px;
+ color: @ui-option-type;
+ width: 100%;
+
+ font-size: 14px;
+ text-align: left;
+
+ &:hover {
+ text-decoration: none;
+ color: @ui-option-type-hover;
+ background-color: @ui-option-hover;
+ }
+ }
+
+ &.-opens-dialog {
+ .menu-label:after {
+ // adds an ellipsis (...) after the menu label for actions that open a dialog
+ content: '\2026';
+ }
+ }
+ button:disabled {
+ cursor: not-allowed;
+ color: @ui-option-disabled-type;
+ &:hover {
+ color: @ui-option-disabled-type-hover;
+ background-color: @ui-option;
+ }
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less
index c51fd37fe4..f9d8772d45 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less
@@ -493,11 +493,11 @@ input.umb-group-builder__group-sort-value {
font-weight: bold;
font-size: 14px;
color: @ui-action-type;
-
- &:hover{
+
+ &:hover {
text-decoration: none;
- color:@ui-action-type-hover;
- border-color:@ui-action-border-hover;
+ color: @ui-action-type-hover;
+ border-color: @ui-action-border-hover;
}
}
@@ -554,7 +554,13 @@ input.umb-group-builder__group-sort-value {
overflow: hidden;
}
- .editor-validation-pattern{
+ .editor-validation-message {
+ min-width: 100%;
+ min-height: 25px;
+ margin-top: 4px;
+ }
+
+ .editor-validation-pattern {
border: 1px solid @gray-7;
margin: 10px 0 0;
padding: 6px;
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less
index 56b4e19122..bf0dd9d109 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less
@@ -1,5 +1,4 @@
.umb-nested-content {
- text-align: center;
position: relative;
}
@@ -15,6 +14,17 @@
pointer-events: none;
}
+.umb-nested-content--mandatory {
+ /*
+ yeah so this is a pain, but we must be super specific in targeting the mandatory property labels,
+ otherwise all properties within a reqired, nested, nested content property will all appear mandatory
+ */
+ > ng-form > .control-group > .umb-el-wrap > .control-header label:after {
+ content: '*';
+ color: @red;
+ }
+}
+
.umb-nested-content-overlay {
position: absolute;
top: 0;
@@ -135,6 +145,8 @@
.umb-nested-content__icon {
+ background: transparent;
+ border: 0 none;
display: inline-block;
padding: 4px;
margin: 2px;
@@ -170,6 +182,7 @@
.umb-nested-content__add-content {
display: flex;
+ width: 100%;
align-items: center;
justify-content: center;
border: 1px dashed @ui-action-discreet-border;
@@ -235,7 +248,6 @@
}
.umb-nested-content__placeholder {
- height: 22px;
padding: 4px 6px;
border: 1px dashed #d8d7d9;
background: 0 0;
@@ -246,33 +258,18 @@
text-align: center;
&--selected {
- border: 1px solid #d8d7d9;
+ border: none;
text-align: left;
+ padding: 0;
}
}
-.umb-nested-content__placeholder-name{
- font-size: 15px;
-}
-
.umb-nested-content__placeholder:hover {
color: #2152a3;
border-color: #2152a3;
text-decoration: none;
}
-.umb-nested-content__placeholder-icon-holder {
- width: 20px;
- text-align: center;
- display: inline-block;
-}
-
-.umb-nested-content__placeholder-icon {
- font-size: 18px;
- vertical-align: middle;
-}
-
-
.form-horizontal .umb-nested-content--narrow .controls-row {
margin-left: 40% !important;
}
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-panel-group.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-panel-group.less
index 487c1881b1..46d7f04af6 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-panel-group.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-panel-group.less
@@ -107,5 +107,5 @@
.umb-panel-group__details-status-action-description {
margin-top: 5px;
font-size: 12px;
- padding-left: 165px;
+ padding-left:165px;
}
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less
new file mode 100644
index 0000000000..3ce284870e
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less
@@ -0,0 +1,96 @@
+.umb-property-actions {
+ display: inline;
+}
+
+.umb-property-actions__toggle,
+.umb-property-actions__menu-open-toggle {
+ position: relative;
+ display: flex;
+ flex: 0 0 auto;
+ padding: 6px 6px;
+ text-align: center;
+ cursor: pointer;
+ border-radius: 3px;
+
+ background-color: @ui-action-hover;
+
+ i {
+ height: 3px !important;
+ width: 3px !important;
+ border-radius: 3px;
+ background: @ui-action-type;
+ display: inline-block;
+ margin: 0 2px 0 0;
+
+ &:last-child {
+ margin: 0;
+ }
+ }
+ &:hover {
+ i {
+ background: @ui-action-type-hover;
+ }
+ }
+}
+.umb-property-actions__menu-open-toggle {
+ position: absolute;
+ z-index:1;
+ outline: none;// this is not acceccible by keyboard, since we use the .umb-property-actions__toggle for that.
+
+ top: -15px;
+ border-radius: 3px 3px 0 0;
+
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+
+ border: 1px solid @dropdownBorder;
+
+ border-bottom: 1px solid @gray-9;
+
+ .box-shadow(0 5px 20px rgba(0,0,0,.3));
+
+ background-color: white;
+
+}
+
+.umb-property .umb-property-actions {
+ float: left;
+}
+.umb-property .umb-property-actions__toggle {
+ margin-top: 2px;
+ opacity: 0;
+ transition: opacity 120ms;
+}
+.umb-property:hover .umb-property-actions__toggle,
+.umb-property .umb-property-actions__toggle:focus {
+ opacity: 1;
+}
+// Revert-style-hack that ensures that we only show property-actions on properties that are directly begin hovered.
+.umb-property:hover .umb-property:not(:hover) .umb-property-actions__toggle {
+ opacity: 0;
+}
+
+.umb-property-actions__menu {
+
+ position: absolute;
+ z-index: 1000;
+
+ display: block;
+
+ float: left;
+ min-width: 160px;
+ list-style: none;
+
+ .umb-contextmenu {
+
+ border-top-left-radius: 0;
+ margin-top:1px;
+
+ }
+
+ .umb-contextmenu-item > button {
+
+ z-index:2;// need to stay on top of menu-toggle-open shadow.
+
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less
index 5e766b7578..94c0318fca 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less
@@ -161,6 +161,7 @@ input.umb-table__input {
line-height: 20px;
color: @ui-option-type;
vertical-align: bottom;
+ text-decoration: none;
}
.umb-table-body__checkicon,
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less
index 9ddad03b48..7caec3c78e 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less
@@ -63,6 +63,10 @@ a.umb-user-details-details__back-link {
.umb-user-details-details__sidebar {
flex: 0 0 @sidebarwidth;
+
+ .umb-button{
+ margin-left:0px;
+ }
}
@media (max-width: 768px) {
@@ -101,6 +105,7 @@ a.umb-user-details-details__back-link {
.umb-user-details-details__information-item {
margin-bottom: 10px;
font-size: 13px;
+ margin-top:10px;
}
.umb-user-details-details__information-item-label {
diff --git a/src/Umbraco.Web.UI.Client/src/less/dashboards/healthcheck.less b/src/Umbraco.Web.UI.Client/src/less/dashboards/healthcheck.less
index 3a448f557d..bf01c21dca 100644
--- a/src/Umbraco.Web.UI.Client/src/less/dashboards/healthcheck.less
+++ b/src/Umbraco.Web.UI.Client/src/less/dashboards/healthcheck.less
@@ -8,7 +8,6 @@
.umb-healthcheck-help-text {
line-height: 1.6em;
- max-width: 750px;
}
.umb-healthcheck-action-bar {
diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less
index cfbb8b78ab..72abb3ba00 100644
--- a/src/Umbraco.Web.UI.Client/src/less/forms.less
+++ b/src/Umbraco.Web.UI.Client/src/less/forms.less
@@ -528,7 +528,8 @@ input[type="checkbox"][readonly] {
.help-inline {
display: inline-block;
vertical-align: middle;
- padding-left: 5px;
+ padding-top: 4px;
+ padding-left: 2px;
}
div.help {
diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less
index 920fcdb1eb..86a1acbeae 100644
--- a/src/Umbraco.Web.UI.Client/src/less/main.less
+++ b/src/Umbraco.Web.UI.Client/src/less/main.less
@@ -116,24 +116,42 @@ h5.-black {
margin: 20px;
}
.umb-control-group {
- border-bottom: 1px solid @gray-11;
- padding-bottom: 20px;
+ position: relative;
+ &::after {
+ content: '';
+ display:block;
+ margin-top: 20px;
+ width: 100%;
+ height: 1px;
+ background-color: @gray-11;
+ }
}
.umb-control-group.-no-border {
- border: none;
+ &::after {
+ margin-top: 0px;
+ height: 0;
+ background-color: transparent;
+ }
}
.umb-property:last-of-type .umb-control-group {
- border: none;
- margin-bottom: 0 !important;
- padding-bottom: 0;
+ &::after {
+ margin-top: 0px;
+ height: 0;
+ background-color: transparent;
+ }
+ margin-bottom: 0 !important;
}
/* BLOCK MODE */
.block-form .umb-control-group {
- border-bottom: none;
- padding-bottom: 0;
+ margin-top: 0px;
+ &::after {
+ margin-top: 0px;
+ height: 0;
+ background-color: transparent;
+ }
}
.block-form .umb-control-group label .help-block,
@@ -163,7 +181,36 @@ h5.-black {
}
.umb-control-group .umb-el-wrap {
- padding: 0
+ padding: 0;
+}
+.form-horizontal .umb-control-group .control-header {
+ float: left;
+ width: 160px;
+ padding-top: 5px;
+ text-align: left;
+
+ .control-label {
+ float: left;
+ width: auto;
+ padding-top: 0;
+ text-align: left;
+ }
+
+ .control-description {
+ display: block;
+ clear: both;
+ max-width:480px;// avoiding description becoming too wide when its placed on top of property.
+ margin-bottom: 10px;
+ }
+}
+@media (max-width: 767px) {
+
+ .form-horizontal .umb-control-group .control-header {
+ float: none;
+ width: 100%;
+ }
+
+
}
/* LABELS*/
diff --git a/src/Umbraco.Web.UI.Client/src/less/properties.less b/src/Umbraco.Web.UI.Client/src/less/properties.less
index 152ea49bbd..8523fe9300 100644
--- a/src/Umbraco.Web.UI.Client/src/less/properties.less
+++ b/src/Umbraco.Web.UI.Client/src/less/properties.less
@@ -97,7 +97,8 @@
.history-line {
width: 2px;
- height: 100%;
+ top: 10px;
+ bottom: 10px;
margin: 0 0 0 14px;
background-color: @gray-8;
position: absolute;
diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less
index dc2d402671..823daedf22 100644
--- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less
+++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less
@@ -415,8 +415,6 @@
text-decoration: none;
display: flex;
flex-direction: row;
- opacity: 0;
- visibility: hidden;
}
.umb-sortable-thumbnails.ui-sortable:not(.ui-sortable-disabled) {
@@ -425,9 +423,8 @@
}
}
-.umb-sortable-thumbnails li:hover .umb-sortable-thumbnails__actions {
+.umb-sortable-thumbnails li:hover .umb-sortable-thumbnails__action {
opacity: 1;
- visibility: visible;
}
.umb-sortable-thumbnails .umb-sortable-thumbnails__action {
@@ -443,6 +440,12 @@
margin-left: 5px;
text-decoration: none;
.box-shadow(0 1px 2px rgba(0,0,0,0.25));
+ opacity: 0;
+ transition: opacity .1s ease-in-out;
+
+ .tabbing-active &:focus {
+ opacity: 1;
+ }
}
.umb-sortable-thumbnails .umb-sortable-thumbnails__action.-red {
diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less
index 166640829b..e8f6d4ee58 100644
--- a/src/Umbraco.Web.UI.Client/src/less/variables.less
+++ b/src/Umbraco.Web.UI.Client/src/less/variables.less
@@ -132,6 +132,7 @@
@ui-option-type: @blueExtraDark;
@ui-option-type-hover: @blueMid;
+@ui-option: white;
@ui-option-hover: @sand-7;
@ui-option-disabled-type: @gray-6;
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html
index 071d093ab4..4096192081 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html
+++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html
@@ -60,7 +60,7 @@
- Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos
+Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos
+IIndexPopulator
+ IIndexPopulator
The health checker evaluates various areas of your site for best practice settings, configuration, potential problems, etc. You can easily fix problems by pressing a button. - You can add your own health checks, have a look at the documentation for more information about custom health checks.
++ The health checker evaluates various areas of your site for best practice settings, configuration, potential problems, etc. You can easily fix problems by pressing a button. + You can add your own health checks, have a look at the documentation for more information about custom health checks. +
+
- (wait)
+ (
- Umbraco currently runs in debug mode. This means you can use the built-in performance profiler to assess the performance when rendering pages. -
-- If you want to activate the profiler for a specific page rendering, simply add umbDebug=true to the querystring when requesting the page. -
-- If you want the profiler to be activated by default for all page renderings, you can use the toggle below. - It will set a cookie in your browser, which then activates the profiler automatically. - In other words, the profiler will only be active by default in your browser - not everyone else's. -
++ Umbraco currently runs in debug mode. This means you can use the built-in performance profiler to assess the performance when rendering pages. +
++ If you want to activate the profiler for a specific page rendering, simply add umbDebug=true to the querystring when requesting the page. +
++ If you want the profiler to be activated by default for all page renderings, you can use the toggle below. + It will set a cookie in your browser, which then activates the profiler automatically. + In other words, the profiler will only be active by default in your browser - not everyone else's. +
+- You should never let a production site run in debug mode. Debug mode is turned off by setting debug="false" on the <compilation /> element in web.config. -
++ You should never let a production site run in debug mode. Debug mode is turned off by setting debug="false" on the <compilation /> element in web.config. +
+- Umbraco currently does not run in debug mode, so you can't use the built-in profiler. This is how it should be for a production site. -
-- Debug mode is turned on by setting debug="true" on the <compilation /> element in web.config. -
++ Umbraco currently does not run in debug mode, so you can't use the built-in profiler. This is how it should be for a production site. +
++ Debug mode is turned on by setting debug="true" on the <compilation /> element in web.config. +
+This section contains the building blocks for your Umbraco site. Follow the below links to find out more about working with the items in the Settings section:
-This section contains the building blocks for your Umbraco site. Follow the below links to find out more about working with the items in the Settings section:
+Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos
+Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos
+| {{::relation.name}} | -{{::property.name}}{{$last ? '' : ', '}} | +{{::relation.name}} | +{{::property.name}} |
| {{::relation.name}} | -{{::property.name}}{{$last ? '' : ', '}} | +{{::relation.name}} | +{{::property.name}} |
| {{::relation.name}} | -{{::property.name}}{{$last ? '' : ', '}} | +{{::relation.name}} | +{{::property.name}} |
{{mandatoryMessage}}
+{{datePickerForm.datepicker.errorMsg}}
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js
index 3b341f7ac0..a6d615cdd1 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js
@@ -1,5 +1,5 @@
angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleController",
- function($scope) {
+ function ($scope, validationMessageService) {
//setup the default config
var config = {
@@ -89,4 +89,10 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleCo
$scope.model.value = null;
}
}
+
+ // Set the message to use for when a mandatory field isn't completed.
+ // Will either use the one provided on the property type or a localised default.
+ validationMessageService.getMandatoryMessage($scope.model.validation).then(function (value) {
+ $scope.mandatoryMessage = value;
+ });
});
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html
index 5f873e9e43..1059df2994 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html
@@ -1,21 +1,29 @@
{{mandatoryMessage}}
+{{mandatoryMessage}}
+{{emailFieldForm.textbox.errorMsg}}
+{{property.notSupportedMessage}}
{{model.label}} {{textboxFieldForm.textbox.errorMsg}}
-{{mandatoryMessage}}
To proceed, please edit the "web.config" file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named "UmbracoDbDSN" and save the file.
-
- Click the retry button when
- done.
- More information on editing web.config here.
- Don't worry - no content will be deleted and everything will continue working afterwards! -
- ]]>No further actions needs to be taken. Click Next to proceed.]]>
No further actions needs to be taken. Click Next to proceed.]]>
-
|
-
| - |
-
-
- -
- |
- - |