@@ -33,11 +33,11 @@ angular.module('umbraco.directives')
return {
restrict: 'A',
link: function (scope, element, attr) {
-
+
var listItems = [];
var currentIndex = 0;
var focusSet = false;
-
+
$timeout(function(){
// get list of all links in the list
listItems = element.find("li :tabbable");
@@ -82,7 +82,7 @@ angular.module('umbraco.directives')
function arrowDown() {
if (currentIndex < listItems.length - 1) {
- // only bump the current index if the focus is already
+ // only bump the current index if the focus is already
// set else we just want to focus the first element
if (focusSet) {
currentIndex++;
@@ -112,4 +112,4 @@ angular.module('umbraco.directives')
}
};
- }]);
\ No newline at end of file
+ }]);
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 e42ac1d94d..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
@@ -159,9 +159,6 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location
element.removeClass(SHOW_VALIDATION_CLASS_NAME);
scope.showValidation = false;
notifySubView();
- //clear form state as at this point we retrieve new data from the server
- //and all validation will have cleared at this point
- formCtrl.$setPristine();
}));
var confirmed = false;
@@ -238,6 +235,8 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location
}
});
+ // TODO: I'm unsure why this exists, i believe this may be a hack for something like tinymce which might automatically
+ // change a form value on load but we need it to be $pristine?
$timeout(function () {
formCtrl.$setPristine();
}, 1000);
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js
index 979ac23bb3..26c0403f85 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js
@@ -306,7 +306,15 @@ function valPropertyMsg(serverValidationManager, localizationService, angularHel
formCtrl.$setValidity('valPropertyMsg', false);
startWatch();
-
+ // This check is required in order to be able to reset ourselves and is typically for complex editor
+ // scenarios where the umb-property itself doesn't contain any ng-model controls which means that the
+ // above serverValidityResetter technique will not work to clear valPropertyMsg errors.
+ // In order for this to work we rely on the current form controller's $pristine state. This means that anytime
+ // the form is submitted whether there are validation errors or not the state must be reset... this is automatically
+ // taken care of with the formHelper.resetForm method that should be used in all cases. $pristine is required because it's
+ // a value that is cascaded to all form controls based on the hierarchy of child ng-model controls. This allows us to easily
+ // know if a value has changed. The alternative is what we used to do which was to put a deep $watch on the entire complex value
+ // which is hugely inefficient.
if (propertyErrors.length === 1 && hadError && !formCtrl.$pristine) {
var propertyValidationPath = umbPropCtrl.getValidationPath();
serverValidationManager.removePropertyError(propertyValidationPath, currentCulture, "", currentSegment);
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valservermatch.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valservermatch.directive.js
index 1b4c593735..5f8600c8c0 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valservermatch.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valservermatch.directive.js
@@ -54,6 +54,8 @@ function valServerMatch(serverValidationManager) {
function bindCallback(validationKey, matchVal, matchType) {
+ if (!matchVal) return;
+
if (Utilities.isString(matchVal)) {
matchVal = [matchVal]; // normalize to an array since the value can also natively be an array
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/filters/compareArrays.filter.js b/src/Umbraco.Web.UI.Client/src/common/filters/compareArrays.filter.js
index 13f603260d..0cbf3077e6 100644
--- a/src/Umbraco.Web.UI.Client/src/common/filters/compareArrays.filter.js
+++ b/src/Umbraco.Web.UI.Client/src/common/filters/compareArrays.filter.js
@@ -2,13 +2,17 @@ angular.module("umbraco.filters")
.filter('compareArrays', function() {
return function inArray(array, compareArray, compareProperty) {
+ if (!compareArray || !compareArray.length) {
+ return [...array];
+ }
+
var result = [];
- angular.forEach(array, function(arrayItem){
+ array.forEach(function(arrayItem){
var exists = false;
- angular.forEach(compareArray, function(compareItem){
+ compareArray.forEach(function(compareItem){
if( arrayItem[compareProperty] === compareItem[compareProperty]) {
exists = true;
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js
index fd620bac18..7e7f804656 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js
@@ -95,7 +95,7 @@ function angularHelper($q) {
*/
revalidateNgModel: function (scope, ngModel) {
this.safeApply(scope, function() {
- angular.forEach(ngModel.$parsers, function (parser) {
+ ngModel.$parsers.forEach(function (parser) {
parser(ngModel.$viewValue);
});
});
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js
index ffb1971169..dfa0eae297 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditor.service.js
@@ -4,39 +4,113 @@
*
* @description
* Added in Umbraco 8.7. Service for dealing with Block Editors.
- *
+ *
* Block Editor Service provides the basic features for a block editor.
* The main feature is the ability to create a Model Object which takes care of your data for your Block Editor.
- *
- *
+ *
+ *
* ##Samples
*
* ####Instantiate a Model Object for your property editor:
- *
+ *
*
*
- *
+ *
* See {@link umbraco.services.blockEditorModelObject BlockEditorModelObject} for more samples.
- *
+ *
*/
(function () {
'use strict';
+
+ /**
+ * When performing a runtime copy of Block Editors entries, we copy the ElementType Data Model and inner IDs are kept identical, to ensure new IDs are changed on paste we need to provide a resolver for the ClipboardService.
+ */
+ angular.module('umbraco').run(['clipboardService', 'udiService', function (clipboardService, udiService) {
+
+ function replaceUdi(obj, key, dataObject) {
+ var udi = obj[key];
+ var newUdi = udiService.create("element");
+ obj[key] = newUdi;
+ dataObject.forEach((data) => {
+ if (data.udi === udi) {
+ data.udi = newUdi;
+ }
+ });
+ }
+ function replaceUdisOfObject(obj, propValue) {
+ for (var k in obj) {
+ if(k === "contentUdi") {
+ replaceUdi(obj, k, propValue.contentData);
+ } else if(k === "settingsUdi") {
+ replaceUdi(obj, k, propValue.settingsData);
+ } else {
+ // lets crawl through all properties of layout to make sure get captured all `contentUdi` and `settingsUdi` properties.
+ var propType = typeof obj[k];
+ if(propType === "object" || propType === "array") {
+ replaceUdisOfObject(obj[k], propValue)
+ }
+ }
+ }
+ }
+ function replaceElementTypeBlockListUDIsResolver(obj, propClearingMethod) {
+ replaceRawBlockListUDIsResolver(obj.value, propClearingMethod);
+ }
+
+ clipboardService.registerPastePropertyResolver(replaceElementTypeBlockListUDIsResolver, clipboardService.TYPES.ELEMENT_TYPE);
+
+
+ function replaceRawBlockListUDIsResolver(value, propClearingMethod) {
+ if (typeof value === "object") {
+
+ // we got an object, and it has these three props then we are most likely dealing with a Block Editor.
+ if ((value.layout !== undefined && value.contentData !== undefined && value.settingsData !== undefined)) {
+
+ replaceUdisOfObject(value.layout, value);
+
+ // replace UDIs for inner properties of this Block Editors content data.
+ if(value.contentData.length > 0) {
+ value.contentData.forEach((item) => {
+ for (var k in item) {
+ propClearingMethod(item[k], clipboardService.TYPES.RAW);
+ }
+ });
+ }
+ // replace UDIs for inner properties of this Block Editors settings data.
+ if(value.settingsData.length > 0) {
+ value.settingsData.forEach((item) => {
+ for (var k in item) {
+ propClearingMethod(item[k], clipboardService.TYPES.RAW);
+ }
+ });
+ }
+
+ }
+ }
+ }
+
+ clipboardService.registerPastePropertyResolver(replaceRawBlockListUDIsResolver, clipboardService.TYPES.RAW);
+
+ }]);
+
+
+
+
function blockEditorService(blockEditorModelObject) {
/**
* @ngdocs function
* @name createModelObject
* @methodOf umbraco.services.blockEditorService
- *
+ *
* @description
* Create a new Block Editor Model Object.
* See {@link umbraco.services.blockEditorModelObject blockEditorModelObject}
- *
+ *
* @see umbraco.services.blockEditorModelObject
* @param {object} propertyModelValue data object of the property editor, usually model.value.
* @param {string} propertyEditorAlias alias of the property.
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js
index fe57534ffb..868b8baba7 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js
@@ -4,17 +4,16 @@
*
* @description
* Added in Umbraco 8.7. Model Object for dealing with data of Block Editors.
- *
+ *
* Block Editor Model Object provides the basic features for editing data of a block editor.
* Use the Block Editor Service to instantiate the Model Object.
* See {@link umbraco.services.blockEditorService blockEditorService}
- *
+ *
*/
(function () {
'use strict';
-
- function blockEditorModelObjectFactory($interpolate, $q, udiService, contentResource, localizationService) {
+ function blockEditorModelObjectFactory($interpolate, $q, udiService, contentResource, localizationService, umbRequestHelper, clipboardService) {
/**
* Simple mapping from property model content entry to editing model,
@@ -231,12 +230,13 @@
var notSupportedProperties = [
"Umbraco.Tags",
"Umbraco.UploadField",
- "Umbraco.ImageCropper"
+ "Umbraco.ImageCropper",
+ "Umbraco.NestedContent"
];
/**
- * Formats the content apps and ensures unsupported property's have the notsupported view (returns a promise)
+ * Formats the content apps and ensures unsupported property's have the notsupported view
* @param {any} scaffold
*/
function formatScaffoldData(scaffold) {
@@ -255,7 +255,7 @@
// could be empty in tests
if (!scaffold.apps) {
console.warn("No content apps found in scaffold");
- return $q.resolve(scaffold);
+ return scaffold;
}
// replace view of content app
@@ -271,22 +271,27 @@
scaffold.apps.splice(infoAppIndex, 1);
}
+ return scaffold;
+ }
+
+ /**
+ * Creates a settings content app, we only want to do this if settings is present on the specific block.
+ * @param {any} contentModel
+ */
+ function appendSettingsContentApp(contentModel, settingsName) {
+ if (!contentModel.apps) {
+ return
+ }
+
// add the settings app
- return localizationService.localize("blockEditor_tabBlockSettings").then(
- function (settingsName) {
-
- var settingsTab = {
- "name": settingsName,
- "alias": "settings",
- "icon": "icon-settings",
- "view": "views/common/infiniteeditors/blockeditor/blockeditor.settings.html",
- "hasError": false
- };
- scaffold.apps.push(settingsTab);
-
- return scaffold;
- }
- );
+ var settingsTab = {
+ "name": settingsName,
+ "alias": "settings",
+ "icon": "icon-settings",
+ "view": "views/common/infiniteeditors/blockeditor/blockeditor.settings.html",
+ "hasError": false
+ };
+ contentModel.apps.push(settingsTab);
}
/**
@@ -309,6 +314,8 @@
this.__watchers = [];
+ this.__labels = {};
+
// ensure basic part of data-structure is in place:
this.value = propertyModelValue;
this.value.layout = this.value.layout || {};
@@ -318,13 +325,25 @@
this.propertyEditorAlias = propertyEditorAlias;
this.blockConfigurations = blockConfigurations;
+ this.blockConfigurations.forEach(blockConfiguration => {
+ if (blockConfiguration.view != null && blockConfiguration.view !== "") {
+ blockConfiguration.view = umbRequestHelper.convertVirtualToAbsolutePath(blockConfiguration.view);
+ }
+ if (blockConfiguration.stylesheet != null && blockConfiguration.stylesheet !== "") {
+ blockConfiguration.stylesheet = umbRequestHelper.convertVirtualToAbsolutePath(blockConfiguration.stylesheet);
+ }
+ if (blockConfiguration.thumbnail != null && blockConfiguration.thumbnail !== "") {
+ blockConfiguration.thumbnail = umbRequestHelper.convertVirtualToAbsolutePath(blockConfiguration.thumbnail);
+ }
+ });
+
this.scaffolds = [];
this.isolatedScope = scopeOfExistance.$new(true);
this.isolatedScope.blockObjects = {};
this.__watchers.push(this.isolatedScope.$on("$destroy", this.destroy.bind(this)));
- this.__watchers.push(propertyEditorScope.$on("postFormSubmitting", this.sync.bind(this)));
+ this.__watchers.push(propertyEditorScope.$on("formSubmittingFinalPhase", this.sync.bind(this)));
};
@@ -344,24 +363,25 @@
// update our values
this.value = propertyModelValue;
this.value.layout = this.value.layout || {};
- this.value.data = this.value.data || [];
+ this.value.contentData = this.value.contentData || [];
+ this.value.settingsData = this.value.settingsData || [];
// re-create the watchers
this.__watchers = [];
this.__watchers.push(this.isolatedScope.$on("$destroy", this.destroy.bind(this)));
- this.__watchers.push(propertyEditorScope.$on("postFormSubmitting", this.sync.bind(this)));
+ this.__watchers.push(propertyEditorScope.$on("formSubmittingFinalPhase", this.sync.bind(this)));
},
/**
* @ngdoc method
* @name getBlockConfiguration
* @methodOf umbraco.services.blockEditorModelObject
- * @description Get block configuration object for a given contentTypeKey.
- * @param {string} key contentTypeKey to recive the configuration model for.
- * @returns {Object | null} Configuration model for the that specific block. Or ´null´ if the contentTypeKey isnt available in the current block configurations.
+ * @description Get block configuration object for a given contentElementTypeKey.
+ * @param {string} key contentElementTypeKey to recive the configuration model for.
+ * @returns {Object | null} Configuration model for the that specific block. Or ´null´ if the contentElementTypeKey isnt available in the current block configurations.
*/
getBlockConfiguration: function (key) {
- return this.blockConfigurations.find(bc => bc.contentTypeKey === key) || null;
+ return this.blockConfigurations.find(bc => bc.contentElementTypeKey === key) || null;
},
/**
@@ -373,12 +393,24 @@
* @returns {Promise} A Promise object which resolves when all scaffold models are loaded.
*/
load: function () {
+
+ var self = this;
+
var tasks = [];
+ tasks.push(localizationService.localize("blockEditor_tabBlockSettings").then(
+ function (settingsName) {
+ // self.__labels might not exists anymore, this happens if this instance has been destroyed before the load is complete.
+ if(self.__labels) {
+ self.__labels.settingsName = settingsName;
+ }
+ }
+ ));
+
var scaffoldKeys = [];
this.blockConfigurations.forEach(blockConfiguration => {
- scaffoldKeys.push(blockConfiguration.contentTypeKey);
+ scaffoldKeys.push(blockConfiguration.contentElementTypeKey);
if (blockConfiguration.settingsElementTypeKey != null) {
scaffoldKeys.push(blockConfiguration.settingsElementTypeKey);
}
@@ -387,19 +419,11 @@
// removing duplicates.
scaffoldKeys = scaffoldKeys.filter((value, index, self) => self.indexOf(value) === index);
- var self = this;
-
scaffoldKeys.forEach(contentTypeKey => {
tasks.push(contentResource.getScaffoldByKey(-20, contentTypeKey).then(scaffold => {
// self.scaffolds might not exists anymore, this happens if this instance has been destroyed before the load is complete.
if (self.scaffolds) {
- return formatScaffoldData(scaffold).then(s => {
- self.scaffolds.push(s);
- return s;
- });
- }
- else {
- return $q.resolve(scaffold);
+ self.scaffolds.push(formatScaffoldData(scaffold));
}
}));
});
@@ -415,7 +439,7 @@
* @return {Array} array of strings representing alias.
*/
getAvailableAliasesForBlockContent: function () {
- return this.blockConfigurations.map(blockConfiguration => this.getScaffoldFromKey(blockConfiguration.contentTypeKey).contentTypeAlias);
+ return this.blockConfigurations.map(blockConfiguration => this.getScaffoldFromKey(blockConfiguration.contentElementTypeKey).contentTypeAlias);
},
/**
@@ -431,7 +455,7 @@
var blocks = [];
this.blockConfigurations.forEach(blockConfiguration => {
- var scaffold = this.getScaffoldFromKey(blockConfiguration.contentTypeKey);
+ var scaffold = this.getScaffoldFromKey(blockConfiguration.contentElementTypeKey);
if (scaffold) {
blocks.push({
blockConfigModel: blockConfiguration,
@@ -500,32 +524,29 @@
}
var blockConfiguration = this.getBlockConfiguration(dataModel.contentTypeKey);
- var contentScaffold;
+ var contentScaffold = null;
if (blockConfiguration === null) {
- console.error("The block entry of " + contentUdi + " is not being initialized because its contentTypeKey is not allowed for this PropertyEditor");
- }
- else {
- contentScaffold = this.getScaffoldFromKey(blockConfiguration.contentTypeKey);
+ console.warn("The block of " + contentUdi + " is not being initialized because its contentTypeKey('" + dataModel.contentTypeKey + "') is not allowed for this PropertyEditor");
+ } else {
+ contentScaffold = this.getScaffoldFromKey(blockConfiguration.contentElementTypeKey);
if (contentScaffold === null) {
- console.error("The block entry of " + contentUdi + " is not begin initialized cause its Element Type was not loaded.");
+ console.error("The block of " + contentUdi + " is not begin initialized cause its Element Type was not loaded.");
}
}
if (blockConfiguration === null || contentScaffold === null) {
blockConfiguration = {
- label: "Unsupported Block",
+ label: "Unsupported",
unsupported: true
};
- contentScaffold = {};
-
}
var blockObject = {};
// Set an angularJS cloneNode method, to avoid this object begin cloned.
blockObject.cloneNode = function () {
- return null;// angularJS accept this as a cloned value as long as the
+ return null;// angularJS accept this as a cloned value as long as the
}
blockObject.key = String.CreateGuid().replace(/-/g, "");
blockObject.config = Utilities.copy(blockConfiguration);
@@ -544,10 +565,14 @@
, 10);
// make basics from scaffold
- blockObject.content = Utilities.copy(contentScaffold);
- ensureUdiAndKey(blockObject.content, contentUdi);
+ if(contentScaffold !== null) {// We might not have contentScaffold
+ blockObject.content = Utilities.copy(contentScaffold);
+ ensureUdiAndKey(blockObject.content, contentUdi);
- mapToElementModel(blockObject.content, dataModel);
+ mapToElementModel(blockObject.content, dataModel);
+ } else {
+ blockObject.content = null;
+ }
blockObject.data = dataModel;
blockObject.layout = layoutEntry;
@@ -577,6 +602,9 @@
ensureUdiAndKey(blockObject.settings, settingsUdi);
mapToElementModel(blockObject.settings, settingsData);
+
+ // add settings content-app
+ appendSettingsContentApp(blockObject.content, this.__labels.settingsName);
}
}
@@ -587,8 +615,7 @@
if (this.config.settingsElementTypeKey !== null) {
mapElementValues(settings, this.settings);
}
- }
-
+ };
blockObject.sync = function () {
if (this.content !== null) {
@@ -597,7 +624,7 @@
if (this.config.settingsElementTypeKey !== null) {
mapToPropertyModel(this.settings, this.settingsData);
}
- }
+ };
// first time instant update of label.
blockObject.label = getBlockLabel(blockObject);
@@ -623,7 +650,7 @@
// remove model from isolatedScope.
delete this.__scope.blockObjects["_" + this.key];
- // NOTE: It seems like we should call this.__scope.$destroy(); since that is the only way to remove a scope from it's parent,
+ // NOTE: It seems like we should call this.__scope.$destroy(); since that is the only way to remove a scope from it's parent,
// however that is not the case since __scope is actually this.isolatedScope which gets cleaned up when the outer scope is
// destroyed. If we do that here it breaks the scope chain and validation.
delete this.__scope;
@@ -636,7 +663,6 @@
}
return blockObject;
-
},
/**
@@ -648,11 +674,8 @@
* @param {Object} blockObject The BlockObject to be removed and destroyed.
*/
removeDataAndDestroyModel: function (blockObject) {
- var udi = blockObject.content.udi;
- var settingsUdi = null;
- if (blockObject.settings) {
- settingsUdi = blockObject.settings.udi;
- }
+ var udi = blockObject.layout.contentUdi;
+ var settingsUdi = blockObject.layout.settingsUdi || null;
this.destroyBlockObject(blockObject);
this.removeDataByUdi(udi);
if (settingsUdi) {
@@ -691,18 +714,18 @@
* @name create
* @methodOf umbraco.services.blockEditorModelObject
* @description Create a empty layout entry, notice the layout entry is not added to the property editors model layout object, since the layout sturcture depends on the property editor.
- * @param {string} contentTypeKey the contentTypeKey of the block you wish to create, if contentTypeKey is not avaiable in the block configuration then ´null´ will be returned.
- * @return {Object | null} Layout entry object, to be inserted at a decired location in the layout object. Or null if contentTypeKey is unavaiaible.
+ * @param {string} contentElementTypeKey the contentElementTypeKey of the block you wish to create, if contentElementTypeKey is not avaiable in the block configuration then ´null´ will be returned.
+ * @return {Object | null} Layout entry object, to be inserted at a decired location in the layout object. Or null if contentElementTypeKey is unavaiaible.
*/
- create: function (contentTypeKey) {
+ create: function (contentElementTypeKey) {
- var blockConfiguration = this.getBlockConfiguration(contentTypeKey);
+ var blockConfiguration = this.getBlockConfiguration(contentElementTypeKey);
if (blockConfiguration === null) {
return null;
}
var entry = {
- contentUdi: createDataEntry(contentTypeKey, this.value.contentData)
+ contentUdi: createDataEntry(contentElementTypeKey, this.value.contentData)
}
if (blockConfiguration.settingsElementTypeKey != null) {
@@ -721,16 +744,16 @@
*/
createFromElementType: function (elementTypeDataModel) {
- elementTypeDataModel = Utilities.copy(elementTypeDataModel);
+ elementTypeDataModel = clipboardService.parseContentForPaste(elementTypeDataModel, clipboardService.TYPES.ELEMENT_TYPE);
- var contentTypeKey = elementTypeDataModel.contentTypeKey;
+ var contentElementTypeKey = elementTypeDataModel.contentTypeKey;
- var layoutEntry = this.create(contentTypeKey);
+ var layoutEntry = this.create(contentElementTypeKey);
if (layoutEntry === null) {
return null;
}
- var dataModel = getDataByUdi(layoutEntry.udi, this.value.contentData);
+ var dataModel = getDataByUdi(layoutEntry.contentUdi, this.value.contentData);
if (dataModel === null) {
return null;
}
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..58ed07367e 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,34 @@
*
*/
function clipboardService(notificationsService, eventsService, localStorageService, iconHelper) {
-
- var clearPropertyResolvers = [];
-
+ const TYPES = {};
+ TYPES.ELEMENT_TYPE = "elementType";
+ TYPES.RAW = "raw";
+
+ var clearPropertyResolvers = {};
+ var pastePropertyResolvers = {};
+ var clipboardTypeResolvers = {};
+
+ clipboardTypeResolvers[TYPES.ELEMENT_TYPE] = function(data, propMethod) {
+ for (var t = 0; t < data.variants[0].tabs.length; t++) {
+ var tab = data.variants[0].tabs[t];
+ for (var p = 0; p < tab.properties.length; p++) {
+ var prop = tab.properties[p];
+ propMethod(prop, TYPES.ELEMENT_TYPE);
+ }
+ }
+ }
+ clipboardTypeResolvers[TYPES.RAW] = function(data, propMethod) {
+ for (var p = 0; p < data.length; p++) {
+ propMethod(data[p], TYPES.RAW);
+ }
+ }
+
+
var STORAGE_KEY = "umbClipboardService";
-
+
var retriveStorage = function() {
if (localStorageService.isSupported === false) {
return null;
@@ -27,58 +48,59 @@ 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;
}
- function clearPropertyForStorage(prop) {
+ function resolvePropertyForStorage(prop, type) {
- for (var i=0; i allowedAlias === entry.alias).length > 0)
- ||
+ ||
(entry.aliases && entry.aliases.filter(entryAlias => allowedAliases.filter(allowedAlias => allowedAlias === entryAlias).length > 0).length === entry.aliases.length)
);
}
-
-
+
+
+
+
+ function resolvePropertyForPaste(prop, type) {
+
+ type = type || "raw";
+ var resolvers = pastePropertyResolvers[type];
+
+ for (var i=0; i {
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(type, 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 +298,35 @@ 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) {
-
+
+ if (type === "elementTypeArray") {
+ type = "elementType";
+ }
+
var storage = retriveStorage();
-
+
// Clean up each entry
- var copiedDatas = datas.map(data => prepareEntryForStorage(data, firstLevelClearupMethod));
-
+ var copiedDatas = datas.map(data => prepareEntryForStorage(type, 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 +338,7 @@ function clipboardService(notificationsService, eventsService, localStorageServi
service.isSupported = function() {
return localStorageService.isSupported;
};
-
+
/**
* @ngdoc method
* @name umbraco.services.supportsCopy#hasEntriesOfType
@@ -253,14 +351,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 +366,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 +391,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 +406,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 +420,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 562a9766a6..6d41ea087d 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
@@ -117,6 +117,9 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
return $q.resolve(data);
}, function (err) {
+
+ formHelper.resetForm({ scope: args.scope, hasErrors: true });
+
self.handleSaveError({
showNotifications: args.showNotifications,
softRedirect: args.softRedirect,
@@ -148,7 +151,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
// first check if tab is already added
var foundInfoTab = false;
- angular.forEach(tabs, function (tab) {
+ tabs.forEach(function (tab) {
if (tab.id === infoTab.id && tab.alias === infoTab.alias) {
foundInfoTab = true;
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenttypehelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenttypehelper.service.js
index 1be66cc68f..9cec15d519 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/contenttypehelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/contenttypehelper.service.js
@@ -11,7 +11,7 @@ function contentTypeHelper(contentTypeResource, dataTypeResource, $filter, $inje
var newArray = [];
- angular.forEach(array, function (arrayItem) {
+ array.forEach(function (arrayItem) {
if (Utilities.isObject(arrayItem)) {
newArray.push(arrayItem.id);
@@ -116,13 +116,12 @@ function contentTypeHelper(contentTypeResource, dataTypeResource, $filter, $inje
throw new Error("Cannot add this composition, these properties already exist on the content type: " + overlappingAliases.join());
}
- angular.forEach(compositeContentType.groups, function (compositionGroup) {
-
+ compositeContentType.groups.forEach(function (compositionGroup) {
// order composition groups based on sort order
compositionGroup.properties = $filter('orderBy')(compositionGroup.properties, 'sortOrder');
// get data type details
- angular.forEach(compositionGroup.properties, function (property) {
+ compositionGroup.properties.forEach(function (property) {
dataTypeResource.getById(property.dataTypeId)
.then(function (dataType) {
property.dataTypeIcon = dataType.icon;
@@ -134,7 +133,7 @@ function contentTypeHelper(contentTypeResource, dataTypeResource, $filter, $inje
compositionGroup.inherited = true;
// set inherited state on properties
- angular.forEach(compositionGroup.properties, function (compositionProperty) {
+ compositionGroup.properties.forEach(function (compositionProperty) {
compositionProperty.inherited = true;
});
@@ -142,7 +141,7 @@ function contentTypeHelper(contentTypeResource, dataTypeResource, $filter, $inje
compositionGroup.tabState = "inActive";
// if groups are named the same - merge the groups
- angular.forEach(contentType.groups, function (contentTypeGroup) {
+ contentType.groups.forEach(function (contentTypeGroup) {
if (contentTypeGroup.name === compositionGroup.name) {
@@ -224,7 +223,7 @@ function contentTypeHelper(contentTypeResource, dataTypeResource, $filter, $inje
var groups = [];
- angular.forEach(contentType.groups, function (contentTypeGroup) {
+ contentType.groups.forEach(function (contentTypeGroup) {
if (contentTypeGroup.tabState !== "init") {
@@ -238,7 +237,7 @@ function contentTypeHelper(contentTypeResource, dataTypeResource, $filter, $inje
var properties = [];
// remove all properties from composite content type
- angular.forEach(contentTypeGroup.properties, function (property) {
+ contentTypeGroup.properties.forEach(function (property) {
if (property.contentTypeId !== compositeContentType.id) {
properties.push(property);
}
@@ -283,7 +282,7 @@ function contentTypeHelper(contentTypeResource, dataTypeResource, $filter, $inje
var sortOrder = 0;
- angular.forEach(properties, function (property) {
+ properties.forEach(function (property) {
if (!property.inherited && property.propertyState !== "init") {
property.sortOrder = sortOrder;
}
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 0f4f04c6bf..381d09f62d 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
@@ -761,6 +761,23 @@ When building a custom infinite editor view you can use the same components as a
open(editor);
}
+ /**
+ * @ngdoc method
+ * @name umbraco.services.editorService#userGroupEditor
+ * @methodOf umbraco.services.editorService
+ *
+ * @description
+ * Opens the user group picker in infinite editing, the submit callback returns the saved user group
+ * @param {Object} editor rendering options
+ * @param {Callback} editor.submit Submits the editor
+ * @param {Callback} editor.close Closes the editor
+ * @returns {Object} editor object
+ */
+ function userGroupEditor(editor) {
+ editor.view = "views/users/group.html";
+ open(editor);
+ }
+
/**
* @ngdoc method
* @name umbraco.services.editorService#templateEditor
@@ -1028,6 +1045,7 @@ When building a custom infinite editor view you can use the same components as a
nodePermissions: nodePermissions,
insertCodeSnippet: insertCodeSnippet,
userGroupPicker: userGroupPicker,
+ userGroupEditor: userGroupEditor,
templateEditor: templateEditor,
sectionPicker: sectionPicker,
insertField: insertField,
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 d9c11770cc..bd6bbcc5b3 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) {
@@ -46,7 +46,12 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
args.scope.$broadcast("formSubmitting", { scope: args.scope, action: args.action });
this.focusOnFirstError(currentForm);
- args.scope.$broadcast("postFormSubmitting", { 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();
//then check if the form is valid
if (!args.skipValidation) {
@@ -101,18 +106,32 @@ 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;
+ }
- args.scope.$broadcast("formSubmitted", { scope: args.scope });
+ // Set the form state to pristine
+ currentForm.$setPristine();
+ currentForm.$setUntouched();
+
+ args.scope.$broadcast(args.hasErrors ? "formSubmittedValidationFailed" : "formSubmitted", { scope: args.scope });
},
showNotifications: function (args) {
@@ -137,7 +156,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) {
@@ -176,7 +195,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/listviewhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js
index 28156e70c3..ee1e2a2311 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js
@@ -419,7 +419,7 @@
if (isSelectedAll(items, selection)) {
// unselect all items
- angular.forEach(items, function (item) {
+ items.forEach(function (item) {
item.selected = false;
});
@@ -432,7 +432,7 @@
selection.length = 0;
// select all items
- angular.forEach(items, function (item) {
+ items.forEach(function (item) {
var obj = {
id: item.id
};
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 2a5d182a59..3164c3ab19 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
@@ -127,6 +127,13 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
}
}
+ function showBackdrop() {
+ var backDropOptions = {
+ 'element': $('#leftcolumn')[0]
+ };
+ backdropService.open(backDropOptions);
+ }
+
var service = {
/**
@@ -427,13 +434,9 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
showMenu: function (args) {
var self = this;
- var backDropOptions = {
- 'element': $('#leftcolumn')[0]
- };
-
return treeService.getMenu({ treeNode: args.node })
.then(function (data) {
- backdropService.open(backDropOptions);
+ showBackdrop();
//check for a default
//NOTE: event will be undefined when a call to hideDialog is made so it won't re-load the default again.
// but perhaps there's a better way to deal with with an additional parameter in the args ? it works though.
@@ -544,6 +547,7 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
}
}
else {
+ showBackdrop();
service.showDialog({
node: node,
action: action,
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 803cd857b7..8e9525af84 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
@@ -12,7 +12,7 @@
*
*
* searchService.searchMembers({term: 'bob'}).then(function(results){
- * angular.forEach(results, function(result){
+ * results.forEach(function(result){
* //returns:
* {name: "name", id: 1234, menuUrl: "url", editorPath: "url", metaData: {}, subtitle: "/path/etc" }
* })
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 5d6b4646a3..6c0165ebfe 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
@@ -107,7 +107,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
//queue rules loading
if (configuredStylesheets) {
- angular.forEach(configuredStylesheets, function (val, key) {
+ configuredStylesheets.forEach(function (val, key) {
if (val.indexOf(Umbraco.Sys.ServerVariables.umbracoSettings.cssPath + "/") === 0) {
// current format (full path to stylesheet)
@@ -119,7 +119,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
}
promises.push(stylesheetResource.getRulesByName(val).then(function (rules) {
- angular.forEach(rules, function (rule) {
+ rules.forEach(function (rule) {
var r = {};
r.title = rule.name;
if (rule.selector[0] == ".") {
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 cc529abb56..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
@@ -15,7 +15,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* This will convert a virtual path (i.e. ~/App_Plugins/Blah/Test.html ) to an absolute path
- *
+ *
* @param {string} a virtual path, if this is already an absolute path it will just be returned, if this is a relative path an exception will be thrown
*/
convertVirtualToAbsolutePath: function(virtualPath) {
@@ -31,6 +31,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
return Umbraco.Sys.ServerVariables.application.applicationPath + virtualPath.trimStart("~/");
},
+
/**
* @ngdoc method
* @name umbraco.services.umbRequestHelper#dictionaryToQueryString
@@ -39,7 +40,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* This will turn an array of key/value pairs or a standard dictionary into a query string
- *
+ *
* @param {Array} queryStrings An array of key/value pairs
*/
dictionaryToQueryString: function (queryStrings) {
@@ -76,9 +77,9 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* This will return the webapi Url for the requested key based on the servervariables collection
- *
+ *
* @param {string} apiName The webapi name that is found in the servervariables["umbracoUrls"] dictionary
- * @param {string} actionName The webapi action name
+ * @param {string} actionName The webapi action name
* @param {object} queryStrings Can be either a string or an array containing key/value pairs
*/
getApiUrl: function (apiName, actionName, queryStrings) {
@@ -103,7 +104,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* This returns a promise with an underlying http call, it is a helper method to reduce
- * the amount of duplicate code needed to query http resources and automatically handle any
+ * the amount of duplicate code needed to query http resources and automatically handle any
* Http errors. See /docs/source/using-promises-resources.md
*
* @param {object} opts A mixed object which can either be a string representing the error message to be
@@ -117,7 +118,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
* The error callback must return an object containing: {errorMsg: errorMessage, data: originalData, status: status }
*/
resourcePromise: function (httpPromise, opts) {
-
+
/** The default success callback used if one is not supplied in the opts */
function defaultSuccess(data, status, headers, config) {
//when it's successful, just return the data
@@ -151,7 +152,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
return httpPromise.then(function (response) {
- //invoke the callback
+ //invoke the callback
var result = callbacks.success.apply(this, [response.data, response.status, response.headers, response.config]);
formHelper.showNotifications(response.data);
@@ -183,7 +184,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
overlayService.ysod(error);
}
else {
- //show a simple error notification
+ //show a simple error notification
notificationsService.error("Server error", "Contact administrator, see log for full details. " + result.errorMsg + "");
}
@@ -209,7 +210,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* Used for saving content/media/members specifically
- *
+ *
* @param {Object} args arguments object
* @returns {Promise} http promise object.
*/
@@ -233,7 +234,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
if (args.showNotifications === null || args.showNotifications === undefined) {
args.showNotifications = true;
}
-
+
//save the active tab id so we can set it when the data is returned.
var activeTab = _.find(args.content.tabs, function (item) {
return item.active;
@@ -298,7 +299,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
overlayService.ysod(error);
}
else {
- //show a simple error notification
+ //show a simple error notification
notificationsService.error("Server error", "Contact administrator, see log for full details. " + response.data.ExceptionMessage + "");
}
@@ -329,7 +330,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
});
}
else if (!jsonData.key || !jsonData.value) { throw "jsonData object must have both a key and a value property"; }
-
+
return $http({
method: 'POST',
url: url,
@@ -364,7 +365,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
return $q.reject(response);
});
},
-
+
/**
* @ngdoc method
* @name umbraco.resources.contentResource#downloadFile
@@ -372,7 +373,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* Downloads a file to the client using AJAX/XHR
- *
+ *
* @param {string} httpPath the path (url) to the resource being downloaded
* @returns {Promise} http promise object.
*/
@@ -386,7 +387,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
// Use an arraybuffer
return $http.get(httpPath, { responseType: 'arraybuffer' })
.then(function (response) {
-
+
var octetStreamMime = 'application/octet-stream';
var success = false;
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 cfe0940aa2..ebffc4cf97 100644
--- a/src/Umbraco.Web.UI.Client/src/installer/steps/database.html
+++ b/src/Umbraco.Web.UI.Client/src/installer/steps/database.html
@@ -40,7 +40,7 @@
-
+ Sorry, we can not find what you are looking for.
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html
index fc1bec4ec1..c23aaa7cb9 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html
+++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html
@@ -15,7 +15,7 @@
@@ -275,7 +273,7 @@
description="Here you can add custom installer / uninstaller events to perform certain tasks during installation and uninstallation.
All actions are formed as a xml node, containing data for the action to be performed.">