+
-
diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec
index c72e01baa3..477bb143f9 100644
--- a/build/NuSpecs/UmbracoCms.Core.nuspec
+++ b/build/NuSpecs/UmbracoCms.Core.nuspec
@@ -1,7 +1,7 @@
icon-alert.svg+ +
++ +Icon with additional attribute. It can be treated like any other dom element ++
++ +Manual svg string +This format is only used in the iconpicker.html ++
++@example + **/ + +(function () { + "use strict"; + + function UmbIconDirective(iconHelper) { + + var directive = { + replace: true, + transclude: true, + templateUrl: "views/components/umb-icon.html", + scope: { + icon: "@", + svgString: "=?" + }, + + link: function (scope) { + + if (scope.svgString === undefined && scope.svgString !== null && scope.icon !== undefined && scope.icon !== null) { + var icon = scope.icon.split(" ")[0]; // Ensure that only the first part of the icon is used as sometimes the color is added too, e.g. see umbeditorheader.directive scope.openIconPicker + + _requestIcon(icon); + } + scope.$watch("icon", function (newValue, oldValue) { + if (newValue && oldValue) { + var newicon = newValue.split(" ")[0]; + var oldicon = oldValue.split(" ")[0]; + + if (newicon !== oldicon) { + _requestIcon(newicon); + } + } + }); + + function _requestIcon(icon) { + // Reset svg string before requesting new icon. + scope.svgString = null; + + iconHelper.getIcon(icon) + .then(data => { + if (data !== null && data.svgString !== undefined) { + // Watch source SVG string + //icon.svgString.$$unwrapTrustedValue(); + scope.svgString = data.svgString; + } + }); + } + } + + }; + + return directive; + } + + angular.module("umbraco.directives").directive("umbIcon", UmbIconDirective); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js index aa28d49c4a..241f1e80e8 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js @@ -322,6 +322,41 @@ Use this directive to generate a thumbnail grid of media items. scope.$on('$destroy', function() { unbindItemsWatcher(); }); + //determine if sort is current + scope.sortColumn = "name"; + scope.sortReverse = false; + scope.sortDirection = "asc"; + //check sort status + scope.isSortDirection = function (col, direction) { + return col === scope.sortColumn && direction === scope.sortDirection; + }; + //change sort + scope.setSort = function (col) { + if (scope.sortColumn === col) { + scope.sortReverse = !scope.sortReverse; + } + else { + scope.sortColumn = col; + if (col === "updateDate") { + scope.sortReverse = true; + } + else { + scope.sortReverse = false; + } + } + scope.sortDirection = scope.sortReverse ? "desc" : "asc"; + + } + // sort function + scope.sortBy = function (item) { + if (scope.sortColumn === "updateDate") { + return [-item['isFolder'],item['updateDate']]; + } + else { + return [-item['isFolder'],item['name']]; + } + }; + } @@ -345,7 +380,8 @@ Use this directive to generate a thumbnail grid of media items. onlyImages: "@", onlyFolders: "@", includeSubFolders: "@", - currentFolderId: "@" + currentFolderId: "@", + showMediaList: "=" }, link: link }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js index 66e03a7302..3865ffcdae 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js @@ -63,7 +63,7 @@ } // update children miniListView.children = data.items; - _.each(miniListView.children, function(c) { + miniListView.children.forEach(c => { // child allowed by default c.allowed = true; @@ -95,7 +95,8 @@ var filtered = angular.isFunction(scope.entityTypeFilter.filter) ? _.filter(miniListView.children, scope.entityTypeFilter.filter) : _.where(miniListView.children, scope.entityTypeFilter.filter); - _.each(filtered, (node) => node.allowed = false); + + filtered.forEach(node => node.allowed = false); } // update pagination diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtooltip.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtooltip.directive.js index ef7006be2c..ce1885a7cf 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtooltip.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtooltip.directive.js @@ -77,13 +77,17 @@ Use this directive to render a tooltip. scope.tooltipStyles.left = 0; scope.tooltipStyles.top = 0; - function setTooltipPosition(event) { + function setTooltipPosition(event) { - var container = $("#contentwrapper"); - var containerLeft = container[0].offsetLeft; - var containerRight = containerLeft + container[0].offsetWidth; - var containerTop = container[0].offsetTop; - var containerBottom = containerTop + container[0].offsetHeight; + var overlay = $(event.target).closest('.umb-overlay'); + var container = overlay.length > 0 ? overlay : $("#contentwrapper"); + + let rect = container[0].getBoundingClientRect(); + + var containerLeft = rect.left; + var containerRight = containerLeft + rect.width; + var containerTop = rect.top; + var containerBottom = containerTop + rect.height; var elementHeight = null; var elementWidth = null; @@ -102,39 +106,43 @@ Use this directive to render a tooltip. position.left = event.pageX - (elementWidth / 2); position.top = event.pageY; - // check to see if element is outside screen - // outside right - if (position.left + elementWidth > containerRight) { - position.right = 10; - position.left = "inherit"; + if (overlay.length > 0) { + position.left = event.pageX - rect.left - (elementWidth / 2); + position.top = event.pageY - rect.top; } + else { + // check to see if element is outside screen + // outside right + if (position.left + elementWidth > containerRight) { + position.right = 10; + position.left = "inherit"; + } - // outside bottom - if (position.top + elementHeight > containerBottom) { - position.bottom = 10; - position.top = "inherit"; - } + // outside bottom + if (position.top + elementHeight > containerBottom) { + position.bottom = 10; + position.top = "inherit"; + } - // outside left - if (position.left < containerLeft) { - position.left = containerLeft + 10; - position.right = "inherit"; - } + // outside left + if (position.left < containerLeft) { + position.left = containerLeft + 10; + position.right = "inherit"; + } - // outside top - if (position.top < containerTop) { - position.top = 10; - position.bottom = "inherit"; + // outside top + if (position.top < containerTop) { + position.top = 10; + position.bottom = "inherit"; + } } scope.tooltipStyles = position; el.css(position); - } setTooltipPosition(scope.event); - } var directive = { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfileupload.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfileupload.directive.js index 6a8ffa7969..3581aed9e0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfileupload.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfileupload.directive.js @@ -19,6 +19,17 @@ function umbFileUpload() { //clear the element value - this allows us to pick the same file again and again el.val(''); }); + + el.on('drag dragstart dragend dragover dragenter dragleave drop', function (e) { + e.preventDefault(); + e.stopPropagation(); + }) + .on('dragover dragenter', function () { + scope.$emit("isDragover", { value: true }); + }) + .on('dragleave dragend drop', function () { + scope.$emit("isDragover", { value: false }); + }); } }; } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js index 653b4f427c..db1e38adc6 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js @@ -78,6 +78,8 @@ /** Called when the component initializes */ function onInit() { $scope.$on("filesSelected", onFilesSelected); + $scope.$on("isDragover", isDragover); + initialize(); } @@ -118,7 +120,9 @@ isImage: mediaHelper.detectIfImageByExtension(file), extension: getExtension(file) }; + f.fileSrc = getThumbnail(f); + return f; }); @@ -228,19 +232,22 @@ var index = i; //capture var isImage = mediaHelper.detectIfImageByExtension(files[i].name); + var extension = getExtension(files[i].name); - //save the file object to the files collection - vm.files.push({ + var f = { isImage: isImage, - extension: getExtension(files[i].name), + extension: extension, fileName: files[i].name, isClientSide: true - }); + }; + + // Save the file object to the files collection + vm.files.push(f); //special check for a comma in the name newVal += files[i].name.split(',').join('-') + ","; - if (isImage) { + if (isImage || extension === "svg") { var deferred = $q.defer(); @@ -293,6 +300,11 @@ } } + function isDragover(e, args) { + vm.dragover = args.value; + angularHelper.safeApply($scope); + } + }; var umbPropertyFileUploadComponent = { @@ -303,6 +315,7 @@ propertyAlias: "@", value: "<", hideSelection: "<", + dragover: "<", /** * Called when a file is selected on this instance */ diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js index 77c26e066e..c7894da171 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js @@ -204,7 +204,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location var parts = nextPath.split("?"); var query = {}; if (parts.length > 1) { - _.each(parts[1].split("&"), function (q) { + parts[1].split("&").forEach(q => { var keyVal = q.split("="); query[keyVal[0]] = keyVal[1]; }); diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.mocks.js index 3b58c127a7..aeb6229360 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.mocks.js @@ -8,12 +8,12 @@ angular.module('umbraco.mocks'). } function returnNodebyIds(status, data, headers) { - var ids = mocksUtils.getParameterByName(data, "ids") || "1234,1234,4234"; + var ids = mocksUtils.getParameterByName(data, "ids") || ['1234','1234','4234']; var items = []; - _.each(ids, function(id){ - items.push(_getNode( parseInt( id, 10 )) ); - }); + for (var i = 0; i < ids.length; i += 1) { + items.push(_getNode(parseInt(ids[i], 10))); + } return [200, items, null]; } @@ -26,8 +26,6 @@ angular.module('umbraco.mocks'). var id = mocksUtils.getParameterByName(data, "id") || 1234; id = parseInt(id, 10); - - return [200, _getNode(id), null]; } diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js b/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js index da6f78a6a5..868bc4c6d5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js @@ -19,7 +19,8 @@ Umbraco.Sys.ServerVariables = { "dashboardApiBaseUrl": "/umbraco/UmbracoApi/Dashboard/", "updateCheckApiBaseUrl": "/umbraco/Api/UpdateCheck/", "relationApiBaseUrl": "/umbraco/UmbracoApi/Relation/", - "rteApiBaseUrl": "/umbraco/UmbracoApi/RichTextPreValue/" + "rteApiBaseUrl": "/umbraco/UmbracoApi/RichTextPreValue/", + "iconApiBaseUrl": "/umbraco/UmbracoApi/Icon/" }, umbracoSettings: { "umbracoPath": "/umbraco", diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/auth.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/auth.resource.js index 078ad2ad43..7dc34c3b5a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/auth.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/auth.resource.js @@ -4,7 +4,7 @@ * @description * This Resource perfomrs actions to common authentication tasks for the Umbraco backoffice user * - * @requires $q + * @requires $q * @requires $http * @requires umbRequestHelper * @requires angularHelper @@ -62,7 +62,7 @@ function authResource($q, $http, umbRequestHelper, angularHelper) { * .then(function(data) { * //Do stuff for login... * }); - * + * * @param {string} login Username of backoffice user * @param {string} password Password of backoffice user * @returns {Promise} resourcePromise object @@ -91,9 +91,9 @@ function authResource($q, $http, umbRequestHelper, angularHelper) { * There are not parameters for this since when the user has clicked on their invite email they will be partially * logged in (but they will not be approved) so we need to use this method to verify the non approved logged in user's details. * Using the getCurrentUser will not work since that only works for approved users - * @returns {} + * @returns {} */ - getCurrentInvitedUser: function () { + getCurrentInvitedUser: function () { return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( @@ -117,7 +117,7 @@ function authResource($q, $http, umbRequestHelper, angularHelper) { * .then(function(data) { * //Do stuff for password reset request... * }); - * + * * @param {string} email Email address of backoffice user * @returns {Promise} resourcePromise object * @@ -164,7 +164,7 @@ function authResource($q, $http, umbRequestHelper, angularHelper) { * .then(function(data) { * //Allow reset of password * }); - * + * * @param {integer} userId User Id * @param {string} resetCode Password reset code * @returns {Promise} resourcePromise object @@ -195,14 +195,14 @@ function authResource($q, $http, umbRequestHelper, angularHelper) { }), 'Password reset code validation failed for userId ' + userId + ', code' + resetCode); }, - + /** * @ngdoc method * @name umbraco.resources.currentUserResource#getPasswordConfig * @methodOf umbraco.resources.currentUserResource * * @description - * Gets the configuration of the user membership provider which is used to configure the change password form + * Gets the configuration of the user membership provider which is used to configure the change password form */ getPasswordConfig: function (userId) { return umbRequestHelper.resourcePromise( @@ -227,7 +227,7 @@ function authResource($q, $http, umbRequestHelper, angularHelper) { * .then(function(data) { * //Password set * }); - * + * * @param {integer} userId User Id * @param {string} password New password * @param {string} confirmPassword Confirmation of new password @@ -346,15 +346,6 @@ function authResource($q, $http, umbRequestHelper, angularHelper) { 'Server call failed for getting current user'); }, - getCurrentUserLinkedLogins: function () { - - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "authenticationApiBaseUrl", - "GetCurrentUserLinkedLogins")), - 'Server call failed for getting current users linked logins'); - }, /** * @ngdoc method diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 4ca85f44e6..3dfbeade4f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -442,9 +442,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { getByIds: function (ids) { var idQuery = ""; - _.each(ids, function (item) { - idQuery += "ids=" + item + "&"; - }); + ids.forEach(id => idQuery += `ids=${id}&`); return umbRequestHelper.resourcePromise( $http.get( @@ -455,9 +453,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { 'Failed to retrieve data for content with multiple ids') .then(function (result) { //each item needs to be re-formatted - _.each(result, function (r) { - umbDataFormatter.formatContentGetData(r) - }); + result.forEach(r => umbDataFormatter.formatContentGetData(r)); return $q.when(result); }); }, diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/currentuser.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/currentuser.resource.js index 60b87e919f..a3be6996b1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/currentuser.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/currentuser.resource.js @@ -2,7 +2,7 @@ * @ngdoc service * @name umbraco.resources.currentUserResource * @description Used for read/updates for the currently logged in user - * + * * **/ function currentUserResource($q, $http, umbRequestHelper, umbDataFormatter) { @@ -35,10 +35,10 @@ function currentUserResource($q, $http, umbRequestHelper, umbDataFormatter) { * .then(function() { * alert('You are allowed to publish this item'); * }); - * + * * * @param {String} permission char representing the permission to check - * @param {Int} id id of content item to delete + * @param {Int} id id of content item to delete * @returns {Promise} resourcePromise object. * */ @@ -52,6 +52,16 @@ function currentUserResource($q, $http, umbRequestHelper, umbDataFormatter) { 'Failed to check permission for item ' + id); }, + getCurrentUserLinkedLogins: function () { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "currentUserApiBaseUrl", + "GetCurrentUserLinkedLogins")), + 'Server call failed for getting current users linked logins'); + }, + saveTourStatus: function (tourStatus) { if (!tourStatus) { @@ -68,7 +78,7 @@ function currentUserResource($q, $http, umbRequestHelper, umbDataFormatter) { }, getTours: function () { - + return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( @@ -98,7 +108,7 @@ function currentUserResource($q, $http, umbRequestHelper, umbDataFormatter) { * * @description * Changes the current users password - * + * * @returns {Promise} resourcePromise object containing the user array. * */ @@ -108,7 +118,7 @@ function currentUserResource($q, $http, umbRequestHelper, umbDataFormatter) { if (!changePasswordArgs) { throw 'No password data to change'; } - + return umbRequestHelper.resourcePromise( $http.post( umbRequestHelper.getApiUrl( diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js index e24f4786eb..06eb2ed4d2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js @@ -226,9 +226,7 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) { getByIds: function (ids) { var idQuery = ""; - _.each(ids, function (item) { - idQuery += "ids=" + item + "&"; - }); + ids.forEach(id => idQuery += `ids=${id}&`); return umbRequestHelper.resourcePromise( $http.get( diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/membergroup.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/membergroup.resource.js index c83a31e47c..f9b9da9944 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/membergroup.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/membergroup.resource.js @@ -32,9 +32,7 @@ function memberGroupResource($q, $http, umbRequestHelper) { getByIds: function (ids) { var idQuery = ""; - _.each(ids, function (item) { - idQuery += "ids=" + item + "&"; - }); + ids.forEach(id => idQuery += `ids=${id}&`); return umbRequestHelper.resourcePromise( $http.get( diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js index 6c15b89c0e..2314fa6d6c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js @@ -16,16 +16,15 @@ function memberTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { } var query = ""; - _.each(filterContentTypes, function (item) { - query += "filterContentTypes=" + item + "&"; - }); + filterContentTypes.forEach(fct => query += `filterContentTypes=${fct}&`); + // if filterContentTypes array is empty we need a empty variable in the querystring otherwise the service returns a error if (filterContentTypes.length === 0) { query += "filterContentTypes=&"; } - _.each(filterPropertyTypes, function (item) { - query += "filterPropertyTypes=" + item + "&"; - }); + + filterPropertyTypes.forEach(fpt => query += `filterPropertyTypes=${fpt}&`); + // if filterPropertyTypes array is empty we need a empty variable in the querystring otherwise the service returns a error if (filterPropertyTypes.length === 0) { query += "filterPropertyTypes=&"; diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js index 2b9e0c0fd5..91f00a36e3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js @@ -295,6 +295,38 @@ "Failed to retrieve data for user " + userId); } + + /** + * @ngdoc method + * @name umbraco.resources.usersResource#getUsers + * @methodOf umbraco.resources.usersResource + * + * @description + * Gets users from ids + * + * ##usage + *+ +
+ * usersResource.getUsers([1,2,3])
+ * .then(function(data) {
+ * alert("It's here");
+ * });
+ *
+ *
+ * @param {Array} userIds user ids.
+ * @returns {Promise} resourcePromise object containing the users array.
+ *
+ */
+ function getUsers(userIds) {
+
+ return umbRequestHelper.resourcePromise(
+ $http.get(
+ umbRequestHelper.getApiUrl(
+ "userApiBaseUrl",
+ "GetByIds",
+ { ids: userIds })),
+ "Failed to retrieve data for users " + userIds);
+ }
+
/**
* @ngdoc method
* @name umbraco.resources.usersResource#createUser
@@ -481,6 +513,7 @@
setUserGroupsOnUsers: setUserGroupsOnUsers,
getPagedResults: getPagedResults,
getUser: getUser,
+ getUsers: getUsers,
createUser: createUser,
inviteUser: inviteUser,
saveUser: saveUser,
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js
index 30e59e9a88..b474b66174 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js
@@ -276,7 +276,7 @@ angular.module('umbraco.services')
//blocking
var promises = [];
var assets = [];
- _.each(nonEmpty, function (path) {
+ nonEmpty.forEach(path => {
path = convertVirtualPath(path);
var asset = service._getAssetPromise(path);
//if not previously loaded, add to list of promises
@@ -325,19 +325,17 @@ angular.module('umbraco.services')
scope = $rootScope;
}
angularHelper.safeApply(scope,
- function () {
- asset.deferred.resolve(true);
- });
+ () => 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); });
+ var cssPaths = cssAssets.map(css => appendRnd(css.path));
+ LazyLoad.css(cssPaths, () => cssAssets.forEach(assetLoaded));
}
if (jsAssets.length > 0) {
- var jsPaths = _.map(jsAssets, function (asset) { return appendRnd(asset.path) });
- LazyLoad.js(jsPaths, function () { _.each(jsAssets, assetLoaded); });
+ var jsPaths = jsAssets.map(js => appendRnd(js.path));
+ LazyLoad.js(jsPaths, () => jsAssets.forEach(assetLoaded));
}
return promise;
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 cb583546a5..0d2ca6623b 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
@@ -11,13 +11,13 @@
*
*/
function clipboardService(notificationsService, eventsService, localStorageService, iconHelper) {
-
+
var clearPropertyResolvers = [];
-
+
var STORAGE_KEY = "umbClipboardService";
-
+
var retriveStorage = function() {
if (localStorageService.isSupported === false) {
return null;
@@ -27,32 +27,32 @@ function clipboardService(notificationsService, eventsService, localStorageServi
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;
}
@@ -86,17 +86,17 @@ function clipboardService(notificationsService, eventsService, localStorageServi
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
@@ -160,29 +160,29 @@ function clipboardService(notificationsService, eventsService, localStorageServi
* Saves a single JS-object with a type and alias to the clipboard.
*/
service.copy = function(type, alias, data, displayLabel, displayIcon, uniqueKey, firstLevelClearupMethod) {
-
+
var storage = retriveStorage();
displayLabel = displayLabel || data.name;
displayIcon = displayIcon || iconHelper.convertFromLegacyIcon(data.icon);
uniqueKey = uniqueKey || data.key || 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, firstLevelClearupMethod), label:displayLabel, icon:displayIcon};
+
+ var entry = {unique:uniqueKey, type:type, alias:alias, data:prepareEntryForStorage(data, firstLevelClearupMethod), label:displayLabel, icon:displayIcon, date:Date.now()};
storage.entries.push(entry);
-
+
if (saveStorage(storage) === true) {
notificationsService.success("Clipboard", "Copied to clipboard.");
} else {
notificationsService.error("Clipboard", "Couldnt copy this data to clipboard.");
}
-
+
};
@@ -203,32 +203,31 @@ function clipboardService(notificationsService, eventsService, localStorageServi
* Saves a single JS-object with a type and alias to the clipboard.
*/
service.copyArray = function(type, aliases, datas, displayLabel, displayIcon, uniqueKey, firstLevelClearupMethod) {
-
+
var storage = retriveStorage();
-
+
// Clean up each entry
var copiedDatas = datas.map(data => prepareEntryForStorage(data, firstLevelClearupMethod));
-
+
// 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};
+ var entry = {unique:uniqueKey, type:type, aliases:aliases, data:copiedDatas, label:displayLabel, icon:displayIcon, date:Date.now()};
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
@@ -240,7 +239,7 @@ function clipboardService(notificationsService, eventsService, localStorageServi
service.isSupported = function() {
return localStorageService.isSupported;
};
-
+
/**
* @ngdoc method
* @name umbraco.services.supportsCopy#hasEntriesOfType
@@ -253,14 +252,14 @@ function clipboardService(notificationsService, eventsService, localStorageServi
* 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
@@ -268,24 +267,24 @@ function clipboardService(notificationsService, eventsService, localStorageServi
*
* @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
@@ -293,14 +292,14 @@ function clipboardService(notificationsService, eventsService, localStorageServi
*
* @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
@@ -308,12 +307,12 @@ function clipboardService(notificationsService, eventsService, localStorageServi
*
* @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.
@@ -322,14 +321,14 @@ function clipboardService(notificationsService, eventsService, localStorageServi
return !isEntryCompatible(entry, type, allowedAliases);
}
);
-
+
storage.entries = filteretEntries;
saveStorage(storage);
};
-
-
-
+
+
+
return service;
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js
index 4ffd0c3c0b..c27283d5ad 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js
@@ -123,10 +123,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
self.handleSaveError({
showNotifications: args.showNotifications,
softRedirect: args.softRedirect,
- err: err,
- rebindCallback: function () {
- rebindCallback.apply(self, [args.content, err.data]);
- }
+ err: err
});
//update editor state to what is current
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js
index 538bd41ce0..0f4f04c6bf 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js
@@ -669,23 +669,6 @@ When building a custom infinite editor view you can use the same components as a
open(editor);
}
- /**
- * @ngdoc method
- * @name umbraco.services.editorService#memberTypeEditor
- * @methodOf umbraco.services.editorService
- *
- * @description
- * Opens the member type editor in infinite editing, the submit callback returns the saved member type
- * @param {Object} editor rendering options
- * @param {Callback} editor.submit Submits the editor
- * @param {Callback} editor.close Closes the editor
- * @returns {Object} editor object
- */
- function memberTypeEditor(editor) {
- editor.view = "views/membertypes/edit.html";
- open(editor);
- }
-
/**
* @ngdoc method
* @name umbraco.services.editorService#queryBuilder
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/focuslock.service.js b/src/Umbraco.Web.UI.Client/src/common/services/focuslock.service.js
new file mode 100644
index 0000000000..a3dd91194e
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/common/services/focuslock.service.js
@@ -0,0 +1,26 @@
+(function () {
+ "use strict";
+
+ function focusLockService() {
+ var elementToInert = document.querySelector('#mainwrapper');
+
+ function addInertAttribute() {
+ elementToInert.setAttribute('inert', true);
+ }
+
+ function removeInertAttribute() {
+ elementToInert.removeAttribute('inert');
+ }
+
+ var service = {
+ addInertAttribute: addInertAttribute,
+ removeInertAttribute: removeInertAttribute
+ }
+
+ return service;
+
+ }
+
+ angular.module("umbraco.services").factory("focusLockService", focusLockService);
+
+})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js
index 5866e28b1e..d9c11770cc 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js
@@ -17,10 +17,10 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
* @function
*
* @description
- * Called by controllers when submitting a form - this ensures that all client validation is checked,
+ * Called by controllers when submitting a form - this ensures that all client validation is checked,
* server validation is cleared, that the correct events execute and status messages are displayed.
* This returns true if the form is valid, otherwise false if form submission cannot continue.
- *
+ *
* @param {object} args An object containing arguments for form submission
*/
submitForm: function (args) {
@@ -33,6 +33,7 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
if (!args.scope) {
throw "args.scope cannot be null";
}
+
if (!args.formCtrl) {
//try to get the closest form controller
currentForm = angularHelper.getRequiredCurrentForm(args.scope);
@@ -44,15 +45,13 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
//the first thing any form must do is broadcast the formSubmitting event
args.scope.$broadcast("formSubmitting", { scope: args.scope, action: args.action });
- // Some property editors need to perform an action after all property editors have reacted to the formSubmitting.
- args.scope.$broadcast("formSubmittingFinalPhase", { scope: args.scope, action: args.action });
-
- // Set the form state to submitted
- currentForm.$setSubmitted();
+ this.focusOnFirstError(currentForm);
+ args.scope.$broadcast("postFormSubmitting", { scope: args.scope, action: args.action });
//then check if the form is valid
if (!args.skipValidation) {
if (currentForm.$invalid) {
+
return false;
}
}
@@ -68,6 +67,32 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
return true;
},
+ /**
+ * @ngdoc function
+ * @name umbraco.services.formHelper#focusOnFirstError
+ * @methodOf umbraco.services.formHelper
+ * @function
+ *
+ * @description
+ * Called by submitForm when a form has been submitted, it will fire a focus on the first found invalid umb-property it finds in the form..
+ *
+ * @param {object} form Pass in a form object.
+ */
+ focusOnFirstError: function(form) {
+ var invalidNgForms = form.$$element.find(`.umb-property ng-form.ng-invalid, .umb-property-editor ng-form.ng-invalid-required`);
+ var firstInvalidNgForm = invalidNgForms.first();
+
+ if(firstInvalidNgForm.length !== 0) {
+ var focusableFields = [...firstInvalidNgForm.find("umb-range-slider .noUi-handle,input,textarea,select,button")];
+ if(focusableFields.length !== 0) {
+ var firstErrorEl = focusableFields.find(el => el.type !== "hidden" && el.hasAttribute("readonly") === false);
+ if(firstErrorEl.length !== 0) {
+ firstErrorEl.focus();
+ }
+ }
+ }
+ },
+
/**
* @ngdoc function
* @name umbraco.services.formHelper#submitForm
@@ -76,32 +101,18 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
*
* @description
* Called by controllers when a form has been successfully submitted, this ensures the correct events are raised.
- *
+ *
* @param {object} args An object containing arguments for form submission
*/
resetForm: function (args) {
-
- var currentForm;
-
if (!args) {
throw "args cannot be null";
}
if (!args.scope) {
throw "args.scope cannot be null";
}
- if (!args.formCtrl) {
- //try to get the closest form controller
- currentForm = angularHelper.getRequiredCurrentForm(args.scope);
- }
- else {
- currentForm = args.formCtrl;
- }
- // Set the form state to pristine
- currentForm.$setPristine();
- currentForm.$setUntouched();
-
- args.scope.$broadcast(args.hasErrors ? "formSubmittedValidationFailed" : "formSubmitted", { scope: args.scope });
+ args.scope.$broadcast("formSubmitted", { scope: args.scope });
},
showNotifications: function (args) {
@@ -126,7 +137,7 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
* @description
* Needs to be called when a form submission fails, this will wire up all server validation errors in ModelState and
* add the correct messages to the notifications. If a server error has occurred this will show a ysod.
- *
+ *
* @param {object} err The error object returned from the http promise
*/
handleError: function (err) {
@@ -165,7 +176,7 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
*
* @description
* This wires up all of the server validation model state so that valServer and valServerField directives work
- *
+ *
* @param {object} err The error object returned from the http promise
*/
handleServerValidation: function (modelState) {
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/iconhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/iconhelper.service.js
index 0fa2d0df1a..0d0135fff8 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/iconhelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/iconhelper.service.js
@@ -3,7 +3,7 @@
* @name umbraco.services.iconHelper
* @description A helper service for dealing with icons, mostly dealing with legacy tree icons
**/
-function iconHelper($q, $timeout) {
+function iconHelper($http, $q, $sce, $timeout, umbRequestHelper) {
var converter = [
{ oldIcon: ".sprNew", newIcon: "add" },
@@ -85,11 +85,15 @@ function iconHelper($q, $timeout) {
{ oldIcon: ".sprTreeDeveloperPython", newIcon: "icon-linux" }
];
+ var collectedIcons;
+
var imageConverter = [
{oldImage: "contour.png", newIcon: "icon-umb-contour"}
];
- var collectedIcons;
+ var iconCache = [];
+ var liveRequests = [];
+ var allIconsRequested = false;
return {
@@ -154,9 +158,110 @@ function iconHelper($q, $timeout) {
return false;
},
- /** Return a list of icons, optionally filter them */
+ /** Converts the icon from legacy to a new one if an old one is detected */
+ convertFromLegacyIcon: function (icon) {
+ if (this.isLegacyIcon(icon)) {
+ //its legacy so convert it if we can
+ var found = _.find(converter, function (item) {
+ return item.oldIcon.toLowerCase() === icon.toLowerCase();
+ });
+ return (found ? found.newIcon : icon);
+ }
+ return icon;
+ },
+
+ convertFromLegacyImage: function (icon) {
+ var found = _.find(imageConverter, function (item) {
+ return item.oldImage.toLowerCase() === icon.toLowerCase();
+ });
+ return (found ? found.newIcon : undefined);
+ },
+
+ /** If we detect that the tree node has legacy icons that can be converted, this will convert them */
+ convertFromLegacyTreeNodeIcon: function (treeNode) {
+ if (this.isLegacyTreeNodeIcon(treeNode)) {
+ return this.convertFromLegacyIcon(treeNode.icon);
+ }
+ return treeNode.icon;
+ },
+
+ /** Gets a single IconModel */
+ getIcon: function(iconName) {
+ return $q((resolve, reject) => {
+ var icon = this._getIconFromCache(iconName);
+
+ if(icon !== undefined) {
+ resolve(icon);
+ } else {
+ var iconRequestPath = Umbraco.Sys.ServerVariables.umbracoUrls.iconApiBaseUrl + 'GetIcon?iconName=' + iconName;
+
+ // If the current icon is being requested, wait a bit so that we don't have to make another http request and can instead get the icon from the cache.
+ // This is a bit rough and ready and could probably be improved used an event based system
+ if(liveRequests.indexOf(iconRequestPath) >= 0) {
+ setTimeout(() => {
+ resolve(this.getIcon(iconName));
+ }, 10);
+ } else {
+ liveRequests.push(iconRequestPath);
+ // TODO - fix bug where Umbraco.Sys.ServerVariables.umbracoUrls.iconApiBaseUrl is undefinied when help icon
+ umbRequestHelper.resourcePromise(
+ $http.get(iconRequestPath)
+ ,'Failed to retrieve icon: ' + iconName)
+ .then(icon => {
+ if(icon) {
+ var trustedIcon = {
+ name: icon.Name,
+ svgString: $sce.trustAsHtml(icon.SvgString)
+ };
+ this._cacheIcon(trustedIcon);
+
+ liveRequests = _.filter(liveRequests, iconRequestPath);
+
+ resolve(trustedIcon);
+ }
+ })
+ .catch(err => {
+ console.warn(err);
+ });
+ };
+
+ }
+ });
+ },
+
+ /** Gets all the available icons in the backoffice icon folder and returns them as an array of IconModels */
+ getAllIcons: function() {
+ return $q((resolve, reject) => {
+ if(allIconsRequested === false) {
+ allIconsRequested = true;
+
+ umbRequestHelper.resourcePromise(
+ $http.get(Umbraco.Sys.ServerVariables.umbracoUrls.iconApiBaseUrl + 'GetAllIcons')
+ ,'Failed to retrieve icons')
+ .then(icons => {
+ icons.forEach(icon => {
+ var trustedIcon = {
+ name: icon.Name,
+ svgString: $sce.trustAsHtml(icon.SvgString)
+ };
+
+ this._cacheIcon(trustedIcon);
+ });
+
+ resolve(iconCache);
+ })
+ .catch(err => {
+ console.warn(err);
+ });;
+ } else {
+ resolve(iconCache);
+ }
+ });
+ },
+
+ /** LEGACY - Return a list of icons from icon fonts, optionally filter them */
/** It fetches them directly from the active stylesheets in the browser */
- getIcons: function(){
+ getLegacyIcons: function(){
var deferred = $q.defer();
$timeout(function(){
if(collectedIcons){
@@ -188,8 +293,13 @@ function iconHelper($q, $timeout) {
s = s.substring(0, hasPseudo);
}
- if(collectedIcons.indexOf(s) < 0){
- collectedIcons.push(s);
+ var icon = {
+ name: s,
+ svgString: undefined
+ };
+
+ if(collectedIcons.indexOf(icon) < 0 && s !== "icon-chevron-up" && s !== "icon-chevron-down"){
+ collectedIcons.push(icon);
}
}
}
@@ -198,35 +308,20 @@ function iconHelper($q, $timeout) {
deferred.resolve(collectedIcons);
}
}, 100);
-
+
return deferred.promise;
},
- /** Converts the icon from legacy to a new one if an old one is detected */
- convertFromLegacyIcon: function (icon) {
- if (this.isLegacyIcon(icon)) {
- //its legacy so convert it if we can
- var found = _.find(converter, function (item) {
- return item.oldIcon.toLowerCase() === icon.toLowerCase();
- });
- return (found ? found.newIcon : icon);
- }
- return icon;
+ /** A simple cache to ensure that the icon is only requested from the server if is isn't already in memory */
+ _cacheIcon: function(icon) {
+ if(_.find(iconCache, {name: icon.name}) === undefined) {
+ iconCache = _.union(iconCache, [icon]);
+ }
},
- convertFromLegacyImage: function (icon) {
- var found = _.find(imageConverter, function (item) {
- return item.oldImage.toLowerCase() === icon.toLowerCase();
- });
- return (found ? found.newIcon : undefined);
- },
-
- /** If we detect that the tree node has legacy icons that can be converted, this will convert them */
- convertFromLegacyTreeNodeIcon: function (treeNode) {
- if (this.isLegacyTreeNodeIcon(treeNode)) {
- return this.convertFromLegacyIcon(treeNode.icon);
- }
- return treeNode.icon;
+ /** Returns the cached icon or undefined */
+ _getIconFromCache: function(iconName) {
+ return _.find(iconCache, {name: iconName});
}
};
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js
index 75c9dccc30..6e5ee82ec7 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js
@@ -435,11 +435,16 @@ function mediaHelper(umbRequestHelper, $http, $log) {
imagePath,
animationProcessMode: options.animationProcessMode,
cacheBusterValue: options.cacheBusterValue,
- focalPoint: options.focalPoint,
+ focalPointLeft: options.focalPoint.left,
+ focalPointTop: options.focalPoint.top,
height: options.height,
mode: options.mode,
upscale: options.upscale || false,
- width: options.width
+ width: options.width,
+ cropX1: options.crop.x1,
+ cropX2: options.crop.x2,
+ cropY1: options.crop.y1,
+ cropY2: options.crop.y2
})),
"Failed to retrieve processed image URL for image: " + imagePath);
}
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 f903c44ad5..0907538b24 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
@@ -169,7 +169,7 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
//if the routing parameter keys are the same, we'll compare their values to see if any have changed and if so then the routing will be allowed.
if (diff1.length === 0 && diff2.length === 0) {
var partsChanged = 0;
- _.each(currRoutingKeys, function (k) {
+ currRoutingKeys.forEach(k => {
if (currUrlParams[k] != nextUrlParams[k]) {
partsChanged++;
}
@@ -206,7 +206,8 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
var toRetain = _.union(retainedQueryStrings, toRetain);
var currentSearch = $location.search();
$location.search('');
- _.each(toRetain, function (k) {
+
+ toRetain.forEach(k => {
if (currentSearch[k]) {
$location.search(k, currentSearch[k]);
}
@@ -240,7 +241,7 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
var toRetain = Utilities.copy(nextRouteParams);
var updated = false;
- _.each(retainedQueryStrings, function (r) {
+ retainedQueryStrings.forEach(r => {
// if mculture is set to null in nextRouteParams, the value will be undefined and we will not retain any query string that has a value of "null"
if (currRouteParams[r] && nextRouteParams[r] !== undefined && !nextRouteParams[r]) {
toRetain[r] = currRouteParams[r];
@@ -319,7 +320,7 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
appState.setGlobalState("showTray", false);
},
- /**
+ /**
* @ngdoc method
* @name umbraco.services.navigationService#syncTree
* @methodOf umbraco.services.navigationService
@@ -352,14 +353,14 @@ 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) {
@@ -628,12 +629,12 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
getTreeTemplateUrl: function (treeAlias, action) {
var packageTreeFolder = treeService.getTreePackageFolder(treeAlias);
if (packageTreeFolder) {
- return Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
+ return (Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
"/" + packageTreeFolder +
- "/backoffice/" + treeAlias + "/" + action + ".html";
+ "/backoffice/" + treeAlias + "/" + action + ".html").toLowerCase();
}
else {
- return "views/" + treeAlias + "/" + action + ".html";
+ return ("views/" + treeAlias + "/" + action + ".html").toLowerCase();;
}
},
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 123845a63b..ea05dad4e7 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
@@ -8,14 +8,14 @@
(function () {
"use strict";
- function overlayService(eventsService, backdropService) {
+ function overlayService(eventsService, backdropService, focusLockService) {
var currentOverlay = null;
function open(newOverlay) {
// prevent two open overlays at the same time
- if(currentOverlay) {
+ if (currentOverlay) {
close();
}
@@ -23,32 +23,34 @@
var overlay = newOverlay;
// set the default overlay position to center
- if(!overlay.position) {
+ if (!overlay.position) {
overlay.position = "center";
}
// set the default overlay size to small
- if(!overlay.size) {
+ if (!overlay.size) {
overlay.size = "small";
}
// use a default empty view if nothing is set
- if(!overlay.view) {
+ if (!overlay.view) {
overlay.view = "views/common/overlays/default/default.html";
}
// option to disable backdrop clicks
- if(overlay.disableBackdropClick) {
+ if (overlay.disableBackdropClick) {
backdropOptions.disableEventsOnClick = true;
}
overlay.show = true;
+ focusLockService.addInertAttribute();
backdropService.open(backdropOptions);
currentOverlay = overlay;
eventsService.emit("appState.overlay", overlay);
}
function close() {
+ focusLockService.removeInertAttribute();
backdropService.close();
currentOverlay = null;
eventsService.emit("appState.overlay", null);
@@ -90,7 +92,6 @@
}
open(overlay);
-
}
function confirmDelete(overlay) {
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/search.service.js b/src/Umbraco.Web.UI.Client/src/common/services/search.service.js
index fef286ec7e..803cd857b7 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/search.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/search.service.js
@@ -21,8 +21,8 @@
*
*/
angular.module('umbraco.services')
- .factory('searchService', function ($q, $log, entityResource, contentResource, umbRequestHelper, $injector, searchResultFormatter) {
-
+ .factory('searchService', function (entityResource, $injector, searchResultFormatter) {
+
return {
/**
@@ -42,12 +42,11 @@ angular.module('umbraco.services')
throw "args.term is required";
}
- return entityResource.search(args.term, "Member", args.searchFrom).then(function (data) {
- _.each(data, function (item) {
- searchResultFormatter.configureMemberResult(item);
+ return entityResource.search(args.term, "Member", args.searchFrom)
+ .then(data => {
+ data.forEach(item => searchResultFormatter.configureMemberResult(item));
+ return data;
});
- return data;
- });
},
/**
@@ -67,12 +66,11 @@ angular.module('umbraco.services')
throw "args.term is required";
}
- return entityResource.search(args.term, "Document", args.searchFrom, args.canceler, args.dataTypeKey).then(function (data) {
+ return entityResource.search(args.term, "Document", args.searchFrom, args.canceler, args.dataTypeKey)
_.each(data, function (item) {
- searchResultFormatter.configureContentResult(item);
+ data.forEach(item => searchResultFormatter.configureContentResult(item));
+ return data;
});
- return data;
- });
},
/**
@@ -92,12 +90,11 @@ angular.module('umbraco.services')
throw "args.term is required";
}
- return entityResource.search(args.term, "Media", args.searchFrom, args.canceler, args.dataTypeKey).then(function (data) {
- _.each(data, function (item) {
- searchResultFormatter.configureMediaResult(item);
+ return entityResource.search(args.term, "Media", args.searchFrom, args.canceler, args.dataTypeKey)
+ .then(data => {
+ data.forEach(item => searchResultFormatter.configureMediaResult(item));
+ return data;
});
- return data;
- });
},
/**
@@ -117,10 +114,8 @@ angular.module('umbraco.services')
throw "args.term is required";
}
- return entityResource.searchAll(args.term, args.canceler).then(function (data) {
-
- _.each(data, function (resultByType) {
-
+ return entityResource.searchAll(args.term, args.canceler).then(data => {
+ Object.values(data).forEach(resultByType => {
//we need to format the search result data to include things like the subtitle, urls, etc...
// this is done with registered angular services as part of the SearchableTreeAttribute, if that
// is not found, than we format with the default formatter
@@ -140,7 +135,7 @@ angular.module('umbraco.services')
}
}
//now apply the formatter for each result
- _.each(resultByType.results, function (item) {
+ resultByType.results.forEach(item => {
formatterMethod.apply(this, [item, resultByType.treeAlias, resultByType.appAlias]);
});
@@ -148,12 +143,10 @@ angular.module('umbraco.services')
return data;
});
-
},
// TODO: This doesn't do anything!
setCurrent: function (sectionAlias) {
-
var currentSection = sectionAlias;
}
};
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 9945396262..5d6b4646a3 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
@@ -1372,6 +1372,9 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
// throw "args.model.value is required";
//}
+ // force TinyMCE to load plugins/themes from minified files (see http://archive.tinymce.com/wiki.php/api4:property.tinymce.suffix.static)
+ args.editor.suffix = ".min";
+
var unwatch = null;
//Starts a watch on the model value so that we can update TinyMCE if the model changes behind the scenes or from the server
@@ -1516,6 +1519,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
});
args.editor.on('Dirty', function (e) {
+ syncContent(); // Set model.value to the RTE's content
+
//make the form dirty manually so that the track changes works, setting our model doesn't trigger
// the angular bits because tinymce replaces the textarea.
if (args.currentForm) {
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js
index 0d6216f7cc..37485ea7eb 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js
@@ -54,7 +54,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS
//take the last child
var childPath = this.getPath(node.children[node.children.length - 1]).join(",");
//check if this already exists, if so exit
- if (expandedPaths.indexOf(childPath) !== -1) {
+ if (expandedPaths.includes(childPath)) {
return;
}
@@ -65,18 +65,18 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS
var clonedPaths = expandedPaths.slice(0); //make a copy to iterate over so we can modify the original in the iteration
- _.each(clonedPaths, function (p) {
+ clonedPaths.forEach(p => {
if (childPath.startsWith(p + ",")) {
//this means that the node's path supercedes this path stored so we can remove the current 'p' and replace it with node.path
expandedPaths.splice(expandedPaths.indexOf(p), 1); //remove it
- if (expandedPaths.indexOf(childPath) === -1) {
+ if (expandedPaths.includes(childPath) === false) {
expandedPaths.push(childPath); //replace it
}
}
else if (p.startsWith(childPath + ",")) {
//this means we've already tracked a deeper node so we shouldn't track this one
}
- else if (expandedPaths.indexOf(childPath) === -1) {
+ else if (expandedPaths.includes(childPath) === false) {
expandedPaths.push(childPath); //track it
}
});
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js
index 90125e7de6..78c8b5fa88 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js
@@ -264,9 +264,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
//reset the tabs and set the active one
if (response.data.tabs && response.data.tabs.length > 0) {
- _.each(response.data.tabs, function (item) {
- item.active = false;
- });
+ response.data.tabs.forEach(item => item.active = false);
response.data.tabs[activeTabIndex].active = true;
}
@@ -327,7 +325,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
if (!jsonData) { throw "jsonData cannot be null"; }
if (Utilities.isArray(jsonData)) {
- _.each(jsonData, function (item) {
+ jsonData.forEach(item => {
if (!item.key || !item.value) { throw "jsonData array item must have both a key and a value property"; }
});
}
@@ -345,7 +343,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
var formData = new FormData();
//add the json data
if (Utilities.isArray(data)) {
- _.each(data, function(item) {
+ data.forEach(item => {
formData.append(item.key, !Utilities.isString(item.value) ? Utilities.toJson(item.value) : item.value);
});
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/usershelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/usershelper.service.js
index 1bda86c3b3..e6450798fb 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/usershelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/usershelper.service.js
@@ -12,36 +12,23 @@
{ "value": 4, "name": "Inactive", "key": "Inactive", "color": "warning" }
];
- localizationService.localizeMany(_.map(userStates, function (userState) {
- return "user_state" + userState.key;
- })).then(function (data) {
- var reg = /^\[[\S\s]*]$/g;
- _.each(data, function (value, index) {
- if (!reg.test(value)) {
- // Only translate if key exists
- userStates[index].name = value;
- }
+ localizationService.localizeMany(userStates.map(userState => "user_state" + userState.key))
+ .then(data => {
+ var reg = /^\[[\S\s]*]$/g;
+ data.forEach((value, index) => {
+ if (!reg.test(value)) {
+ // Only translate if key exists
+ userStates[index].name = value;
+ }
+ });
});
- });
- function getUserStateFromValue(value) {
- var foundUserState;
- angular.forEach(userStates, function (userState) {
- if(userState.value === value) {
- foundUserState = userState;
- }
- });
- return foundUserState;
+ function getUserStateFromValue(value) {
+ return userStates.find(userState => userState.value === value);
}
function getUserStateByKey(key) {
- var foundUserState;
- angular.forEach(userStates, function (userState) {
- if(userState.key === key) {
- foundUserState = userState;
- }
- });
- return foundUserState;
+ return userStates.find(userState => userState.key === key);
}
function getUserStatesFilter(userStatesObject) {
@@ -49,7 +36,7 @@
var userStatesFilter = [];
for (var key in userStatesObject) {
- if (userStatesObject.hasOwnProperty(key)) {
+ if (hasOwnProperty.call(userStatesObject, key)) {
var userState = getUserStateByKey(key);
if(userState) {
userState.count = userStatesObject[key];
@@ -59,7 +46,6 @@
}
return userStatesFilter;
-
}
////////////
@@ -71,10 +57,7 @@
};
return service;
-
}
angular.module('umbraco.services').factory('usersHelper', usersHelperService);
-
-
})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js
index 82353df744..1fb5884d4c 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js
@@ -173,9 +173,9 @@ function umbModelMapper() {
/** This converts the source model to a basic entity model, it will throw an exception if there isn't enough data to create the model */
convertToEntityBasic: function (source) {
var required = ["id", "name", "icon", "parentId", "path"];
- _.each(required, function (k) {
- if (!_.has(source, k)) {
- throw "The source object does not contain the property " + k;
+ required.forEach(k => {
+ if (!hasOwnProperty.call(source, k)) {
+ throw `The source object does not contain the property ${k}`;
}
});
var optional = ["metaData", "key", "alias"];
diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/database.html b/src/Umbraco.Web.UI.Client/src/installer/steps/database.html
index fc870858cf..cfe0940aa2 100644
--- a/src/Umbraco.Web.UI.Client/src/installer/steps/database.html
+++ b/src/Umbraco.Web.UI.Client/src/installer/steps/database.html
@@ -1,118 +1,121 @@
- Enter connection and authentication details for the database you want to install Umbraco on -
++ Enter connection and authentication details for the database you want to install Umbraco on +
-
Query:
- {{ model.query }}
+ {{model.query}}
- Name:
-
+
+
Settings will only save if the entered json configuration is valid
- -
+
Modifying a row configuration name will result in loss of + data for any existing content that is based on this configuration.
+Modifying only the label will not result in data loss.
+
-
-
-
+
+
{{area.maxItems}}
@@ -84,7 +81,7 @@