From edb0a934a1f03547e377d4878c6369fe50f66423 Mon Sep 17 00:00:00 2001 From: Marc Goodson Date: Sat, 4 Feb 2017 18:35:47 +0000 Subject: [PATCH 001/106] Fixes U4-9480 Allow more than one item from the dictionary object to be returned on the querystring --- .../src/common/services/umbrequesthelper.service.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 a3d1e5b0c6..1f20ef5ca6 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 @@ -47,15 +47,17 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ return _.map(queryStrings, function (item) { var key = null; var val = null; + var encodedQueryStrings = []; + // can be multiple parameters passed via array for (var k in item) { key = k; val = item[k]; - break; - } + encodedQueryStrings.push(encodeURIComponent(key) + "=" + encodeURIComponent(val)); + } if (key === null || val === null) { throw "The object in the array was not formatted as a key/value pair"; - } - return encodeURIComponent(key) + "=" + encodeURIComponent(val); + } + return encodedQueryStrings.join("&"); }).join("&"); } else if (angular.isObject(queryStrings)) { From 78bc38fe1bd1b82dbfe68fb24b26c848abe02cac Mon Sep 17 00:00:00 2001 From: Marc Goodson Date: Sat, 4 Feb 2017 18:40:28 +0000 Subject: [PATCH 002/106] getUserLog and getLog don't have an id parameter these methods blow up when the api doesn't return because they referenced an id variable that does not exist --- .../src/common/resources/log.resource.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js index 8059975fc1..d74c7d82ac 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js @@ -68,7 +68,7 @@ function logResource($q, $http, umbRequestHelper) { "logApiBaseUrl", "GetCurrentUserLog", [{ logtype: type, sinceDate: since }])), - 'Failed to retrieve user data for id ' + id); + 'Failed to retrieve log data for current user of type ' + type + ' since ' + since); }, /** @@ -99,7 +99,7 @@ function logResource($q, $http, umbRequestHelper) { "logApiBaseUrl", "GetLog", [{ logtype: type, sinceDate: since }])), - 'Failed to retrieve user data for id ' + id); + 'Failed to retrieve log data of type ' + type + ' since ' + since); } }; } From ab14b65078e6e3e29bd1f9cbce5923aa5a9d6c2c Mon Sep 17 00:00:00 2001 From: TheBekker Date: Mon, 13 Mar 2017 22:54:20 +0100 Subject: [PATCH 003/106] [fix-U4-9038] get and set media start node for mediaPickerOverlay in grid --- .../propertyeditors/grid/editors/media.controller.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js index e289a389cb..bb580a906d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js @@ -1,10 +1,17 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.Grid.MediaController", - function ($scope, $rootScope, $timeout) { + function ($scope, $rootScope, $timeout, userService) { + + if (!$scope.model.config.startNodeId) { + userService.getCurrentUser().then(function (userData) { + $scope.model.config.startNodeId = userData.startMediaId; + }); + } $scope.setImage = function(){ $scope.mediaPickerOverlay = {}; $scope.mediaPickerOverlay.view = "mediapicker"; + $scope.mediaPickerOverlay.startNodeId = $scope.model.config && $scope.model.config.startNodeId ? $scope.model.config.startNodeId : undefined; $scope.mediaPickerOverlay.cropSize = $scope.control.editor.config && $scope.control.editor.config.size ? $scope.control.editor.config.size : undefined; $scope.mediaPickerOverlay.showDetails = true; $scope.mediaPickerOverlay.disableFolderSelect = true; From 58f9ff4b798d33fed467e73bb61b2d279cd67b38 Mon Sep 17 00:00:00 2001 From: Marc Goodson Date: Tue, 11 Apr 2017 20:37:59 +0100 Subject: [PATCH 004/106] Add some debug logging when there is a distributed calls configuration issue --- src/Umbraco.Core/Sync/ConfigServerRegistrar.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs b/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs index 8245491d97..841a229a33 100644 --- a/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs +++ b/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs @@ -4,6 +4,7 @@ using System.Web; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; +using Umbraco.Core.Logging; namespace Umbraco.Core.Sync { @@ -40,8 +41,9 @@ namespace Umbraco.Core.Sync .ToList(); if (serversA.Length == 0) - { + { _serverRole = ServerRole.Unknown; // config error, actually + LogHelper.Debug(string.Format("Server Role Unknown: DistributedCalls are enabled but no servers are listed")); } else { @@ -50,7 +52,10 @@ namespace Umbraco.Core.Sync var serverName = master.ServerName; if (appId.IsNullOrWhiteSpace() && serverName.IsNullOrWhiteSpace()) + { _serverRole = ServerRole.Unknown; // config error, actually + LogHelper.Debug(string.Format("Server Role Unknown: Server Name or AppId missing from Server configuration in DistributedCalls settings")); + } else _serverRole = IsCurrentServer(appId, serverName) ? ServerRole.Master From 336266c0ebb092e681e787e811dbe7f2faef6517 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 18 Apr 2017 12:32:44 +1000 Subject: [PATCH 005/106] U4-9775 AlreadyClosedException is thrown when retrieving media if the app domain is currently in a shutdown state and the site is under heavy load --- .../XmlPublishedCache/PublishedMediaCache.cs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index f3cc101b21..4d57833077 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -10,6 +10,7 @@ using Examine; using Examine.LuceneEngine.SearchCriteria; using Examine.Providers; using Lucene.Net.Documents; +using Lucene.Net.Store; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Dynamics; @@ -211,13 +212,23 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache var result = searchProvider.Search(filter.Compile()).FirstOrDefault(); if (result != null) return ConvertFromSearchResult(result); } - catch (FileNotFoundException ex) + catch (Exception ex) { - //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - //TODO: Need to fix examine in LB scenarios! - LogHelper.Error("Could not load data from Examine index for media", ex); + if (ex is FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + //TODO: Need to fix examine in LB scenarios! + LogHelper.Error("Could not load data from Examine index for media", ex); + } + else if (ex is AlreadyClosedException) + { + //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot + //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. + LogHelper.Error("Could not load data from Examine index for media, the app domain is most likely in a shutdown state", ex); + } + else throw; } } From 13ccd86192eea71434fc84e162670edae9201bdf Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 18 Apr 2017 12:35:21 +1000 Subject: [PATCH 006/106] Adds another check for not being able to read the index --- .../PublishedCache/XmlPublishedCache/PublishedMediaCache.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 4d57833077..a4793f9a67 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -174,6 +174,11 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // the manager will return the singleton without throwing initialization errors, however if examine isn't configured correctly a null // reference error will occur because the examine settings are null. } + catch (AlreadyClosedException) + { + //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot + //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. + } } return null; } From b88e71405017b786029ee3efe3ead219090641cb Mon Sep 17 00:00:00 2001 From: Dave Long Date: Fri, 21 Apr 2017 08:33:21 +0200 Subject: [PATCH 007/106] Fixed Serialization issue in Password Reset Validate Model --- src/Umbraco.Web/Models/ValidatePasswordResetCodeModel.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Models/ValidatePasswordResetCodeModel.cs b/src/Umbraco.Web/Models/ValidatePasswordResetCodeModel.cs index cba92eeff7..7e30e0881b 100644 --- a/src/Umbraco.Web/Models/ValidatePasswordResetCodeModel.cs +++ b/src/Umbraco.Web/Models/ValidatePasswordResetCodeModel.cs @@ -1,8 +1,10 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; namespace Umbraco.Web.Models { + [Serializable] [DataContract(Name = "validatePasswordReset", Namespace = "")] public class ValidatePasswordResetCodeModel { From 81354cea3c1920ed1c5d584ba1c8b90c61ae95a9 Mon Sep 17 00:00:00 2001 From: Harvey Williams Date: Mon, 24 Apr 2017 16:29:35 +0100 Subject: [PATCH 008/106] U4-9806 Media Picker media hover should show full title Added title to all media items in the media picker rather than just SVG and normal images. Solving an issue where whole titles could not be visible if the title was too long. --- .../src/views/components/umb-media-grid.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html index f30851e62e..7c5f0f728f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html @@ -1,5 +1,5 @@
-
+
@@ -12,10 +12,10 @@
- {{item.name}} + {{item.name}} - {{item.name}} + {{item.name}} {{item.name}} From 9cd7e0998669d8b6e1447a504d38bc7ea25872f8 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 28 Apr 2017 10:04:43 +1000 Subject: [PATCH 009/106] POC to normalize all event entities when we track events so that all args contain the latest (non stale) entity --- .../Events/CancellableObjectEventArgs.cs | 159 +++++++++++------- .../Events/ScopeEventDispatcherBase.cs | 91 +++++++++- src/Umbraco.Core/TypeExtensions.cs | 4 +- src/Umbraco.Core/TypeHelper.cs | 40 ++++- .../Scoping/ScopeEventDispatcherTests.cs | 54 +++++- 5 files changed, 285 insertions(+), 63 deletions(-) diff --git a/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs b/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs index 7815747f18..6787e6aa7a 100644 --- a/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs +++ b/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs @@ -5,81 +5,126 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Events -{ - /// - /// Event args for a strongly typed object that can support cancellation - /// - /// - [HostProtection(SecurityAction.LinkDemand, SharedState = true)] - public class CancellableObjectEventArgs : CancellableEventArgs, IEquatable> - { - public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages messages, IDictionary additionalData) +{ + /// + /// Used as a base class for the generic type CancellableObjectEventArgs{T} so that we can get direct 'object' access to the underlying EventObject + /// + [HostProtection(SecurityAction.LinkDemand, SharedState = true)] + public abstract class CancellableObjectEventArgs : CancellableEventArgs + { + protected CancellableObjectEventArgs(object eventObject, bool canCancel, EventMessages messages, IDictionary additionalData) : base(canCancel, messages, additionalData) - { + { EventObject = eventObject; } - public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages eventMessages) + protected CancellableObjectEventArgs(object eventObject, bool canCancel, EventMessages eventMessages) : base(canCancel, eventMessages) { EventObject = eventObject; } - public CancellableObjectEventArgs(T eventObject, EventMessages eventMessages) + protected CancellableObjectEventArgs(object eventObject, EventMessages eventMessages) : this(eventObject, true, eventMessages) { } - public CancellableObjectEventArgs(T eventObject, bool canCancel) - : base(canCancel) - { - EventObject = eventObject; - } + protected CancellableObjectEventArgs(object eventObject, bool canCancel) + : base(canCancel) + { + EventObject = eventObject; + } - public CancellableObjectEventArgs(T eventObject) - : this(eventObject, true) - { - } + protected CancellableObjectEventArgs(object eventObject) + : this(eventObject, true) + { + } - /// - /// Returns the object relating to the event - /// - /// - /// This is protected so that inheritors can expose it with their own name - /// - protected T EventObject { get; set; } + /// + /// Returns the object relating to the event + /// + /// + /// This is protected so that inheritors can expose it with their own name + /// + internal object EventObject { get; set; } - public bool Equals(CancellableObjectEventArgs other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return base.Equals(other) && EqualityComparer.Default.Equals(EventObject, other.EventObject); - } + } + + /// + /// Event args for a strongly typed object that can support cancellation + /// + /// + [HostProtection(SecurityAction.LinkDemand, SharedState = true)] + public class CancellableObjectEventArgs : CancellableObjectEventArgs, IEquatable> + { + public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages messages, IDictionary additionalData) + : base(eventObject, canCancel, messages, additionalData) + { + } - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((CancellableObjectEventArgs) obj); - } + public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages eventMessages) + : base(eventObject, canCancel, eventMessages) + { + } - public override int GetHashCode() - { - unchecked - { - return (base.GetHashCode() * 397) ^ EqualityComparer.Default.GetHashCode(EventObject); - } - } + public CancellableObjectEventArgs(T eventObject, EventMessages eventMessages) + : base(eventObject, eventMessages) + { + } - public static bool operator ==(CancellableObjectEventArgs left, CancellableObjectEventArgs right) - { - return Equals(left, right); - } + public CancellableObjectEventArgs(T eventObject, bool canCancel) + : base(eventObject, canCancel) + { + } - public static bool operator !=(CancellableObjectEventArgs left, CancellableObjectEventArgs right) - { - return !Equals(left, right); - } - } + public CancellableObjectEventArgs(T eventObject) + : base(eventObject) + { + } + + /// + /// Returns the object relating to the event + /// + /// + /// This is protected so that inheritors can expose it with their own name + /// + protected new T EventObject + { + get { return (T)base.EventObject; } + set { base.EventObject = value; } + } + + public bool Equals(CancellableObjectEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && EqualityComparer.Default.Equals(EventObject, other.EventObject); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((CancellableObjectEventArgs)obj); + } + + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ EqualityComparer.Default.GetHashCode(EventObject); + } + } + + public static bool operator ==(CancellableObjectEventArgs left, CancellableObjectEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(CancellableObjectEventArgs left, CancellableObjectEventArgs right) + { + return !Equals(left, right); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs b/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs index d8462d18b3..14990cad73 100644 --- a/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs +++ b/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs @@ -1,6 +1,8 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Events { @@ -64,10 +66,11 @@ namespace Umbraco.Core.Events { if (_events == null) return Enumerable.Empty(); - + switch (filter) { case EventDefinitionFilter.All: + UpdateToLatestEntity(_events); return _events; case EventDefinitionFilter.FirstIn: var l1 = new OrderedHashSet(); @@ -75,6 +78,7 @@ namespace Umbraco.Core.Events { l1.Add(e); } + UpdateToLatestEntity(l1); return l1; case EventDefinitionFilter.LastIn: var l2 = new OrderedHashSet(keepOldest: false); @@ -82,12 +86,97 @@ namespace Umbraco.Core.Events { l2.Add(e); } + UpdateToLatestEntity(l2); return l2; default: throw new ArgumentOutOfRangeException("filter", filter, null); } } + private void UpdateToLatestEntity(IEnumerable events) + { + //used to keep the 'latest' entity + var allEntities = new List(); + var cancelableArgs = new List(); + + foreach (var eventDefinition in events) + { + var args = eventDefinition.Args as CancellableObjectEventArgs; + if (args != null) + { + cancelableArgs.Add(args); + + var list = TypeHelper.CreateGenericEnumerableFromOjbect(args.EventObject); + + if (list == null) + { + //extract the event object + var obj = args.EventObject as IEntity; + if (obj != null) + { + allEntities.Add(obj); + } + } + else + { + foreach (var entity in list) + { + //extract the event object + var obj = entity as IEntity; + if (obj != null) + { + allEntities.Add(obj); + } + } + } + } + } + + var latestEntities = new OrderedHashSet(keepOldest: true); + foreach (var entity in allEntities.OrderByDescending(entity => entity.UpdateDate)) + { + latestEntities.Add(entity); + } + + foreach (var args in cancelableArgs) + { + var list = TypeHelper.CreateGenericEnumerableFromOjbect(args.EventObject); + if (list == null) + { + //try to find the args entity in the latest entity - based on the equality operators, this will + //match by Id since that is the default equality checker for IEntity. If one is found, than it is + //the most recent entity instance so update the args with that instance so we don't emit a stale instance. + var foundEntity = latestEntities.FirstOrDefault(x => Equals(x, args.EventObject)); + if (foundEntity != null) + { + args.EventObject = foundEntity; + } + } + else + { + var updated = false; + + for (int i = 0; i < list.Count; i++) + { + //try to find the args entity in the latest entity - based on the equality operators, this will + //match by Id since that is the default equality checker for IEntity. If one is found, than it is + //the most recent entity instance so update the args with that instance so we don't emit a stale instance. + var foundEntity = latestEntities.FirstOrDefault(x => Equals(x, list[i])); + if (foundEntity != null) + { + list[i] = foundEntity; + updated = true; + } + } + + if (updated) + { + args.EventObject = list; + } + } + } + } + public void ScopeExit(bool completed) { if (_events == null) return; diff --git a/src/Umbraco.Core/TypeExtensions.cs b/src/Umbraco.Core/TypeExtensions.cs index 76dc79c219..84d2a2d47a 100644 --- a/src/Umbraco.Core/TypeExtensions.cs +++ b/src/Umbraco.Core/TypeExtensions.cs @@ -166,8 +166,8 @@ namespace Umbraco.Core return true; } return false; - } - + } + /// /// Determines whether [is of generic type] [the specified type]. /// diff --git a/src/Umbraco.Core/TypeHelper.cs b/src/Umbraco.Core/TypeHelper.cs index 14c441dcd5..1a2d1df6f5 100644 --- a/src/Umbraco.Core/TypeHelper.cs +++ b/src/Umbraco.Core/TypeHelper.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Reflection; @@ -19,7 +20,44 @@ namespace Umbraco.Core = new ConcurrentDictionary(); private static readonly Assembly[] EmptyAssemblies = new Assembly[0]; - + + /// + /// Based on a type we'll check if it is IEnumerable{T} (or similar) and if so we'll return a List{T}, this will also deal with array types and return List{T} for those too. + /// If it cannot be done, null is returned. + /// + /// + /// + internal static IList CreateGenericEnumerableFromOjbect(object obj) + { + var type = obj.GetType(); + + if (type.IsGenericType) + { + var genericTypeDef = type.GetGenericTypeDefinition(); + + if (genericTypeDef == typeof(IEnumerable<>) + || genericTypeDef == typeof(ICollection<>) + || genericTypeDef == typeof(Collection<>) + || genericTypeDef == typeof(IList<>) + || genericTypeDef == typeof(List<>)) + { + //if it is a IEnumerable<>, IList or ICollection<> we'll use a List<> + var genericType = typeof(List<>).MakeGenericType(type.GetGenericArguments()); + //pass in obj to fill the list + return (IList)Activator.CreateInstance(genericType, obj); + } + } + if (type.IsArray) + { + //if its an array, we'll use a List<> + var genericType = typeof(List<>).MakeGenericType(type.GetElementType()); + //pass in obj to fill the list + return (IList)Activator.CreateInstance(genericType, obj); + } + + return null; + } + /// /// Checks if the method is actually overriding a base method /// diff --git a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs index b68e2a3f9f..a4e48c07fe 100644 --- a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs @@ -4,13 +4,16 @@ using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core.Events; +using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Scoping; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Entities; namespace Umbraco.Tests.Scoping { [TestFixture] - public class ScopeEventDispatcherTests + public class ScopeEventDispatcherTests : BaseUmbracoConfigurationTest { [SetUp] public void Setup() @@ -79,7 +82,7 @@ namespace Umbraco.Tests.Scoping var events = scope.Events.GetEvents(EventDefinitionFilter.All).ToArray(); var knownNames = new[] { "DoThing1", "DoThing2", "DoThing3" }; - var knownArgTypes = new[] { typeof (SaveEventArgs), typeof (SaveEventArgs), typeof (SaveEventArgs) }; + var knownArgTypes = new[] { typeof(SaveEventArgs), typeof(SaveEventArgs), typeof(SaveEventArgs) }; for (var i = 0; i < events.Length; i++) { @@ -89,6 +92,51 @@ namespace Umbraco.Tests.Scoping } } + /// + /// This will test that when we track events that before we Get the events we normalize all of the + /// event entities to be the latest one (most current) found amongst the event so that there is + /// no 'stale' entities in any of the args + /// + [Test] + public void LatestEntities() + { + DoThingForContent += OnDoThingFail; + + var now = DateTime.Now; + var contentType = MockedContentTypes.CreateBasicContentType(); + var content1 = MockedContent.CreateBasicContent(contentType); + content1.Id = 123; + content1.UpdateDate = now.AddMinutes(1); + var content2 = MockedContent.CreateBasicContent(contentType); + content2.Id = 123; + content1.UpdateDate = now.AddMinutes(2); + var content3 = MockedContent.CreateBasicContent(contentType); + content3.Id = 123; + content1.UpdateDate = now.AddMinutes(3); + + var scopeProvider = new ScopeProvider(Mock.Of()); + using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher())) + { + + scope.Events.Dispatch(DoThingForContent, this, new SaveEventArgs(content1)); + scope.Events.Dispatch(DoThingForContent, this, new SaveEventArgs(content2)); + scope.Events.Dispatch(DoThingForContent, this, new SaveEventArgs(content3)); + + // events have been queued + var events = scope.Events.GetEvents(EventDefinitionFilter.All).ToArray(); + Assert.AreEqual(3, events.Length); + + foreach (var t in events) + { + var args = (SaveEventArgs)t.Args; + foreach (var entity in args.SavedEntities) + { + Assert.AreEqual(content3, entity); + } + } + } + } + [TestCase(true)] [TestCase(false)] public void EventsDispatching_Passive(bool complete) @@ -177,6 +225,8 @@ namespace Umbraco.Tests.Scoping Assert.Fail(); } + public static event EventHandler> DoThingForContent; + public static event EventHandler> DoThing1; public static event EventHandler> DoThing2; From 3779b3c782399485096a9d3da4e45f2ed2fc3151 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 28 Apr 2017 11:15:19 +1000 Subject: [PATCH 010/106] Updates base entities to contain a DeletedDate if the entity gets deleted --- src/Umbraco.Core/Models/ContentXmlEntity.cs | 3 +++ src/Umbraco.Core/Models/EntityBase/Entity.cs | 3 +++ src/Umbraco.Core/Models/EntityBase/IAggregateRoot.cs | 2 +- src/Umbraco.Core/Models/EntityBase/IEntity.cs | 6 ++++++ .../Persistence/Repositories/ContentXmlRepository.cs | 2 ++ .../Repositories/DataTypeDefinitionRepository.cs | 4 ++++ .../Persistence/Repositories/DictionaryRepository.cs | 2 ++ .../Persistence/Repositories/EntityContainerRepository.cs | 2 ++ .../Persistence/Repositories/PetaPocoRepositoryBase.cs | 1 + .../Persistence/Repositories/TemplateRepository.cs | 2 ++ 10 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Models/ContentXmlEntity.cs b/src/Umbraco.Core/Models/ContentXmlEntity.cs index 0450fdc72e..93185834a3 100644 --- a/src/Umbraco.Core/Models/ContentXmlEntity.cs +++ b/src/Umbraco.Core/Models/ContentXmlEntity.cs @@ -42,6 +42,7 @@ namespace Umbraco.Core.Models public Guid Key { get; set; } public DateTime CreateDate { get; set; } public DateTime UpdateDate { get; set; } + public DateTime? DeletedDate { get; set; } /// /// Special case, always return false, this will cause the repositories managing @@ -60,5 +61,7 @@ namespace Umbraco.Core.Models DeepCloneHelper.DeepCloneRefProperties(this, clone); return clone; } + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/EntityBase/Entity.cs b/src/Umbraco.Core/Models/EntityBase/Entity.cs index d605759ed1..d4da2676c1 100644 --- a/src/Umbraco.Core/Models/EntityBase/Entity.cs +++ b/src/Umbraco.Core/Models/EntityBase/Entity.cs @@ -101,6 +101,9 @@ namespace Umbraco.Core.Models.EntityBase set { SetPropertyValueAndDetectChanges(value, ref _updateDate, Ps.Value.UpdateDateSelector); } } + [IgnoreDataMember] + public DateTime? DeletedDate { get; set; } + internal virtual void ResetIdentity() { _hasIdentity = false; diff --git a/src/Umbraco.Core/Models/EntityBase/IAggregateRoot.cs b/src/Umbraco.Core/Models/EntityBase/IAggregateRoot.cs index 4298dd9cf4..8ba1e09c99 100644 --- a/src/Umbraco.Core/Models/EntityBase/IAggregateRoot.cs +++ b/src/Umbraco.Core/Models/EntityBase/IAggregateRoot.cs @@ -3,7 +3,7 @@ /// /// Marker interface for aggregate roots /// - public interface IAggregateRoot : IEntity + public interface IAggregateRoot : IEntityDeleted { } diff --git a/src/Umbraco.Core/Models/EntityBase/IEntity.cs b/src/Umbraco.Core/Models/EntityBase/IEntity.cs index 81f5f632ef..a16a7daf2b 100644 --- a/src/Umbraco.Core/Models/EntityBase/IEntity.cs +++ b/src/Umbraco.Core/Models/EntityBase/IEntity.cs @@ -3,6 +3,12 @@ using System.Runtime.Serialization; namespace Umbraco.Core.Models.EntityBase { + public interface IEntityDeleted : IEntity + { + [DataMember] + DateTime? DeletedDate { get; set; } + } + /// /// Defines an Entity. /// Entities should always have an Id, Created and Modified date diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs index 464ae16961..ef3ce3eec2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs @@ -70,6 +70,8 @@ namespace Umbraco.Core.Persistence.Repositories { //Remove 'published' xml from the cmsContentXml table for the unpublished content Database.Delete("WHERE nodeId = @Id", new { Id = entity.Id }); + + entity.DeletedDate = DateTime.Now; } protected override void PersistNewItem(ContentXmlEntity entity) diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index 1ebfb4bc47..1690b36148 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -270,6 +270,8 @@ AND umbracoNode.id <> @id", //Delete (base) node data Database.Delete("WHERE uniqueID = @Id", new { Id = entity.Key }); + + entity.DeletedDate = DateTime.Now; } #endregion @@ -539,6 +541,8 @@ AND umbracoNode.id <> @id", Database.Execute( "DELETE FROM cmsDataTypePreValues WHERE id=@Id", new { Id = entity.Id }); + + entity.DeletedDate = DateTime.Now; } protected override void PersistNewItem(PreValueEntity entity) diff --git a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs index 41bdf020cd..968c2d9cb0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs @@ -195,6 +195,8 @@ namespace Umbraco.Core.Persistence.Repositories //Clear the cache entries that exist by uniqueid/item key IsolatedCache.ClearCacheItem(GetCacheIdKey(entity.ItemKey)); IsolatedCache.ClearCacheItem(GetCacheIdKey(entity.Key)); + + entity.DeletedDate = DateTime.Now; } private void RecursiveDelete(Guid parentId) diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs index b741238fb9..bc23d7201c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs @@ -167,6 +167,8 @@ namespace Umbraco.Core.Persistence.Repositories // delete Database.Delete(nodeDto); + + entity.DeletedDate = DateTime.Now; } protected override void PersistNewItem(EntityContainer entity) diff --git a/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs index dc61da119e..afe1a78b68 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs @@ -74,6 +74,7 @@ namespace Umbraco.Core.Persistence.Repositories { Database.Execute(delete, new { Id = GetEntityId(entity) }); } + entity.DeletedDate = DateTime.Now; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index 6bc801da4d..7f265aceb7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -311,6 +311,8 @@ namespace Umbraco.Core.Persistence.Repositories var masterpageName = string.Concat(entity.Alias, ".master"); _masterpagesFileSystem.DeleteFile(masterpageName); } + + entity.DeletedDate = DateTime.Now; } #endregion From 0b61685fc87df5af44edb532bc8b607f6a0ab080 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 28 Apr 2017 13:26:56 +1000 Subject: [PATCH 011/106] Adds ability to supersede events so we don't have an issue of saved event being raised for an entity after it's been deleted. --- src/Umbraco.Core/Events/DeleteEventArgs.cs | 4 + .../Events/ScopeEventDispatcherBase.cs | 179 ++++++++++++++++-- .../Events/SupersedeEventAttribute.cs | 20 ++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Scoping/ScopeEventDispatcherTests.cs | 91 ++++++++- 5 files changed, 267 insertions(+), 28 deletions(-) create mode 100644 src/Umbraco.Core/Events/SupersedeEventAttribute.cs diff --git a/src/Umbraco.Core/Events/DeleteEventArgs.cs b/src/Umbraco.Core/Events/DeleteEventArgs.cs index df13363b95..64cd4720cd 100644 --- a/src/Umbraco.Core/Events/DeleteEventArgs.cs +++ b/src/Umbraco.Core/Events/DeleteEventArgs.cs @@ -3,6 +3,10 @@ using System.Collections.Generic; namespace Umbraco.Core.Events { + [SupersedeEvent(typeof(SaveEventArgs<>))] + [SupersedeEvent(typeof(PublishEventArgs<>))] + [SupersedeEvent(typeof(MoveEventArgs<>))] + [SupersedeEvent(typeof(CopyEventArgs<>))] public class DeleteEventArgs : CancellableObjectEventArgs>, IEquatable>, IDeletingMediaFilesEventArgs { /// diff --git a/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs b/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs index 14990cad73..01371f97c6 100644 --- a/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs +++ b/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs @@ -8,6 +8,7 @@ namespace Umbraco.Core.Events { public abstract class ScopeEventDispatcherBase : IEventDispatcher { + //events will be enlisted in the order they are raised private List _events; private readonly bool _raiseCancelable; @@ -70,72 +71,161 @@ namespace Umbraco.Core.Events switch (filter) { case EventDefinitionFilter.All: - UpdateToLatestEntity(_events); - return _events; + return FilterSupersededAndUpdateToLatestEntity(_events); case EventDefinitionFilter.FirstIn: var l1 = new OrderedHashSet(); foreach (var e in _events) { l1.Add(e); } - UpdateToLatestEntity(l1); - return l1; + return FilterSupersededAndUpdateToLatestEntity(l1); case EventDefinitionFilter.LastIn: var l2 = new OrderedHashSet(keepOldest: false); foreach (var e in _events) { l2.Add(e); } - UpdateToLatestEntity(l2); - return l2; + return FilterSupersededAndUpdateToLatestEntity(l2); default: throw new ArgumentOutOfRangeException("filter", filter, null); } } - - private void UpdateToLatestEntity(IEnumerable events) + + private class EventDefinitionTypeData { - //used to keep the 'latest' entity - var allEntities = new List(); + public IEventDefinition EventDefinition { get; set; } + public Type EventArgType { get; set; } + public SupersedeEventAttribute[] SupersedeAttributes { get; set; } + } + + /// + /// This will iterate over the events (latest first) and filter out any events or entities in event args that are included + /// in more recent events that Supersede previous ones. For example, If an Entity has been Saved and then Deleted, we don't want + /// to raise the Saved event (well actually we just don't want to include it in the args for that saved event) + /// + /// + /// + private static IEnumerable FilterSupersededAndUpdateToLatestEntity(IReadOnlyList events) + { + //used to keep the 'latest' entity and associated event definition data + var allEntities = new List>(); + + //tracks all CancellableObjectEventArgs instances in the events which is the only type of args we can work with var cancelableArgs = new List(); - - foreach (var eventDefinition in events) + + var result = new List(); + + //This will eagerly load all of the event arg types and their attributes so we don't have to continuously look this data up + var allArgTypesWithAttributes = events.Select(x => x.Args.GetType()) + .Distinct() + .ToDictionary(x => x, x => x.GetCustomAttributes(false).ToArray()); + + //Iterate all events and collect the actual entities in them and relates them to their corresponding EventDefinitionTypeData + //we'll process the list in reverse because events are added in the order they are raised and we want to filter out + //any entities from event args that are not longer relevant + //(i.e. if an item is Deleted after it's Saved, we won't include the item in the Saved args) + for (var index = events.Count - 1; index >= 0; index--) { + var eventDefinition = events[index]; + + var argType = eventDefinition.Args.GetType(); + var attributes = allArgTypesWithAttributes[eventDefinition.Args.GetType()]; + + var meta = new EventDefinitionTypeData + { + EventDefinition = eventDefinition, + EventArgType = argType, + SupersedeAttributes = attributes + }; + var args = eventDefinition.Args as CancellableObjectEventArgs; if (args != null) { - cancelableArgs.Add(args); - var list = TypeHelper.CreateGenericEnumerableFromOjbect(args.EventObject); - + if (list == null) { //extract the event object var obj = args.EventObject as IEntity; if (obj != null) { - allEntities.Add(obj); + //Now check if this entity already exists in other event args that supersede this current event arg type + if (IsFiltered(obj, meta, allEntities) == false) + { + //if it's not filtered we can adde these args to the response + cancelableArgs.Add(args); + result.Add(eventDefinition); + //track the entity + allEntities.Add(Tuple.Create(obj, meta)); + } } } else { + var toRemove = new List(); foreach (var entity in list) { //extract the event object var obj = entity as IEntity; if (obj != null) { - allEntities.Add(obj); + //Now check if this entity already exists in other event args that supersede this current event arg type + if (IsFiltered(obj, meta, allEntities)) + { + //track it to be removed + toRemove.Add(obj); + } + else + { + //track the entity, it's not filtered + allEntities.Add(Tuple.Create(obj, meta)); + } } } + + //remove anything that has been filtered + foreach (var entity in toRemove) + { + list.Remove(entity); + } + + //track the event and include in the response if there's still entities remaining in the list + if (list.Count > 0) + { + if (toRemove.Count > 0) + { + //re-assign if the items have changed + args.EventObject = list; + } + cancelableArgs.Add(args); + result.Add(eventDefinition); + } } } + else + { + //it's not a cancelable event arg so we just include it in the result + result.Add(eventDefinition); + } } + //Now we'll deal with ensuring that only the latest(non stale) entities are used throughout all event args + UpdateToLatestEntities(allEntities, cancelableArgs); + + //we need to reverse the result since we've been adding by latest added events first! + result.Reverse(); + + return result; + } + + private static void UpdateToLatestEntities(IEnumerable> allEntities, IEnumerable cancelableArgs) + { + //Now we'll deal with ensuring that only the latest(non stale) entities are used throughout all event args + var latestEntities = new OrderedHashSet(keepOldest: true); - foreach (var entity in allEntities.OrderByDescending(entity => entity.UpdateDate)) + foreach (var entity in allEntities.OrderByDescending(entity => entity.Item1.UpdateDate)) { - latestEntities.Add(entity); + latestEntities.Add(entity.Item1); } foreach (var args in cancelableArgs) @@ -155,7 +245,7 @@ namespace Umbraco.Core.Events else { var updated = false; - + for (int i = 0; i < list.Count; i++) { //try to find the args entity in the latest entity - based on the equality operators, this will @@ -177,6 +267,55 @@ namespace Umbraco.Core.Events } } + /// + /// This will check against all of the processed entity/events (allEntities) to see if this entity already exists in + /// event args that supersede the event args being passed in and if so returns true. + /// + /// + /// + /// + /// + private static bool IsFiltered( + IEntity entity, + EventDefinitionTypeData eventDef, + List> allEntities) + { + var argType = eventDef.EventDefinition.Args.GetType(); + + //check if the entity is found in any processed event data that could possible supersede this one + var foundByEntity = allEntities + .Where(x => x.Item2.SupersedeAttributes.Length > 0 + //if it's the same arg type than it cannot supersede + && x.Item2.EventArgType != argType + && Equals(x.Item1, entity)) + .ToArray(); + + //no args have been processed with this entity so it should not be filtered + if (foundByEntity.Length == 0) + return false; + + if (argType.IsGenericType) + { + var supercededBy = foundByEntity + .FirstOrDefault(x => + x.Item2.SupersedeAttributes.Any(y => + //if the attribute type is a generic type def then compare with the generic type def of the event arg + (y.SupersededEventArgsType.IsGenericTypeDefinition && y.SupersededEventArgsType == argType.GetGenericTypeDefinition()) + //if the attribute type is not a generic type def then compare with the normal type of the event arg + || (y.SupersededEventArgsType.IsGenericTypeDefinition == false && y.SupersededEventArgsType == argType))); + return supercededBy != null; + } + else + { + var supercededBy = foundByEntity + .FirstOrDefault(x => + x.Item2.SupersedeAttributes.Any(y => + //since the event arg type is not a generic type, then we just compare type 1:1 + y.SupersededEventArgsType == argType)); + return supercededBy != null; + } + } + public void ScopeExit(bool completed) { if (_events == null) return; diff --git a/src/Umbraco.Core/Events/SupersedeEventAttribute.cs b/src/Umbraco.Core/Events/SupersedeEventAttribute.cs new file mode 100644 index 0000000000..c7a14ea158 --- /dev/null +++ b/src/Umbraco.Core/Events/SupersedeEventAttribute.cs @@ -0,0 +1,20 @@ +using System; + +namespace Umbraco.Core.Events +{ + /// + /// This is used to know if the event arg attributed should supersede another event arg type when + /// tracking events for the same entity. If one event args supercedes another then the event args that have been superseded + /// will mean that the event will not be dispatched or the args will be filtered to exclude the entity. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + internal class SupersedeEventAttribute : Attribute + { + public Type SupersededEventArgsType { get; private set; } + + public SupersedeEventAttribute(Type supersededEventArgsType) + { + SupersededEventArgsType = supersededEventArgsType; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 74cbe5d534..eae33101f8 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -339,6 +339,7 @@ + diff --git a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs index a4e48c07fe..b3b1ab271f 100644 --- a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs @@ -92,6 +92,55 @@ namespace Umbraco.Tests.Scoping } } + [Test] + public void SupersededEvents() + { + DoSaveForContent += OnDoThingFail; + DoDeleteForContent += OnDoThingFail; + DoForTestArgs += OnDoThingFail; + DoForTestArgs2 += OnDoThingFail; + + var contentType = MockedContentTypes.CreateBasicContentType(); + + var content1 = MockedContent.CreateBasicContent(contentType); + content1.Id = 123; + + var content2 = MockedContent.CreateBasicContent(contentType); + content2.Id = 456; + + var content3 = MockedContent.CreateBasicContent(contentType); + content3.Id = 789; + + var scopeProvider = new ScopeProvider(Mock.Of()); + using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher())) + { + + //content1 will be filtered from the args + scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(new[]{ content1 , content3})); + scope.Events.Dispatch(DoDeleteForContent, this, new DeleteEventArgs(content1)); + scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content2)); + //this entire event will be filtered + scope.Events.Dispatch(DoForTestArgs, this, new TestEventArgs(content1)); + scope.Events.Dispatch(DoForTestArgs2, this, new TestEventArgs2(content1)); + + // events have been queued + var events = scope.Events.GetEvents(EventDefinitionFilter.All).ToArray(); + Assert.AreEqual(4, events.Length); + + Assert.AreEqual(typeof(SaveEventArgs), events[0].Args.GetType()); + Assert.AreEqual(1, ((SaveEventArgs)events[0].Args).SavedEntities.Count()); + Assert.AreEqual(content3.Id, ((SaveEventArgs)events[0].Args).SavedEntities.First().Id); + + Assert.AreEqual(typeof(DeleteEventArgs), events[1].Args.GetType()); + Assert.AreEqual(content1.Id, ((DeleteEventArgs) events[1].Args).DeletedEntities.First().Id); + + Assert.AreEqual(typeof(SaveEventArgs), events[2].Args.GetType()); + Assert.AreEqual(content2.Id, ((SaveEventArgs)events[2].Args).SavedEntities.First().Id); + + Assert.AreEqual(typeof(TestEventArgs2), events[3].Args.GetType()); + } + } + /// /// This will test that when we track events that before we Get the events we normalize all of the /// event entities to be the latest one (most current) found amongst the event so that there is @@ -100,7 +149,7 @@ namespace Umbraco.Tests.Scoping [Test] public void LatestEntities() { - DoThingForContent += OnDoThingFail; + DoSaveForContent += OnDoThingFail; var now = DateTime.Now; var contentType = MockedContentTypes.CreateBasicContentType(); @@ -116,11 +165,10 @@ namespace Umbraco.Tests.Scoping var scopeProvider = new ScopeProvider(Mock.Of()); using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher())) - { - - scope.Events.Dispatch(DoThingForContent, this, new SaveEventArgs(content1)); - scope.Events.Dispatch(DoThingForContent, this, new SaveEventArgs(content2)); - scope.Events.Dispatch(DoThingForContent, this, new SaveEventArgs(content3)); + { + scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content1)); + scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content2)); + scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content3)); // events have been queued var events = scope.Events.GetEvents(EventDefinitionFilter.All).ToArray(); @@ -225,14 +273,41 @@ namespace Umbraco.Tests.Scoping Assert.Fail(); } - public static event EventHandler> DoThingForContent; - + public static event EventHandler> DoSaveForContent; + public static event EventHandler> DoDeleteForContent; + public static event EventHandler DoForTestArgs; + public static event EventHandler DoForTestArgs2; public static event EventHandler> DoThing1; public static event EventHandler> DoThing2; public static event TypedEventHandler> DoThing3; + public class TestEventArgs : CancellableObjectEventArgs + { + public TestEventArgs(object eventObject) : base(eventObject) + { + } + + public object MyEventObject + { + get { return EventObject; } + } + } + + [SupersedeEvent(typeof(TestEventArgs))] + public class TestEventArgs2 : CancellableObjectEventArgs + { + public TestEventArgs2(object eventObject) : base(eventObject) + { + } + + public object MyEventObject + { + get { return EventObject; } + } + } + public class PassiveEventDispatcher : ScopeEventDispatcherBase { public PassiveEventDispatcher() From ec9020f63ebbeea4bbd7dc0ebc898ad925fb9cc5 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 28 Apr 2017 13:34:32 +1000 Subject: [PATCH 012/106] Fixes mappings --- src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs | 2 ++ src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs | 3 ++- .../Models/Mapping/ContentTypeModelMapperExtensions.cs | 2 ++ src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs | 1 + src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs | 5 +++-- src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs | 1 + 6 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs b/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs index aa033c91b0..dec0930c07 100644 --- a/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs @@ -28,6 +28,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(x => x.Snippet, exp => exp.Ignore()); config.CreateMap() + .ForMember(x => x.DeletedDate, exp => exp.Ignore()) .ForMember(x => x.Id, exp => exp.Ignore()) .ForMember(x => x.Key, exp => exp.Ignore()) .ForMember(x => x.Path, exp => exp.Ignore()) @@ -40,6 +41,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(x => x.HasIdentity, exp => exp.Ignore()); config.CreateMap() + .ForMember(x => x.DeletedDate, exp => exp.Ignore()) .ForMember(x => x.Id, exp => exp.Ignore()) .ForMember(x => x.Key, exp => exp.Ignore()) .ForMember(x => x.Path, exp => exp.Ignore()) diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs index de5b5a14fd..24620509e9 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs @@ -48,6 +48,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(type => type.Key, expression => expression.Ignore()) .ForMember(type => type.CreateDate, expression => expression.Ignore()) .ForMember(type => type.UpdateDate, expression => expression.Ignore()) + .ForMember(type => type.DeletedDate, expression => expression.Ignore()) .ForMember(type => type.HasIdentity, expression => expression.Ignore()); config.CreateMap() @@ -72,7 +73,7 @@ namespace Umbraco.Web.Models.Mapping config.CreateMap() //do the base mapping .MapBaseContentTypeSaveToEntity(applicationContext) - .ConstructUsing((source) => new MediaType(source.ParentId)) + .ConstructUsing((source) => new MediaType(source.ParentId)) .AfterMap((source, dest) => { ContentTypeModelMapperExtensions.AfterMapMediaTypeSaveToEntity(source, dest, applicationContext); diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapperExtensions.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapperExtensions.cs index cd42c87a56..274771fead 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapperExtensions.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapperExtensions.cs @@ -31,6 +31,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.HasIdentity, map => map.Ignore()) .ForMember(dest => dest.CreateDate, map => map.Ignore()) .ForMember(dest => dest.UpdateDate, map => map.Ignore()) + .ForMember(dest => dest.DeletedDate, map => map.Ignore()) .ForMember(dest => dest.PropertyTypes, map => map.Ignore()); } @@ -175,6 +176,7 @@ namespace Umbraco.Web.Models.Mapping //These get persisted as part of the saving procedure, nothing to do with the display model .ForMember(dto => dto.CreateDate, expression => expression.Ignore()) .ForMember(dto => dto.UpdateDate, expression => expression.Ignore()) + .ForMember(dto => dto.DeletedDate, expression => expression.Ignore()) .ForMember(dto => dto.AllowedAsRoot, expression => expression.MapFrom(display => display.AllowAsRoot)) .ForMember(dto => dto.CreatorId, expression => expression.Ignore()) diff --git a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs index e78aeaf6a3..7e9a00f760 100644 --- a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs @@ -108,6 +108,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(x => x.Level, expression => expression.Ignore()) .ForMember(x => x.SortOrder, expression => expression.Ignore()) .ForMember(x => x.CreateDate, expression => expression.Ignore()) + .ForMember(x => x.DeletedDate, expression => expression.Ignore()) .ForMember(x => x.UpdateDate, expression => expression.Ignore()); //Converts a property editor to a new list of pre-value fields - used when creating a new data type or changing a data type with new pre-vals diff --git a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs index e0eb318866..976026bd97 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs @@ -55,8 +55,9 @@ namespace Umbraco.Web.Models.Mapping .ForMember(member => member.SortOrder, expression => expression.Ignore()) .ForMember(member => member.AdditionalData, expression => expression.Ignore()) .ForMember(member => member.FailedPasswordAttempts, expression => expression.Ignore()) - //TODO: Support these eventually - .ForMember(member => member.PasswordQuestion, expression => expression.Ignore()) + .ForMember(member => member.DeletedDate, expression => expression.Ignore()) + //TODO: Support these eventually + .ForMember(member => member.PasswordQuestion, expression => expression.Ignore()) .ForMember(member => member.RawPasswordAnswerValue, expression => expression.Ignore()); //FROM IMember TO MediaItemDisplay diff --git a/src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs b/src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs index d673e573a8..19677fccc8 100644 --- a/src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs @@ -14,6 +14,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(x => x.Notifications, exp => exp.Ignore()); config.CreateMap() + .ForMember(x => x.DeletedDate, exp => exp.Ignore()) .ForMember(x => x.Key, exp => exp.Ignore()) .ForMember(x => x.Path, exp => exp.Ignore()) .ForMember(x => x.CreateDate, exp => exp.Ignore()) From a69eba61c8b56566b6c489325047db8aa4a8605d Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 28 Apr 2017 14:54:08 +1000 Subject: [PATCH 013/106] Fixes detecting enumerable and ensures that the event is added to the result if we can't cast to IEntity --- src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs | 5 +++++ src/Umbraco.Core/TypeHelper.cs | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs b/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs index 01371f97c6..0a6d48035f 100644 --- a/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs +++ b/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs @@ -159,6 +159,11 @@ namespace Umbraco.Core.Events allEntities.Add(Tuple.Create(obj, meta)); } } + else + { + //Can't retrieve the entity so cant' filter or inspect, just add to the output + result.Add(eventDefinition); + } } else { diff --git a/src/Umbraco.Core/TypeHelper.cs b/src/Umbraco.Core/TypeHelper.cs index 1a2d1df6f5..1aabf3cef4 100644 --- a/src/Umbraco.Core/TypeHelper.cs +++ b/src/Umbraco.Core/TypeHelper.cs @@ -20,6 +20,7 @@ namespace Umbraco.Core = new ConcurrentDictionary(); private static readonly Assembly[] EmptyAssemblies = new Assembly[0]; + /// /// Based on a type we'll check if it is IEnumerable{T} (or similar) and if so we'll return a List{T}, this will also deal with array types and return List{T} for those too. @@ -39,7 +40,9 @@ namespace Umbraco.Core || genericTypeDef == typeof(ICollection<>) || genericTypeDef == typeof(Collection<>) || genericTypeDef == typeof(IList<>) - || genericTypeDef == typeof(List<>)) + || genericTypeDef == typeof(List<>) + //this will occur when Linq is used and we get the odd WhereIterator or DistinctIterators since those are special iterator types + || obj is IEnumerable) { //if it is a IEnumerable<>, IList or ICollection<> we'll use a List<> var genericType = typeof(List<>).MakeGenericType(type.GetGenericArguments()); From 8140dfdc04517e7a8956ba453558a4523235f505 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 28 Apr 2017 16:57:13 +1000 Subject: [PATCH 014/106] ads notes --- src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs b/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs index 0a6d48035f..e7684f5be8 100644 --- a/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs +++ b/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs @@ -186,6 +186,10 @@ namespace Umbraco.Core.Events allEntities.Add(Tuple.Create(obj, meta)); } } + else + { + //we don't need to do anything here, we can't cast to IEntity so we cannot filter, so it will just remain in the list + } } //remove anything that has been filtered From 1274fbaefdf39230b96ad1b700b0af86557afcdb Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 28 Apr 2017 18:14:50 +1000 Subject: [PATCH 015/106] Fixes up the equality members for the event args types to include Gethash code and sequence equal checking, this is done by a base class used by event args that deal with collections. --- .../Events/CancellableObjectEventArgs.cs | 133 +++++++++++----- src/Umbraco.Core/Events/DeleteEventArgs.cs | 5 +- src/Umbraco.Core/Events/ImportEventArgs.cs | 2 +- .../Events/ImportPackageEventArgs.cs | 5 +- src/Umbraco.Core/Events/PublishEventArgs.cs | 2 +- src/Umbraco.Core/Events/SaveEventArgs.cs | 5 +- src/Umbraco.Core/HashCodeCombiner.cs | 149 +++++++++--------- src/Umbraco.Core/HashCodeHelper.cs | 101 ++++++++++++ src/Umbraco.Core/OrderedHashSet.cs | 2 +- src/Umbraco.Core/TypeHelper.cs | 3 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Scoping/ScopeEventDispatcherTests.cs | 73 ++++++++- 12 files changed, 348 insertions(+), 133 deletions(-) create mode 100644 src/Umbraco.Core/HashCodeHelper.cs diff --git a/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs b/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs index 6787e6aa7a..1c9b08f636 100644 --- a/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs +++ b/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs @@ -1,11 +1,13 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Security.Permissions; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Events -{ +{ /// /// Used as a base class for the generic type CancellableObjectEventArgs{T} so that we can get direct 'object' access to the underlying EventObject /// @@ -48,36 +50,36 @@ namespace Umbraco.Core.Events /// internal object EventObject { get; set; } - } - + } + /// /// Event args for a strongly typed object that can support cancellation /// /// - [HostProtection(SecurityAction.LinkDemand, SharedState = true)] - public class CancellableObjectEventArgs : CancellableObjectEventArgs, IEquatable> + [HostProtection(SecurityAction.LinkDemand, SharedState = true)] + public class CancellableObjectEventArgs : CancellableObjectEventArgs, IEquatable> { - public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages messages, IDictionary additionalData) + public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages messages, IDictionary additionalData) : base(eventObject, canCancel, messages, additionalData) { } - public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages eventMessages) + public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages eventMessages) : base(eventObject, canCancel, eventMessages) { } - public CancellableObjectEventArgs(T eventObject, EventMessages eventMessages) + public CancellableObjectEventArgs(T eventObject, EventMessages eventMessages) : base(eventObject, eventMessages) { } - public CancellableObjectEventArgs(T eventObject, bool canCancel) + public CancellableObjectEventArgs(T eventObject, bool canCancel) : base(eventObject, canCancel) { } - public CancellableObjectEventArgs(T eventObject) + public CancellableObjectEventArgs(T eventObject) : base(eventObject) { } @@ -90,41 +92,86 @@ namespace Umbraco.Core.Events /// protected new T EventObject { - get { return (T)base.EventObject; } + get { return (T) base.EventObject; } set { base.EventObject = value; } } - - public bool Equals(CancellableObjectEventArgs other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + + public bool Equals(CancellableObjectEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; return base.Equals(other) && EqualityComparer.Default.Equals(EventObject, other.EventObject); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((CancellableObjectEventArgs)obj); - } - - public override int GetHashCode() - { - unchecked - { - return (base.GetHashCode() * 397) ^ EqualityComparer.Default.GetHashCode(EventObject); - } - } - - public static bool operator ==(CancellableObjectEventArgs left, CancellableObjectEventArgs right) - { - return Equals(left, right); - } - - public static bool operator !=(CancellableObjectEventArgs left, CancellableObjectEventArgs right) - { - return !Equals(left, right); - } + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((CancellableObjectEventArgs)obj); + } + + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ EqualityComparer.Default.GetHashCode(EventObject); + } + } + + public static bool operator ==(CancellableObjectEventArgs left, CancellableObjectEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(CancellableObjectEventArgs left, CancellableObjectEventArgs right) + { + return !Equals(left, right); + } + } + + [HostProtection(SecurityAction.LinkDemand, SharedState = true)] + public class CancellableEnumerableObjectEventArgs : CancellableObjectEventArgs>, IEquatable> + { + public CancellableEnumerableObjectEventArgs(IEnumerable eventObject, bool canCancel, EventMessages messages, IDictionary additionalData) : base(eventObject, canCancel, messages, additionalData) + { + } + + public CancellableEnumerableObjectEventArgs(IEnumerable eventObject, bool canCancel, EventMessages eventMessages) : base(eventObject, canCancel, eventMessages) + { + } + + public CancellableEnumerableObjectEventArgs(IEnumerable eventObject, EventMessages eventMessages) : base(eventObject, eventMessages) + { + } + + public CancellableEnumerableObjectEventArgs(IEnumerable eventObject, bool canCancel) : base(eventObject, canCancel) + { + } + + public CancellableEnumerableObjectEventArgs(IEnumerable eventObject) : base(eventObject) + { + } + + public bool Equals(CancellableEnumerableObjectEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return EventObject.SequenceEqual(other.EventObject); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((CancellableEnumerableObjectEventArgs)obj); + } + + public override int GetHashCode() + { + return HashCodeHelper.GetHashCode(EventObject); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/DeleteEventArgs.cs b/src/Umbraco.Core/Events/DeleteEventArgs.cs index 64cd4720cd..d0a4f024e1 100644 --- a/src/Umbraco.Core/Events/DeleteEventArgs.cs +++ b/src/Umbraco.Core/Events/DeleteEventArgs.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; namespace Umbraco.Core.Events { @@ -7,7 +8,7 @@ namespace Umbraco.Core.Events [SupersedeEvent(typeof(PublishEventArgs<>))] [SupersedeEvent(typeof(MoveEventArgs<>))] [SupersedeEvent(typeof(CopyEventArgs<>))] - public class DeleteEventArgs : CancellableObjectEventArgs>, IEquatable>, IDeletingMediaFilesEventArgs + public class DeleteEventArgs : CancellableEnumerableObjectEventArgs, IEquatable>, IDeletingMediaFilesEventArgs { /// /// Constructor accepting multiple entities that are used in the delete operation @@ -110,7 +111,7 @@ namespace Umbraco.Core.Events { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return base.Equals(other) && MediaFilesToDelete.Equals(other.MediaFilesToDelete); + return base.Equals(other) && MediaFilesToDelete.SequenceEqual(other.MediaFilesToDelete); } public override bool Equals(object obj) diff --git a/src/Umbraco.Core/Events/ImportEventArgs.cs b/src/Umbraco.Core/Events/ImportEventArgs.cs index dcecf5c36b..892149c0a2 100644 --- a/src/Umbraco.Core/Events/ImportEventArgs.cs +++ b/src/Umbraco.Core/Events/ImportEventArgs.cs @@ -4,7 +4,7 @@ using System.Xml.Linq; namespace Umbraco.Core.Events { - public class ImportEventArgs : CancellableObjectEventArgs>, IEquatable> + public class ImportEventArgs : CancellableEnumerableObjectEventArgs, IEquatable> { /// /// Constructor accepting an XElement with the xml being imported diff --git a/src/Umbraco.Core/Events/ImportPackageEventArgs.cs b/src/Umbraco.Core/Events/ImportPackageEventArgs.cs index 231e58c07e..4477faea50 100644 --- a/src/Umbraco.Core/Events/ImportPackageEventArgs.cs +++ b/src/Umbraco.Core/Events/ImportPackageEventArgs.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Packaging.Models; namespace Umbraco.Core.Events { - internal class ImportPackageEventArgs : CancellableObjectEventArgs>, IEquatable> + internal class ImportPackageEventArgs : CancellableEnumerableObjectEventArgs, IEquatable> { private readonly MetaData _packageMetaData; @@ -32,7 +32,8 @@ namespace Umbraco.Core.Events public bool Equals(ImportPackageEventArgs other) { if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(this, other)) return true; + //TODO: MetaData for package metadata has no equality operators :/ return base.Equals(other) && _packageMetaData.Equals(other._packageMetaData); } diff --git a/src/Umbraco.Core/Events/PublishEventArgs.cs b/src/Umbraco.Core/Events/PublishEventArgs.cs index 1aa7c2308c..10bf94146c 100644 --- a/src/Umbraco.Core/Events/PublishEventArgs.cs +++ b/src/Umbraco.Core/Events/PublishEventArgs.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; namespace Umbraco.Core.Events { - public class PublishEventArgs : CancellableObjectEventArgs>, IEquatable> + public class PublishEventArgs : CancellableEnumerableObjectEventArgs, IEquatable> { /// /// Constructor accepting multiple entities that are used in the publish operation diff --git a/src/Umbraco.Core/Events/SaveEventArgs.cs b/src/Umbraco.Core/Events/SaveEventArgs.cs index e816a8f8bd..cd19038d8e 100644 --- a/src/Umbraco.Core/Events/SaveEventArgs.cs +++ b/src/Umbraco.Core/Events/SaveEventArgs.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; +using System.Linq; namespace Umbraco.Core.Events { - public class SaveEventArgs : CancellableObjectEventArgs> + public class SaveEventArgs : CancellableEnumerableObjectEventArgs { /// /// Constructor accepting multiple entities that are used in the saving operation @@ -116,7 +117,5 @@ namespace Umbraco.Core.Events { get { return EventObject; } } - - } } \ No newline at end of file diff --git a/src/Umbraco.Core/HashCodeCombiner.cs b/src/Umbraco.Core/HashCodeCombiner.cs index b97a3cfacf..d3a55d5256 100644 --- a/src/Umbraco.Core/HashCodeCombiner.cs +++ b/src/Umbraco.Core/HashCodeCombiner.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; @@ -7,32 +6,32 @@ using System.Text; namespace Umbraco.Core { - /// - /// Used to create a hash code from multiple objects. - /// - /// - /// .Net has a class the same as this: System.Web.Util.HashCodeCombiner and of course it works for all sorts of things - /// which we've not included here as we just need a quick easy class for this in order to create a unique - /// hash of directories/files to see if they have changed. - /// - internal class HashCodeCombiner - { - private long _combinedHash = 5381L; + /// + /// Used to create a hash code from multiple objects. + /// + /// + /// .Net has a class the same as this: System.Web.Util.HashCodeCombiner and of course it works for all sorts of things + /// which we've not included here as we just need a quick easy class for this in order to create a unique + /// hash of directories/files to see if they have changed. + /// + internal class HashCodeCombiner + { + private long _combinedHash = 5381L; - internal void AddInt(int i) - { - _combinedHash = ((_combinedHash << 5) + _combinedHash) ^ i; - } + internal void AddInt(int i) + { + _combinedHash = ((_combinedHash << 5) + _combinedHash) ^ i; + } - internal void AddObject(object o) - { - AddInt(o.GetHashCode()); - } + internal void AddObject(object o) + { + AddInt(o.GetHashCode()); + } - internal void AddDateTime(DateTime d) - { - AddInt(d.GetHashCode()); - } + internal void AddDateTime(DateTime d) + { + AddInt(d.GetHashCode()); + } internal void AddString(string s) { @@ -40,61 +39,61 @@ namespace Umbraco.Core AddInt((StringComparer.InvariantCulture).GetHashCode(s)); } - internal void AddCaseInsensitiveString(string s) - { - if (s != null) - AddInt((StringComparer.InvariantCultureIgnoreCase).GetHashCode(s)); - } + internal void AddCaseInsensitiveString(string s) + { + if (s != null) + AddInt((StringComparer.InvariantCultureIgnoreCase).GetHashCode(s)); + } - internal void AddFileSystemItem(FileSystemInfo f) - { - //if it doesn't exist, don't proceed. - if (!f.Exists) - return; + internal void AddFileSystemItem(FileSystemInfo f) + { + //if it doesn't exist, don't proceed. + if (!f.Exists) + return; - AddCaseInsensitiveString(f.FullName); - AddDateTime(f.CreationTimeUtc); - AddDateTime(f.LastWriteTimeUtc); - - //check if it is a file or folder - var fileInfo = f as FileInfo; - if (fileInfo != null) - { - AddInt(fileInfo.Length.GetHashCode()); - } - - var dirInfo = f as DirectoryInfo; - if (dirInfo != null) - { - foreach (var d in dirInfo.GetFiles()) - { - AddFile(d); - } - foreach (var s in dirInfo.GetDirectories()) - { - AddFolder(s); - } - } - } + AddCaseInsensitiveString(f.FullName); + AddDateTime(f.CreationTimeUtc); + AddDateTime(f.LastWriteTimeUtc); - internal void AddFile(FileInfo f) - { - AddFileSystemItem(f); - } + //check if it is a file or folder + var fileInfo = f as FileInfo; + if (fileInfo != null) + { + AddInt(fileInfo.Length.GetHashCode()); + } - internal void AddFolder(DirectoryInfo d) - { - AddFileSystemItem(d); - } + var dirInfo = f as DirectoryInfo; + if (dirInfo != null) + { + foreach (var d in dirInfo.GetFiles()) + { + AddFile(d); + } + foreach (var s in dirInfo.GetDirectories()) + { + AddFolder(s); + } + } + } - /// - /// Returns the hex code of the combined hash code - /// - /// - internal string GetCombinedHashCode() - { - return _combinedHash.ToString("x", CultureInfo.InvariantCulture); - } + internal void AddFile(FileInfo f) + { + AddFileSystemItem(f); + } - } + internal void AddFolder(DirectoryInfo d) + { + AddFileSystemItem(d); + } + + /// + /// Returns the hex code of the combined hash code + /// + /// + internal string GetCombinedHashCode() + { + return _combinedHash.ToString("x", CultureInfo.InvariantCulture); + } + + } } diff --git a/src/Umbraco.Core/HashCodeHelper.cs b/src/Umbraco.Core/HashCodeHelper.cs new file mode 100644 index 0000000000..a3cedbdbb5 --- /dev/null +++ b/src/Umbraco.Core/HashCodeHelper.cs @@ -0,0 +1,101 @@ +using System.Collections.Generic; + +namespace Umbraco.Core +{ + /// + /// Borrowed from http://stackoverflow.com/a/2575444/694494 + /// + internal static class HashCodeHelper + { + public static int GetHashCode(T1 arg1, T2 arg2) + { + unchecked + { + return 31 * arg1.GetHashCode() + arg2.GetHashCode(); + } + } + + public static int GetHashCode(T1 arg1, T2 arg2, T3 arg3) + { + unchecked + { + int hash = arg1.GetHashCode(); + hash = 31 * hash + arg2.GetHashCode(); + return 31 * hash + arg3.GetHashCode(); + } + } + + public static int GetHashCode(T1 arg1, T2 arg2, T3 arg3, + T4 arg4) + { + unchecked + { + int hash = arg1.GetHashCode(); + hash = 31 * hash + arg2.GetHashCode(); + hash = 31 * hash + arg3.GetHashCode(); + return 31 * hash + arg4.GetHashCode(); + } + } + + public static int GetHashCode(T[] list) + { + unchecked + { + int hash = 0; + foreach (var item in list) + { + hash = 31 * hash + item.GetHashCode(); + } + return hash; + } + } + + public static int GetHashCode(IEnumerable list) + { + unchecked + { + int hash = 0; + foreach (var item in list) + { + hash = 31 * hash + item.GetHashCode(); + } + return hash; + } + } + + /// + /// Gets a hashcode for a collection for that the order of items + /// does not matter. + /// So {1, 2, 3} and {3, 2, 1} will get same hash code. + /// + public static int GetHashCodeForOrderNoMatterCollection( + IEnumerable list) + { + unchecked + { + int hash = 0; + int count = 0; + foreach (var item in list) + { + hash += item.GetHashCode(); + count++; + } + return 31 * hash + count.GetHashCode(); + } + } + + /// + /// Alternative way to get a hashcode is to use a fluent + /// interface like this:
+ /// return 0.CombineHashCode(field1).CombineHashCode(field2). + /// CombineHashCode(field3); + ///
+ public static int CombineHashCode(this int hashCode, T arg) + { + unchecked + { + return 31 * hashCode + arg.GetHashCode(); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/OrderedHashSet.cs b/src/Umbraco.Core/OrderedHashSet.cs index 2fd545c915..801f1a9a41 100644 --- a/src/Umbraco.Core/OrderedHashSet.cs +++ b/src/Umbraco.Core/OrderedHashSet.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core { private readonly bool _keepOldest; - public OrderedHashSet(bool keepOldest = true) + public OrderedHashSet(bool keepOldest = true) { _keepOldest = keepOldest; } diff --git a/src/Umbraco.Core/TypeHelper.cs b/src/Umbraco.Core/TypeHelper.cs index 1aabf3cef4..f8e10ff1fb 100644 --- a/src/Umbraco.Core/TypeHelper.cs +++ b/src/Umbraco.Core/TypeHelper.cs @@ -19,9 +19,8 @@ namespace Umbraco.Core private static readonly ConcurrentDictionary GetFieldsCache = new ConcurrentDictionary(); - private static readonly Assembly[] EmptyAssemblies = new Assembly[0]; + private static readonly Assembly[] EmptyAssemblies = new Assembly[0]; - /// /// Based on a type we'll check if it is IEnumerable{T} (or similar) and if so we'll return a List{T}, this will also deal with array types and return List{T} for those too. /// If it cannot be done, null is returned. diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index eae33101f8..270fbbc323 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -341,6 +341,7 @@ + diff --git a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs index b3b1ab271f..825432756b 100644 --- a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs @@ -158,14 +158,14 @@ namespace Umbraco.Tests.Scoping content1.UpdateDate = now.AddMinutes(1); var content2 = MockedContent.CreateBasicContent(contentType); content2.Id = 123; - content1.UpdateDate = now.AddMinutes(2); + content2.UpdateDate = now.AddMinutes(2); var content3 = MockedContent.CreateBasicContent(contentType); content3.Id = 123; - content1.UpdateDate = now.AddMinutes(3); + content3.UpdateDate = now.AddMinutes(3); var scopeProvider = new ScopeProvider(Mock.Of()); using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher())) - { + { scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content1)); scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content2)); scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content3)); @@ -180,11 +180,78 @@ namespace Umbraco.Tests.Scoping foreach (var entity in args.SavedEntities) { Assert.AreEqual(content3, entity); + Assert.IsTrue(object.ReferenceEquals(content3, entity)); } } } } + [Test] + public void FirstIn() + { + DoSaveForContent += OnDoThingFail; + + var now = DateTime.Now; + var contentType = MockedContentTypes.CreateBasicContentType(); + var content1 = MockedContent.CreateBasicContent(contentType); + content1.Id = 123; + content1.UpdateDate = now.AddMinutes(1); + var content2 = MockedContent.CreateBasicContent(contentType); + content2.Id = 123; + content1.UpdateDate = now.AddMinutes(2); + var content3 = MockedContent.CreateBasicContent(contentType); + content3.Id = 123; + content1.UpdateDate = now.AddMinutes(3); + + var scopeProvider = new ScopeProvider(Mock.Of()); + using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher())) + { + scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content1)); + scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content2)); + scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content3)); + + // events have been queued + var events = scope.Events.GetEvents(EventDefinitionFilter.FirstIn).ToArray(); + Assert.AreEqual(1, events.Length); + Assert.AreEqual(content1, ((SaveEventArgs) events[0].Args).SavedEntities.First()); + Assert.IsTrue(object.ReferenceEquals(content1, ((SaveEventArgs)events[0].Args).SavedEntities.First())); + Assert.AreEqual(content1.UpdateDate, ((SaveEventArgs) events[0].Args).SavedEntities.First().UpdateDate); + } + } + + [Test] + public void LastIn() + { + DoSaveForContent += OnDoThingFail; + + var now = DateTime.Now; + var contentType = MockedContentTypes.CreateBasicContentType(); + var content1 = MockedContent.CreateBasicContent(contentType); + content1.Id = 123; + content1.UpdateDate = now.AddMinutes(1); + var content2 = MockedContent.CreateBasicContent(contentType); + content2.Id = 123; + content2.UpdateDate = now.AddMinutes(2); + var content3 = MockedContent.CreateBasicContent(contentType); + content3.Id = 123; + content3.UpdateDate = now.AddMinutes(3); + + var scopeProvider = new ScopeProvider(Mock.Of()); + using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher())) + { + scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content1)); + scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content2)); + scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content3)); + + // events have been queued + var events = scope.Events.GetEvents(EventDefinitionFilter.LastIn).ToArray(); + Assert.AreEqual(1, events.Length); + Assert.AreEqual(content3, ((SaveEventArgs)events[0].Args).SavedEntities.First()); + Assert.IsTrue(object.ReferenceEquals(content3, ((SaveEventArgs)events[0].Args).SavedEntities.First())); + Assert.AreEqual(content3.UpdateDate, ((SaveEventArgs)events[0].Args).SavedEntities.First().UpdateDate); + } + } + [TestCase(true)] [TestCase(false)] public void EventsDispatching_Passive(bool complete) From f01511c592899c61a6c0fe3446c76a83adb4fdca Mon Sep 17 00:00:00 2001 From: Nicklas Kevin Frank Date: Fri, 28 Apr 2017 14:48:32 +0200 Subject: [PATCH 016/106] Edit DA with minor spelling and compound word changes --- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index b0c2995f07..8462110b4a 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -150,7 +150,7 @@ Udgivelsesdato Dato for Fortryd udgivelse Fjern dato - Sorteringrækkefølgen er opdateret + Sorteringsrækkefølgen er opdateret For at sortere, træk siderne eller klik på en af kolonnehovederne. Du kan vælge flere sider ved at holde "shift" eller "control" nede mens du vælger. Statistik Titel (valgfri) @@ -184,7 +184,7 @@ Vælg en type og skriv en titel "dokument typer".]]> "media typer".]]> - Dokument type uden skabelon + Dokumenttype uden skabelon Ny mappe Ny datatype @@ -579,13 +579,13 @@ Berørte filer og foldere Flere informationer om at opsætte rettigheder for Umbraco her Du er nødt til at give ASP.NET 'modify' rettigheder på følgende filer/foldere - Dine rettighedsinstillinger er næsten perfekte!

Du kan køre Umbraco uden problemer, men du vil ikke være i stand til at installere pakker, som er anbefalet for at få fuldt udbytte af Umbraco.]]>
+ Dine rettighedsindstillinger er næsten perfekte!

Du kan køre Umbraco uden problemer, men du vil ikke være i stand til at installere pakker, som er anbefalet for at få fuldt udbytte af Umbraco.]]>
Hvorledes besluttes Klik her for at læse tekstversionen video tutorials om at opsætte folderrettigheder for Umbraco eller læs tekstversionen.]]> - Dine rettighedsinstillinger kan være et problem!

