Merge remote-tracking branch 'origin/netcore/netcore' into netcore/feature/websecurity

Signed-off-by: Bjarke Berg <mail@bergmania.dk>

# Conflicts:
#	src/Umbraco.Web/Editors/AuthenticationController.cs
This commit is contained in:
Bjarke Berg
2020-06-10 11:10:48 +02:00
110 changed files with 1945 additions and 802 deletions

View File

@@ -3,10 +3,16 @@
module.exports = {
compile: {
build: {
sourcemaps: false
sourcemaps: false,
embedtemplates: true
},
dev: {
sourcemaps: true
sourcemaps: true,
embedtemplates: true
},
test: {
sourcemaps: false,
embedtemplates: true
}
},
sources: {
@@ -17,7 +23,7 @@ module.exports = {
installer: { files: "./src/less/installer.less", watch: "./src/less/**/*.less", out: "installer.css" },
nonodes: { files: "./src/less/pages/nonodes.less", watch: "./src/less/**/*.less", out: "nonodes.style.min.css"},
preview: { files: "./src/less/canvas-designer.less", watch: "./src/less/**/*.less", out: "canvasdesigner.css" },
umbraco: { files: "./src/less/belle.less", watch: "./src/less/**/*.less", out: "umbraco.css" },
umbraco: { files: "./src/less/belle.less", watch: "./src/**/*.less", out: "umbraco.css" },
rteContent: { files: "./src/less/rte-content.less", watch: "./src/less/**/*.less", out: "rte-content.css" }
},

View File

@@ -10,4 +10,14 @@ function setDevelopmentMode(cb) {
return cb();
};
module.exports = { setDevelopmentMode: setDevelopmentMode };
function setTestMode(cb) {
config.compile.current = config.compile.test;
return cb();
};
module.exports = {
setDevelopmentMode: setDevelopmentMode,
setTestMode: setTestMode
};

View File

@@ -6,11 +6,24 @@ var karmaServer = require('karma').Server;
* Build tests
**************************/
// Karma test
// Karma test
function testUnit() {
return new karmaServer({
configFile: __dirname + "/../../test/config/karma.conf.js"
})
.start();
};
// Run karma test server
function runUnitTestServer() {
return new karmaServer({
configFile: __dirname + "/../../test/config/karma.conf.js",
autoWatch: true,
port: 9999,
singleRun: false,
browsers: ['ChromeDebugging'],
keepalive: true
})
.start();
@@ -24,4 +37,4 @@ function testE2e() {
.start();
};
module.exports = { testUnit: testUnit, testE2e: testE2e };
module.exports = { testUnit: testUnit, testE2e: testE2e, runUnitTestServer: runUnitTestServer };

View File

@@ -1,7 +1,7 @@
'use strict';
const config = require('../config');
const {watch, parallel, dest, src} = require('gulp');
const {watch, series, parallel, dest, src} = require('gulp');
var _ = require('lodash');
var MergeStream = require('merge-stream');
@@ -9,9 +9,7 @@ var MergeStream = require('merge-stream');
var processJs = require('../util/processJs');
var processLess = require('../util/processLess');
//const { less } = require('./less');
//const { views } = require('./views');
var {js} = require('./js');
function watchTask(cb) {
@@ -30,24 +28,27 @@ function watchTask(cb) {
watch(group.watch, { ignoreInitial: true, interval: watchInterval }, function Less_Group_Compile() { return processLess(group.files, group.out); });
}
});
//Setup a watcher for all groups of view files
var viewWatcher;
_.forEach(config.sources.views, function (group) {
if(group.watch !== false) {
viewWatcher = watch(group.files, { ignoreInitial: true, interval: watchInterval });
viewWatcher.on('change', function(path, stats) {
var task = src(group.files);
viewWatcher = watch(group.files, { ignoreInitial: true, interval: watchInterval },
parallel(
function MoveViewsAndRegenerateJS() {
var task = src(group.files);
_.forEach(config.roots, function(root){
console.log("copying " + group.files + " to " + root + config.targets.views + group.folder);
task = task.pipe( dest(root + config.targets.views + group.folder) );
})
});
_.forEach(config.roots, function(root){
console.log("copying " + group.files + " to " + root + config.targets.views + group.folder);
task = task.pipe( dest(root + config.targets.views + group.folder) );
});
},
js
)
);
}
});
return cb();
};

View File

@@ -28,7 +28,9 @@ module.exports = function (files, out) {
.pipe(sort());
//in production, embed the templates
task = task.pipe(embedTemplates({ basePath: "./src/", minimize: { loose: true } }))
if(config.compile.current.embedtemplates === true) {
task = task.pipe(embedTemplates({ basePath: "./src/", minimize: { loose: true } }));
}
task = task.pipe(concat(out)).pipe(wrap('(function(){\n%= body %\n})();'))

View File

@@ -13,11 +13,11 @@
const { src, dest, series, parallel, lastRun } = require('gulp');
const config = require('./gulp/config');
const { setDevelopmentMode } = require('./gulp/modes');
const { setDevelopmentMode, setTestMode } = require('./gulp/modes');
const { dependencies } = require('./gulp/tasks/dependencies');
const { js } = require('./gulp/tasks/js');
const { less } = require('./gulp/tasks/less');
const { testE2e, testUnit } = require('./gulp/tasks/test');
const { testE2e, testUnit, runUnitTestServer } = require('./gulp/tasks/test');
const { views } = require('./gulp/tasks/views');
const { watchTask } = require('./gulp/tasks/watchTask');
@@ -31,6 +31,6 @@ exports.build = series(parallel(dependencies, js, less, views), testUnit);
exports.dev = series(setDevelopmentMode, parallel(dependencies, js, less, views), watchTask);
exports.watch = series(watchTask);
//
exports.runTests = series(js, testUnit);
exports.testUnit = series(testUnit);
exports.testE2e = series(testE2e);
exports.runTests = series(setTestMode, parallel(js, testUnit));
exports.runUnit = series(setTestMode, parallel(js, runUnitTestServer), watchTask);
exports.testE2e = series(setTestMode, parallel(testE2e));

View File

@@ -12,9 +12,9 @@
<div ng-controller="My.Controller as vm">
<div class="my-gallery">
<a href="" ng-repeat="image in images" ng-click="vm.openLightbox($index, images)">
<button type="button" ng-repeat="image in images" ng-click="vm.openLightbox($index, images)">
<img ng-src="image.source" />
</a>
</button>
</div>
<umb-lightbox

View File

@@ -12,6 +12,9 @@
*/
function clipboardService(notificationsService, eventsService, localStorageService, iconHelper) {
var clearPropertyResolvers = [];
var STORAGE_KEY = "umbClipboardService";
@@ -53,13 +56,32 @@ function clipboardService(notificationsService, eventsService, localStorageServi
return false;
}
var prepareEntryForStorage = function(entryData) {
var shallowCloneData = Object.assign({}, entryData);// Notice only a shallow copy, since we dont need to deep copy. (that will happen when storing the data)
delete shallowCloneData.key;
delete shallowCloneData.$$hashKey;
return shallowCloneData;
function clearPropertyForStorage(prop) {
for (var i=0; i<clearPropertyResolvers.length; i++) {
clearPropertyResolvers[i](prop, clearPropertyForStorage);
}
}
var prepareEntryForStorage = function(entryData, firstLevelClearupMethod) {
var cloneData = Utilities.copy(entryData);
if (firstLevelClearupMethod != undefined) {
firstLevelClearupMethod(cloneData);
}
// remove keys from sub-entries
for (var t = 0; t < cloneData.variants[0].tabs.length; t++) {
var tab = cloneData.variants[0].tabs[t];
for (var p = 0; p < tab.properties.length; p++) {
var prop = tab.properties[p];
clearPropertyForStorage(prop);
}
}
return cloneData;
}
var isEntryCompatible = function(entry, type, allowedAliases) {
@@ -75,6 +97,38 @@ function clipboardService(notificationsService, eventsService, localStorageServi
var service = {};
/**
* @ngdoc method
* @name umbraco.services.clipboardService#registrerClearPropertyResolver
* @methodOf umbraco.services.clipboardService
*
* @param {string} function A method executed for every property and inner properties copied.
*
* @description
* Executed for all properties including inner properties when performing a copy action.
*
* @deprecated Incorrect spelling please use 'registerClearPropertyResolver'
*/
service.registrerClearPropertyResolver = function(resolver) {
this.registerClearPropertyResolver(resolver);
};
/**
* @ngdoc method
* @name umbraco.services.clipboardService#registerClearPropertyResolver
* @methodOf umbraco.services.clipboardService
*
* @param {string} function A method executed for every property and inner properties copied.
*
* @description
* Executed for all properties including inner properties when performing a copy action.
*/
service.registerClearPropertyResolver = function(resolver) {
clearPropertyResolvers.push(resolver);
};
/**
* @ngdoc method
* @name umbraco.services.clipboardService#copy
@@ -84,15 +138,19 @@ function clipboardService(notificationsService, eventsService, localStorageServi
* @param {string} alias A string defining the alias of the data to store, example: 'product'
* @param {object} entry A object containing the properties to be saved, this could be the object of a ElementType, ContentNode, ...
* @param {string} displayLabel (optional) A string swetting the label to display when showing paste entries.
* @param {string} displayIcon (optional) A string setting the icon to display when showing paste entries.
* @param {string} uniqueKey (optional) A string prodiving an identifier for this entry, existing entries with this key will be removed to ensure that you only have the latest copy of this data.
*
* @description
* Saves a single JS-object with a type and alias to the clipboard.
*/
service.copy = function(type, alias, data, displayLabel) {
service.copy = function(type, alias, data, displayLabel, displayIcon, uniqueKey, firstLevelClearupMethod) {
var storage = retriveStorage();
var uniqueKey = data.key || data.$$hashKey || console.error("missing unique key for this content");
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(
@@ -101,7 +159,7 @@ function clipboardService(notificationsService, eventsService, localStorageServi
}
);
var entry = {unique:uniqueKey, type:type, alias:alias, data:prepareEntryForStorage(data), label:displayLabel || data.name, icon:iconHelper.convertFromLegacyIcon(data.icon)};
var entry = {unique:uniqueKey, type:type, alias:alias, data:prepareEntryForStorage(data, firstLevelClearupMethod), label:displayLabel, icon:displayIcon};
storage.entries.push(entry);
if (saveStorage(storage) === true) {
@@ -124,16 +182,17 @@ function clipboardService(notificationsService, eventsService, localStorageServi
* @param {string} displayLabel A string setting the label to display when showing paste entries.
* @param {string} displayIcon A string setting the icon to display when showing paste entries.
* @param {string} uniqueKey A string prodiving an identifier for this entry, existing entries with this key will be removed to ensure that you only have the latest copy of this data.
* @param {string} firstLevelClearupMethod A string prodiving an identifier for this entry, existing entries with this key will be removed to ensure that you only have the latest copy of this data.
*
* @description
* Saves a single JS-object with a type and alias to the clipboard.
*/
service.copyArray = function(type, aliases, datas, displayLabel, displayIcon, uniqueKey) {
service.copyArray = function(type, aliases, datas, displayLabel, displayIcon, uniqueKey, firstLevelClearupMethod) {
var storage = retriveStorage();
// Clean up each entry
var copiedDatas = datas.map(data => prepareEntryForStorage(data));
var copiedDatas = datas.map(data => prepareEntryForStorage(data, firstLevelClearupMethod));
// remove previous copies of this entry:
storage.entries = storage.entries.filter(

View File

@@ -32,14 +32,18 @@
.umb-lightbox__close {
position: absolute;
top: 20px;
right: 60px;
right: 20px;
height: 40px;
width: 40px;
}
.umb-lightbox__close i {
font-size: 20px;
cursor: pointer;
height: 40px;
width: 40px;
height: inherit;
width: inherit;
position: absolute;
top: 0;
left: 0;
}
.umb-lightbox__images {
@@ -75,12 +79,20 @@
right: 20px;
top: 50%;
transform: translate(0, -50%);
.umb-lightbox__control-icon {
margin-right: -4px;
}
}
.umb-lightbox__control.-prev {
left: 20px;
top: 50%;
transform: translate(0, -50%);
.umb-lightbox__control-icon {
margin-left: -4px;
}
}
.umb-lightbox__control-icon {

View File

@@ -7,18 +7,19 @@
<div ng-if="notification.view">
<div ng-include="notification.view"></div>
</div>
<div ng-if="notification.headline" ng-switch on="{{notification}}">
<a ng-href="{{notification.url}}" ng-switch-when="{{notification.url && notification.url.trim() != ''}}" target="_blank">
<strong>{{notification.headline}}</strong>
<div ng-if="notification.headline">
<a ng-if="notification.url" ng-href="{{notification.url}}" href="" target="_blank">
<strong ng-bind="notification.headline"></strong>
<span ng-bind-html="notification.message"></span>
</a>
<div ng-switch-default>
<strong>{{notification.headline}}</strong>
<div ng-if="!notification.url">
<strong ng-bind="notification.headline"></strong>
<span ng-bind-html="notification.message"></span>
</div>
</div>
<button type="button" class='close -align-right' ng-click="removeNotification($index)" aria-hidden="true">
<button type="button" class="close -align-right" ng-click="removeNotification($index)" aria-hidden="true">
<span aria-hidden="true">&times;</span>
</button>
</li>

View File

@@ -7,10 +7,16 @@
on-outside-click="clickCancel()">
<button class="umb_confirm-action__overlay-action -confirm btn-reset" ng-click="clickConfirm()" localize="title" title="@buttons_confirmActionConfirm" type="button">
<i class="icon-check"></i>
<i class="icon-check" aria-hidden="true"></i>
<span class="sr-only">
<localize key="buttons_confirmActionConfirm">Confirm</localize>
</span>
</button>
<button class="umb_confirm-action__overlay-action -cancel btn-reset" ng-click="clickCancel()" localize="title" title="@buttons_confirmActionCancel" type="button">
<i class="icon-delete"></i>
<i class="icon-delete" aria-hidden="true"></i>
<span class="sr-only">
<localize key="buttons_confirmActionCancel">Cancel</localize>
</span>
</button>
</div>

View File

@@ -1,9 +1,12 @@
<div class="umb-lightbox">
<div class="umb-lightbox__backdrop" ng-click="close()" hotkey="esc"></div>
<div class="umb-lightbox__close" title="Close" ng-click="close()">
<i class="icon-delete umb-lightbox__control"></i>
</div>
<button type="button" class="btn-reset umb-lightbox__close" localize="title" title="@general_close" ng-click="close()">
<i class="icon-delete umb-lightbox__control" aria-hidden="true"></i>
<span class="sr-only">
<localize key="general_close">Close</localize>
</span>
</button>
<div class="umb-lightbox__images">
<div class="umb-lightbox__image shadow-depth-2" ng-repeat="item in items" ng-show="$index === activeItemIndex">
@@ -11,12 +14,18 @@
</div>
</div>
<div class="umb-lightbox__control -prev" title="Previous" ng-if="activeItemIndex > 0" ng-click="prev()" hotkey="left">
<i class="icon-previous umb-lightbox__control-icon"></i>
</div>
<button type="button" class="btn-reset umb-lightbox__control -prev" localize="title" title="@general_previous" ng-if="activeItemIndex > 0" ng-click="prev()" hotkey="left">
<i class="icon-previous umb-lightbox__control-icon" aria-hidden="true"></i>
<span class="sr-only">
<localize key="general_previous">Previous</localize>
</span>
</button>
<div class="umb-lightbox__control -next" title="Next" ng-if="activeItemIndex + 1 < items.length" ng-click="next()" hotkey="right">
<i class="icon-next umb-lightbox__control-icon"></i>
</div>
<button type="button" class="btn-reset umb-lightbox__control -next" localize="title" title="general_next" ng-if="activeItemIndex + 1 < items.length" ng-click="next()" hotkey="right">
<i class="icon-next umb-lightbox__control-icon" aria-hidden="true"></i>
<span class="sr-only">
<localize key="general_next">Next</localize>
</span>
</button>
</div>

View File

@@ -44,6 +44,6 @@
aria-label="Edit"></button>
</div>
</div>
</div>
</div>

View File

@@ -9,7 +9,7 @@
<button type="button" class="btn" ng-click="addField()">
<localize key="general_add">Add</localize>
</button>
<span class="help-inline" ng-bind="errorMsg"</span>
<span class="help-inline" ng-bind="errorMsg"></span>
</div>
<div class="control-group">
<table class="table" ng-show="model.value.length > 0">

View File

@@ -1,6 +1,58 @@
(function () {
'use strict';
/**
* When performing a copy, we do copy the ElementType Data Model, but each inner Nested Content property is still stored as the Nested Content Model, aka. each property is just storing its value. To handle this we need to ensure we handle both scenarios.
*/
angular.module('umbraco').run(['clipboardService', function (clipboardService) {
function clearNestedContentPropertiesForStorage(prop, propClearingMethod) {
// if prop.editor is "Umbraco.NestedContent"
if ((typeof prop === 'object' && prop.editor === "Umbraco.NestedContent")) {
var value = prop.value;
for (var i = 0; i < value.length; i++) {
var obj = value[i];
// remove the key
delete obj.key;
// Loop through all inner properties:
for (var k in obj) {
propClearingMethod(obj[k]);
}
}
}
}
clipboardService.registrerClearPropertyResolver(clearNestedContentPropertiesForStorage)
function clearInnerNestedContentPropertiesForStorage(prop, propClearingMethod) {
// if we got an array, and it has a entry with ncContentTypeAlias this meants that we are dealing with a NestedContent property inside a NestedContent property.
if ((Array.isArray(prop) && prop.length > 0 && prop[0].ncContentTypeAlias !== undefined)) {
for (var i = 0; i < prop.length; i++) {
var obj = prop[i];
// remove the key
delete obj.key;
// Loop through all inner properties:
for (var k in obj) {
propClearingMethod(obj[k]);
}
}
}
}
clipboardService.registrerClearPropertyResolver(clearInnerNestedContentPropertiesForStorage)
}]);
angular
.module('umbraco')
.component('nestedContentPropertyEditor', {
@@ -13,7 +65,7 @@
}
});
function NestedContentController($scope, $interpolate, $filter, $timeout, contentResource, localizationService, iconHelper, clipboardService, eventsService, overlayService, $routeParams, editorState) {
function NestedContentController($scope, $interpolate, $filter, $timeout, contentResource, localizationService, iconHelper, clipboardService, eventsService, overlayService) {
var vm = this;
var model = $scope.$parent.$parent.model;
@@ -76,7 +128,7 @@
}
localizationService.localize("clipboard_labelForArrayOfItemsFrom", [model.label, nodeName]).then(function (data) {
clipboardService.copyArray("elementTypeArray", aliases, vm.nodes, data, "icon-thumbnail-list", model.id);
clipboardService.copyArray("elementTypeArray", aliases, vm.nodes, data, "icon-thumbnail-list", model.id, clearNodeForCopy);
});
}
@@ -385,6 +437,11 @@
});
}
function clearNodeForCopy(clonedData) {
delete clonedData.key;
delete clonedData.$$hashKey;
}
vm.showCopy = clipboardService.isSupported();
vm.showPaste = false;
@@ -392,7 +449,7 @@
syncCurrentNode();
clipboardService.copy("elementType", node.contentTypeAlias, node);
clipboardService.copy("elementType", node.contentTypeAlias, node, null, null, null, clearNodeForCopy);
$event.stopPropagation();
}

View File

@@ -13,8 +13,8 @@ var app = angular.module('umbraco', [
'ngSanitize',
//'ngMessages',
'tmh.dynamicLocale'
'tmh.dynamicLocale',
//'ngFileUpload',
//'LocalStorageModule',
'LocalStorageModule'
//'chart.js'
]);