Du kan afvikle Umbraco uden problemer, men du vil ikke være i stand til at oprette foldere eller installere pakker, hvilket er anbefalet for at få fuldt udbytte af Umbraco.]]>
- Dine rettighedsinstillinger er ikke klar til Umbraco!

For at afvikle Umbraco er du nødt til at opdatere dine rettighedsinstillinger.]]>
- Dine rettighedsinstillinger er perfekte!

Du er nu parat til at afvikle Umbraco og installere pakker!]]>
+ Dine rettighedsindstillinger kan være et problem!

Du kan afvikle Umbraco uden problemer, men du vil ikke være i stand til at oprette foldere eller installere pakker, hvilket er anbefalet for at få fuldt udbytte af Umbraco.]]>
+ Dine rettighedsindstillinger er ikke klar til Umbraco!

For at afvikle Umbraco er du nødt til at opdatere dine rettighedsindstillinger.]]>
+ Dine rettighedsindstillinger er perfekte!

Du er nu parat til at afvikle Umbraco og installere pakker!]]>
Løser folder problem Følg dette link for mere information om udfordringer med ASP.NET og oprettelse af foldere Sætter folderrettigheder op @@ -597,7 +597,7 @@ Dette er vores liste over anbefalede moduler. Kryds dem af du ønsker at installere eller se den fulde liste af moduler ]]> Kun anbefalet for erfarne brugere Jeg ønsker at begynder med et simpelt website - "Runway" er et simpelt website som stiller nogle basale dokumenttyper og skabeloner til rådighed. Instaleringsprogrammet kan automatisk opsætte Runway for dig, men du kan nemt redigere, udvide eller fjerne det. Det er ikke nødvendigt og du kan sagtens bruge Umbraco uden. Men Runway tilbyder et fundament, som er baseret på 'Best Practices', som får dig igang hurtigere end nogensinde før. Hvis du vælger at installere Runway, kan du efter eget valg vælge de grundlæggende byggesten kaldet 'Runway Modules' til at forbedre dine Runway-sider.

Inkluderet med Runway:Home Page, Getting Started page, Installing Modules page.
Valgfri Moduler: Top Navigation, Sitemap, Contact, Gallery.

]]>
+ "Runway" er et simpelt website som stiller nogle basale dokumenttyper og skabeloner til rådighed. Installeringsprogrammet kan automatisk opsætte Runway for dig, men du kan nemt redigere, udvide eller fjerne det. Det er ikke nødvendigt og du kan sagtens bruge Umbraco uden. Men Runway tilbyder et fundament, som er baseret på 'Best Practices', som får dig igang hurtigere end nogensinde før. Hvis du vælger at installere Runway, kan du efter eget valg vælge de grundlæggende byggesten kaldet 'Runway Modules' til at forbedre dine Runway-sider.

Inkluderet med Runway:Home Page, Getting Started page, Installing Modules page.
Valgfri Moduler: Top Navigation, Sitemap, Contact, Gallery.

]]>
Hvad er Runway Skridt 1/5: Acceptér licens Skridt 2/5: Database-konfiguration @@ -714,7 +714,7 @@ Mange hilsner fra Umbraco robotten Pakke opbevaringsbase Bekræft af-installering Pakken blev fjernet - Pakken er på succefuld vis blevet fjernet + Pakken er på succesfuld vis blevet fjernet Afinstallér pakke @@ -795,7 +795,7 @@ Mange hilsner fra Umbraco robotten Tilføj række Tilføj indhold Slip indhold - Instillinger tilføjet + Indstillinger tilføjet Indholdet er ikke tilladt her Indholdet er tilladt her @@ -940,7 +940,7 @@ Mange hilsner fra Umbraco robotten Faneblad Titel på faneblad Faneblade - Master Dokument Type + Master Dokumentype Opret matchende skabelon @@ -1024,7 +1024,7 @@ Mange hilsner fra Umbraco robotten Indsæt pladsholder for indholdsområde Indsæt - Hvad vil du indsætte ? + Hvad vil du indsætte? Oversættelse Indsætter en oversætbar tekst, som skifter efter det sprog, som websitet vises i. From fbac0aba8b2f81f02271d00eedaf44954e4a32fe Mon Sep 17 00:00:00 2001 From: Nicklas Kevin Frank Date: Fri, 28 Apr 2017 14:56:57 +0200 Subject: [PATCH 017/106] Update da.xml with translation for concierge --- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 8462110b4a..604733849a 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -905,7 +905,7 @@ Mange hilsner fra Umbraco robotten Rediger script - Concierge + Portner Indhold Courier Udvikler From 7c2ddde311362969160eae43629d188fcb7638a5 Mon Sep 17 00:00:00 2001 From: Nicklas Kevin Frank Date: Fri, 28 Apr 2017 17:24:43 +0200 Subject: [PATCH 018/106] Fixed typo in Dokumenttype --- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 604733849a..2ee899f3a1 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -940,7 +940,7 @@ Mange hilsner fra Umbraco robotten Faneblad Titel på faneblad Faneblade - Master Dokumentype + Master Dokumenttype Opret matchende skabelon From ea14170272224e48420060d0e6db6400211315fd Mon Sep 17 00:00:00 2001 From: Stephan Date: Sun, 30 Apr 2017 10:14:48 +0200 Subject: [PATCH 019/106] minor cleanup --- .../Events/CancellableObjectEventArgs.cs | 36 +++++++++---------- .../Events/ScopeEventDispatcherBase.cs | 4 +-- .../Models/EntityBase/IAggregateRoot.cs | 2 +- .../Models/EntityBase/IDeletableEntity.cs | 11 ++++++ src/Umbraco.Core/Models/EntityBase/IEntity.cs | 8 +---- src/Umbraco.Core/TypeHelper.cs | 33 ++++++++--------- src/Umbraco.Core/Umbraco.Core.csproj | 1 + 7 files changed, 51 insertions(+), 44 deletions(-) create mode 100644 src/Umbraco.Core/Models/EntityBase/IDeletableEntity.cs diff --git a/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs b/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs index 1c9b08f636..63562eb53e 100644 --- a/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs +++ b/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs @@ -94,13 +94,13 @@ namespace Umbraco.Core.Events { get { return (T) base.EventObject; } set { base.EventObject = value; } - } + } public bool Equals(CancellableObjectEventArgs other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return base.Equals(other) && EqualityComparer.Default.Equals(EventObject, other.EventObject); + return base.Equals(other) && EqualityComparer.Default.Equals(EventObject, other.EventObject); } public override bool Equals(object obj) @@ -133,31 +133,31 @@ namespace Umbraco.Core.Events [HostProtection(SecurityAction.LinkDemand, SharedState = true)] public class CancellableEnumerableObjectEventArgs : CancellableObjectEventArgs>, IEquatable> { - public CancellableEnumerableObjectEventArgs(IEnumerable eventObject, bool canCancel, EventMessages messages, IDictionary additionalData) : base(eventObject, canCancel, messages, additionalData) - { - } + public CancellableEnumerableObjectEventArgs(IEnumerable eventObject, bool canCancel, EventMessages messages, IDictionary additionalData) + : base(eventObject, canCancel, messages, additionalData) + { } - public CancellableEnumerableObjectEventArgs(IEnumerable eventObject, bool canCancel, EventMessages eventMessages) : base(eventObject, canCancel, eventMessages) - { - } + public CancellableEnumerableObjectEventArgs(IEnumerable eventObject, bool canCancel, EventMessages eventMessages) + : base(eventObject, canCancel, eventMessages) + { } - public CancellableEnumerableObjectEventArgs(IEnumerable eventObject, EventMessages eventMessages) : base(eventObject, eventMessages) - { - } + public CancellableEnumerableObjectEventArgs(IEnumerable eventObject, EventMessages eventMessages) + : base(eventObject, eventMessages) + { } - public CancellableEnumerableObjectEventArgs(IEnumerable eventObject, bool canCancel) : base(eventObject, canCancel) - { - } + public CancellableEnumerableObjectEventArgs(IEnumerable eventObject, bool canCancel) + : base(eventObject, canCancel) + { } - public CancellableEnumerableObjectEventArgs(IEnumerable eventObject) : base(eventObject) - { - } + public CancellableEnumerableObjectEventArgs(IEnumerable eventObject) + : base(eventObject) + { } public bool Equals(CancellableEnumerableObjectEventArgs other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - + return EventObject.SequenceEqual(other.EventObject); } diff --git a/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs b/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs index 581f117979..c703a10cb4 100644 --- a/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs +++ b/src/Umbraco.Core/Events/ScopeEventDispatcherBase.cs @@ -150,7 +150,7 @@ namespace Umbraco.Core.Events var args = eventDefinition.Args as CancellableObjectEventArgs; if (args != null) { - var list = TypeHelper.CreateGenericEnumerableFromOjbect(args.EventObject); + var list = TypeHelper.CreateGenericEnumerableFromObject(args.EventObject); if (list == null) { @@ -248,7 +248,7 @@ namespace Umbraco.Core.Events foreach (var args in cancelableArgs) { - var list = TypeHelper.CreateGenericEnumerableFromOjbect(args.EventObject); + var list = TypeHelper.CreateGenericEnumerableFromObject(args.EventObject); if (list == null) { //try to find the args entity in the latest entity - based on the equality operators, this will diff --git a/src/Umbraco.Core/Models/EntityBase/IAggregateRoot.cs b/src/Umbraco.Core/Models/EntityBase/IAggregateRoot.cs index 8ba1e09c99..aacb5185e0 100644 --- a/src/Umbraco.Core/Models/EntityBase/IAggregateRoot.cs +++ b/src/Umbraco.Core/Models/EntityBase/IAggregateRoot.cs @@ -3,7 +3,7 @@ /// /// Marker interface for aggregate roots /// - public interface IAggregateRoot : IEntityDeleted + public interface IAggregateRoot : IDeletableEntity { } diff --git a/src/Umbraco.Core/Models/EntityBase/IDeletableEntity.cs b/src/Umbraco.Core/Models/EntityBase/IDeletableEntity.cs new file mode 100644 index 0000000000..42f91b6a6c --- /dev/null +++ b/src/Umbraco.Core/Models/EntityBase/IDeletableEntity.cs @@ -0,0 +1,11 @@ +using System; +using System.Runtime.Serialization; + +namespace Umbraco.Core.Models.EntityBase +{ + public interface IDeletableEntity : IEntity + { + [DataMember] + DateTime? DeletedDate { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/EntityBase/IEntity.cs b/src/Umbraco.Core/Models/EntityBase/IEntity.cs index a16a7daf2b..059983bb38 100644 --- a/src/Umbraco.Core/Models/EntityBase/IEntity.cs +++ b/src/Umbraco.Core/Models/EntityBase/IEntity.cs @@ -3,12 +3,6 @@ using System.Runtime.Serialization; namespace Umbraco.Core.Models.EntityBase { - public interface IEntityDeleted : IEntity - { - [DataMember] - DateTime? DeletedDate { get; set; } - } - /// /// Defines an Entity. /// Entities should always have an Id, Created and Modified date @@ -26,7 +20,7 @@ namespace Umbraco.Core.Models.EntityBase /// /// Guid based Id /// - /// The key is currectly used to store the Unique Id from the + /// The key is currectly used to store the Unique Id from the /// umbracoNode table, which many of the entities are based on. [DataMember] Guid Key { get; set; } diff --git a/src/Umbraco.Core/TypeHelper.cs b/src/Umbraco.Core/TypeHelper.cs index f8e10ff1fb..166b7e308f 100644 --- a/src/Umbraco.Core/TypeHelper.cs +++ b/src/Umbraco.Core/TypeHelper.cs @@ -16,31 +16,31 @@ namespace Umbraco.Core { private static readonly ConcurrentDictionary, PropertyInfo[]> GetPropertiesCache = new ConcurrentDictionary, PropertyInfo[]>(); - private static readonly ConcurrentDictionary GetFieldsCache + private static readonly ConcurrentDictionary GetFieldsCache = new ConcurrentDictionary(); - private static readonly Assembly[] EmptyAssemblies = new Assembly[0]; - + private static readonly Assembly[] EmptyAssemblies = new Assembly[0]; + /// /// Based on a type we'll check if it is IEnumerable{T} (or similar) and if so we'll return a List{T}, this will also deal with array types and return List{T} for those too. /// If it cannot be done, null is returned. /// /// /// - internal static IList CreateGenericEnumerableFromOjbect(object obj) + internal static IList CreateGenericEnumerableFromObject(object obj) { var type = obj.GetType(); - + if (type.IsGenericType) { - var genericTypeDef = type.GetGenericTypeDefinition(); + var genericTypeDef = type.GetGenericTypeDefinition(); if (genericTypeDef == typeof(IEnumerable<>) || genericTypeDef == typeof(ICollection<>) || genericTypeDef == typeof(Collection<>) || genericTypeDef == typeof(IList<>) - || genericTypeDef == typeof(List<>) - //this will occur when Linq is used and we get the odd WhereIterator or DistinctIterators since those are special iterator types + || genericTypeDef == typeof(List<>) + //this will occur when Linq is used and we get the odd WhereIterator or DistinctIterators since those are special iterator types || obj is IEnumerable) { //if it is a IEnumerable<>, IList or ICollection<> we'll use a List<> @@ -49,16 +49,17 @@ namespace Umbraco.Core return (IList)Activator.CreateInstance(genericType, obj); } } + if (type.IsArray) - { + { //if its an array, we'll use a List<> - var genericType = typeof(List<>).MakeGenericType(type.GetElementType()); + var genericType = typeof(List<>).MakeGenericType(type.GetElementType()); //pass in obj to fill the list - return (IList)Activator.CreateInstance(genericType, obj); - } - + return (IList)Activator.CreateInstance(genericType, obj); + } + return null; - } + } /// /// Checks if the method is actually overriding a base method @@ -85,8 +86,8 @@ namespace Umbraco.Core if (assembly.IsAppCodeAssembly() || assembly.IsGlobalAsaxAssembly()) return EmptyAssemblies; - - // find all assembly references that are referencing the current type's assembly since we + + // find all assembly references that are referencing the current type's assembly since we // should only be scanning those assemblies because any other assembly will definitely not // contain sub type's of the one we're currently looking for var name = assembly.GetName().Name; diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 270fbbc323..795ad24328 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -343,6 +343,7 @@ + From d64fa7f92d89143a8763f1a871145b993ace59a2 Mon Sep 17 00:00:00 2001 From: Shannon Date: Sun, 30 Apr 2017 19:14:36 +1000 Subject: [PATCH 020/106] Fixes tests, ensures we null check when generating hashes for items in a collection --- src/Umbraco.Core/HashCodeHelper.cs | 3 +++ src/Umbraco.Tests/Cache/CacheRefresherEventHandlerTests.cs | 3 ++- src/Umbraco.Tests/Scoping/ScopedXmlTests.cs | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/HashCodeHelper.cs b/src/Umbraco.Core/HashCodeHelper.cs index a3cedbdbb5..f0f281056d 100644 --- a/src/Umbraco.Core/HashCodeHelper.cs +++ b/src/Umbraco.Core/HashCodeHelper.cs @@ -44,6 +44,7 @@ namespace Umbraco.Core int hash = 0; foreach (var item in list) { + if (item == null) continue; hash = 31 * hash + item.GetHashCode(); } return hash; @@ -57,6 +58,7 @@ namespace Umbraco.Core int hash = 0; foreach (var item in list) { + if (item == null) continue; hash = 31 * hash + item.GetHashCode(); } return hash; @@ -77,6 +79,7 @@ namespace Umbraco.Core int count = 0; foreach (var item in list) { + if (item == null) continue; hash += item.GetHashCode(); count++; } diff --git a/src/Umbraco.Tests/Cache/CacheRefresherEventHandlerTests.cs b/src/Umbraco.Tests/Cache/CacheRefresherEventHandlerTests.cs index 3aa1db675f..8816896975 100644 --- a/src/Umbraco.Tests/Cache/CacheRefresherEventHandlerTests.cs +++ b/src/Umbraco.Tests/Cache/CacheRefresherEventHandlerTests.cs @@ -7,6 +7,7 @@ using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Cache; +using System.Linq; namespace Umbraco.Tests.Cache { @@ -31,7 +32,7 @@ namespace Umbraco.Tests.Cache new EventDefinition(null, ServiceContext.SectionService, new EventArgs(), "Deleted"), new EventDefinition(null, ServiceContext.SectionService, new EventArgs(), "New"), - new EventDefinition>(null, ServiceContext.UserService, new SaveEventArgs((IUserType) null)), + new EventDefinition>(null, ServiceContext.UserService, new SaveEventArgs(Enumerable.Empty())), new EventDefinition>(null, ServiceContext.UserService, new DeleteEventArgs((IUserType) null)), new EventDefinition>(null, ServiceContext.UserService, new SaveEventArgs((IUser) null)), diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index 6652c3f2a2..ff45a3c17c 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -179,7 +179,9 @@ namespace Umbraco.Tests.Scoping scope.Complete(); } - Assert.AreEqual(complete ? 2 : 0, evented); + //The reason why there is only 1 event occuring is because we are publishing twice for the same event for the same + //object and the scope deduplicates the events (uses the latest) + Assert.AreEqual(complete ? 1 : 0, evented); // this should never change Assert.AreEqual(beforeOuterXml, beforeXml.OuterXml); From 378d0f79397eae1a9beea11df934605e68b177d6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Sun, 30 Apr 2017 19:21:13 +1000 Subject: [PATCH 021/106] chagnes test params to be slightly less hacky --- .../Cache/CacheRefresherEventHandlerTests.cs | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/Umbraco.Tests/Cache/CacheRefresherEventHandlerTests.cs b/src/Umbraco.Tests/Cache/CacheRefresherEventHandlerTests.cs index 8816896975..a5036188fd 100644 --- a/src/Umbraco.Tests/Cache/CacheRefresherEventHandlerTests.cs +++ b/src/Umbraco.Tests/Cache/CacheRefresherEventHandlerTests.cs @@ -33,69 +33,69 @@ namespace Umbraco.Tests.Cache new EventDefinition(null, ServiceContext.SectionService, new EventArgs(), "New"), new EventDefinition>(null, ServiceContext.UserService, new SaveEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.UserService, new DeleteEventArgs((IUserType) null)), + new EventDefinition>(null, ServiceContext.UserService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.UserService, new SaveEventArgs((IUser) null)), - new EventDefinition>(null, ServiceContext.UserService, new DeleteEventArgs((IUser) null)), - new EventDefinition>(null, ServiceContext.UserService, new DeleteEventArgs((IUser) null)), + new EventDefinition>(null, ServiceContext.UserService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.UserService, new DeleteEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.UserService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.LocalizationService, new SaveEventArgs((IDictionaryItem) null)), - new EventDefinition>(null, ServiceContext.LocalizationService, new DeleteEventArgs((IDictionaryItem) null)), + new EventDefinition>(null, ServiceContext.LocalizationService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.LocalizationService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.DataTypeService, new SaveEventArgs((IDataTypeDefinition) null)), - new EventDefinition>(null, ServiceContext.DataTypeService, new DeleteEventArgs((IDataTypeDefinition) null)), + new EventDefinition>(null, ServiceContext.DataTypeService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.DataTypeService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.FileService, new SaveEventArgs((Stylesheet) null)), - new EventDefinition>(null, ServiceContext.FileService, new DeleteEventArgs((Stylesheet) null)), + new EventDefinition>(null, ServiceContext.FileService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.FileService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.DomainService, new SaveEventArgs((IDomain) null)), - new EventDefinition>(null, ServiceContext.DomainService, new DeleteEventArgs((IDomain) null)), + new EventDefinition>(null, ServiceContext.DomainService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.DomainService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.LocalizationService, new SaveEventArgs((ILanguage) null)), - new EventDefinition>(null, ServiceContext.LocalizationService, new DeleteEventArgs((ILanguage) null)), + new EventDefinition>(null, ServiceContext.LocalizationService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.LocalizationService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.ContentTypeService, new SaveEventArgs((IContentType) null)), - new EventDefinition>(null, ServiceContext.ContentTypeService, new DeleteEventArgs((IContentType) null)), - new EventDefinition>(null, ServiceContext.ContentTypeService, new SaveEventArgs((IMediaType) null)), - new EventDefinition>(null, ServiceContext.ContentTypeService, new DeleteEventArgs((IMediaType) null)), + new EventDefinition>(null, ServiceContext.ContentTypeService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.ContentTypeService, new DeleteEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.ContentTypeService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.ContentTypeService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.MemberTypeService, new SaveEventArgs((IMemberType) null)), - new EventDefinition>(null, ServiceContext.MemberTypeService, new DeleteEventArgs((IMemberType) null)), + new EventDefinition>(null, ServiceContext.MemberTypeService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.MemberTypeService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.FileService, new SaveEventArgs((ITemplate) null)), - new EventDefinition>(null, ServiceContext.FileService, new DeleteEventArgs((ITemplate) null)), + new EventDefinition>(null, ServiceContext.FileService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.FileService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.MacroService, new SaveEventArgs((IMacro) null)), - new EventDefinition>(null, ServiceContext.MacroService, new DeleteEventArgs((IMacro) null)), + new EventDefinition>(null, ServiceContext.MacroService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.MacroService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.MemberService, new SaveEventArgs((IMember) null)), - new EventDefinition>(null, ServiceContext.MemberService, new DeleteEventArgs((IMember) null)), + new EventDefinition>(null, ServiceContext.MemberService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.MemberService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.MemberGroupService, new SaveEventArgs((IMemberGroup) null)), - new EventDefinition>(null, ServiceContext.MemberGroupService, new DeleteEventArgs((IMemberGroup) null)), + new EventDefinition>(null, ServiceContext.MemberGroupService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.MemberGroupService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.MediaService, new SaveEventArgs((IMedia) null)), - new EventDefinition>(null, ServiceContext.MediaService, new DeleteEventArgs((IMedia) null)), + new EventDefinition>(null, ServiceContext.MediaService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.MediaService, new DeleteEventArgs(Enumerable.Empty())), new EventDefinition>(null, ServiceContext.MediaService, new MoveEventArgs(new MoveEventInfo(null, "", -1)), "Moved"), new EventDefinition>(null, ServiceContext.MediaService, new MoveEventArgs(new MoveEventInfo(null, "", -1)), "Trashed"), new EventDefinition(null, ServiceContext.MediaService, new RecycleBinEventArgs(Guid.NewGuid(), new Dictionary>(), true)), - new EventDefinition>(null, ServiceContext.ContentService, new SaveEventArgs((IContent) null)), - new EventDefinition>(null, ServiceContext.ContentService, new DeleteEventArgs((IContent) null)), + new EventDefinition>(null, ServiceContext.ContentService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.ContentService, new DeleteEventArgs(Enumerable.Empty())), new EventDefinition>(null, ServiceContext.ContentService, new CopyEventArgs(null, null, -1)), new EventDefinition>(null, ServiceContext.ContentService, new MoveEventArgs(new MoveEventInfo(null, "", -1)), "Trashed"), new EventDefinition(null, ServiceContext.ContentService, new RecycleBinEventArgs(Guid.NewGuid(), new Dictionary>(), true)), - new EventDefinition>(null, ServiceContext.ContentService, new PublishEventArgs((IContent) null), "Published"), - new EventDefinition>(null, ServiceContext.ContentService, new PublishEventArgs((IContent) null), "UnPublished"), + new EventDefinition>(null, ServiceContext.ContentService, new PublishEventArgs(Enumerable.Empty()), "Published"), + new EventDefinition>(null, ServiceContext.ContentService, new PublishEventArgs(Enumerable.Empty()), "UnPublished"), - new EventDefinition>(null, ServiceContext.PublicAccessService, new SaveEventArgs((PublicAccessEntry) null)), - new EventDefinition>(null, ServiceContext.PublicAccessService, new DeleteEventArgs((PublicAccessEntry) null)), + new EventDefinition>(null, ServiceContext.PublicAccessService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.PublicAccessService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.RelationService, new SaveEventArgs((IRelationType) null)), - new EventDefinition>(null, ServiceContext.RelationService, new DeleteEventArgs((IRelationType) null)), + new EventDefinition>(null, ServiceContext.RelationService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.RelationService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, ServiceContext.RelationService, new SaveEventArgs((IRelationType) null)), - new EventDefinition>(null, ServiceContext.RelationService, new DeleteEventArgs((IRelationType) null)), + new EventDefinition>(null, ServiceContext.RelationService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, ServiceContext.RelationService, new DeleteEventArgs(Enumerable.Empty())), }; foreach (var definition in definitions) From e77c5f86674b0bcf99600dec0d45db2adc6e44cc Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Sun, 30 Apr 2017 14:04:00 +0200 Subject: [PATCH 022/106] Keep the Concierge Product name --- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 2ee899f3a1..017f9fca24 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -905,7 +905,7 @@ Mange hilsner fra Umbraco robotten Rediger script - Portner + Concierge Indhold Courier Udvikler From 004e00599ddb72eafa445239f79f19bdff7334ed Mon Sep 17 00:00:00 2001 From: Shannon Date: Sun, 30 Apr 2017 23:19:45 +1000 Subject: [PATCH 023/106] Fixing nuget package issues , semver 2 is only for 452+ which we dont have, we shouldnt be using it and it adds more dependencies that we don't need right now --- src/Umbraco.Core/Umbraco.Core.csproj | 5 ++--- src/Umbraco.Core/packages.config | 6 +----- src/Umbraco.Tests/Umbraco.Tests.csproj | 5 ++--- src/Umbraco.Tests/packages.config | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 9 +++++---- src/Umbraco.Web/packages.config | 4 ++-- 6 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 795ad24328..a8d1cc55bd 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -100,9 +100,8 @@ ..\packages\Owin.1.0\lib\net40\Owin.dll True - - ..\packages\Semver.2.0.4\lib\netstandard1.1\Semver.dll - True + + ..\packages\semver.1.1.2\lib\net45\Semver.dll diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config index 191eb11c01..dd00d61ea6 100644 --- a/src/Umbraco.Core/packages.config +++ b/src/Umbraco.Core/packages.config @@ -15,11 +15,7 @@ - + - - - - \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index c0acf99f83..eb57d210fc 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -97,9 +97,8 @@ ..\packages\Owin.1.0\lib\net40\Owin.dll - - ..\packages\Semver.2.0.4\lib\netstandard1.1\Semver.dll - True + + ..\packages\semver.1.1.2\lib\net45\Semver.dll diff --git a/src/Umbraco.Tests/packages.config b/src/Umbraco.Tests/packages.config index 63a6c4b259..a9cbce1aad 100644 --- a/src/Umbraco.Tests/packages.config +++ b/src/Umbraco.Tests/packages.config @@ -23,7 +23,7 @@ - + diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 9061f70a9d..d3e31fa366 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -184,9 +184,8 @@ ..\packages\Owin.1.0\lib\net40\Owin.dll True - - ..\packages\Semver.2.0.4\lib\netstandard1.1\Semver.dll - True + + ..\packages\semver.1.1.2\lib\net45\Semver.dll System @@ -442,7 +441,9 @@ - + + ASPXCodeBehind + diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index 01ce6a3f92..db4437962b 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -26,12 +26,12 @@ - + - + \ No newline at end of file From 2479c5a2725e2fa752a6be7b9a0d6b253efa2d11 Mon Sep 17 00:00:00 2001 From: Shannon Date: Sun, 30 Apr 2017 23:21:19 +1000 Subject: [PATCH 024/106] updates other nuget refs that shouldnt be there --- src/Umbraco.Web/packages.config | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index db4437962b..b7625770a3 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -28,10 +28,6 @@ - - - - \ No newline at end of file From d3fcdfb176d13ca1031cddda56ef954325d15565 Mon Sep 17 00:00:00 2001 From: Shannon Date: Sun, 30 Apr 2017 23:24:26 +1000 Subject: [PATCH 025/106] updates nuget semver ref in nuspec --- build/NuSpecs/UmbracoCms.Core.nuspec | 4 ++-- build/NuSpecs/UmbracoCms.nuspec | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index acda20f82a..47e10bbcef 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -37,8 +37,8 @@ - - + + diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index d30ae4efa3..c9912aeb89 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -19,7 +19,7 @@ - + From d9b507c94773b597103ba3fe1fddf12d72fc54de Mon Sep 17 00:00:00 2001 From: Shannon Date: Sun, 30 Apr 2017 23:26:04 +1000 Subject: [PATCH 026/106] bumps version to RC4 so we can build new dependencies against forms/deploy --- build/UmbracoVersion.txt | 2 +- src/SolutionInfo.cs | 2 +- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index 5ed0e7d05b..6bd247599c 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,3 +1,3 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) 7.6.0 -RC3 \ No newline at end of file +RC4 \ No newline at end of file diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 6628e7b793..61ca43c851 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -12,4 +12,4 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyFileVersion("7.6.0")] -[assembly: AssemblyInformationalVersion("7.6.0-RC3")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("7.6.0-RC4")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index f03a43fd29..da8d8235b3 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration /// Gets the version comment (like beta or RC). /// /// The version comment. - public static string CurrentComment { get { return "RC3"; } } + public static string CurrentComment { get { return "RC4"; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx From d5124e97398b37ae8a419f01be62c89ba1fcbd81 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 1 May 2017 21:10:22 +1000 Subject: [PATCH 027/106] Ensures that an UmbracoContext exist before emitting the cache handler events --- .../Cache/CacheRefresherEventHandler.cs | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs index 8b58ec92ed..df25d9dfe0 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs +++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; using Umbraco.Core; using Umbraco.Core.Events; using Umbraco.Core.Models; @@ -11,9 +12,14 @@ using umbraco.BusinessLogic; using umbraco.cms.businesslogic; using System.Linq; using System.Reflection; +using System.Web; +using System.Web.Hosting; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.ObjectResolution; using Umbraco.Core.Publishing; +using Umbraco.Web.Routing; +using Umbraco.Web.Security; using Content = Umbraco.Core.Models.Content; using ApplicationTree = Umbraco.Core.Models.ApplicationTree; using DeleteEventArgs = umbraco.cms.businesslogic.DeleteEventArgs; @@ -787,14 +793,39 @@ namespace Umbraco.Web.Cache /// /// internal static void HandleEvents(IEnumerable events) - { - foreach (var e in events) + { + //TODO: We should remove this in v8, this is a backwards compat hack and is needed because when we are using Deploy, the events will be raised on a background + //thread which means that cache refreshers will also execute on a background thread and in many cases developers may be using UmbracoContext.Current in their + //cache refresher handlers, so before we execute all of the events, we'll ensure a context + UmbracoContext tempContext = null; + if (UmbracoContext.Current == null) { - var handler = FindHandler(e); - if (handler == null) continue; - - handler.Invoke(null, new[] { e.Sender, e.Args }); + var httpContext = new HttpContextWrapper(HttpContext.Current ?? new HttpContext(new SimpleWorkerRequest("temp.aspx", "", new StringWriter()))); + tempContext = UmbracoContext.EnsureContext( + httpContext, + ApplicationContext.Current, + new WebSecurity(httpContext, ApplicationContext.Current), + UmbracoConfig.For.UmbracoSettings(), + UrlProviderResolver.Current.Providers, + true); } + + try + { + foreach (var e in events) + { + var handler = FindHandler(e); + if (handler == null) continue; + + handler.Invoke(null, new[] { e.Sender, e.Args }); + } + } + finally + { + if (tempContext != null) + tempContext.Dispose(); + } + } /// From c1853e8c51938382e151712cbe0308baedff67f4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 2 May 2017 13:42:20 +1000 Subject: [PATCH 028/106] bumps version to 7.6.0 rtm --- build/UmbracoVersion.txt | 3 +-- src/SolutionInfo.cs | 2 +- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index 6bd247599c..6ef1d69bd9 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,3 +1,2 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) -7.6.0 -RC4 \ No newline at end of file +7.6.0 \ No newline at end of file diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 61ca43c851..ad697d99bf 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -12,4 +12,4 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyFileVersion("7.6.0")] -[assembly: AssemblyInformationalVersion("7.6.0-RC4")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("7.6.0")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index da8d8235b3..952dd9a22a 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration /// Gets the version comment (like beta or RC). /// /// The version comment. - public static string CurrentComment { get { return "RC4"; } } + public static string CurrentComment { get { return ""; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx From 1520fa77d55d20712e578bad81d58cd61f720aba Mon Sep 17 00:00:00 2001 From: wanddy Date: Tue, 2 May 2017 14:12:58 +0800 Subject: [PATCH 029/106] Add missing translation item for en.xml and zh.xml Signed-off-by: wanddy --- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 2 ++ src/Umbraco.Web.UI/umbraco/config/lang/zh.xml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index ad30f0c62f..aca237bc9f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -1252,6 +1252,8 @@ To manage your website, simply open the Umbraco back office and start adding con Templates XSLT Files Analytics + Partial Views + Partial View Macro Files New update ready diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml index 1f538e7fa0..0341316f25 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml @@ -1252,6 +1252,8 @@ 模板 XSLT文件 分析 + 分部视图 + 分部视图宏文件 有可用更新 From ab551b2c966148f7f437ff84e32e36d91c7ade37 Mon Sep 17 00:00:00 2001 From: wanddy Date: Tue, 2 May 2017 14:20:44 +0800 Subject: [PATCH 030/106] Fix some menu tree header won't be translated. This may be caused by the explicit definition of tree node name. Signed-off-by: wanddy --- src/Umbraco.Web/Trees/PartialViewMacrosTree.cs | 2 +- src/Umbraco.Web/Trees/PartialViewsTree.cs | 2 +- src/Umbraco.Web/Trees/TemplatesTreeController.cs | 2 +- .../umbraco.presentation/umbraco/Trees/loadRelationTypes.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web/Trees/PartialViewMacrosTree.cs b/src/Umbraco.Web/Trees/PartialViewMacrosTree.cs index dfc0636804..cf13a24d36 100644 --- a/src/Umbraco.Web/Trees/PartialViewMacrosTree.cs +++ b/src/Umbraco.Web/Trees/PartialViewMacrosTree.cs @@ -9,7 +9,7 @@ namespace Umbraco.Web.Trees /// /// Tree for displaying partial view macros in the developer app /// - [Tree(Constants.Applications.Developer, "partialViewMacros", "Partial View Macro Files", sortOrder: 6)] + [Tree(Constants.Applications.Developer, "partialViewMacros", null, sortOrder: 6)] public class PartialViewMacrosTree : PartialViewsTree { public PartialViewMacrosTree(string application) : base(application) diff --git a/src/Umbraco.Web/Trees/PartialViewsTree.cs b/src/Umbraco.Web/Trees/PartialViewsTree.cs index 11d6e3e804..594355f6c7 100644 --- a/src/Umbraco.Web/Trees/PartialViewsTree.cs +++ b/src/Umbraco.Web/Trees/PartialViewsTree.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.Trees /// /// Tree for displaying partial views in the settings app /// - [Tree(Constants.Applications.Settings, "partialViews", "Partial Views", sortOrder: 2)] + [Tree(Constants.Applications.Settings, "partialViews", null, sortOrder: 2)] public class PartialViewsTree : FileSystemTree { public PartialViewsTree(string application) : base(application) { } diff --git a/src/Umbraco.Web/Trees/TemplatesTreeController.cs b/src/Umbraco.Web/Trees/TemplatesTreeController.cs index 61cff8f862..73f9389150 100644 --- a/src/Umbraco.Web/Trees/TemplatesTreeController.cs +++ b/src/Umbraco.Web/Trees/TemplatesTreeController.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.Trees { [UmbracoTreeAuthorize(Constants.Trees.Templates)] [LegacyBaseTree(typeof (loadTemplates))] - [Tree(Constants.Applications.Settings, Constants.Trees.Templates, "Templates", sortOrder:1)] + [Tree(Constants.Applications.Settings, Constants.Trees.Templates, null, sortOrder:1)] [PluginController("UmbracoTrees")] [CoreTree] public class TemplatesTreeController : TreeController diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadRelationTypes.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadRelationTypes.cs index d94ff6942e..a173926257 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadRelationTypes.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadRelationTypes.cs @@ -89,7 +89,7 @@ namespace umbraco /// the 'Relation Types' root node protected override void CreateRootNode(ref XmlTreeNode rootNode) { - rootNode.Text = "Relation Types"; + //rootNode.Text = "Relation Types"; rootNode.Icon = BaseTree.FolderIcon; rootNode.OpenIcon = BaseTree.FolderIconOpen; rootNode.NodeType = this.TreeAlias; // (Was prefixed with init) From 0eafa14246be70c8d3e4d14a2108781cda076fd4 Mon Sep 17 00:00:00 2001 From: Maciej Sadowski Date: Tue, 2 May 2017 15:36:39 +0200 Subject: [PATCH 031/106] fixed console error on media picker --- .../imaging/umbimagegravity.directive.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js index 836d998412..ad7466ce54 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js @@ -105,12 +105,14 @@ angular.module("umbraco.directives") }); //// INIT ///// - $image.load(function(){ - $timeout(function(){ - setDimensions(); - scope.loaded = true; - scope.onImageLoaded(); - }); + $image.load(function() { + $timeout(function() { + setDimensions(); + scope.loaded = true; + if (angular.isFunction(scope.onImageLoaded)) { + scope.onImageLoaded(); + } + }); }); $(window).on('resize.umbImageGravity', function(){ From be2eb3f555c720bae5b7394e26b98c05249a4983 Mon Sep 17 00:00:00 2001 From: Denise Michelle del Bando Date: Tue, 2 May 2017 23:16:32 -0400 Subject: [PATCH 032/106] fix Add basic xss prevention logic missing import on CleanForXss --- src/Umbraco.Web.UI/umbraco/users/PermissionEditor.aspx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/users/PermissionEditor.aspx b/src/Umbraco.Web.UI/umbraco/users/PermissionEditor.aspx index d80a60c92c..75b5d6e66a 100644 --- a/src/Umbraco.Web.UI/umbraco/users/PermissionEditor.aspx +++ b/src/Umbraco.Web.UI/umbraco/users/PermissionEditor.aspx @@ -1,5 +1,5 @@ <%@ Page Language="C#" AutoEventWireup="true" MasterPageFile="../masterpages/umbracoPage.Master" CodeBehind="PermissionEditor.aspx.cs" Inherits="umbraco.cms.presentation.user.PermissionEditor" %> - +<%@ Import Namespace="Umbraco.Web" %> <%@ Register Src="../controls/Tree/TreeControl.ascx" TagName="TreeControl" TagPrefix="umbraco" %> <%@ Register Src="NodePermissions.ascx" TagName="NodePermissions" TagPrefix="user" %> <%@ Register TagPrefix="ui" Namespace="umbraco.uicontrols" Assembly="controls" %> From 1030fd5294287a038c3e2b5e793bd8f9336be73f Mon Sep 17 00:00:00 2001 From: Claus Date: Wed, 3 May 2017 10:21:00 +0200 Subject: [PATCH 033/106] U4-9704 Sorting UDI media picker items, makes it store Int's --- .../propertyeditors/mediapicker/mediapicker.controller.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index dafe6cc3c7..6652c52cc0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -112,10 +112,9 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl // content picker. THen we don't have to worry about setting ids, render models, models, we just set one and let the // watch do all the rest. $timeout(function(){ - angular.forEach($scope.images, function(value, key){ - r.push(value.id); + angular.forEach($scope.images, function(value, key) { + r.push($scope.model.config.idType === "udi" ? value.udi : value.id); }); - $scope.ids = r; $scope.sync(); }, 500, false); From 3e294e290bfe2a07ac35c7f2ccc9e230d99523a4 Mon Sep 17 00:00:00 2001 From: Tom Steer Date: Thu, 4 May 2017 09:10:35 +0100 Subject: [PATCH 034/106] Change Context to be public on MigrationBase --- src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs index a19aaa24ad..486f84b444 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs @@ -23,7 +23,7 @@ namespace Umbraco.Core.Persistence.Migrations Logger = logger; } - internal IMigrationContext Context; + public IMigrationContext Context; public abstract void Up(); public abstract void Down(); From e7eaf6959f5e4d9f9cd5e6e176e66bf6493b3d5f Mon Sep 17 00:00:00 2001 From: Claus Date: Thu, 4 May 2017 15:08:37 +0200 Subject: [PATCH 035/106] U4-9849 Query Builder not working in template editor. Always returns Model.Content.Site().Children() adding missing nullchecks in the endpoint action. ensuring promise returns before setting init data - the unresolved promise was being used on the query model instead of the actual text string the promise returns - causing the endpoint to fail to bind the model. --- .../querybuilder/querybuilder.controller.js | 119 +++++++++--------- .../Editors/TemplateQueryController.cs | 86 ++++++------- 2 files changed, 100 insertions(+), 105 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js index 979428de0c..ea9ad6ca97 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js @@ -1,14 +1,13 @@ -(function () { +(function() { "use strict"; function QueryBuilderOverlayController($scope, templateQueryResource, localizationService) { - var everything = localizationService.localize("template_allContent"); - var myWebsite = localizationService.localize("template_websiteRoot"); + var everything = ""; + var myWebsite = ""; + var ascendingTranslation = ""; + var descendingTranslation = ""; - var ascendingTranslation = localizationService.localize("template_ascending"); - var descendingTranslation = localizationService.localize("template_descending"); - var vm = this; vm.properties = []; @@ -21,34 +20,6 @@ format: "YYYY-MM-DD" }; - vm.query = { - contentType: { - name: everything - }, - source: { - name: myWebsite - }, - filters: [ - { - property: undefined, - operator: undefined - } - ], - sort: { - property: { - alias: "", - name: "", - }, - direction: "ascending", //This is the value for sorting sent to server - translation: { - currentLabel: ascendingTranslation, //This is the localized UI value in the the dialog - ascending: ascendingTranslation, - descending: descendingTranslation - } - - } - }; - vm.chooseSource = chooseSource; vm.getPropertyOperators = getPropertyOperators; vm.addFilter = addFilter; @@ -63,21 +34,48 @@ function onInit() { + vm.query = { + contentType: { + name: everything + }, + source: { + name: myWebsite + }, + filters: [ + { + property: undefined, + operator: undefined + } + ], + sort: { + property: { + alias: "", + name: "", + }, + direction: "ascending", //This is the value for sorting sent to server + translation: { + currentLabel: ascendingTranslation, //This is the localized UI value in the the dialog + ascending: ascendingTranslation, + descending: descendingTranslation + } + } + }; + templateQueryResource.getAllowedProperties() - .then(function (properties) { + .then(function(properties) { vm.properties = properties; }); templateQueryResource.getContentTypes() - .then(function (contentTypes) { + .then(function(contentTypes) { vm.contentTypes = contentTypes; }); templateQueryResource.getFilterConditions() - .then(function (conditions) { + .then(function(conditions) { vm.conditions = conditions; }); - + throttledFunc(); } @@ -111,10 +109,11 @@ } function getPropertyOperators(property) { - var conditions = _.filter(vm.conditions, function (condition) { - var index = condition.appliesTo.indexOf(property.type); - return index >= 0; - }); + var conditions = _.filter(vm.conditions, + function(condition) { + var index = condition.appliesTo.indexOf(property.type); + return index >= 0; + }); return conditions; } @@ -123,10 +122,8 @@ } function trashFilter(query, filter) { - for (var i = 0; i < query.filters.length; i++) - { - if (query.filters[i] == filter) - { + for (var i = 0; i < query.filters.length; i++) { + if (query.filters[i] == filter) { query.filters.splice(i, 1); } } @@ -173,7 +170,7 @@ function setFilterTerm(filter, term) { filter.term = term; - if(filter.constraintValue) { + if (filter.constraintValue) { throttledFunc(); } } @@ -183,22 +180,32 @@ } function datePickerChange(event, filter) { - if(event.date && event.date.isValid()) { + if (event.date && event.date.isValid()) { filter.constraintValue = event.date.format(vm.datePickerConfig.format); throttledFunc(); } } - var throttledFunc = _.throttle(function () { - - templateQueryResource.postTemplateQuery(vm.query) - .then(function (response) { - $scope.model.result = response; - }); + var throttledFunc = _.throttle(function() { - }, 200); + templateQueryResource.postTemplateQuery(vm.query) + .then(function(response) { + $scope.model.result = response; + }); - onInit(); + }, + 200); + + localizationService.localizeMany([ + "template_allContent", "template_websiteRoot", "template_ascending", "template_descending" + ]) + .then(function(res) { + everything = res[0]; + myWebsite = res[1]; + ascendingTranslation = res[2]; + descendingTranslation = res[3]; + onInit(); + }); } angular.module("umbraco").controller("Umbraco.Overlays.QueryBuilderController", QueryBuilderOverlayController); diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs index cd004fe926..d7351b0e97 100644 --- a/src/Umbraco.Web/Editors/TemplateQueryController.cs +++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Text; using Umbraco.Core.Models; using Umbraco.Web.Mvc; -using Umbraco.Web.WebApi.Filters; using Umbraco.Web.WebApi; using System; using System.Diagnostics; @@ -13,8 +12,6 @@ using Umbraco.Core.Services; namespace Umbraco.Web.Editors { - - /// /// The API controller used for building content queries within the template /// @@ -26,10 +23,9 @@ namespace Umbraco.Web.Editors { } public TemplateQueryController(UmbracoContext umbracoContext) - :base(umbracoContext) + : base(umbracoContext) { } - private IEnumerable Terms { get @@ -77,20 +73,19 @@ namespace Umbraco.Web.Editors var sb = new StringBuilder(); var indention = Environment.NewLine + "\t\t\t\t\t\t"; - + sb.Append("Model.Content.Site()"); var timer = new Stopwatch(); - + timer.Start(); var currentPage = umbraco.TypedContentAtRoot().FirstOrDefault(); timer.Stop(); - var pointerNode = currentPage; // adjust the "FROM" - if (model != null && model.Source.Id > 0) + if (model != null && model.Source != null && model.Source.Id > 0) { var targetNode = umbraco.TypedContent(model.Source.Id); @@ -120,10 +115,10 @@ namespace Umbraco.Web.Editors } } } - - // TYPE to return if filtered by type + + // TYPE to return if filtered by type IEnumerable contents; - if (model != null && string.IsNullOrEmpty(model.ContentType.Alias) == false) + if (model != null && model.ContentType != null && string.IsNullOrEmpty(model.ContentType.Alias) == false) { timer.Start(); @@ -154,13 +149,13 @@ namespace Umbraco.Web.Editors foreach (var condition in model.Filters) { - if(string.IsNullOrEmpty( condition.ConstraintValue)) continue; - + if (string.IsNullOrEmpty(condition.ConstraintValue)) continue; + //x is passed in as the parameter alias for the linq where statement clause var operation = condition.BuildCondition("x"); var tokenizedOperation = condition.BuildTokenizedCondition(token); - clause = string.IsNullOrEmpty(clause) ? operation : string.Concat(new[] { clause, " && ", operation }); + clause = string.IsNullOrEmpty(clause) ? operation : string.Concat(new[] { clause, " && ", operation }); tokenizedClause = string.IsNullOrEmpty(tokenizedClause) ? tokenizedOperation : string.Concat(new[] { tokenizedClause, " && ", tokenizedOperation }); token++; @@ -168,7 +163,6 @@ namespace Umbraco.Web.Editors if (string.IsNullOrEmpty(clause) == false) { - timer.Start(); //trial-run the tokenized clause to time the execution @@ -178,14 +172,13 @@ namespace Umbraco.Web.Editors timer.Stop(); - + //the query to output to the editor sb.Append(indention); sb.Append(".Where(x => x.IsVisible())"); sb.Append(indention); sb.AppendFormat(".Where(x => {0})", clause); - } else { @@ -197,7 +190,6 @@ namespace Umbraco.Web.Editors sb.Append(indention); sb.Append(".Where(x => x.IsVisible())"); - } if (model.Sort != null && string.IsNullOrEmpty(model.Sort.Property.Alias) == false) @@ -231,20 +223,19 @@ namespace Umbraco.Web.Editors queryResult.ExecutionTime = timer.ElapsedMilliseconds; queryResult.ResultCount = contents.Count(); queryResult.SampleResults = contents.Take(20).Select(x => new TemplateQueryResult() - { - Icon = "icon-file", - Name = x.Name - }); + { + Icon = "icon-file", + Name = x.Name + }); - - return queryResult; + return queryResult; } private object GetConstraintValue(QueryCondition condition) { switch (condition.Property.Type) { - case "int" : + case "int": return int.Parse(condition.ConstraintValue); case "datetime": DateTime dt; @@ -254,42 +245,41 @@ namespace Umbraco.Web.Editors } } - private IEnumerable SortByDefaultPropertyValue(IEnumerable contents, SortExpression sortExpression) + private IEnumerable SortByDefaultPropertyValue(IEnumerable contents, SortExpression sortExpression) { switch (sortExpression.Property.Alias) { - case "id" : + case "id": return sortExpression.Direction == "ascending" - ? contents.OrderBy(x => x.Id) - : contents.OrderByDescending(x => x.Id); - case "createDate" : - + ? contents.OrderBy(x => x.Id) + : contents.OrderByDescending(x => x.Id); + case "createDate": + return sortExpression.Direction == "ascending" - ? contents.OrderBy(x => x.CreateDate) - : contents.OrderByDescending(x => x.CreateDate); + ? contents.OrderBy(x => x.CreateDate) + : contents.OrderByDescending(x => x.CreateDate); case "publishDate": - + return sortExpression.Direction == "ascending" - ? contents.OrderBy(x => x.UpdateDate) - : contents.OrderByDescending(x => x.UpdateDate); + ? contents.OrderBy(x => x.UpdateDate) + : contents.OrderByDescending(x => x.UpdateDate); case "name": return sortExpression.Direction == "ascending" - ? contents.OrderBy(x => x.Name) - : contents.OrderByDescending(x => x.Name); - default : + ? contents.OrderBy(x => x.Name) + : contents.OrderByDescending(x => x.Name); + default: return sortExpression.Direction == "ascending" - ? contents.OrderBy(x => x.Name) - : contents.OrderByDescending(x => x.Name); - + ? contents.OrderBy(x => x.Name) + : contents.OrderByDescending(x => x.Name); } } - + private IEnumerable GetChildContentTypeAliases(IPublishedContent targetNode, IPublishedContent current) { var aliases = new List(); - - if (targetNode.Id == current.Id) return aliases; + + if (targetNode == null || targetNode.Id == current.Id) return aliases; if (targetNode.Id != current.Id) { aliases.Add(targetNode.DocumentTypeAlias); @@ -309,7 +299,7 @@ namespace Umbraco.Web.Editors { var contentTypes = ApplicationContext.Services.ContentTypeService.GetAllContentTypes() - .Select(x => new ContentTypeModel() { Alias = x.Alias, Name = Services.TextService.Localize("template/contentOfType", tokens: new string[] { x.Name } ) }) + .Select(x => new ContentTypeModel() { Alias = x.Alias, Name = Services.TextService.Localize("template/contentOfType", tokens: new string[] { x.Name }) }) .OrderBy(x => x.Name).ToList(); contentTypes.Insert(0, new ContentTypeModel() { Alias = string.Empty, Name = Services.TextService.Localize("template/allContent") }); @@ -332,7 +322,5 @@ namespace Umbraco.Web.Editors { return Terms; } - - } } \ No newline at end of file From 8a096cd82661bdbdb294c51e533e91720dfcc3ff Mon Sep 17 00:00:00 2001 From: Claus Date: Fri, 5 May 2017 10:51:31 +0200 Subject: [PATCH 036/106] Merge pull request #1794 from TheBekker/fix-U4-9038 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [fix-U4-9038] get and set media start node for mediaPickerOverlay in … --- .../propertyeditors/grid/editors/media.controller.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js index e289a389cb..bb580a906d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js @@ -1,10 +1,17 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.Grid.MediaController", - function ($scope, $rootScope, $timeout) { + function ($scope, $rootScope, $timeout, userService) { + + if (!$scope.model.config.startNodeId) { + userService.getCurrentUser().then(function (userData) { + $scope.model.config.startNodeId = userData.startMediaId; + }); + } $scope.setImage = function(){ $scope.mediaPickerOverlay = {}; $scope.mediaPickerOverlay.view = "mediapicker"; + $scope.mediaPickerOverlay.startNodeId = $scope.model.config && $scope.model.config.startNodeId ? $scope.model.config.startNodeId : undefined; $scope.mediaPickerOverlay.cropSize = $scope.control.editor.config && $scope.control.editor.config.size ? $scope.control.editor.config.size : undefined; $scope.mediaPickerOverlay.showDetails = true; $scope.mediaPickerOverlay.disableFolderSelect = true; From eb949e2cbc1a47619a259b807d59f4f00362e57a Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 5 May 2017 12:46:04 +0200 Subject: [PATCH 037/106] Fix UdiString to escape paths --- src/Umbraco.Core/StringUdi.cs | 4 ++-- src/Umbraco.Core/Udi.cs | 5 +++-- src/Umbraco.Tests/UdiTests.cs | 28 ++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/StringUdi.cs b/src/Umbraco.Core/StringUdi.cs index 7f1189677f..0f42f4b5f6 100644 --- a/src/Umbraco.Core/StringUdi.cs +++ b/src/Umbraco.Core/StringUdi.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core /// The entity type part of the udi. /// The string id part of the udi. public StringUdi(string entityType, string id) - : base(entityType, "umb://" + entityType + "/" + id) + : base(entityType, "umb://" + entityType + "/" + Uri.EscapeUriString(id)) { Id = id; } @@ -32,7 +32,7 @@ namespace Umbraco.Core public StringUdi(Uri uriValue) : base(uriValue) { - Id = uriValue.AbsolutePath.TrimStart('/'); + Id = Uri.UnescapeDataString(uriValue.AbsolutePath.TrimStart('/')); } /// diff --git a/src/Umbraco.Core/Udi.cs b/src/Umbraco.Core/Udi.cs index 9d67c6ccab..142bf025a9 100644 --- a/src/Umbraco.Core/Udi.cs +++ b/src/Umbraco.Core/Udi.cs @@ -101,7 +101,8 @@ namespace Umbraco.Core public override string ToString() { // UriValue is created in the ctor and is never null - return UriValue.ToString(); + // use AbsoluteUri here and not ToString else it's not encoded! + return UriValue.AbsoluteUri; } /// @@ -159,7 +160,7 @@ namespace Umbraco.Core } if (udiType == UdiType.StringUdi) { - udi = path == string.Empty ? GetRootUdi(uri.Host) : new StringUdi(uri.Host, path); + udi = path == string.Empty ? GetRootUdi(uri.Host) : new StringUdi(uri.Host, Uri.UnescapeDataString(path)); return true; } if (tryParse) return false; diff --git a/src/Umbraco.Tests/UdiTests.cs b/src/Umbraco.Tests/UdiTests.cs index 9b803d5fa3..6e7e4671a9 100644 --- a/src/Umbraco.Tests/UdiTests.cs +++ b/src/Umbraco.Tests/UdiTests.cs @@ -31,6 +31,34 @@ namespace Umbraco.Tests Assert.AreEqual("umb://" + Constants.UdiEntityType.AnyString + "/test-id", udi.ToString()); } + [Test] + public void StringEncodingTest() + { + // absolute path is unescaped + var uri = new Uri("umb://" + Constants.UdiEntityType.AnyString + "/this%20is%20a%20test"); + Assert.AreEqual("umb://" + Constants.UdiEntityType.AnyString + "/this is a test", uri.ToString()); + Assert.AreEqual("umb://" + Constants.UdiEntityType.AnyString + "/this%20is%20a%20test", uri.AbsoluteUri); + Assert.AreEqual("/this%20is%20a%20test", uri.AbsolutePath); + + Assert.AreEqual("/this is a test", Uri.UnescapeDataString(uri.AbsolutePath)); + Assert.AreEqual("%2Fthis%20is%20a%20test", Uri.EscapeDataString("/this is a test")); + Assert.AreEqual("/this%20is%20a%20test", Uri.EscapeUriString("/this is a test")); + + var udi = Udi.Parse("umb://" + Constants.UdiEntityType.AnyString + "/this%20is%20a%20test"); + Assert.AreEqual(Constants.UdiEntityType.AnyString, udi.EntityType); + Assert.IsInstanceOf(udi); + var stringEntityId = udi as StringUdi; + Assert.IsNotNull(stringEntityId); + Assert.AreEqual("this is a test", stringEntityId.Id); + Assert.AreEqual("umb://" + Constants.UdiEntityType.AnyString + "/this%20is%20a%20test", udi.ToString()); + + var udi2 = new StringUdi(Constants.UdiEntityType.AnyString, "this is a test"); + Assert.AreEqual(udi, udi2); + + var udi3 = new StringUdi(Constants.UdiEntityType.AnyString, "path to/this is a test.xyz"); + Assert.AreEqual("umb://" + Constants.UdiEntityType.AnyString + "/path%20to/this%20is%20a%20test.xyz", udi3.ToString()); + } + [Test] public void GuidEntityCtorTest() { From c5c87ff50df0f638a475042c35072f2163f6e96a Mon Sep 17 00:00:00 2001 From: Dave Woestenborghs Date: Fri, 5 May 2017 13:27:32 +0200 Subject: [PATCH 038/106] Return empty string when e-mail is not filled Fix for http://issues.umbraco.org/issue/U4-9862 --- .../ValueConverters/EmailAddressValueConverter.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs index 7a2a06cf67..15a7a816bd 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs @@ -20,6 +20,11 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) { + if(source == null) + { + return string.Empty; + } + return source.ToString(); } From fcf5ecc16680849c859a25cd429c669345064f0c Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 5 May 2017 14:14:37 +0200 Subject: [PATCH 039/106] Uses vswhere to figure out where VS17 is installed (if needed) Removes targets file that was almost unused, moved the relevant bits to the main csproj Updates BuildBelle.bat to be completely independent of installed version of node & npm on the machine --- .gitignore | 2 + build/Build.bat | 44 ++++++++++- build/BuildBelle.bat | 69 +++++++++++------ src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 21 +++++- src/umbraco.presentation.targets | 96 ------------------------ 5 files changed, 111 insertions(+), 121 deletions(-) delete mode 100644 src/umbraco.presentation.targets diff --git a/.gitignore b/.gitignore index 16994e0ab1..fb8092468e 100644 --- a/.gitignore +++ b/.gitignore @@ -142,3 +142,5 @@ build/ui-docs.zip build/csharp-docs.zip build/msbuild.log .vs/ + +build/tools/ diff --git a/build/Build.bat b/build/Build.bat index fc0d8a69ea..660cfa34be 100644 --- a/build/Build.bat +++ b/build/Build.bat @@ -49,10 +49,52 @@ REM This is necessary because SETLOCAL is on in InstallGit.cmd so that one might REM but the path setting is lost due to SETLOCAL path=C:\Program Files (x86)\Git\cmd;C:\Program Files\Git\cmd;%PATH% +SET toolsFolder=%CD%\tools\ +IF NOT EXIST %toolsFolder% ( + MD tools +) + +SET nuGetExecutable=%CD%\tools\nuget.exe +IF NOT EXIST %nuGetExecutable% ( + ECHO Getting NuGet so we can fetch some tools + ECHO Downloading https://dist.nuget.org/win-x86-commandline/latest/nuget.exe to %nuGetExecutable% + powershell -Command "(New-Object Net.WebClient).DownloadFile('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', '%nuGetExecutable%')" +) + +:: We need 7za.exe for BuildBelle.bat +IF NOT EXIST %toolsFolder%7za.exe ( + ECHO 7zip not found - fetching now + %nuGetExecutable% install 7-Zip.CommandLine -OutputDirectory %toolsFolder% -Verbosity quiet +) + +:: We need 7za.exe for VS2017+ +IF NOT EXIST %toolsFolder%vswhere.exe ( + ECHO vswhere not found - fetching now + %nuGetExecutable% install vswhere -OutputDirectory %toolsFolder% -Verbosity quiet +) + +:: Put 7za.exe and vswhere.exe in a predictable path (not version specific) +FOR /f "delims=" %%A in ('dir %toolsFolder%7-Zip.CommandLine.* /b') DO SET "sevenZipExePath=%toolsFolder%%%A\" +MOVE %sevenZipExePath%tools\7za.exe %toolsFolder%7za.exe + +FOR /f "delims=" %%A in ('dir %toolsFolder%vswhere.* /b') DO SET "vswhereExePath=%toolsFolder%%%A\" +MOVE %vswhereExePath%tools\vswhere.exe %toolsFolder%vswhere.exe + ECHO. ECHO Making sure we have a web.config IF NOT EXIST %CD%\..\src\Umbraco.Web.UI\web.config COPY %CD%\..\src\Umbraco.Web.UI\web.Template.config %CD%\..\src\Umbraco.Web.UI\web.config +for /f "usebackq tokens=1* delims=: " %%i in (`%CD%\tools\vswhere.exe -latest -requires Microsoft.Component.MSBuild`) do ( + if /i "%%i"=="installationPath" set InstallDir=%%j +) + +SET VSWherePath="%InstallDir%\MSBuild" +ECHO. +ECHO Visual Studio is installed in: %InstallDir% + +SET MSBUILDPATH=C:\Program Files (x86)\MSBuild\14.0\Bin +SET MSBUILD="%MSBUILDPATH%\MsBuild.exe" + ECHO. ECHO. ECHO Performing MSBuild and producing Umbraco binaries zip files @@ -60,7 +102,7 @@ ECHO This takes a few minutes and logging is set to report warnings ECHO and errors only so it might seems like nothing is happening for a while. ECHO You can check the msbuild.log file for progress. ECHO. -%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment% /p:NugetPackagesDirectory=%nuGetFolder% /consoleloggerparameters:Summary;ErrorsOnly;WarningsOnly /fileLogger +%MSBUILD% "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment% /p:NugetPackagesDirectory=%nuGetFolder% /p:VSWherePath=%VSWherePath% IF ERRORLEVEL 1 GOTO :error ECHO. diff --git a/build/BuildBelle.bat b/build/BuildBelle.bat index 8a07ee380a..ae2d5ba8b5 100644 --- a/build/BuildBelle.bat +++ b/build/BuildBelle.bat @@ -1,33 +1,58 @@ @ECHO OFF SETLOCAL + :: SETLOCAL is on, so changes to the path not persist to the actual user's path -SET release=%1 -ECHO Installing Npm NuGet Package - -SET nuGetFolder=%CD%\..\src\packages\ -ECHO Configured packages folder: %nuGetFolder% +SET toolsFolder=%CD%\tools\ ECHO Current folder: %CD% -%CD%\..\src\.nuget\NuGet.exe install Npm.js -OutputDirectory %nuGetFolder% -Verbosity quiet +SET nodeFileName=node-v6.9.1-win-x86.7z +SET nodeExtractFolder=%toolsFolder%node.js.691 -for /f "delims=" %%A in ('dir %nuGetFolder%node.js.* /b') do set "nodePath=%nuGetFolder%%%A\" -for /f "delims=" %%A in ('dir %nuGetFolder%npm.js.* /b') do set "npmPath=%nuGetFolder%%%A\tools\" +IF NOT EXIST %nodeExtractFolder% ( + ECHO Downloading http://nodejs.org/dist/v6.9.1/%nodeFileName% to %toolsFolder%%nodeFileName% + powershell -Command "(New-Object Net.WebClient).DownloadFile('http://nodejs.org/dist/v6.9.1/%nodeFileName%', '%toolsFolder%%nodeFileName%')" + ECHO Extracting %nodeFileName% to %nodeExtractFolder% + %toolsFolder%\7za.exe x %toolsFolder%\%nodeFileName% -o%nodeExtractFolder% -aos > nul +) +FOR /f "delims=" %%A in ('dir %nodeExtractFolder%\node* /b') DO SET "nodePath=%nodeExtractFolder%\%%A" -ECHO Adding Npm and Node to path -REM SETLOCAL is on, so changes to the path not persist to the actual user's path -PATH=%npmPath%;%nodePath%;%PATH% +SET nuGetExecutable=%CD%\tools\nuget.exe +IF NOT EXIST %nuGetExecutable% ( + ECHO Downloading https://dist.nuget.org/win-x86-commandline/latest/nuget.exe to %nuGetExecutable% + powershell -Command "(New-Object Net.WebClient).DownloadFile('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', '%nuGetExecutable%')" +) -SET buildFolder=%CD% +SET drive=%CD:~0,2% +SET nuGetFolder=%drive%\packages\ +FOR /f "delims=" %%A in ('dir %nuGetFolder%npm.* /b') DO SET "npmPath=%nuGetFolder%%%A\" +IF [%npmPath%] == [] GOTO :installnpm +IF NOT [%npmPath%] == [] GOTO :build -ECHO Change directory to %CD%\..\src\Umbraco.Web.UI.Client\ -CD %CD%\..\src\Umbraco.Web.UI.Client\ +:installnpm + ECHO Downloading npm + ECHO Configured packages folder: %nuGetFolder% + ECHO Installing Npm NuGet Package + %nuGetExecutable% install Npm -OutputDirectory %nuGetFolder% -Verbosity detailed + REM Ensures that we look for the just downloaded NPM, not whatever the user has installed on their machine + FOR /f "delims=" %%A in ('dir %nuGetFolder%npm.* /b') DO SET "npmPath=%nuGetFolder%%%A\" + GOTO :build -ECHO Do npm install and the grunt build of Belle -call npm cache clean --quiet -call npm install --quiet -call npm install -g grunt-cli --quiet -call npm install -g bower --quiet -call grunt build --buildversion=%release% +:build + ECHO Adding Npm and Node to path + REM SETLOCAL is on, so changes to the path not persist to the actual user's path + PATH=%npmPath%;%nodePath%;%PATH% -ECHO Move back to the build folder -CD %buildFolder% \ No newline at end of file + SET buildFolder=%CD% + + ECHO Change directory to %CD%\..\src\Umbraco.Web.UI.Client\ + CD %CD%\..\src\Umbraco.Web.UI.Client\ + + ECHO Do npm install and the grunt build of Belle + call npm cache clean --quiet + call npm install --quiet + call npm install -g grunt-cli --quiet + call npm install -g bower --quiet + call grunt build --buildversion=%release% + + ECHO Move back to the build folder + CD %buildFolder% \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 54c8c2c59c..512cab02b1 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -2435,14 +2435,31 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" - - + + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll + + + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v11.0\Web\Microsoft.Web.Publishing.Tasks.dll + + + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.Publishing.Tasks.dll + + + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.Tasks.dll + + + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v15.0\Web\Microsoft.Web.Publishing.Tasks.dll + + + + + diff --git a/src/umbraco.presentation.targets b/src/umbraco.presentation.targets deleted file mode 100644 index 2a33705d6f..0000000000 --- a/src/umbraco.presentation.targets +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - $(MSBuildStartupDirectory)\..\src\ - - - - - $(SolutionDir) - $(Computername). - - - - $(ProjectDir) - $(WebProjectOutputDir) - $(ProjDir)web.Template.$(ConfigEnvironment)$(Configuration).config - - - - $(ProjDir)web.Template.$(Configuration).config - - - - $(ProjDir) - - - - $(ProjOutputDir)\ - - - - - - - - - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll - - - - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v11.0\Web\Microsoft.Web.Publishing.Tasks.dll - - - - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.Publishing.Tasks.dll - - - - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.Tasks.dll - - - - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v15.0\Web\Microsoft.Web.Publishing.Tasks.dll - - - - - - - - - - - - @(ConfigFiles) - $(OriginalFileName.Replace("%(ConfigFiles.Extension)",".$(Configuration)%(ConfigFiles.Extension)")) - $(OriginalFileName.Replace("$(ProjDir)", "$(ProjOutputDir)")) - - - - - - - - - - \ No newline at end of file From 3c4b3f44c7b0cb9790a7f07839afbbb4c6a61b3f Mon Sep 17 00:00:00 2001 From: Claus Date: Fri, 5 May 2017 14:52:01 +0200 Subject: [PATCH 040/106] U4-9864 Missing import and xss check in other PermissionEditor.aspx --- .../umbraco.presentation/umbraco/users/PermissionEditor.aspx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/PermissionEditor.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/users/PermissionEditor.aspx index 815a420f88..75b5d6e66a 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/PermissionEditor.aspx +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/PermissionEditor.aspx @@ -1,5 +1,5 @@ <%@ Page Language="C#" AutoEventWireup="true" MasterPageFile="../masterpages/umbracoPage.Master" CodeBehind="PermissionEditor.aspx.cs" Inherits="umbraco.cms.presentation.user.PermissionEditor" %> - +<%@ Import Namespace="Umbraco.Web" %> <%@ Register Src="../controls/Tree/TreeControl.ascx" TagName="TreeControl" TagPrefix="umbraco" %> <%@ Register Src="NodePermissions.ascx" TagName="NodePermissions" TagPrefix="user" %> <%@ Register TagPrefix="ui" Namespace="umbraco.uicontrols" Assembly="controls" %> @@ -27,7 +27,7 @@