From 6b19ab3b468c295132f6e428e832fe8bdbf6a5dc Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Sat, 27 Oct 2018 22:04:47 +0100 Subject: [PATCH 01/65] Prototyped edit view --- .../src/views/relationtypes/edit.html | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html new file mode 100644 index 0000000000..55c5ec2d88 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html @@ -0,0 +1,104 @@ +
+ + +
+ + + + + + + + + +
1234
+ 0123-4567-8910-abcd +
+ + + +
    +
  • + +
  • +
  • + +
  • +
+
+ + + +
Document
+
+ + + +
Document
+
+ + + +
12
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
ParentChildCreatedComment
SomethingSomething else2018-10-27 18:58A comment here
SomethingSomething else2018-10-27 18:58A comment here
SomethingSomething else2018-10-27 18:58A comment here
+
+
+
+
+
+ + + + + + + +
+
+
From 083de953113482ace9f9c8d9b386b593ba71f120 Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Sat, 27 Oct 2018 23:37:08 +0100 Subject: [PATCH 02/65] Stubbed out relation type controller and resource --- .../common/resources/relationtype.resource.js | 40 ++++++++++++++ .../Editors/RelationTypeController.cs | 52 +++++++++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 3 files changed, 93 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js create mode 100644 src/Umbraco.Web/Editors/RelationTypeController.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js new file mode 100644 index 0000000000..46df8e2608 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js @@ -0,0 +1,40 @@ +/** + * @ngdoc service + * @name umbraco.resources.relationTypeResource + * @description Loads in data for relation types. + */ +function relationTypeResource($q, $http, umbRequestHelper) { + return { + + /** + * @ngdoc method + * @name umbraco.resources.relationTypeResource#getById + * @methodOf umbraco.resources.relationTypeResource + * @param {Int} id of the dictionary item to get. + * @returns {Promise} resourcePromise containing relation type data. + */ + getById: function (id) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "relationTypeBaseUrl", + "GetById", + [{ id: id }] + ) + ), + "Failed to get item " + id + ); + }, + + save: function () { + + }, + + deleteById: function (id) { + + } + + }; +} + +angular.module("umbraco.resources").factory("relationTypeResource", relationTypeResource); diff --git a/src/Umbraco.Web/Editors/RelationTypeController.cs b/src/Umbraco.Web/Editors/RelationTypeController.cs new file mode 100644 index 0000000000..cfd6c2e56f --- /dev/null +++ b/src/Umbraco.Web/Editors/RelationTypeController.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http; +using AutoMapper; +using Umbraco.Core.Models; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi.Filters; +using Constants = Umbraco.Core.Constants; +using RelationType = Umbraco.Web.Models.ContentEditing.RelationType; + +namespace Umbraco.Web.Editors +{ + /// + /// The API controller for editing relation types. + /// + [PluginController("UmbracoApi")] + [UmbracoTreeAuthorize(Constants.Trees.RelationTypes)] + [EnableOverrideAuthorization] + public class RelationTypeController : BackOfficeNotificationsController + { + /// + /// Gets a relation type by ID. + /// + /// The relation type ID. + /// Returns the . + public RelationType GetById(int id) + { + var relationType = Services.RelationService.GetRelationTypeById(id); + + if (relationType == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + return Mapper.Map(relationType); + } + + public void PostSave() + { + throw new NotImplementedException(); + } + + public void DeleteById() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 656bbd29c5..e3bdd95eb4 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -111,6 +111,7 @@ + From 06d19f2735a455bdb8fbe2dedccc81f03792feb8 Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Sun, 28 Oct 2018 19:04:00 +0000 Subject: [PATCH 03/65] Load relation type edit view --- .../common/resources/relationtype.resource.js | 2 +- .../views/relationtypes/edit.controller.js | 30 +++++++++++++ .../src/views/relationtypes/edit.html | 28 ++++++------ .../Editors/BackOfficeServerVariables.cs | 4 ++ .../Editors/RelationTypeController.cs | 8 ++-- .../Models/ContentEditing/RelationType.cs | 1 + .../ContentEditing/RelationTypeDisplay.cs | 44 +++++++++++++++++++ .../Models/Mapping/RelationMapperProfile.cs | 25 +++++++++++ .../Trees/RelationTypeTreeController.cs | 5 +-- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 10 files changed, 124 insertions(+), 24 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js create mode 100644 src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js index 46df8e2608..81fdbdb838 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js @@ -17,7 +17,7 @@ function relationTypeResource($q, $http, umbRequestHelper) { return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( - "relationTypeBaseUrl", + "relationTypeApiBaseUrl", "GetById", [{ id: id }] ) diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js new file mode 100644 index 0000000000..37eb1ea6d9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js @@ -0,0 +1,30 @@ +function RelationTypeEditController($scope, $routeParams, relationTypeResource, editorState, navigationService) { + + var vm = this; + + vm.page = {}; + vm.page.loading = false; + vm.page.saveButtonState = "init"; + vm.page.menu = {} + + if($routeParams.create) { + alert("create"); + } else { + vm.page.loading = true; + + relationTypeResource.getById($routeParams.id) + .then(function(data) { + vm.relation = data; + + editorState.set(vm.relation); + + navigationService.syncTree({ tree: "relationTypes", path: data.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + + vm.page.loading = false; + }); + } +} + +angular.module("umbraco").controller("Umbraco.Editors.RelationTypes.EditController", RelationTypeEditController); diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html index 55c5ec2d88..3f88c914f4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html @@ -1,15 +1,13 @@ -
- +
+
- + + name="vm.relation.name" + alias="vm.relation.alias" + hide-description="true" + hide-icon="true"> @@ -17,8 +15,8 @@ -
1234
- 0123-4567-8910-abcd +
{{vm.relation.id}}
+ {{vm.relation.key}}
@@ -26,12 +24,12 @@
@@ -39,12 +37,12 @@ -
Document
+
{{vm.relation.parentObjectTypeName}}
-
Document
+
{{vm.relation.childObjectTypeName}}
diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 98114f0664..a05ac5338e 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -299,6 +299,10 @@ namespace Umbraco.Web.Editors { "languageApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( controller => controller.GetAllLanguages()) + }, + { + "relationTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + controller => controller.GetById(1)) } } }, diff --git a/src/Umbraco.Web/Editors/RelationTypeController.cs b/src/Umbraco.Web/Editors/RelationTypeController.cs index cfd6c2e56f..2b9242ef31 100644 --- a/src/Umbraco.Web/Editors/RelationTypeController.cs +++ b/src/Umbraco.Web/Editors/RelationTypeController.cs @@ -7,10 +7,10 @@ using System.Threading.Tasks; using System.Web.Http; using AutoMapper; using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; -using RelationType = Umbraco.Web.Models.ContentEditing.RelationType; namespace Umbraco.Web.Editors { @@ -26,8 +26,8 @@ namespace Umbraco.Web.Editors /// Gets a relation type by ID. /// /// The relation type ID. - /// Returns the . - public RelationType GetById(int id) + /// Returns the . + public RelationTypeDisplay GetById(int id) { var relationType = Services.RelationService.GetRelationTypeById(id); @@ -36,7 +36,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.NotFound); } - return Mapper.Map(relationType); + return Mapper.Map(relationType); } public void PostSave() diff --git a/src/Umbraco.Web/Models/ContentEditing/RelationType.cs b/src/Umbraco.Web/Models/ContentEditing/RelationType.cs index 129376da5c..f52ac507ac 100644 --- a/src/Umbraco.Web/Models/ContentEditing/RelationType.cs +++ b/src/Umbraco.Web/Models/ContentEditing/RelationType.cs @@ -3,6 +3,7 @@ using System.Runtime.Serialization; namespace Umbraco.Web.Models.ContentEditing { + [Obsolete("Use Umbraco.Web.Models.ContentEditing.RelationTypeDisplay instead")] [DataContract(Name = "relationType", Namespace = "")] public class RelationType { diff --git a/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs new file mode 100644 index 0000000000..3861e0938d --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs @@ -0,0 +1,44 @@ +using System; +using System.ComponentModel; +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "relationType", Namespace = "")] + public class RelationTypeDisplay : EntityBasic + { + /// + /// Gets or sets a boolean indicating whether the RelationType is Bidirectional (true) or Parent to Child (false) + /// + [DataMember(Name = "isBidirectional", IsRequired = true)] + public bool IsBidirectional { get; set; } + + /// + /// Gets or sets the Parents object type id + /// + /// Corresponds to the NodeObjectType in the umbracoNode table + [DataMember(Name = "parentObjectType", IsRequired = true)] + public Guid ParentObjectType { get; set; } + + /// + /// Gets or sets the Parent's object type name. + /// + [DataMember(Name = "parentObjectTypeName")] + [ReadOnly(true)] + public string ParentObjectTypeName { get; set; } + + /// + /// Gets or sets the Childs object type id + /// + /// Corresponds to the NodeObjectType in the umbracoNode table + [DataMember(Name = "childObjectType", IsRequired = true)] + public Guid ChildObjectType { get; set; } + + /// + /// Gets or sets the Child's object type name. + /// + [DataMember(Name = "childObjectTypeName")] + [ReadOnly(true)] + public string ChildObjectTypeName { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs index 31acf4e5e1..95f4fab054 100644 --- a/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs @@ -1,5 +1,7 @@ using AutoMapper; +using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; using Relation = Umbraco.Web.Models.ContentEditing.Relation; using RelationType = Umbraco.Web.Models.ContentEditing.RelationType; @@ -9,6 +11,29 @@ namespace Umbraco.Web.Models.Mapping { public RelationMapperProfile() { + // FROM IRelationType to RelationTypeDisplay + CreateMap() + .ForMember(x => x.Icon, expression => expression.Ignore()) + .ForMember(x => x.Trashed, expression => expression.Ignore()) + .ForMember(x => x.Alias, expression => expression.Ignore()) + .ForMember(x => x.Path, expression => expression.Ignore()) + .ForMember(x => x.AdditionalData, expression => expression.Ignore()) + .ForMember(x => x.ChildObjectTypeName, expression => expression.Ignore()) + .ForMember(x => x.ParentObjectTypeName, expression => expression.Ignore()) + .ForMember( + x => x.Udi, + expression => expression.MapFrom( + content => Udi.Create(Constants.UdiEntityType.RelationType, content.Key))) + .AfterMap((src, dest) => + { + // Build up the path + dest.Path = "-1," + src.Id; + + // Set the "friendly" names for the parent and child object types + dest.ParentObjectTypeName = ObjectTypes.GetUmbracoObjectType(src.ParentObjectType).GetFriendlyName(); + dest.ChildObjectTypeName = ObjectTypes.GetUmbracoObjectType(src.ChildObjectType).GetFriendlyName(); + }); + //FROM IRelationType TO RelationType CreateMap(); diff --git a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs index 4930b4dc5e..884b6c5f54 100644 --- a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs @@ -65,10 +65,7 @@ namespace Umbraco.Web.Trees queryStrings, rt.Name, "icon-trafic", - false, - //TODO: Rebuild the macro editor in angular, then we dont need to have this at all (which is just a path to the legacy editor) - "/" + queryStrings.GetValue("application") + "/framed/" + - Uri.EscapeDataString("/umbraco/developer/RelationTypes/EditRelationType.aspx?id=" + rt.Id) + false ))); } return nodes; diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e3bdd95eb4..7e51974bc9 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -148,6 +148,7 @@ + From 78b7bb6a2299244b410b9036d79125366c5b9651 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 8 Nov 2018 15:33:14 +0000 Subject: [PATCH 04/65] Recreate benchmark project and update deps Fix #3561 --- .../Config/QuickRunConfigAttribute.cs | 13 +- .../CtorInvokeBenchmarks.cs | 2 +- src/Umbraco.Tests.Benchmarks/Program.cs | 7 +- .../Properties/AssemblyInfo.cs | 4 +- .../TryConvertToBenchmarks.cs | 8 +- .../Umbraco.Tests.Benchmarks.csproj | 110 ++----- src/Umbraco.Tests.Benchmarks/app.config | 300 +----------------- src/umbraco.sln | 14 +- 8 files changed, 52 insertions(+), 406 deletions(-) diff --git a/src/Umbraco.Tests.Benchmarks/Config/QuickRunConfigAttribute.cs b/src/Umbraco.Tests.Benchmarks/Config/QuickRunConfigAttribute.cs index f7d6b6bb72..52d670de3c 100644 --- a/src/Umbraco.Tests.Benchmarks/Config/QuickRunConfigAttribute.cs +++ b/src/Umbraco.Tests.Benchmarks/Config/QuickRunConfigAttribute.cs @@ -1,13 +1,14 @@ -using BenchmarkDotNet.Configs; +using System; +using BenchmarkDotNet.Configs; using BenchmarkDotNet.Horology; using BenchmarkDotNet.Jobs; -using System; namespace Umbraco.Tests.Benchmarks.Config { /// /// Configures the benchmark to run with less warmup and a shorter iteration time than the standard benchmark. /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class QuickRunConfigAttribute : Attribute, IConfigSource { /// @@ -15,11 +16,11 @@ namespace Umbraco.Tests.Benchmarks.Config /// public QuickRunConfigAttribute() { - Config = (ManualConfig) ManualConfig.CreateEmpty() + this.Config = (ManualConfig)ManualConfig.CreateEmpty() .With(Job.Default.WithLaunchCount(1) // benchmark process will be launched only once .WithIterationTime(new TimeInterval(100, TimeUnit.Millisecond)) // 100ms per iteration .WithWarmupCount(3) // 3 warmup iteration - .WithTargetCount(3)); // 3 target iteration + .WithIterationCount(3)); // 3 target iteration } /// @@ -28,6 +29,6 @@ namespace Umbraco.Tests.Benchmarks.Config protected ManualConfig Config { get; } /// - IConfig IConfigSource.Config => Config; + IConfig IConfigSource.Config => this.Config; } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs index 1d5876187b..5588e13d12 100644 --- a/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Benchmarks .WithLaunchCount(1) // benchmark process will be launched only once .WithIterationTime(TimeInterval.FromMilliseconds(400)) .WithWarmupCount(3) - .WithTargetCount(6)); + .WithIterationCount(6)); } } diff --git a/src/Umbraco.Tests.Benchmarks/Program.cs b/src/Umbraco.Tests.Benchmarks/Program.cs index c9332e7fa3..62137bd85d 100644 --- a/src/Umbraco.Tests.Benchmarks/Program.cs +++ b/src/Umbraco.Tests.Benchmarks/Program.cs @@ -2,11 +2,8 @@ namespace Umbraco.Tests.Benchmarks { - internal class Program + internal static class Program { - public static void Main(string[] args) - { - new BenchmarkSwitcher(typeof(Program).Assembly).Run(args); - } + private static void Main(string[] args) => new BenchmarkSwitcher(typeof(Program).Assembly).Run(args); } } diff --git a/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs b/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs index 2ab0051c26..9f5a3c7453 100644 --- a/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Umbraco.Tests.Benchmarks")] -[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyCopyright("Copyright © 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -20,7 +20,7 @@ using System.Runtime.InteropServices; [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("86deb346-089f-4106-89c8-d852b9cf2a33")] +[assembly: Guid("3a33adc9-c6c0-4db1-a613-a9af0210df3d")] // Version information for an assembly consists of the following four values: // diff --git a/src/Umbraco.Tests.Benchmarks/TryConvertToBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TryConvertToBenchmarks.cs index 57b47dc1d0..7e73c5e438 100644 --- a/src/Umbraco.Tests.Benchmarks/TryConvertToBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/TryConvertToBenchmarks.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; using Umbraco.Core; namespace Umbraco.Tests.Benchmarks @@ -12,9 +14,9 @@ namespace Umbraco.Tests.Benchmarks private static readonly string Date = "Saturday 10, November 2012"; [Benchmark(Description = "List to IEnumerable")] - public IEnumerable TryConvertToEnumerable() + public IList TryConvertToEnumerable() { - return List.TryConvertTo>().Result; + return List.TryConvertTo>().Result.ToList(); } [Benchmark(Description = "Int to Double")] @@ -41,4 +43,4 @@ namespace Umbraco.Tests.Benchmarks return Date.TryConvertTo().Result; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 9755e4f9db..99bb768842 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -1,20 +1,17 @@  - + Debug AnyCPU - {86DEB346-089F-4106-89C8-D852B9CF2A33} + {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D} Exe - Properties Umbraco.Tests.Benchmarks Umbraco.Tests.Benchmarks v4.7.2 512 - - - - false + true + true AnyCPU @@ -25,7 +22,6 @@ DEBUG;TRACE prompt 4 - latest AnyCPU @@ -35,92 +31,27 @@ TRACE prompt 4 - latest - - - Always + false + 7.3 - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - @@ -130,27 +61,34 @@ - + {31785bc3-256c-4613-b2f5-a1b0bdded8c1} Umbraco.Core + + {07fbc26b-2927-4a22-8d96-d644c667fecc} + Umbraco.Examine + {5d3b8245-ada6-453f-a008-50ed04bfe770} Umbraco.Tests + + {4c4c194c-b5e4-4991-8f87-4373e24cc19f} + Umbraco.Web.UI + {651e1350-91b6-44b7-bd60-7207006d7003} Umbraco.Web - - - $(NuGetPackageFolders.Split(';')[0]) - + + + 0.11.2 + + - - - \ No newline at end of file + diff --git a/src/Umbraco.Tests.Benchmarks/app.config b/src/Umbraco.Tests.Benchmarks/app.config index b5e577b22c..56efbc7b5f 100644 --- a/src/Umbraco.Tests.Benchmarks/app.config +++ b/src/Umbraco.Tests.Benchmarks/app.config @@ -1,298 +1,6 @@ - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/src/umbraco.sln b/src/umbraco.sln index ceb687b876..41bd8359c3 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -82,8 +82,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{5B03EF4E ..\build\NuSpecs\build\UmbracoCms.targets = ..\build\NuSpecs\build\UmbracoCms.targets EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.Benchmarks", "Umbraco.Tests.Benchmarks\Umbraco.Tests.Benchmarks.csproj", "{86DEB346-089F-4106-89C8-D852B9CF2A33}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DocTools", "DocTools", "{53594E5B-64A2-4545-8367-E3627D266AE8}" ProjectSection(SolutionItems) = preProject ApiDocs\docfx.filter.yml = ApiDocs\docfx.filter.yml @@ -92,6 +90,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DocTools", "DocTools", "{53 ApiDocs\toc.yml = ApiDocs\toc.yml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.Benchmarks", "Umbraco.Tests.Benchmarks\Umbraco.Tests.Benchmarks.csproj", "{3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -120,10 +120,10 @@ Global {07FBC26B-2927-4A22-8D96-D644C667FECC}.Debug|Any CPU.Build.0 = Debug|Any CPU {07FBC26B-2927-4A22-8D96-D644C667FECC}.Release|Any CPU.ActiveCfg = Release|Any CPU {07FBC26B-2927-4A22-8D96-D644C667FECC}.Release|Any CPU.Build.0 = Release|Any CPU - {86DEB346-089F-4106-89C8-D852B9CF2A33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {86DEB346-089F-4106-89C8-D852B9CF2A33}.Debug|Any CPU.Build.0 = Debug|Any CPU - {86DEB346-089F-4106-89C8-D852B9CF2A33}.Release|Any CPU.ActiveCfg = Release|Any CPU - {86DEB346-089F-4106-89C8-D852B9CF2A33}.Release|Any CPU.Build.0 = Release|Any CPU + {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -133,8 +133,8 @@ Global {5D3B8245-ADA6-453F-A008-50ED04BFE770} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {E3F9F378-AFE1-40A5-90BD-82833375DBFE} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D} {5B03EF4E-E0AC-4905-861B-8C3EC1A0D458} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D} - {86DEB346-089F-4106-89C8-D852B9CF2A33} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {53594E5B-64A2-4545-8367-E3627D266AE8} = {FD962632-184C-4005-A5F3-E705D92FC645} + {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7A0F2E34-D2AF-4DAB-86A0-7D7764B3D0EC} From a8fc62cf4242a7bc4f6ae4d7cf2768a6aec30ed2 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 12 Nov 2018 16:22:33 +0000 Subject: [PATCH 05/65] Optimize Guid.Combine and HexString generation. --- src/Umbraco.Core/ByteArrayExtensions.cs | 39 --------- src/Umbraco.Core/GuidUtils.cs | 44 ++++++++++ src/Umbraco.Core/HexEncoder.cs | 84 +++++++++++++++++++ .../CombinedGuidsMediaPathScheme.cs | 17 +--- src/Umbraco.Core/Umbraco.Core.csproj | 3 +- .../CombineGuidBenchmarks.cs | 48 +++++++++++ .../HexStringBenchmarks.cs | 69 +++++++++++++++ .../Umbraco.Tests.Benchmarks.csproj | 4 +- .../CoreThings/GuidUtilsTests.cs | 32 +++++++ .../CoreThings/HexEncoderTests.cs | 71 ++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 2 + 11 files changed, 357 insertions(+), 56 deletions(-) delete mode 100644 src/Umbraco.Core/ByteArrayExtensions.cs create mode 100644 src/Umbraco.Core/GuidUtils.cs create mode 100644 src/Umbraco.Core/HexEncoder.cs create mode 100644 src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs create mode 100644 src/Umbraco.Tests.Benchmarks/HexStringBenchmarks.cs create mode 100644 src/Umbraco.Tests/CoreThings/GuidUtilsTests.cs create mode 100644 src/Umbraco.Tests/CoreThings/HexEncoderTests.cs diff --git a/src/Umbraco.Core/ByteArrayExtensions.cs b/src/Umbraco.Core/ByteArrayExtensions.cs deleted file mode 100644 index dacdd509ca..0000000000 --- a/src/Umbraco.Core/ByteArrayExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Umbraco.Core -{ - public static class ByteArrayExtensions - { - private static readonly char[] BytesToHexStringLookup = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - - public static string ToHexString(this byte[] bytes) - { - int i = 0, p = 0, bytesLength = bytes.Length; - var chars = new char[bytesLength * 2]; - while (i < bytesLength) - { - var b = bytes[i++]; - chars[p++] = BytesToHexStringLookup[b / 0x10]; - chars[p++] = BytesToHexStringLookup[b % 0x10]; - } - return new string(chars, 0, chars.Length); - } - - public static string ToHexString(this byte[] bytes, char separator, int blockSize, int blockCount) - { - int p = 0, bytesLength = bytes.Length, count = 0, size = 0; - var chars = new char[bytesLength * 2 + blockCount]; - for (var i = 0; i < bytesLength; i++) - { - var b = bytes[i++]; - chars[p++] = BytesToHexStringLookup[b / 0x10]; - chars[p++] = BytesToHexStringLookup[b % 0x10]; - if (count == blockCount) continue; - if (++size < blockSize) continue; - - chars[p++] = '/'; - size = 0; - count++; - } - return new string(chars, 0, chars.Length); - } - } -} diff --git a/src/Umbraco.Core/GuidUtils.cs b/src/Umbraco.Core/GuidUtils.cs new file mode 100644 index 0000000000..3768e1385d --- /dev/null +++ b/src/Umbraco.Core/GuidUtils.cs @@ -0,0 +1,44 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Umbraco.Core +{ + /// + /// Utility methods for the struct. + /// + internal static class GuidUtils + { + /// + /// Combines two guid instances utilizing an exclusive disjunction. + /// The resultant guid is not guaranteed to be unique since the number of unique bits is halved. + /// + /// The first guid. + /// The seconds guid. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Guid Combine(Guid a, Guid b) + { + var ad = new DecomposedGuid(a); + var bd = new DecomposedGuid(b); + + ad.Hi ^= bd.Hi; + ad.Lo ^= bd.Lo; + + return ad.Value; + } + + /// + /// A decomposed guid. Allows access to the high and low bits without unsafe code. + /// + [StructLayout(LayoutKind.Explicit)] + private struct DecomposedGuid + { + [FieldOffset(00)] public Guid Value; + [FieldOffset(00)] public long Hi; + [FieldOffset(08)] public long Lo; + + public DecomposedGuid(Guid value) : this() => this.Value = value; + } + } +} diff --git a/src/Umbraco.Core/HexEncoder.cs b/src/Umbraco.Core/HexEncoder.cs new file mode 100644 index 0000000000..073dc8b543 --- /dev/null +++ b/src/Umbraco.Core/HexEncoder.cs @@ -0,0 +1,84 @@ +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Umbraco.Core +{ + /// + /// Provides methods for encoding byte arrays into hexidecimal strings. + /// + internal static class HexEncoder + { + // LUT's that provide the hexidecimal representation of each possible byte value. + private static readonly char[] HexLutBase = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + // The base LUT arranged in 16x each item order. 0 * 16, 1 * 16, .... F * 16 + private static readonly char[] HexLutHi = Enumerable.Range(0, 256).Select(x => HexLutBase[x / 0x10]).ToArray(); + + // The base LUT repeated 16x. + private static readonly char[] HexLutLo = Enumerable.Range(0, 256).Select(x => HexLutBase[x % 0x10]).ToArray(); + + /// + /// Converts a to a hexidecimal formatted padded to 2 digits. + /// + /// The bytes. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Encode(byte[] bytes) + { + var length = bytes.Length; + var chars = new char[length * 2]; + + var index = 0; + for (var i = 0; i < length; i++) + { + var byteIndex = bytes[i]; + chars[index++] = HexLutHi[byteIndex]; + chars[index++] = HexLutLo[byteIndex]; + } + + return new string(chars, 0, chars.Length); + } + + /// + /// Converts a to a hexidecimal formatted padded to 2 digits + /// and split into blocks with the given char separator. + /// + /// The bytes. + /// The separator. + /// The block size. + /// The block count. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Encode(byte[] bytes, char separator, int blockSize, int blockCount) + { + var length = bytes.Length; + var chars = new char[(length * 2) + blockCount]; + var count = 0; + var size = 0; + var index = 0; + + for (var i = 0; i < length; i++) + { + var byteIndex = bytes[i]; + chars[index++] = HexLutHi[byteIndex]; + chars[index++] = HexLutLo[byteIndex]; + + if (count == blockCount) + { + continue; + } + + if (++size < blockSize) + { + continue; + } + + chars[index++] = separator; + size = 0; + count++; + } + + return new string(chars, 0, chars.Length); + } + } +} diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs index ef71aff3bc..37c6a7b209 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs @@ -20,24 +20,11 @@ namespace Umbraco.Core.IO.MediaPathSchemes { // assumes that cuid and puid keys can be trusted - and that a single property type // for a single content cannot store two different files with the same name - var directory = Combine(itemGuid, propertyGuid).ToHexString(/*'/', 2, 4*/); // could use ext to fragment path eg 12/e4/f2/... + var directory = HexEncoder.Encode(GuidUtils.Combine(itemGuid, propertyGuid).ToByteArray()/*'/', 2, 4*/); // could use ext to fragment path eg 12/e4/f2/... return Path.Combine(directory, filename).Replace('\\', '/'); } /// - public string GetDeleteDirectory(string filepath) - { - return Path.GetDirectoryName(filepath); - } - - private static byte[] Combine(Guid guid1, Guid guid2) - { - var bytes1 = guid1.ToByteArray(); - var bytes2 = guid2.ToByteArray(); - var bytes = new byte[bytes1.Length]; - for (var i = 0; i < bytes1.Length; i++) - bytes[i] = (byte) (bytes1[i] ^ bytes2[i]); - return bytes; - } + public string GetDeleteDirectory(string filepath) => Path.GetDirectoryName(filepath); } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index ac115e843b..49fa197850 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -111,7 +111,6 @@ - @@ -320,6 +319,8 @@ + + diff --git a/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs new file mode 100644 index 0000000000..ce55f6890d --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs @@ -0,0 +1,48 @@ +using System; +using BenchmarkDotNet.Attributes; +using Umbraco.Core; +using Umbraco.Tests.Benchmarks.Config; + +namespace Umbraco.Tests.Benchmarks +{ + [QuickRunWithMemoryDiagnoserConfig] + public class CombineGuidBenchmarks + { + private static readonly Guid _a = Guid.NewGuid(); + private static readonly Guid _b = Guid.NewGuid(); + + [Benchmark] + public byte[] CombineUtils() => GuidUtils.Combine(_a, _b).ToByteArray(); + + [Benchmark] + public byte[] CombineLoop() => Combine(_a, _b); + + private static byte[] Combine(Guid guid1, Guid guid2) + { + var bytes1 = guid1.ToByteArray(); + var bytes2 = guid2.ToByteArray(); + var bytes = new byte[bytes1.Length]; + for (var i = 0; i < bytes1.Length; i++) + { + bytes[i] = (byte)(bytes1[i] ^ bytes2[i]); + } + + return bytes; + } + } + + // Nov 8 2018 + //BenchmarkDotNet=v0.11.2, OS=Windows 10.0.17763.55 (1809/October2018Update/Redstone5) + //Intel Core i7-6600U CPU 2.60GHz(Skylake), 1 CPU, 4 logical and 2 physical cores + // [Host] : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3190.0 + // Job-JIATTD : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3190.0 + + //IterationCount=3 IterationTime=100.0000 ms LaunchCount = 1 + //WarmupCount=3 + + // Method | Mean | Error | StdDev | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | + //------------- |---------:|----------:|----------:|------------:|------------:|------------:|--------------------:| + // CombineUtils | 33.34 ns | 8.086 ns | 0.4432 ns | 0.0133 | - | - | 28 B | + // CombineLoop | 55.03 ns | 11.311 ns | 0.6200 ns | 0.0395 | - | - | 84 B | +} + diff --git a/src/Umbraco.Tests.Benchmarks/HexStringBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/HexStringBenchmarks.cs new file mode 100644 index 0000000000..e29a5a24f3 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/HexStringBenchmarks.cs @@ -0,0 +1,69 @@ +using System; +using System.Text; +using BenchmarkDotNet.Attributes; +using Umbraco.Core; +using Umbraco.Tests.Benchmarks.Config; + +namespace Umbraco.Tests.Benchmarks +{ + [QuickRunConfig] + public class HexStringBenchmarks + { + private byte[] _buffer; + + [Params(8, 16, 32, 64, 128, 256)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + this._buffer = new byte[this.Count]; + var random = new Random(); + random.NextBytes(this._buffer); + } + + [Benchmark(Baseline = true)] + public string ToHexStringBuilder() + { + var sb = new StringBuilder(this._buffer.Length * 2); + for (var i = 0; i < this._buffer.Length; i++) + { + sb.Append(this._buffer[i].ToString("X2")); + } + + return sb.ToString(); + } + + [Benchmark] + public string ToHexStringEncoder() => HexEncoder.Encode(this._buffer); + } + + // Nov 8 2018 + //BenchmarkDotNet=v0.11.2, OS=Windows 10.0.17763.55 (1809/October2018Update/Redstone5) + //Intel Core i7-6600U CPU 2.60GHz(Skylake), 1 CPU, 4 logical and 2 physical cores + // [Host] : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3190.0 + // Job-JIATTD : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3190.0 + + //IterationCount=3 IterationTime=100.0000 ms LaunchCount = 1 + //WarmupCount=3 + + // Method | Count | Mean | Error | StdDev | Ratio | + //------------------- |------ |-------------:|-------------:|-----------:|------:| + // ToHexStringBuilder | 8 | 786.49 ns | 319.92 ns | 17.536 ns | 1.00 | + // ToHexStringEncoder | 8 | 64.19 ns | 30.21 ns | 1.656 ns | 0.08 | + // | | | | | | + // ToHexStringBuilder | 16 | 1,442.43 ns | 503.00 ns | 27.571 ns | 1.00 | + // ToHexStringEncoder | 16 | 133.46 ns | 177.55 ns | 9.732 ns | 0.09 | + // | | | | | | + // ToHexStringBuilder | 32 | 2,869.23 ns | 924.35 ns | 50.667 ns | 1.00 | + // ToHexStringEncoder | 32 | 181.03 ns | 96.64 ns | 5.297 ns | 0.06 | + // | | | | | | + // ToHexStringBuilder | 64 | 5,775.33 ns | 2,825.42 ns | 154.871 ns | 1.00 | + // ToHexStringEncoder | 64 | 331.16 ns | 125.63 ns | 6.886 ns | 0.06 | + // | | | | | | + // ToHexStringBuilder | 128 | 11,662.35 ns | 4,908.03 ns | 269.026 ns | 1.00 | + // ToHexStringEncoder | 128 | 633.78 ns | 57.56 ns | 3.155 ns | 0.05 | + // | | | | | | + // ToHexStringBuilder | 256 | 22,960.11 ns | 14,111.47 ns | 773.497 ns | 1.00 | + // ToHexStringEncoder | 256 | 1,224.76 ns | 547.27 ns | 29.998 ns | 0.05 | +} diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 99bb768842..e5bc3e88b0 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -46,10 +46,12 @@ + + @@ -91,4 +93,4 @@ - + \ No newline at end of file diff --git a/src/Umbraco.Tests/CoreThings/GuidUtilsTests.cs b/src/Umbraco.Tests/CoreThings/GuidUtilsTests.cs new file mode 100644 index 0000000000..5ef8cba356 --- /dev/null +++ b/src/Umbraco.Tests/CoreThings/GuidUtilsTests.cs @@ -0,0 +1,32 @@ +using System; +using NUnit.Framework; +using Umbraco.Core; + +namespace Umbraco.Tests.CoreThings +{ + public class GuidUtilsTests + { + [Test] + public void GuidCombineMethodsAreEqual() + { + var a = Guid.NewGuid(); + var b = Guid.NewGuid(); + + Assert.AreEqual(GuidUtils.Combine(a, b).ToByteArray(), Combine(a, b)); + } + + // Reference implementation taken from original code. + private static byte[] Combine(Guid guid1, Guid guid2) + { + var bytes1 = guid1.ToByteArray(); + var bytes2 = guid2.ToByteArray(); + var bytes = new byte[bytes1.Length]; + for (var i = 0; i < bytes1.Length; i++) + { + bytes[i] = (byte)(bytes1[i] ^ bytes2[i]); + } + + return bytes; + } + } +} diff --git a/src/Umbraco.Tests/CoreThings/HexEncoderTests.cs b/src/Umbraco.Tests/CoreThings/HexEncoderTests.cs new file mode 100644 index 0000000000..588fff83e8 --- /dev/null +++ b/src/Umbraco.Tests/CoreThings/HexEncoderTests.cs @@ -0,0 +1,71 @@ +using System; +using System.Text; +using NUnit.Framework; +using Umbraco.Core; + +namespace Umbraco.Tests.CoreThings +{ + public class HexEncoderTests + { + [Test] + public void ToHexStringCreatesCorrectValue() + { + var buffer = new byte[255]; + var random = new Random(); + random.NextBytes(buffer); + + var sb = new StringBuilder(buffer.Length * 2); + for (var i = 0; i < buffer.Length; i++) + { + sb.Append(buffer[i].ToString("X2")); + } + + var expected = sb.ToString(); + + var actual = HexEncoder.Encode(buffer); + Assert.AreEqual(expected, actual); + } + + [Test] + public void ToHexStringWithSeparatorCreatesCorrectValue() + { + var buffer = new byte[255]; + var random = new Random(); + random.NextBytes(buffer); + + var expected = ToHexString(buffer, '/', 2, 4); + var actual = HexEncoder.Encode(buffer, '/', 2, 4); + + Assert.AreEqual(expected, actual); + } + + private static readonly char[] _bytesToHexStringLookup = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + // Reference implementation taken from original extension method. + private static string ToHexString(byte[] bytes, char separator, int blockSize, int blockCount) + { + int p = 0, bytesLength = bytes.Length, count = 0, size = 0; + var chars = new char[(bytesLength * 2) + blockCount]; + for (var i = 0; i < bytesLength; i++) + { + var b = bytes[i]; + chars[p++] = _bytesToHexStringLookup[b / 0x10]; + chars[p++] = _bytesToHexStringLookup[b % 0x10]; + if (count == blockCount) + { + continue; + } + + if (++size < blockSize) + { + continue; + } + + chars[p++] = separator; + size = 0; + count++; + } + return new string(chars, 0, chars.Length); + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 156bc06a14..8d3da825f5 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -118,6 +118,8 @@ + + From 10f6f8df909810a42bcec3bb7b207f3a9867f95b Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Thu, 15 Nov 2018 22:22:15 +0000 Subject: [PATCH 06/65] Show relations in edit view --- .../views/relationtypes/edit.controller.js | 18 +++++-- .../src/views/relationtypes/edit.html | 45 ++++++---------- .../Editors/RelationTypeController.cs | 7 ++- .../Models/ContentEditing/Relation.cs | 4 +- .../Models/ContentEditing/RelationDisplay.cs | 52 +++++++++++++++++++ .../ContentEditing/RelationTypeDisplay.cs | 9 ++++ .../Models/Mapping/RelationMapperProfile.cs | 4 ++ .../Trees/RelationTypeTreeController.cs | 6 +-- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 9 files changed, 108 insertions(+), 38 deletions(-) create mode 100644 src/Umbraco.Web/Models/ContentEditing/RelationDisplay.cs diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js index 37eb1ea6d9..290117850c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js @@ -1,4 +1,4 @@ -function RelationTypeEditController($scope, $routeParams, relationTypeResource, editorState, navigationService) { +function RelationTypeEditController($scope, $routeParams, relationTypeResource, editorState, navigationService, dateHelper, userService) { var vm = this; @@ -14,17 +14,29 @@ function RelationTypeEditController($scope, $routeParams, relationTypeResource, relationTypeResource.getById($routeParams.id) .then(function(data) { - vm.relation = data; + vm.relationType = data; - editorState.set(vm.relation); + editorState.set(vm.relationType); navigationService.syncTree({ tree: "relationTypes", path: data.path, forceReload: true }).then(function (syncArgs) { vm.page.menu.currentNode = syncArgs.node; }); + formatDates(vm.relationType.relations); + vm.page.loading = false; }); } + + function formatDates(relations) { + if(relations) { + userService.getCurrentUser().then(function (currentUser) { + angular.forEach(relations, function (relation) { + relation.timestampFormatted = dateHelper.getLocalDate(relation.createDate, currentUser.locale, 'LLL'); + }); + }); + } + } } angular.module("umbraco").controller("Umbraco.Editors.RelationTypes.EditController", RelationTypeEditController); diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html index 3f88c914f4..c822c90a15 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html @@ -4,8 +4,8 @@ @@ -15,8 +15,8 @@ -
{{vm.relation.id}}
- {{vm.relation.key}} +
{{vm.relationType.id}}
+ {{vm.relationType.key}}
@@ -24,12 +24,12 @@
@@ -37,23 +37,22 @@ -
{{vm.relation.parentObjectTypeName}}
+
{{vm.relationType.parentObjectTypeName}}
-
{{vm.relation.childObjectTypeName}}
+
{{vm.relationType.childObjectTypeName}}
- -
12
+ +
{{vm.relationType.relations.length}}
- +
- @@ -61,23 +60,11 @@ - - - - - - - - - - - - - - - - - + + + + +
ParentCreated Comment
SomethingSomething else2018-10-27 18:58A comment here
SomethingSomething else2018-10-27 18:58A comment here
SomethingSomething else2018-10-27 18:58A comment here
{{relation.parentId}}{{relation.childId}}{{relation.timestampFormatted}}{{relation.comment}}
diff --git a/src/Umbraco.Web/Editors/RelationTypeController.cs b/src/Umbraco.Web/Editors/RelationTypeController.cs index 2b9242ef31..552f3601e9 100644 --- a/src/Umbraco.Web/Editors/RelationTypeController.cs +++ b/src/Umbraco.Web/Editors/RelationTypeController.cs @@ -35,8 +35,13 @@ namespace Umbraco.Web.Editors { throw new HttpResponseException(HttpStatusCode.NotFound); } + + var relations = Services.RelationService.GetByRelationTypeId(relationType.Id); - return Mapper.Map(relationType); + var display = Mapper.Map(relationType); + display.Relations = Mapper.Map, IEnumerable>(relations); + + return display; } public void PostSave() diff --git a/src/Umbraco.Web/Models/ContentEditing/Relation.cs b/src/Umbraco.Web/Models/ContentEditing/Relation.cs index b166c67f55..2b012dc750 100644 --- a/src/Umbraco.Web/Models/ContentEditing/Relation.cs +++ b/src/Umbraco.Web/Models/ContentEditing/Relation.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.Text; @@ -6,6 +7,7 @@ using System.Threading.Tasks; namespace Umbraco.Web.Models.ContentEditing { + [Obsolete("Use Umbraco.Web.Models.ContentEditing.RelationDisplay instead")] [DataContract(Name = "relation", Namespace = "")] public class Relation { diff --git a/src/Umbraco.Web/Models/ContentEditing/RelationDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/RelationDisplay.cs new file mode 100644 index 0000000000..24ebabc615 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/RelationDisplay.cs @@ -0,0 +1,52 @@ +using System; +using System.ComponentModel; +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "relation", Namespace = "")] + public class RelationDisplay + { + /// + /// Gets or sets the Parent Id of the Relation (Source). + /// + [DataMember(Name = "parentId")] + [ReadOnly(true)] + public int ParentId { get; set; } + + /// + /// Gets or sets the Parent Name of the relation (Source). + /// + [DataMember(Name = "parentName")] + [ReadOnly(true)] + public string ParentName { get; set; } + + /// + /// Gets or sets the Child Id of the Relation (Destination). + /// + [DataMember(Name = "childId")] + [ReadOnly(true)] + public int ChildId { get; set; } + + /// + /// Gets or sets the Child Name of the relation (Destination). + /// + [DataMember(Name = "childName")] + [ReadOnly(true)] + public string ChildName { get; set; } + + /// + /// Gets or sets the date when the Relation was created. + /// + [DataMember(Name = "createDate")] + [ReadOnly(true)] + public DateTime CreateDate { get; set; } + + /// + /// Gets or sets a comment for the Relation. + /// + [DataMember(Name = "comment")] + [ReadOnly(true)] + public string Comment { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs index 3861e0938d..4873d2288d 100644 --- a/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs @@ -1,4 +1,6 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.ComponentModel; using System.Runtime.Serialization; @@ -40,5 +42,12 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "childObjectTypeName")] [ReadOnly(true)] public string ChildObjectTypeName { get; set; } + + /// + /// Gets or sets the relations associated with this relation type. + /// + [DataMember(Name = "relations")] + [ReadOnly(true)] + public IEnumerable Relations { get; set; } } } diff --git a/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs index 95f4fab054..e56fd2ad3b 100644 --- a/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs @@ -20,6 +20,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(x => x.AdditionalData, expression => expression.Ignore()) .ForMember(x => x.ChildObjectTypeName, expression => expression.Ignore()) .ForMember(x => x.ParentObjectTypeName, expression => expression.Ignore()) + .ForMember(x => x.Relations, expression => expression.Ignore()) .ForMember( x => x.Udi, expression => expression.MapFrom( @@ -34,6 +35,9 @@ namespace Umbraco.Web.Models.Mapping dest.ChildObjectTypeName = ObjectTypes.GetUmbracoObjectType(src.ChildObjectType).GetFriendlyName(); }); + // FROM IRelation to RelationDisplay + CreateMap(); + //FROM IRelationType TO RelationType CreateMap(); diff --git a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs index 884b6c5f54..5dbe18a690 100644 --- a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs @@ -24,10 +24,8 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { //Create the normal create action - menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)) - //Since we haven't implemented anything for relationtypes in angular, this needs to be converted to - //use the legacy format - .ConvertLegacyMenuItem(null, "initrelationTypes", queryStrings.GetValue("application")); + menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)); + //refresh action menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 7e51974bc9..76ea0d92cf 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -148,6 +148,7 @@ + From d019ba81f3ba8aae56e9350c4059159db1d0cdc6 Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Thu, 15 Nov 2018 22:34:43 +0000 Subject: [PATCH 07/65] Get relation names --- .../src/views/relationtypes/edit.controller.js | 16 +++++++++++++++- .../src/views/relationtypes/edit.html | 4 ++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js index 290117850c..11e8321350 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js @@ -1,4 +1,4 @@ -function RelationTypeEditController($scope, $routeParams, relationTypeResource, editorState, navigationService, dateHelper, userService) { +function RelationTypeEditController($scope, $routeParams, relationTypeResource, editorState, navigationService, dateHelper, userService, entityResource) { var vm = this; @@ -23,6 +23,7 @@ function RelationTypeEditController($scope, $routeParams, relationTypeResource, }); formatDates(vm.relationType.relations); + getRelationNames(vm.relationType); vm.page.loading = false; }); @@ -37,6 +38,19 @@ function RelationTypeEditController($scope, $routeParams, relationTypeResource, }); } } + + function getRelationNames(relationType) { + if(relationType.relations) { + angular.forEach(relationType.relations, function(relation){ + entityResource.getById(relation.parentId, relationType.parentObjectTypeName).then(function(entity) { + relation.parentName = entity.name; + }); + entityResource.getById(relation.childId, relationType.childObjectTypeName).then(function(entity) { + relation.childName = entity.name; + }); + }); + } + } } angular.module("umbraco").controller("Umbraco.Editors.RelationTypes.EditController", RelationTypeEditController); diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html index c822c90a15..1a1bcfe6c9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html @@ -61,8 +61,8 @@ Comment - {{relation.parentId}} - {{relation.childId}} + {{relation.parentName}} + {{relation.childName}} {{relation.timestampFormatted}} {{relation.comment}} From 738b42964d7b204872433e981a307d1e7241e705 Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Tue, 20 Nov 2018 23:15:16 +0000 Subject: [PATCH 08/65] Added save action --- .../common/resources/relationtype.resource.js | 34 +++++++++++-- .../services/umbdataformatter.service.js | 16 ++++++ .../views/relationtypes/edit.controller.js | 49 ++++++++++++++----- .../src/views/relationtypes/edit.html | 3 +- .../Editors/RelationTypeController.cs | 27 +++++++++- .../ContentEditing/RelationTypeDisplay.cs | 14 +++++- .../Models/ContentEditing/RelationTypeSave.cs | 14 ++++++ .../Models/Mapping/RelationMapperProfile.cs | 3 ++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 9 files changed, 141 insertions(+), 20 deletions(-) create mode 100644 src/Umbraco.Web/Models/ContentEditing/RelationTypeSave.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js index 81fdbdb838..e4a59d01d5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js @@ -3,14 +3,26 @@ * @name umbraco.resources.relationTypeResource * @description Loads in data for relation types. */ -function relationTypeResource($q, $http, umbRequestHelper) { +function relationTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { return { /** * @ngdoc method * @name umbraco.resources.relationTypeResource#getById * @methodOf umbraco.resources.relationTypeResource - * @param {Int} id of the dictionary item to get. + * + * @description + * Gets a relation type with a given ID. + * + * ##usage + *
+         * relationTypeResource.getById(1234)
+         *    .then(function() {
+         *        alert('Found it!');
+         *    });
+         * 
+ * + * @param {Int} id of the relation type to get. * @returns {Promise} resourcePromise containing relation type data. */ getById: function (id) { @@ -26,8 +38,24 @@ function relationTypeResource($q, $http, umbRequestHelper) { ); }, - save: function () { + /** + * @ngdoc method + * @name umbraco.resources.relationTypeResource#save + * @methodof umbraco.resources.relationTypeResource + * + * @description + * Updates a relation type. + * + * @param {Object} relationType The relation type object to update. + * @returns {Promise} A resourcePromise object. + */ + save: function (relationType) { + var saveModel = umbDataFormatter.formatRelationTypePostData(relationType); + return umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("relationTypeApiBaseUrl", "PostSave"), saveModel), + "Failed to save data for relation type ID" + relationType.id + ); }, deleteById: function (id) { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js index b18cb73eae..d7c071f039 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -429,6 +429,22 @@ } return displayModel; + }, + + /** + * Formats the display model used to display the relation type to a model used to save the relation type. + * @param {Object} relationType + */ + formatRelationTypePostData : function(relationType) { + var saveModel = { + id: relationType.id, + name: relationType.name, + alias: relationType.alias, + key : relationType.key, + isBidirectional: relationType.isBidirectional + }; + + return saveModel; } }; } diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js index 11e8321350..0ec6e18214 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js @@ -1,4 +1,4 @@ -function RelationTypeEditController($scope, $routeParams, relationTypeResource, editorState, navigationService, dateHelper, userService, entityResource) { +function RelationTypeEditController($scope, $routeParams, relationTypeResource, editorState, navigationService, dateHelper, userService, entityResource, formHelper, contentEditingHelper) { var vm = this; @@ -7,6 +7,8 @@ function RelationTypeEditController($scope, $routeParams, relationTypeResource, vm.page.saveButtonState = "init"; vm.page.menu = {} + vm.save = saveRelationType; + if($routeParams.create) { alert("create"); } else { @@ -14,21 +16,24 @@ function RelationTypeEditController($scope, $routeParams, relationTypeResource, relationTypeResource.getById($routeParams.id) .then(function(data) { - vm.relationType = data; - - editorState.set(vm.relationType); - - navigationService.syncTree({ tree: "relationTypes", path: data.path, forceReload: true }).then(function (syncArgs) { - vm.page.menu.currentNode = syncArgs.node; - }); - - formatDates(vm.relationType.relations); - getRelationNames(vm.relationType); - + bindRelationType(data); vm.page.loading = false; }); } + function bindRelationType(relationType) { + formatDates(relationType.relations); + getRelationNames(relationType); + + vm.relationType = relationType; + + editorState.set(vm.relationType); + + navigationService.syncTree({ tree: "relationTypes", path: relationType.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + } + function formatDates(relations) { if(relations) { userService.getCurrentUser().then(function (currentUser) { @@ -51,6 +56,26 @@ function RelationTypeEditController($scope, $routeParams, relationTypeResource, }); } } + + function saveRelationType() { + vm.page.saveButtonState = "busy"; + + if (formHelper.submitForm({ scope: $scope, statusMessage: "Saving..." })) { + relationTypeResource.save(vm.relationType).then(function (data) { + formHelper.resetForm({ scope: $scope, notifications: data.notifications }); + bindRelationType(data); + vm.page.saveButtonState = "success"; + }, function (error) { + contentEditingHelper.handleSaveError({ + redirectOnFailure: false, + err: error + }); + + notificationsService.error(error.data.message); + vm.page.saveButtonState = "error"; + }); + } + } } angular.module("umbraco").controller("Umbraco.Editors.RelationTypes.EditController", RelationTypeEditController); diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html index 1a1bcfe6c9..bd7b073957 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html @@ -1,7 +1,7 @@
- + diff --git a/src/Umbraco.Web/Editors/RelationTypeController.cs b/src/Umbraco.Web/Editors/RelationTypeController.cs index 552f3601e9..e2466fef9c 100644 --- a/src/Umbraco.Web/Editors/RelationTypeController.cs +++ b/src/Umbraco.Web/Editors/RelationTypeController.cs @@ -9,6 +9,7 @@ using AutoMapper; using Umbraco.Core.Models; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; @@ -44,9 +45,31 @@ namespace Umbraco.Web.Editors return display; } - public void PostSave() + public RelationTypeDisplay PostSave(RelationTypeSave relationType) { - throw new NotImplementedException(); + var relationTypePersisted = Services.RelationService.GetRelationTypeById(relationType.Key); + + if (relationTypePersisted == null) + { + // TODO: Translate message + throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse("Relation type does not exist")); + } + + Mapper.Map(relationType, relationTypePersisted); + + try + { + Services.RelationService.Save(relationTypePersisted); + var display = Mapper.Map(relationTypePersisted); + display.AddSuccessNotification("Relation type saved", ""); + + return display; + } + catch (Exception ex) + { + Logger.Error(GetType(), ex, "Error saving relation type with {Id}", relationType.Id); + throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse("Something went wrong when saving the relation type")); + } } public void DeleteById() diff --git a/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs index 4873d2288d..c443175260 100644 --- a/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.Serialization; @@ -7,8 +6,13 @@ using System.Runtime.Serialization; namespace Umbraco.Web.Models.ContentEditing { [DataContract(Name = "relationType", Namespace = "")] - public class RelationTypeDisplay : EntityBasic + public class RelationTypeDisplay : EntityBasic, INotificationModel { + public RelationTypeDisplay() + { + Notifications = new List(); + } + /// /// Gets or sets a boolean indicating whether the RelationType is Bidirectional (true) or Parent to Child (false) /// @@ -49,5 +53,11 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "relations")] [ReadOnly(true)] public IEnumerable Relations { get; set; } + + /// + /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. + /// + [DataMember(Name = "notifications")] + public List Notifications { get; private set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/RelationTypeSave.cs b/src/Umbraco.Web/Models/ContentEditing/RelationTypeSave.cs new file mode 100644 index 0000000000..df118f37ee --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/RelationTypeSave.cs @@ -0,0 +1,14 @@ +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "relationType", Namespace = "")] + public class RelationTypeSave : EntityBasic + { + /// + /// Gets or sets a boolean indicating whether the RelationType is Bidirectional (true) or Parent to Child (false) + /// + [DataMember(Name = "isBidirectional", IsRequired = true)] + public bool IsBidirectional { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs index e56fd2ad3b..5a4f9cd9a3 100644 --- a/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs @@ -38,6 +38,9 @@ namespace Umbraco.Web.Models.Mapping // FROM IRelation to RelationDisplay CreateMap(); + // FROM RelationTypeSave to IRelationType + CreateMap(); + //FROM IRelationType TO RelationType CreateMap(); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 76ea0d92cf..04d0765598 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -150,6 +150,7 @@ + From 5872b6de22ca63a0ae15e41ad21160a086008ee5 Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Thu, 22 Nov 2018 14:19:40 +0000 Subject: [PATCH 09/65] Prototyped create view --- .../views/relationtypes/create.controller.js | 5 ++ .../src/views/relationtypes/create.html | 62 +++++++++++++++++++ .../Trees/RelationTypeTreeController.cs | 2 +- 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/relationtypes/create.html diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js new file mode 100644 index 0000000000..b59cd4aa71 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js @@ -0,0 +1,5 @@ +function RelationTypeCreateController($scope) { + var vm = this; +} + +angular.module("umbraco").controller("Umbraco.Editors.RelationTypes.CreateController", RelationTypeCreateController); diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.html new file mode 100644 index 0000000000..43988f4ad9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.html @@ -0,0 +1,62 @@ + + + diff --git a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs index 5dbe18a690..096c7a6870 100644 --- a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { //Create the normal create action - menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)); + menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)); //refresh action menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); From 12d11b7c88f879b268de65a39611b66b497903ae Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Thu, 29 Nov 2018 22:07:17 +0000 Subject: [PATCH 10/65] Render create view Load object types from API --- .../common/resources/relationtype.resource.js | 19 +++++++++ .../views/relationtypes/create.controller.js | 40 ++++++++++++++++++- .../src/views/relationtypes/create.html | 32 +++++++-------- .../Editors/RelationTypeController.cs | 24 +++++++++++ .../Models/ContentEditing/ObjectType.cs | 15 +++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 6 files changed, 112 insertions(+), 19 deletions(-) create mode 100644 src/Umbraco.Web/Models/ContentEditing/ObjectType.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js index e4a59d01d5..3112d7378b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js @@ -38,6 +38,25 @@ function relationTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { ); }, + /** + * @ngdoc method + * @name umbraco.resources.relationTypeResource#getRelationObjectTypes + * @methodof umbraco.resources.relationTypeResource + * + * @description + * Gets a list of Umbraco object types which can be associated with a relation. + * + * @returns {Object} A collection of Umbraco object types. + */ + getRelationObjectTypes: function() { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl("relationTypeApiBaseUrl", "GetRelationObjectTypes") + ), + "Failed to get object types" + ); + }, + /** * @ngdoc method * @name umbraco.resources.relationTypeResource#save diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js index b59cd4aa71..b7389c0e56 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js @@ -1,5 +1,43 @@ -function RelationTypeCreateController($scope) { +function RelationTypeCreateController($scope, relationTypeResource, navigationService, formHelper, appState) { var vm = this; + vm.relationType = {}; + vm.objectTypes = {}; + + vm.createRelationType = createRelationType; + + init(); + + function init() { + relationTypeResource.getRelationObjectTypes().then(function (data) { + vm.objectTypes = data; + }, function (err) { + alert("oh no"); + }) + } + + function createRelationType() { + if (formHelper.submitForm({ scope: $scope, formCtrl: this.createRelationTypeForm, statusMessage: "Creating relation type..." })) { + var node = $scope.dialogOptions.currentNode; + + relationTypeResource.create(vm.relationType).then(function (data) { + navigationService.hideMenu(); + + // Set the new item as active in the tree + var currentPath = node.path ? node.path : "-1"; + navigationService.syncTree({ tree: "relationType", path: currentPath + "," + data, forceReload: true, activate: true }); + + formHelper.resetForm({ scope: $scope }); + + var currentSection = appState.getSectionState("currentSection"); + $location.path("/" + currentSection + "/relationTypes/edit/" + data); + }, function (err) { + if (err.data && err.data.message) { + notificationsService.error(err.data.message); + navigationService.hideMenu(); + } + }); + } + } } angular.module("umbraco").controller("Umbraco.Editors.RelationTypes.CreateController", RelationTypeCreateController); diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.html index 43988f4ad9..1d07c8e1b4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.html +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.html @@ -1,9 +1,9 @@
[DataMember(Name = "isBidirectional", IsRequired = true)] public bool IsBidirectional { get; set; } + + /// + /// Gets or sets the parent object type ID. + /// + [DataMember(Name = "parentObjectType", IsRequired = false)] + public Guid ParentObjectType { get; set; } + + /// + /// Gets or sets the child object type ID. + /// + [DataMember(Name = "childObjectType", IsRequired = false)] + public Guid ChildObjectType { get; set; } } } From a6ab47150520b8d2a09374a6cf46af29bb7bc116 Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Fri, 30 Nov 2018 14:09:48 +0000 Subject: [PATCH 12/65] Added delete endpoint. Updated tree controller to use Angular delete view --- .../src/views/relationtypes/delete.html | 0 .../Editors/RelationTypeController.cs | 18 ++++++++++++++++-- .../Trees/RelationTypeTreeController.cs | 19 +++++++------------ 3 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.html diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Umbraco.Web/Editors/RelationTypeController.cs b/src/Umbraco.Web/Editors/RelationTypeController.cs index 3751dd5bcf..05de96699d 100644 --- a/src/Umbraco.Web/Editors/RelationTypeController.cs +++ b/src/Umbraco.Web/Editors/RelationTypeController.cs @@ -126,9 +126,23 @@ namespace Umbraco.Web.Editors } } - public void DeleteById() + /// + /// Deletes a relation type with a given ID. + /// + /// The ID of the relation type to delete. + /// A . + [HttpPost] + [HttpDelete] + public HttpResponseMessage DeleteById(int id) { - throw new NotImplementedException(); + var relationType = Services.RelationService.GetRelationTypeById(id); + + if(relationType == null) + throw new HttpResponseException(HttpStatusCode.NotFound); + + Services.RelationService.Delete(relationType); + + return Request.CreateResponse(HttpStatusCode.OK); } } } diff --git a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs index 096c7a6870..6131ca8646 100644 --- a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs @@ -34,9 +34,10 @@ namespace Umbraco.Web.Trees var relationType = Services.RelationService.GetRelationTypeById(int.Parse(id)); if (relationType == null) return new MenuItemCollection(); + + menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)); - //add delete option for all macros - menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)) + /*menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)) //Since we haven't implemented anything for relationtypes in angular, this needs to be converted to //use the legacy format .ConvertLegacyMenuItem(new EntitySlim @@ -45,7 +46,7 @@ namespace Umbraco.Web.Trees Level = 1, ParentId = -1, Name = relationType.Name - }, "relationTypes", queryStrings.GetValue("application")); + }, "relationTypes", queryStrings.GetValue("application"));*/ return menu; } @@ -56,15 +57,9 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { - nodes.AddRange(Services.RelationService - .GetAllRelationTypes().Select(rt => CreateTreeNode( - rt.Id.ToString(), - id, - queryStrings, - rt.Name, - "icon-trafic", - false - ))); + nodes.AddRange(Services.RelationService.GetAllRelationTypes() + .Select(rt => CreateTreeNode(rt.Id.ToString(), id, queryStrings, rt.Name, + "icon-trafic", false))); } return nodes; } From 2dfcb8ed4e48e62585bb79de36095ff5f695d12e Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Sun, 2 Dec 2018 18:10:03 +0000 Subject: [PATCH 13/65] Completed delete action --- .../common/resources/relationtype.resource.js | 31 ++++++++++++----- .../views/relationtypes/delete.controller.js | 33 +++++++++++++++++++ .../src/views/relationtypes/delete.html | 12 +++++++ 3 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js index 4035ce0994..f17aae0a6b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js @@ -27,13 +27,7 @@ function relationTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { */ getById: function (id) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "relationTypeApiBaseUrl", - "GetById", - [{ id: id }] - ) - ), + $http.get(umbRequestHelper.getApiUrl("relationTypeApiBaseUrl", "GetById", [{ id: id }])), "Failed to get item " + id ); }, @@ -97,8 +91,29 @@ function relationTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { ); }, + /** + * @ngdoc method + * @name umbraco.resources.relationTypeResource#deleteById + * @methodof umbraco.resources.relationTypeResource + * + * @description + * Deletes a relation type with a given ID. + * + * * ## Usage + *
+         * relationTypeResource.deleteById(1234).then(function() {
+         *    alert('Deleted it!');
+         * });
+         * 
+ * + * @param {Int} id The ID of the relation type to delete. + * @returns {Promose} resourcePromise object. + */ deleteById: function (id) { - + return umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("relationTypeApiBaseUrl", "DeleteById", [{ id: id }])), + "Failed to delete item " + id + ); } }; diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.controller.js new file mode 100644 index 0000000000..ead951ae3a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.controller.js @@ -0,0 +1,33 @@ +function RelationTypeDeleteController($scope, $location, relationTypeResource, treeService, navigationService, appState) { + + var vm = this; + + vm.cancel = cancel; + vm.performDelete = performDelete; + + function cancel() { + navigationService.hideDialog(); + } + + function performDelete() { + // stop from firing again on double-click + if ($scope.busy) { return false; } + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + $scope.busy = true; + + relationTypeResource.deleteById($scope.currentNode.id).then(function () { + $scope.currentNode.loading = false; + + treeService.removeNode($scope.currentNode); + + navigationService.hideMenu(); + + var currentSection = appState.getSectionState("currentSection"); + $location.path("/" + currentSection + "/"); + }); + } +} + +angular.module("umbraco").controller("Umbraco.Editors.RelationTypes.DeleteController", RelationTypeDeleteController); diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.html index e69de29bb2..e0fdbc6751 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.html @@ -0,0 +1,12 @@ +
+
+ +

+ Are you sure you want to delete {{currentNode.name}}? +

+ + + + +
+
From 4617c0c24fac1967fdd445e1ce3f6e29e707505e Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Sun, 2 Dec 2018 18:17:20 +0000 Subject: [PATCH 14/65] Tidy up - remove obsoleted classes --- .../views/relationtypes/create.controller.js | 8 ++++ .../views/relationtypes/delete.controller.js | 8 ++++ .../views/relationtypes/edit.controller.js | 8 ++++ src/Umbraco.Web/Editors/RelationController.cs | 16 +++---- .../Models/ContentEditing/Relation.cs | 45 ------------------- .../Models/ContentEditing/RelationType.cs | 43 ------------------ .../Models/Mapping/RelationMapperProfile.cs | 8 ---- .../Trees/RelationTypeTreeController.cs | 18 +------- src/Umbraco.Web/Umbraco.Web.csproj | 2 - 9 files changed, 34 insertions(+), 122 deletions(-) delete mode 100644 src/Umbraco.Web/Models/ContentEditing/Relation.cs delete mode 100644 src/Umbraco.Web/Models/ContentEditing/RelationType.cs diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js index 073099994b..7ac95dc4d9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js @@ -1,3 +1,11 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.RelationTypes.CreateController + * @function + * + * @description + * The controller for creating relation types. + */ function RelationTypeCreateController($scope, $location, relationTypeResource, navigationService, formHelper, appState, notificationsService) { var vm = this; vm.relationType = {}; diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.controller.js index ead951ae3a..1a32f17a46 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.controller.js @@ -1,3 +1,11 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.RelationTypes.DeleteController + * @function + * + * @description + * The controller for deleting relation types. + */ function RelationTypeDeleteController($scope, $location, relationTypeResource, treeService, navigationService, appState) { var vm = this; diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js index 0ec6e18214..da32804f6d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js @@ -1,3 +1,11 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.RelationTypes.EditController + * @function + * + * @description + * The controller for editing relation types. + */ function RelationTypeEditController($scope, $routeParams, relationTypeResource, editorState, navigationService, dateHelper, userService, entityResource, formHelper, contentEditingHelper) { var vm = this; diff --git a/src/Umbraco.Web/Editors/RelationController.cs b/src/Umbraco.Web/Editors/RelationController.cs index c287e8a429..430f1af690 100644 --- a/src/Umbraco.Web/Editors/RelationController.cs +++ b/src/Umbraco.Web/Editors/RelationController.cs @@ -6,40 +6,40 @@ using System.Web.Http; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; -using Relation = Umbraco.Web.Models.ContentEditing.Relation; namespace Umbraco.Web.Editors { [PluginController("UmbracoApi")] - [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Content)] + [UmbracoApplicationAuthorize(Constants.Applications.Content)] public class RelationController : UmbracoAuthorizedJsonController { - public Relation GetById(int id) + public RelationDisplay GetById(int id) { - return Mapper.Map(Services.RelationService.GetById(id)); + return Mapper.Map(Services.RelationService.GetById(id)); } //[EnsureUserPermissionForContent("childId")] - public IEnumerable GetByChildId(int childId, string relationTypeAlias = "") + public IEnumerable GetByChildId(int childId, string relationTypeAlias = "") { var relations = Services.RelationService.GetByChildId(childId).ToArray(); if (relations.Any() == false) { - return Enumerable.Empty(); + return Enumerable.Empty(); } if (string.IsNullOrWhiteSpace(relationTypeAlias) == false) { return - Mapper.Map, IEnumerable>( + Mapper.Map, IEnumerable>( relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias))); } - return Mapper.Map, IEnumerable>(relations); + return Mapper.Map, IEnumerable>(relations); } [HttpDelete] diff --git a/src/Umbraco.Web/Models/ContentEditing/Relation.cs b/src/Umbraco.Web/Models/ContentEditing/Relation.cs deleted file mode 100644 index 2b012dc750..0000000000 --- a/src/Umbraco.Web/Models/ContentEditing/Relation.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; - -namespace Umbraco.Web.Models.ContentEditing -{ - [Obsolete("Use Umbraco.Web.Models.ContentEditing.RelationDisplay instead")] - [DataContract(Name = "relation", Namespace = "")] - public class Relation - { - - public Relation() - { - RelationType = new RelationType(); - } - - /// - /// Gets or sets the Parent Id of the Relation (Source) - /// - [DataMember(Name = "parentId")] - public int ParentId { get; set; } - - /// - /// Gets or sets the Child Id of the Relation (Destination) - /// - [DataMember(Name = "childId")] - public int ChildId { get; set; } - - /// - /// Gets or sets the for the Relation - /// - [DataMember(Name = "relationType", IsRequired = true)] - public RelationType RelationType { get; set; } - - /// - /// Gets or sets a comment for the Relation - /// - [DataMember(Name = "comment")] - public string Comment { get; set; } - - } -} diff --git a/src/Umbraco.Web/Models/ContentEditing/RelationType.cs b/src/Umbraco.Web/Models/ContentEditing/RelationType.cs deleted file mode 100644 index f52ac507ac..0000000000 --- a/src/Umbraco.Web/Models/ContentEditing/RelationType.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace Umbraco.Web.Models.ContentEditing -{ - [Obsolete("Use Umbraco.Web.Models.ContentEditing.RelationTypeDisplay instead")] - [DataContract(Name = "relationType", Namespace = "")] - public class RelationType - { - - /// - /// Gets or sets the Name of the RelationType - /// - [DataMember(Name = "name", IsRequired = true)] - public string Name { get; set; } - - /// - /// Gets or sets the Alias of the RelationType - /// - [DataMember(Name = "alias", IsRequired = true)] - public string Alias { get; set; } - - /// - /// Gets or sets a boolean indicating whether the RelationType is Bidirectional (true) or Parent to Child (false) - /// - [DataMember(Name = "isBidirectional", IsRequired = true)] - public bool IsBidirectional { get; set; } - - /// - /// Gets or sets the Parents object type id - /// - /// Corresponds to the NodeObjectType in the umbracoNode table - [DataMember(Name = "parentObjectType", IsRequired = true)] - public Guid ParentObjectType { get; set; } - - /// - /// Gets or sets the Childs object type id - /// - /// Corresponds to the NodeObjectType in the umbracoNode table - [DataMember(Name = "childObjectType", IsRequired = true)] - public Guid ChildObjectType { get; set; } - } -} diff --git a/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs index 5a4f9cd9a3..1622fb907e 100644 --- a/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs @@ -2,8 +2,6 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Web.Models.ContentEditing; -using Relation = Umbraco.Web.Models.ContentEditing.Relation; -using RelationType = Umbraco.Web.Models.ContentEditing.RelationType; namespace Umbraco.Web.Models.Mapping { @@ -40,12 +38,6 @@ namespace Umbraco.Web.Models.Mapping // FROM RelationTypeSave to IRelationType CreateMap(); - - //FROM IRelationType TO RelationType - CreateMap(); - - //FROM IRelation TO Relation - CreateMap(); } } } diff --git a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs index 6131ca8646..59e43225de 100644 --- a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs @@ -1,13 +1,10 @@ -using System; -using System.Linq; +using System.Linq; using System.Net.Http.Formatting; using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi.Filters; using Umbraco.Core; using Umbraco.Web._Legacy.Actions; using Umbraco.Core.Services; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Entities; namespace Umbraco.Web.Trees { @@ -36,18 +33,7 @@ namespace Umbraco.Web.Trees if (relationType == null) return new MenuItemCollection(); menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)); - - /*menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)) - //Since we haven't implemented anything for relationtypes in angular, this needs to be converted to - //use the legacy format - .ConvertLegacyMenuItem(new EntitySlim - { - Id = relationType.Id, - Level = 1, - ParentId = -1, - Name = relationType.Name - }, "relationTypes", queryStrings.GetValue("application"));*/ - + return menu; } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 35a3bfd752..f9e1eda168 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -640,9 +640,7 @@ - - From f87dac9972e541ac9f16e6d2f1dfb499c49ff1bb Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Sun, 2 Dec 2018 21:56:03 +0000 Subject: [PATCH 15/65] Remove redundant webforms views --- .../Editors/RelationTypeController.cs | 5 +- src/Umbraco.Web/Umbraco.Web.csproj | 26 -- .../RelationTypes/EditRelationType.aspx | 146 --------- .../RelationTypes/EditRelationType.aspx.cs | 284 ------------------ .../EditRelationType.aspx.designer.cs | 249 --------------- .../RelationTypes/NewRelationType.aspx | 54 ---- .../RelationTypes/NewRelationType.aspx.cs | 89 ------ .../NewRelationType.aspx.designer.cs | 168 ----------- .../RelationTypesWebService.asmx | 1 - .../RelationTypesWebService.asmx.cs | 35 --- .../TreeMenu/ActionDeleteRelationType.cs | 83 ----- .../TreeMenu/ActionNewRelationType.cs | 83 ----- 12 files changed, 2 insertions(+), 1221 deletions(-) delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.designer.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/NewRelationType.aspx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/NewRelationType.aspx.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/NewRelationType.aspx.designer.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionDeleteRelationType.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionNewRelationType.cs diff --git a/src/Umbraco.Web/Editors/RelationTypeController.cs b/src/Umbraco.Web/Editors/RelationTypeController.cs index 05de96699d..ef3def1889 100644 --- a/src/Umbraco.Web/Editors/RelationTypeController.cs +++ b/src/Umbraco.Web/Editors/RelationTypeController.cs @@ -75,7 +75,7 @@ namespace Umbraco.Web.Editors /// A containing the persisted relation type's ID. public HttpResponseMessage PostCreate(RelationTypeSave relationType) { - var relationTypePersisted = new Core.Models.RelationType(relationType.ChildObjectType, relationType.ParentObjectType, relationType.Name.ToSafeAlias(true)) + var relationTypePersisted = new RelationType(relationType.ChildObjectType, relationType.ParentObjectType, relationType.Name.ToSafeAlias(true)) { Name = relationType.Name, IsBidirectional = relationType.IsBidirectional @@ -90,7 +90,7 @@ namespace Umbraco.Web.Editors catch (Exception ex) { Logger.Error(GetType(), ex, "Error creating relation type with {Name}", relationType.Name); - return Request.CreateNotificationValidationErrorResponse("Error creating dictionary item"); + return Request.CreateNotificationValidationErrorResponse("Error creating relation type."); } } @@ -105,7 +105,6 @@ namespace Umbraco.Web.Editors if (relationTypePersisted == null) { - // TODO: Translate message throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse("Relation type does not exist")); } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index f9e1eda168..09b3e67cb0 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1259,25 +1259,6 @@ FeedProxy.aspx - - EditRelationType.aspx - - - EditRelationType.aspx - - - NewRelationType.aspx - ASPXCodeBehind - - - NewRelationType.aspx - - - RelationTypesWebService.asmx - Component - - - insertMasterpageContent.aspx ASPXCodeBehind @@ -1375,13 +1356,6 @@ - - ASPXCodeBehind - - - ASPXCodeBehind - - ASPXCodeBehind diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx deleted file mode 100644 index b81a8c4e5f..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx +++ /dev/null @@ -1,146 +0,0 @@ -<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="EditRelationType.aspx.cs" Inherits="umbraco.cms.presentation.developer.RelationTypes.EditRelationType" MasterPageFile="../../masterpages/umbracoPage.Master" %> -<%@ Register TagPrefix="umb" Namespace="Umbraco.Web._Legacy.Controls" %> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Parent  ChildCreatedComment
 <%# DataBinder.Eval(Container.DataItem, "ParentText") %>  <%# DataBinder.Eval(Container.DataItem, "ChildText") %><%# DataBinder.Eval(Container.DataItem, "DateTime") %><%# DataBinder.Eval(Container.DataItem, "Comment") %>
- -
- - -
- -
- - -
\ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.cs deleted file mode 100644 index 3b1217d808..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.cs +++ /dev/null @@ -1,284 +0,0 @@ -//TODO: Rebuild with new tree format and apis and then remove - -//using System; -//using System.Collections.Generic; -//using System.Web.UI; -//using System.Web.UI.WebControls; -// -//using umbraco.BusinessLogic; -//using Umbraco.Core; -//using Umbraco.Core.Models; -//using RelationType = umbraco.cms.businesslogic.relation.RelationType; - -//namespace umbraco.cms.presentation.developer.RelationTypes -//{ -// /// -// /// Edit an existing RelationType -// /// -// [WebformsPageTreeAuthorize(Constants.Trees.RelationTypes)] -// public partial class EditRelationType : UmbracoEnsuredPage -// { - -// /// -// /// Class scope reference to the current RelationType being edited -// /// -// private IRelationType _relationType; - -// /// -// /// Class scope reference to the relations associated with the current RelationType -// /// -// private List _relations; - -// /// -// /// Umbraco ObjectType used to represent all parent items in this relation type -// /// -// /// -// private UmbracoObjectTypes _parentObjectType; - -// /// -// /// Umbraco ObjectType used to represent all child items in this relation type -// /// -// private UmbracoObjectTypes _childObjectType; - -// /// -// /// Gets the name of the parent object type for all relations in this relation type -// /// -// protected string ParentObjectType -// { -// get -// { -// return this._parentObjectType.GetName(); //UmbracoHelper.GetName(this.parentObjectType); -// } -// } - -// /// -// /// Gets the name of the child object type for all relations in this relation type -// /// -// protected string ChildObjectType -// { -// get -// { -// return this._childObjectType.GetName(); //UmbracoHelper.GetName(this.childObjectType); -// } -// } - -// /// -// /// Gets a string representing the current relation type direction -// /// -// protected string RelationTypeDirection -// { -// get -// { -// return this._relationType.IsBidirectional ? "bidirectional" : "parentToChild"; -// } -// } - -// /// -// /// Gets the Relations for this RelationType, via lazy load -// /// -// private List Relations -// { -// get -// { -// if (this._relations == null) -// { -// this._relations = new List(); - -// using (var reader = uQuery.SqlHelper.ExecuteReader(@" -// SELECT A.id, -// A.parentId, -// B.[text] AS parentText, -// A.childId, -// C.[text] AS childText, -// A.relType, -// A.[datetime], -// A.comment -// FROM umbracoRelation A -// LEFT OUTER JOIN umbracoNode B ON A.parentId = B.id -// LEFT OUTER JOIN umbracoNode C ON A.childId = C.id -// WHERE A.relType = " + this._relationType.Id.ToString())) -// { -// while (reader.Read()) -// { -// var readOnlyRelation = new ReadOnlyRelation(); - -// readOnlyRelation.Id = reader.GetInt("id"); -// readOnlyRelation.ParentId = reader.GetInt("parentId"); -// readOnlyRelation.ParentText = reader.GetString("parentText"); -// readOnlyRelation.ChildId = reader.GetInt("childId"); -// readOnlyRelation.ChildText = reader.GetString("childText"); -// readOnlyRelation.RelType = reader.GetInt("relType"); -// readOnlyRelation.DateTime = reader.GetDateTime("datetime"); -// readOnlyRelation.Comment = reader.GetString("comment"); - -// this._relations.Add(readOnlyRelation); -// } -// } -// } - -// return this._relations; -// } -// } - -// /// -// /// On Load event -// /// -// /// this aspx page -// /// EventArgs (expect empty) -// protected void Page_Load(object sender, EventArgs e) -// { -// int id; -// if (int.TryParse(Request.QueryString["id"], out id)) -// { -// var relationService = Services.RelationService; - -// this._relationType = relationService.GetRelationTypeById(id); -// if (this._relationType != null) -// { -// this._parentObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(this._relationType.ParentObjectType); -// this._childObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(this._relationType.ChildObjectType); - -// // ----------- - -// if (!this.IsPostBack) -// { -// this.EnsureChildControls(); - -// this.idLiteral.Text = this._relationType.Id.ToString(); -// this.nameTextBox.Text = this._relationType.Name; -// this.aliasTextBox.Text = this._relationType.Alias; - -// if (this._relationType.IsBidirectional) -// { -// this.dualRadioButtonList.Items.FindByValue("1").Selected = true; -// } -// else -// { -// this.dualRadioButtonList.Items.FindByValue("0").Selected = true; -// } - -// this.parentLiteral.Text = this._parentObjectType.GetFriendlyName(); -// // UmbracoHelper.GetFriendlyName(this.parentObjectType); -// this.childLiteral.Text = this._childObjectType.GetFriendlyName(); -// // UmbracoHelper.GetFriendlyName(this.childObjectType); - -// this.relationsCountLiteral.Text = this.Relations.Count.ToString(); - -// this.relationsRepeater.DataSource = this.Relations; -// this.relationsRepeater.DataBind(); -// } -// } -// else -// { -// throw new Exception("Unable to get RelationType where ID = " + id); -// } -// } -// else -// { -// throw new Exception("Invalid RelationType ID"); -// } -// } - -// /// -// /// Creates the child controls used in this page -// /// -// protected override void CreateChildControls() -// { -// base.CreateChildControls(); - -// var relationTypeTabPage = this.tabControl.NewTabPage("Relation Type"); -// relationTypeTabPage.Controls.Add(this.idPane); -// relationTypeTabPage.Controls.Add(this.nameAliasPane); -// relationTypeTabPage.Controls.Add(this.directionPane); -// relationTypeTabPage.Controls.Add(this.objectTypePane); - -// var saveMenuImageButton = tabControl.Menu.NewButton(); -// saveMenuImageButton.ToolTip = "save relation type"; -// saveMenuImageButton.Click +=saveMenuImageButton_Click; -// saveMenuImageButton.CausesValidation = true; -// saveMenuImageButton.Text = Services.TextService.Localize("save"); -// saveMenuImageButton.ValidationGroup = "RelationType"; - -// var relationsTabPage = this.tabControl.NewTabPage("Relations"); -// relationsTabPage.Controls.Add(this.relationsCountPane); -// relationsTabPage.Controls.Add(this.relationsPane); - -// /* -// var refreshMenuImageButton = relationsTabPage.Menu.NewImageButton(); -// refreshMenuImageButton.AlternateText = "refresh relations"; -// refreshMenuImageButton.Click += this.RefreshMenuImageButton_Click; -// refreshMenuImageButton.ImageUrl = "/umbraco/developer/RelationTypes/Images/Refresh.gif"; -// refreshMenuImageButton.CausesValidation = false;*/ -// } - -// /// -// /// check that alias hasn't been changed to clash with another (except itself) -// /// -// /// the aliasCustomValidator control -// /// to set validation respose -// protected void AliasCustomValidator_ServerValidate(object source, ServerValidateEventArgs args) -// { -// args.IsValid = (RelationType.GetByAlias(this.aliasTextBox.Text.Trim()) == null) || -// (this.aliasTextBox.Text.Trim() == this._relationType.Alias); -// } - -// /// -// /// Reload the relations, in case they have changed -// /// -// /// expects refreshMenuImageButton -// /// expects ImageClickEventArgs -// private void RefreshMenuImageButton_Click(object sender, ImageClickEventArgs e) -// { -// } - -// /// -// /// Save button in Umbraco menu -// /// -// /// expects saveMenuImageButton object -// /// expects ImageClickEventArgs -// void saveMenuImageButton_Click(object sender, EventArgs e) -// { -// if (this.Page.IsValid) -// { -// var nameChanged = this._relationType.Name != this.nameTextBox.Text.Trim(); -// var aliasChanged = this._relationType.Alias != this.aliasTextBox.Text.Trim(); -// var directionChanged = this._relationType.IsBidirectional != (this.dualRadioButtonList.SelectedValue == "1"); - -// if (nameChanged || aliasChanged || directionChanged) -// { -// string bubbleBody = string.Empty; - -// if (nameChanged) -// { -// bubbleBody += "Name, "; - -// this._relationType.Name = this.nameTextBox.Text.Trim(); - -// // Refresh tree, as the name as changed -// ClientTools.SyncTree(this._relationType.Id.ToString(), true); -// } - -// if (directionChanged) -// { -// bubbleBody += "Direction, "; -// this._relationType.IsBidirectional = this.dualRadioButtonList.SelectedValue == "1"; -// } - -// if (aliasChanged) -// { -// bubbleBody += "Alias, "; -// this._relationType.Alias = this.aliasTextBox.Text.Trim(); -// } - -// bubbleBody = bubbleBody.Remove(bubbleBody.LastIndexOf(','), 1); -// bubbleBody = bubbleBody + "Changed"; - -// var relationService = Services.RelationService; -// relationService.Save(this._relationType); - -// ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Save, "Relation Type Updated", bubbleBody); -// } -// } -// } -// } -//} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.designer.cs deleted file mode 100644 index 7e8475fdfb..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.designer.cs +++ /dev/null @@ -1,249 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.cms.presentation.developer.RelationTypes { - - - public partial class EditRelationType { - - /// - /// tabControl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.TabView tabControl; - - /// - /// idPane control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane idPane; - - /// - /// idPropertyPanel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel idPropertyPanel; - - /// - /// idLiteral control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal idLiteral; - - /// - /// nameAliasPane control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane nameAliasPane; - - /// - /// nameProperyPanel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel nameProperyPanel; - - /// - /// nameTextBox control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox nameTextBox; - - /// - /// nameRequiredFieldValidator control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator nameRequiredFieldValidator; - - /// - /// aliasPropertyPanel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel aliasPropertyPanel; - - /// - /// aliasTextBox control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox aliasTextBox; - - /// - /// aliasRequiredFieldValidator control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator aliasRequiredFieldValidator; - - /// - /// aliasCustomValidator control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CustomValidator aliasCustomValidator; - - /// - /// directionPane control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane directionPane; - - /// - /// dualPropertyPanel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel dualPropertyPanel; - - /// - /// dualRadioButtonList control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RadioButtonList dualRadioButtonList; - - /// - /// objectTypePane control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane objectTypePane; - - /// - /// parentPropertyPanel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel parentPropertyPanel; - - /// - /// parentLiteral control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal parentLiteral; - - /// - /// childPropertyPanel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel childPropertyPanel; - - /// - /// childLiteral control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal childLiteral; - - /// - /// relationsCountPane control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane relationsCountPane; - - /// - /// relationsCountPropertyPanel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel relationsCountPropertyPanel; - - /// - /// relationsCountLiteral control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal relationsCountLiteral; - - /// - /// relationsPane control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane relationsPane; - - /// - /// relationsPropertyPanel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel relationsPropertyPanel; - - /// - /// relationsRepeater control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Repeater relationsRepeater; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/NewRelationType.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/NewRelationType.aspx deleted file mode 100644 index 5939549fa4..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/NewRelationType.aspx +++ /dev/null @@ -1,54 +0,0 @@ -<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="NewRelationType.aspx.cs" Inherits="umbraco.cms.presentation.developer.RelationTypes.NewRelationType" MasterPageFile="../../masterpages/umbracoPage.Master"%> -<%@ Register TagPrefix="umb" Namespace="Umbraco.Web._Legacy.Controls" %> - - - - - - - - - - - - - - - - - - - - - - - - - - - - <% ///* */ %> - - - - - - - - - - - - - - - -
- - or - Cancel -
- - -
- diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/NewRelationType.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/NewRelationType.aspx.cs deleted file mode 100644 index cffe2157e3..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/NewRelationType.aspx.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using System.Web.UI.WebControls; -using Umbraco.Core; -using Umbraco.Web.UI.Pages; -using Umbraco.Core.Models; - -namespace umbraco.cms.presentation.developer.RelationTypes -{ - /// - /// Add a new Relation Type - /// - [WebformsPageTreeAuthorize(Constants.Trees.RelationTypes)] - public partial class NewRelationType : UmbracoEnsuredPage - { - /// - /// On Load event - /// - /// this aspx page - /// EventArgs (expect empty) - protected void Page_Load(object sender, EventArgs e) - { - if (!this.Page.IsPostBack) - { - this.Form.DefaultFocus = this.descriptionTextBox.ClientID; - } - - this.AppendUmbracoObjectTypes(this.parentDropDownList); - this.AppendUmbracoObjectTypes(this.childDropDownList); - } - - /// - /// Server side validation to ensure there are no existing relationshipTypes with the alias of - /// the relation type being added - /// - /// the aliasCustomValidator control - /// to set validation respose - protected void AliasCustomValidator_ServerValidate(object source, ServerValidateEventArgs args) - { - var relationService = Services.RelationService; - args.IsValid = relationService.GetRelationTypeByAlias(this.aliasTextBox.Text.Trim()) == null; - } - - /// - /// Add a new relation type into the database, and redirects to it's editing page. - /// - /// expects the addButton control - /// expects EventArgs for addButton - protected void AddButton_Click(object sender, EventArgs e) - { - if (Page.IsValid) - { - var newRelationTypeAlias = this.aliasTextBox.Text.Trim(); - - var relationService = Services.RelationService; - var relationType = new RelationType(new Guid(this.childDropDownList.SelectedValue), - new Guid(this.parentDropDownList.SelectedValue), newRelationTypeAlias, this.descriptionTextBox.Text) - { - IsBidirectional = this.dualRadioButtonList.SelectedValue == "1" - }; - - relationService.Save(relationType); - - var newRelationTypeId = relationService.GetRelationTypeByAlias(newRelationTypeAlias).Id; - - ClientTools.ChangeContentFrameUrl("developer/RelationTypes/EditRelationType.aspx?id=" + newRelationTypeId).CloseModalWindow().ChildNodeCreated(); - } - } - - /// - /// Adds the Umbraco Object types to a drop down list - /// - /// control for which to add the Umbraco object types - private void AppendUmbracoObjectTypes(ListControl dropDownList) - { - dropDownList.Items.Add(new ListItem(UmbracoObjectTypes.Document.GetFriendlyName(), Constants.ObjectTypes.Strings.Document)); - dropDownList.Items.Add(new ListItem(UmbracoObjectTypes.Media.GetFriendlyName(), Constants.ObjectTypes.Strings.Media)); - dropDownList.Items.Add(new ListItem(UmbracoObjectTypes.Member.GetFriendlyName(), Constants.ObjectTypes.Strings.Member)); - dropDownList.Items.Add(new ListItem(UmbracoObjectTypes.MediaType.GetFriendlyName(), Constants.ObjectTypes.Strings.MediaType)); - dropDownList.Items.Add(new ListItem(UmbracoObjectTypes.DocumentType.GetFriendlyName(), Constants.ObjectTypes.Strings.DocumentType)); - dropDownList.Items.Add(new ListItem(UmbracoObjectTypes.MemberType.GetFriendlyName(), Constants.ObjectTypes.Strings.MemberType)); - dropDownList.Items.Add(new ListItem(UmbracoObjectTypes.DataType.GetFriendlyName(), Constants.ObjectTypes.Strings.DataType)); - dropDownList.Items.Add(new ListItem(UmbracoObjectTypes.MemberGroup.GetFriendlyName(), Constants.ObjectTypes.Strings.MemberGroup)); - dropDownList.Items.Add(new ListItem(UmbracoObjectTypes.Stylesheet.GetFriendlyName(), Constants.ObjectTypes.Strings.Stylesheet)); - dropDownList.Items.Add(new ListItem(UmbracoObjectTypes.Template.GetFriendlyName(), Constants.ObjectTypes.Strings.Template)); - dropDownList.Items.Add(new ListItem(UmbracoObjectTypes.ROOT.GetFriendlyName(), Constants.ObjectTypes.Strings.SystemRoot)); - dropDownList.Items.Add(new ListItem(UmbracoObjectTypes.RecycleBin.GetFriendlyName(), Constants.ObjectTypes.Strings.ContentRecycleBin)); - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/NewRelationType.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/NewRelationType.aspx.designer.cs deleted file mode 100644 index 5f463c7ad8..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/NewRelationType.aspx.designer.cs +++ /dev/null @@ -1,168 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.cms.presentation.developer.RelationTypes { - - - public partial class NewRelationType { - - /// - /// nameAliasPane control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane nameAliasPane; - - /// - /// nameProperyPanel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel nameProperyPanel; - - /// - /// descriptionTextBox control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox descriptionTextBox; - - /// - /// descriptionRequiredFieldValidator control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator descriptionRequiredFieldValidator; - - /// - /// aliasPropertyPanel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel aliasPropertyPanel; - - /// - /// aliasTextBox control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox aliasTextBox; - - /// - /// aliasRequiredFieldValidator control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator aliasRequiredFieldValidator; - - /// - /// aliasCustomValidator control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CustomValidator aliasCustomValidator; - - /// - /// directionPane control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane directionPane; - - /// - /// PropertyPanel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel PropertyPanel1; - - /// - /// dualRadioButtonList control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RadioButtonList dualRadioButtonList; - - /// - /// objectTypePane control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane objectTypePane; - - /// - /// PropertyPanel2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel PropertyPanel2; - - /// - /// parentDropDownList control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList parentDropDownList; - - /// - /// PropertyPanel3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel PropertyPanel3; - - /// - /// childDropDownList control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList childDropDownList; - - /// - /// addButton control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button addButton; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx deleted file mode 100644 index 160745ca2d..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx +++ /dev/null @@ -1 +0,0 @@ -<%@ WebService Language="C#" CodeBehind="RelationTypesWebService.asmx.cs" Class="umbraco.cms.presentation.developer.RelationTypes.RelationTypesWebService" %> diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx.cs deleted file mode 100644 index 0f8ca29c94..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Web.Services; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Web; -using Umbraco.Web.Composing; - -namespace umbraco.cms.presentation.developer.RelationTypes -{ - /// - /// Webservice to delete relation types, this allows deletion via a javacscript call hooked into the tree UI - /// - [WebService(Namespace = "http://tempuri.org/")] - [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] - [System.ComponentModel.ToolboxItem(false)] - [System.Web.Script.Services.ScriptService] // Allows this Web Service to be called from script, using ASP.NET AJAX - public class RelationTypesWebService : WebService - { - /// - /// Delete an Umbraco RelationType and all it's associated Relations - /// - /// database id of the relation type to delete - [WebMethod] - public void DeleteRelationType(int relationTypeId) - { - var user = UmbracoContext.Current.Security.CurrentUser; - - if (user.IsAdmin()) - { - var relationService = Current.Services.RelationService; - var relationType = relationService.GetRelationTypeById(relationTypeId); - relationService.Delete(relationType); - } - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionDeleteRelationType.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionDeleteRelationType.cs deleted file mode 100644 index cf39b17e55..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionDeleteRelationType.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Umbraco.Web._Legacy.Actions; - -namespace umbraco.cms.presentation.developer.RelationTypes.TreeMenu -{ - /// - /// Delete a Relation Type - an Umbraco tree context menu action - /// - public class ActionDeleteRelationType : IAction - { - /// - /// Private field for the singleton instance - /// - private static readonly ActionDeleteRelationType instance = new ActionDeleteRelationType(); - - /// - /// Gets a singleton instance of this action - /// - public static ActionDeleteRelationType Instance - { - get { return instance; } - } - - #region IAction Members - - /// - /// Gets a string alias used to identify this action - /// - public string Alias - { - get { return "delete"; } - } - - /// - /// Gets a unique char to associate with this action - /// - public char Letter - { - get { return '¤'; } - } - - /// - /// Gets a value indicating whether the Umbraco notification area is used ? - /// - public bool ShowInNotifier - { - get { return false; } - } - - /// - /// Gets a value indicating whether this action can be configured for use only by specific members - /// - public bool CanBePermissionAssigned - { - get { return false; } // Since this tree is in the developer section, no further granular permissions are required - } - - /// - /// Gets an icon to be used for the right click action - /// - public string Icon - { - get { return "delete"; } // delete refers to an existing sprite - } - - /// - /// Gets a string for the javascript source - /// - public string JsSource - { - get { return "developer/RelationTypes/TreeMenu/ActionDeleteRelationType.js"; } - } - - /// - /// Gets a javascript string to execute when this action is fired - /// - public string JsFunctionName - { - get { return "javascript:actionDeleteRelationType(UmbClientMgr.mainTree().getActionNode().nodeId,UmbClientMgr.mainTree().getActionNode().nodeName);"; } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionNewRelationType.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionNewRelationType.cs deleted file mode 100644 index 6018539983..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionNewRelationType.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Umbraco.Web._Legacy.Actions; - -namespace umbraco.cms.presentation.developer.RelationTypes.TreeMenu -{ - /// - /// Create new Relation Type - an Umbraco tree context menu action - /// - public class ActionNewRelationType : IAction - { - /// - /// Private field for the singleton instance - /// - private static readonly ActionNewRelationType instance = new ActionNewRelationType(); - - /// - /// Gets a singleton instance of this action - /// - public static ActionNewRelationType Instance - { - get { return instance; } - } - - #region IAction Members - - /// - /// Gets a string alias used to identify this action - /// - public string Alias - { - get { return "create"; } - } - - /// - /// Gets a unique char to associate with this action - /// - public char Letter - { - get { return '®'; } - } - - /// - /// Gets a value indicating whether the Umbraco notification area is used ? - /// - public bool ShowInNotifier - { - get { return false; } - } - - /// - /// Gets a value indicating whether this action can be configured for use only by specific members - /// - public bool CanBePermissionAssigned - { - get { return false; } // Since this tree is in the developer section, no further granular permissions are required - } - - /// - /// Gets an icon to be used for the right click action - /// - public string Icon - { - get { return "add"; } // add refers to an existing sprite - } - - /// - /// Gets a string for the javascript source - /// - public string JsSource - { - get { return "developer/RelationTypes/TreeMenu/ActionNewRelationType.js"; } - } - - /// - /// Gets a javascript string to execute when this action is fired - /// - public string JsFunctionName - { - get { return "javascript:actionNewRelationType();"; } - } - - #endregion - } -} From a4f270bd0267cf5fe4dd8237fe127150ffdb27e0 Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Sun, 2 Dec 2018 22:33:15 +0000 Subject: [PATCH 16/65] Added localisation --- .../src/views/relationtypes/create.html | 12 +++++----- .../src/views/relationtypes/edit.html | 22 +++++++++---------- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 12 ++++++++++ .../Umbraco/config/lang/en_us.xml | 12 ++++++++++ 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.html index 1af1497953..e5f66c9fe0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.html +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.html @@ -2,30 +2,30 @@
- + - +
- + - +
- +
{{vm.relationType.parentObjectTypeName}}
- +
{{vm.relationType.childObjectTypeName}}
- +
{{vm.relationType.relations.length}}
- +
- - - - + + + + diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index e852be1a4e..fa799b7370 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1946,4 +1946,16 @@ To manage your website, simply open the Umbraco back office and start adding con There is no 'restore' relation found for this node. Use the Move menu item to move it manually.The item you want to restore it under ('%0%') is in the recycle bin. Use the Move menu item to move the item manually. + + Direction + Parent to child + Bidirectional + Parent + Child + Count + Relations + Created + Comment + Name + diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 54d86eee5c..543f7b6f41 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1987,4 +1987,16 @@ To manage your website, simply open the Umbraco back office and start adding con Select your notifications forNotification settings saved for + + Direction + Parent to child + Bidirectional + Parent + Child + Count + Relations + Created + Comment + Name + From c6d09cbcff291f8bd0c3c00f85192b61e0abeff3 Mon Sep 17 00:00:00 2001 From: James Coxhead Date: Sun, 2 Dec 2018 22:53:30 +0000 Subject: [PATCH 17/65] Split relation type and associated relations into tabbed view --- .../views/relationtypes/create.controller.js | 2 +- .../views/relationtypes/edit.controller.js | 26 ++++++-- .../src/views/relationtypes/edit.html | 65 ++----------------- .../relationtypes/views/relationType.html | 40 ++++++++++++ .../views/relationtypes/views/relations.html | 23 +++++++ src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 2 + .../Umbraco/config/lang/en_us.xml | 2 + 7 files changed, 94 insertions(+), 66 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/relationtypes/views/relationType.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/relationtypes/views/relations.html diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js index 7ac95dc4d9..8b1d82cf8f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js @@ -19,7 +19,7 @@ function RelationTypeCreateController($scope, $location, relationTypeResource, n relationTypeResource.getRelationObjectTypes().then(function (data) { vm.objectTypes = data; }, function (err) { - alert("oh no"); + notificationsService.error("Could not load form.") }) } diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js index da32804f6d..ed0845a773 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js @@ -6,7 +6,7 @@ * @description * The controller for editing relation types. */ -function RelationTypeEditController($scope, $routeParams, relationTypeResource, editorState, navigationService, dateHelper, userService, entityResource, formHelper, contentEditingHelper) { +function RelationTypeEditController($scope, $routeParams, relationTypeResource, editorState, navigationService, dateHelper, userService, entityResource, formHelper, contentEditingHelper, localizationService) { var vm = this; @@ -17,11 +17,29 @@ function RelationTypeEditController($scope, $routeParams, relationTypeResource, vm.save = saveRelationType; - if($routeParams.create) { - alert("create"); - } else { + init(); + + function init() { vm.page.loading = true; + localizationService.localizeMany(["relationType_tabRelationType", "relationType_tabRelations"]).then(function (data) { + vm.page.navigation = [ + { + "name": data[0], + "alias": "relationType", + "icon": "icon-info", + "view": "views/relationTypes/views/relationType.html", + "active": true + }, + { + "name": data[1], + "alias": "relations", + "icon": "icon-trafic", + "view": "views/relationTypes/views/relations.html" + } + ]; + }); + relationTypeResource.getById($routeParams.id) .then(function(data) { bindRelationType(data); diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html index 2862e89a60..2c86161bda 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html @@ -7,70 +7,13 @@ name="vm.relationType.name" alias="vm.relationType.alias" hide-description="true" - hide-icon="true"> + hide-icon="true" + navigation="vm.page.navigation"> - - - - -
{{vm.relationType.id}}
- {{vm.relationType.key}} -
- - - -
    -
  • - -
  • -
  • - -
  • -
-
- - - -
{{vm.relationType.parentObjectTypeName}}
-
- - - -
{{vm.relationType.childObjectTypeName}}
-
- - - -
{{vm.relationType.relations.length}}
-
- - - -
-
ParentChildCreatedCommentParentChildCreatedComment
{{relation.parentName}}
- - - - - - - - - - - - -
ParentChildCreatedComment
{{relation.parentName}}{{relation.childName}}{{relation.timestampFormatted}}{{relation.comment}}
-
-
- - + + diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/views/relationType.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/views/relationType.html new file mode 100644 index 0000000000..7f31461e69 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/views/relationType.html @@ -0,0 +1,40 @@ + + + + +
{{model.relationType.id}}
+ {{model.relationType.key}} +
+ + + +
    +
  • + +
  • +
  • + +
  • +
+
+ + + +
{{model.relationType.parentObjectTypeName}}
+
+ + + +
{{model.relationType.childObjectTypeName}}
+
+ + + +
{{model.relationType.relations.length}}
+
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/views/relations.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/views/relations.html new file mode 100644 index 0000000000..ba8d9c00da --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/views/relations.html @@ -0,0 +1,23 @@ + + + + +
+ + + + + + + + + + + + + +
ParentChildCreatedComment
{{relation.parentName}}{{relation.childName}}{{relation.timestampFormatted}}{{relation.comment}}
+
+
+
+
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index fa799b7370..af419ac8ab 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1957,5 +1957,7 @@ To manage your website, simply open the Umbraco back office and start adding con Created Comment Name + Relation Type + Relations diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 543f7b6f41..f61e5bc3b1 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1998,5 +1998,7 @@ To manage your website, simply open the Umbraco back office and start adding con Created Comment Name + Relation Type + Relations From 6d898128d43a077c6cff0f060de978052fce5258 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 7 Dec 2018 15:32:02 +0100 Subject: [PATCH 18/65] Fix nuspec for Examine --- build/NuSpecs/UmbracoCms.Web.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/NuSpecs/UmbracoCms.Web.nuspec b/build/NuSpecs/UmbracoCms.Web.nuspec index e9bd8ca6ea..adf090c69b 100644 --- a/build/NuSpecs/UmbracoCms.Web.nuspec +++ b/build/NuSpecs/UmbracoCms.Web.nuspec @@ -25,7 +25,7 @@ - + From 02617885b43747641aa01e9988e31fe4ac4d0fe4 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 7 Dec 2018 12:06:25 +0100 Subject: [PATCH 19/65] Disable media tests that use Examine (no point) --- src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 784f534af4..508a005663 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -111,6 +111,7 @@ namespace Umbraco.Tests.PublishedContent } [Test] + [Ignore("No point testing with Examine, should refactor this test.")] public void Ensure_Children_Sorted_With_Examine() { var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); @@ -138,6 +139,7 @@ namespace Umbraco.Tests.PublishedContent } [Test] + [Ignore("No point testing with Examine, should refactor this test.")] public void Do_Not_Find_In_Recycle_Bin() { var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); @@ -185,6 +187,7 @@ namespace Umbraco.Tests.PublishedContent } [Test] + [Ignore("No point testing with Examine, should refactor this test.")] public void Children_With_Examine() { var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); @@ -212,6 +215,7 @@ namespace Umbraco.Tests.PublishedContent } [Test] + [Ignore("No point testing with Examine, should refactor this test.")] public void Descendants_With_Examine() { var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); @@ -239,6 +243,7 @@ namespace Umbraco.Tests.PublishedContent } [Test] + [Ignore("No point testing with Examine, should refactor this test.")] public void DescendantsOrSelf_With_Examine() { var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); @@ -266,6 +271,7 @@ namespace Umbraco.Tests.PublishedContent } [Test] + [Ignore("No point testing with Examine, should refactor this test.")] public void Ancestors_With_Examine() { var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); @@ -291,6 +297,7 @@ namespace Umbraco.Tests.PublishedContent } [Test] + [Ignore("No point testing with Examine, should refactor this test.")] public void AncestorsOrSelf_With_Examine() { var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); From 0d83da93274a104426196b0904a9d118a6444d2d Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 7 Dec 2018 12:44:54 +0100 Subject: [PATCH 20/65] Fix some tests --- .../TestControllerActivatorBase.cs | 3 --- .../Testing/TestingTests/MockTests.cs | 1 - .../Web/Mvc/SurfaceControllerTests.cs | 10 +++++++--- src/Umbraco.Web/UmbracoHelper.cs | 14 +++----------- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index c65faf76c9..2cf64f04d1 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -163,11 +163,8 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting var membershipHelper = new MembershipHelper(umbCtx, Mock.Of(), Mock.Of()); - var mockedTypedContent = Mock.Of(); - var umbHelper = new UmbracoHelper(umbCtx, Mock.Of(), - mockedTypedContent, Mock.Of(), Mock.Of(), Mock.Of(), diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index d5f5778d1a..51855f7e19 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -60,7 +60,6 @@ namespace Umbraco.Tests.Testing.TestingTests // ReSharper disable once UnusedVariable var helper = new UmbracoHelper(umbracoContext, Mock.Of(), - Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 81f338da87..dce975d0c4 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -103,6 +103,13 @@ namespace Umbraco.Tests.Web.Mvc { var publishedSnapshot = new Mock(); publishedSnapshot.Setup(x => x.Members).Returns(Mock.Of()); + var contentCache = new Mock(); + var content = new Mock(); + content.Setup(x => x.Id).Returns(2); + contentCache.Setup(x => x.GetById(It.IsAny())).Returns(content.Object); + var mediaCache = new Mock(); + publishedSnapshot.Setup(x => x.Content).Returns(contentCache.Object); + publishedSnapshot.Setup(x => x.Media).Returns(mediaCache.Object); var publishedSnapshotService = new Mock(); publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot.Object); var globalSettings = TestObjects.GetGlobalSettings(); @@ -121,9 +128,6 @@ namespace Umbraco.Tests.Web.Mvc var helper = new UmbracoHelper( umbracoContext, Mock.Of(), - Mock.Of(query => query.Content(It.IsAny()) == - //return mock of IPublishedContent for any call to GetById - Mock.Of(content => content.Id == 2)), Mock.Of(), Mock.Of(), Mock.Of(), diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index fbb739b5c2..3bfa433987 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -11,7 +11,6 @@ using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Core.Xml; using Umbraco.Web.Composing; -using Umbraco.Core.Cache; using Umbraco.Web.Routing; using Umbraco.Web.Security; @@ -28,7 +27,6 @@ namespace Umbraco.Web private readonly UmbracoContext _umbracoContext; private readonly IPublishedContent _currentPage; - private readonly IPublishedContentQuery _iQuery; private readonly ServiceContext _services; private IUmbracoComponentRenderer _componentRenderer; @@ -44,22 +42,18 @@ namespace Umbraco.Web /// /// For tests. internal UmbracoHelper(UmbracoContext umbracoContext, IPublishedContent content, - IPublishedContentQuery query, ITagQuery tagQuery, ICultureDictionary cultureDictionary, IUmbracoComponentRenderer componentRenderer, MembershipHelper membershipHelper, ServiceContext services) { - if (tagQuery == null) throw new ArgumentNullException(nameof(tagQuery)); - _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); - _tag = new TagQuery(tagQuery); + _tag = tagQuery ?? throw new ArgumentNullException(nameof(tagQuery)); _cultureDictionary = cultureDictionary ?? throw new ArgumentNullException(nameof(cultureDictionary)); _componentRenderer = componentRenderer ?? throw new ArgumentNullException(nameof(componentRenderer)); _membershipHelper = membershipHelper ?? throw new ArgumentNullException(nameof(membershipHelper)); _currentPage = content ?? throw new ArgumentNullException(nameof(content)); - _iQuery = query ?? throw new ArgumentNullException(nameof(query)); _services = services ?? throw new ArgumentNullException(nameof(services)); } @@ -105,15 +99,13 @@ namespace Umbraco.Web /// Gets the tag context. /// public ITagQuery TagQuery => _tag ?? - (_tag = new TagQuery(_services.TagService, _iQuery ?? ContentQuery)); + (_tag = new TagQuery(_services.TagService, ContentQuery)); /// /// Gets the query context. /// public IPublishedContentQuery ContentQuery => _query ?? - (_query = _iQuery != null - ? new PublishedContentQuery(_iQuery) - : new PublishedContentQuery(UmbracoContext.ContentCache, UmbracoContext.MediaCache)); + (_query = new PublishedContentQuery(UmbracoContext.ContentCache, UmbracoContext.MediaCache)); /// /// Gets the Umbraco context. From bd91706f898d6b5a24964741fee094980a936f69 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 7 Dec 2018 13:01:18 +0100 Subject: [PATCH 21/65] Cleanup test --- src/Umbraco.Tests/UmbracoExamine/IndexTest.cs | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index 78bdb37cae..29abfb9234 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -187,21 +187,21 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID() { + // create a validator with + // publishedValuesOnly false + // parentId 1116 (only content under that parent will be indexed) + var validator = new ContentValueSetValidator(false, 1116); using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - //make parent id 1116 - validator: new ContentValueSetValidator(false, 1116))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, validator: validator)) using (indexer.ProcessNonAsync()) { var searcher = indexer.GetSearcher(); //get a node from the data repo (this one exists underneath 2222) var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]") - .Root - .Elements() - .Where(x => (int)x.Attribute("id") == 2112) - .First(); + .Root.Elements() + .First(x => (int) x.Attribute("id") == 2112); var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112 Assert.AreEqual("-1,1111,2222,2112", currPath); @@ -230,20 +230,21 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Move_Media_To_Non_Indexable_ParentID() { + // create a validator with + // publishedValuesOnly false + // parentId 2222 (only content under that parent will be indexed) + var validator = new ContentValueSetValidator(false, 2222); + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer1 = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - //make parent id 2222 - validator: new ContentValueSetValidator(false, 2222))) + using (var indexer1 = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, validator: validator)) using (indexer1.ProcessNonAsync()) { var searcher = indexer1.GetSearcher(); //get a node from the data repo (this one exists underneath 2222) var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]") - .Root - .Elements() - .Where(x => (int)x.Attribute("id") == 2112) - .First(); + .Root.Elements() + .First(x => (int) x.Attribute("id") == 2112); var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112 Assert.AreEqual("-1,1111,2222,2112", currPath); @@ -251,8 +252,6 @@ namespace Umbraco.Tests.UmbracoExamine //ensure it's indexed indexer1.IndexItem(node.ConvertToValueSet(IndexTypes.Media)); - - //it will exist because it exists under 2222 var results = searcher.Search(searcher.CreateCriteria().Id(2112).Compile()); Assert.AreEqual(1, results.Count()); @@ -264,8 +263,6 @@ namespace Umbraco.Tests.UmbracoExamine //now reindex the node, this should first delete it and then NOT add it because of the parent id constraint indexer1.IndexItems(new[] { node.ConvertToValueSet(IndexTypes.Media) }); - - //now ensure it's deleted results = searcher.Search(searcher.CreateCriteria().Id(2112).Compile()); Assert.AreEqual(0, results.Count()); From 355f02971d495aa24a9c15755fcc8f28605425e9 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 12 Nov 2018 09:42:42 +0100 Subject: [PATCH 22/65] Fix the mardown editor button bar in split view --- .../markdowneditor/markdowneditor.controller.js | 6 +++++- .../propertyeditors/markdowneditor/markdowneditor.html | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js index 639c24f54c..b3c014f54a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js @@ -8,6 +8,10 @@ function MarkdownEditorController($scope, $element, assetsService, editorService $scope.model.value = $scope.model.config.defaultValue; } + // create a unique ID for the markdown editor, so the button bar bindings can handle split view + // - must be bound on scope, not scope.model - otherwise it won't work, because $scope.model is used in both sides of the split view + $scope.editorId = $scope.model.alias + _.uniqueId("-"); + function openMediaPicker(callback) { var mediaPicker = { disableFolderSelect: true, @@ -40,7 +44,7 @@ function MarkdownEditorController($scope, $element, assetsService, editorService $timeout(function () { $scope.markdownEditorInitComplete = false; var converter2 = new Markdown.Converter(); - var editor2 = new Markdown.Editor(converter2, "-" + $scope.model.alias); + var editor2 = new Markdown.Editor(converter2, "-" + $scope.editorId); editor2.run(); //subscribe to the image dialog clicks diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.html index 9b8f44317a..68fc86f002 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.html @@ -1,8 +1,8 @@
-
+
- + -
+
From a07227849073d6f9a697fdf89bd708bb16fe5f04 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 25 Nov 2018 13:50:54 +0100 Subject: [PATCH 23/65] Fix the changing source name when copying a node --- .../src/views/content/content.copy.controller.js | 8 ++++---- src/Umbraco.Web.UI.Client/src/views/content/copy.html | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js index 2642a79ac7..a1e74cc207 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js @@ -25,11 +25,11 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController", $scope.treeModel.hideHeader = userData.startContentIds.length > 0 && userData.startContentIds.indexOf(-1) == -1; }); - var node = $scope.currentNode; + $scope.source = _.clone($scope.currentNode); function treeLoadedHandler(args) { - if (node && node.path) { - $scope.dialogTreeApi.syncTree({ path: node.path, activate: false }); + if ($scope.source && $scope.source.path) { + $scope.dialogTreeApi.syncTree({ path: $scope.source.path, activate: false }); } } @@ -107,7 +107,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController", $scope.busy = true; $scope.error = false; - contentResource.copy({ parentId: $scope.target.id, id: node.id, relateToOriginal: $scope.relateToOriginal, recursive: $scope.recursive }) + contentResource.copy({ parentId: $scope.target.id, id: $scope.source.id, relateToOriginal: $scope.relateToOriginal, recursive: $scope.recursive }) .then(function (path) { $scope.error = false; $scope.success = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/copy.html b/src/Umbraco.Web.UI.Client/src/views/content/copy.html index 03a024c439..9fff73f1cc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/copy.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/copy.html @@ -11,14 +11,14 @@
- {{currentNode.name}} was copied to + {{source.name}} was copied to {{target.name}}

- Choose where to copy {{currentNode.name}} to in the tree structure below + Choose where to copy {{source.name}} to in the tree structure below

From 9b4a580734161f1ce3040fa5b3711eadb5691d5a Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 25 Nov 2018 13:35:14 +0100 Subject: [PATCH 24/65] Fix the changing source name when moving a node --- .../src/views/content/content.move.controller.js | 8 ++++---- src/Umbraco.Web.UI.Client/src/views/content/move.html | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js index a593bbad24..5dceff2571 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js @@ -22,11 +22,11 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController", $scope.treeModel.hideHeader = userData.startContentIds.length > 0 && userData.startContentIds.indexOf(-1) == -1; }); - var node = $scope.currentNode; + $scope.source = _.clone($scope.currentNode); function treeLoadedHandler(args) { - if (node && node.path) { - $scope.dialogTreeApi.syncTree({ path: node.path, activate: false }); + if ($scope.source && $scope.source.path) { + $scope.dialogTreeApi.syncTree({ path: $scope.source.path, activate: false }); } } @@ -84,7 +84,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController", $scope.busy = true; $scope.error = false; - contentResource.move({ parentId: $scope.target.id, id: node.id }) + contentResource.move({ parentId: $scope.target.id, id: $scope.source.id }) .then(function (path) { $scope.error = false; $scope.success = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/move.html b/src/Umbraco.Web.UI.Client/src/views/content/move.html index 708c3d3f82..b655917f6f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/move.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/move.html @@ -11,14 +11,14 @@
- {{currentNode.name}} was moved underneath {{target.name}} + {{source.name}} was moved underneath {{target.name}}
- +

Choose where to move - {{currentNode.name}} + {{source.name}} to in the tree structure below

From 0c9ecacca02f10e43fa2610f3abb991db5d5a263 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 25 Nov 2018 13:44:02 +0100 Subject: [PATCH 25/65] Undo the leftovers from another issue --- src/Umbraco.Web.UI.Client/src/views/content/move.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/move.html b/src/Umbraco.Web.UI.Client/src/views/content/move.html index b655917f6f..2607343a0d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/move.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/move.html @@ -13,7 +13,7 @@
{{source.name}} was moved underneath {{target.name}}
- +

From 14fd9c835dd9baea80d61a95157bbab022bc4459 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 9 Dec 2018 14:17:57 +0100 Subject: [PATCH 26/65] V8: Fix the media picker folder creation and "sub folders" toggle button (#3630) --- .../mediapicker/mediapicker.controller.js | 15 +++++++++------ .../infiniteeditors/mediapicker/mediapicker.html | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index c7a46c4c4a..a274c25287 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -4,9 +4,12 @@ angular.module("umbraco") function($scope, mediaResource, entityResource, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService) { if (!$scope.model.title) { - localizationService.localize("defaultdialogs_selectMedia") - .then(function(data){ - $scope.model.title = data; + localizationService.localizeMany(["defaultdialogs_selectMedia", "general_includeFromsubFolders"]) + .then(function (data) { + $scope.labels = { + title: data[0], + includeSubFolders: data[1] + } }); } @@ -113,10 +116,10 @@ angular.module("umbraco") }; $scope.submitFolder = function() { - if ($scope.newFolderName) { + if ($scope.model.newFolderName) { $scope.creatingFolder = true; mediaResource - .addFolder($scope.newFolderName, $scope.currentFolder.id) + .addFolder($scope.model.newFolderName, $scope.currentFolder.id) .then(function(data) { //we've added a new folder so lets clear the tree cache for that specific item treeService.clearCache({ @@ -126,7 +129,7 @@ angular.module("umbraco") $scope.creatingFolder = false; $scope.gotoFolder(data); $scope.showFolderInput = false; - $scope.newFolderName = ""; + $scope.model.newFolderName = ""; }); } else { $scope.showFolderInput = false; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html index 5046088d28..73a073b731 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html @@ -68,7 +68,7 @@ - From 573db0d142acb01c832a668209106f8dddfd8d30 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 9 Dec 2018 14:25:43 +0100 Subject: [PATCH 27/65] V8 fix remove custom event directives (#3622) --- .../components/events/events.directive.js | 48 ------------------- .../common/infiniteeditors/embed/embed.html | 4 +- .../mediapicker/mediapicker.html | 2 +- .../views/components/umb-groups-builder.html | 6 +-- .../views/components/umb-locked-field.html | 2 +- .../src/views/propertyeditors/tags/tags.html | 2 +- 6 files changed, 8 insertions(+), 56 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js index 21980be925..a6a5336af0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js @@ -3,54 +3,6 @@ **/ angular.module('umbraco.directives') -.directive('onKeyup', function () { - return { - link: function (scope, elm, attrs) { - var f = function () { - scope.$apply(attrs.onKeyup); - }; - elm.on("keyup", f); - scope.$on("$destroy", function(){ elm.off("keyup", f);} ); - } - }; -}) - -.directive('onKeydown', function () { - return { - link: function (scope, elm, attrs) { - var f = function () { - scope.$apply(attrs.onKeydown); - }; - elm.on("keydown", f); - scope.$on("$destroy", function(){ elm.off("keydown", f);} ); - } - }; -}) - -.directive('onBlur', function () { - return { - link: function (scope, elm, attrs) { - var f = function () { - scope.$apply(attrs.onBlur); - }; - elm.on("blur", f); - scope.$on("$destroy", function(){ elm.off("blur", f);} ); - } - }; -}) - -.directive('onFocus', function () { - return { - link: function (scope, elm, attrs) { - var f = function () { - scope.$apply(attrs.onFocus); - }; - elm.on("focus", f); - scope.$on("$destroy", function(){ elm.off("focus", f);} ); - } - }; -}) - .directive('onDragEnter', function () { return { link: function (scope, elm, attrs) { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html index f6a641f2af..f14fb364ab 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html @@ -32,11 +32,11 @@

- + - + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html index 73a073b731..3bb694cac6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html @@ -69,7 +69,7 @@ + ng-blur="submitFolder()" focus-when="{{showFolderInput}}" />
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html index 31ea24b581..c0490a1707 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html @@ -65,7 +65,7 @@ ng-disabled="tab.inherited" umb-auto-focus umb-auto-resize - on-focus="activateGroup(tab)" + ng-focus="activateGroup(tab)" required val-server-field="{{'Groups[' + $index + '].Name'}}" /> @@ -87,7 +87,7 @@
- +
@@ -121,7 +121,7 @@ hotkey="alt+shift+p" hotkey-when="{{tab.tabState === 'active' && property.propertyState=='init'}}" ng-click="addProperty(property, tab)" - on-focus="activateGroup(tab)" + ng-focus="activateGroup(tab)" focus-when="{{property.focus}}"> diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-locked-field.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-locked-field.html index 56c5be6fb2..d13474d221 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-locked-field.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-locked-field.html @@ -24,7 +24,7 @@ title="{{ngModel}}" focus-when="{{!locked}}" umb-select-when="{{!locked}}" - on-blur="lock()" /> + ng-blur="lock()" />
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.html index f1d46e951e..6fcdfafdcd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.html @@ -26,7 +26,7 @@ class="typeahead tags-{{model.alias}}" ng-model="$parent.tagToAdd" ng-keydown="$parent.addTagOnEnter($event)" - on-blur="$parent.addTag()" + ng-blur="$parent.addTag()" localize="placeholder" placeholder="@placeholders_enterTags" /> From e3e3e0645845ca07264f7afafb00ce4db58dc151 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 12 Nov 2018 10:29:11 +0100 Subject: [PATCH 28/65] Markdown editor: Fix the redo button + remove the button hover flicker --- src/Umbraco.Web.UI.Client/lib/markdown/markdown.css | 5 +++++ src/Umbraco.Web.UI.Client/lib/markdown/markdown.editor.js | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.css b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.css index 45c9b39c48..5594f70634 100644 --- a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.css +++ b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.css @@ -26,6 +26,11 @@ width: 100%; } +/* unset the negative margin applied in button-groups.less to avoid flickering when hovering the button bar */ +.wmd-panel .btn-toolbar .btn-group>.btn+.btn { + margin-left: 0; +} + /* .icon-link, .icon-blockquote, diff --git a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.editor.js b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.editor.js index 21ad5d0a8b..e1617d8365 100644 --- a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.editor.js +++ b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.editor.js @@ -1390,7 +1390,7 @@ "Redo - Ctrl+Y" : "Redo - Ctrl+Shift+Z"; // mac and other non-Windows platforms - buttons.redo = makeButton("wmd-redo-button", redoTitle, "icon-share-alt", null, group4); + buttons.redo = makeButton("wmd-redo-button", redoTitle, "icon-redo", null, group4); buttons.redo.execute = function (manager) { if (manager) manager.redo(); }; if (helpOptions) { @@ -2107,4 +2107,4 @@ } -})(); \ No newline at end of file +})(); From e25e509d8467ddaa0ac4de410fe04fc1ce316f20 Mon Sep 17 00:00:00 2001 From: Andy Neil Date: Thu, 8 Nov 2018 15:45:59 +0000 Subject: [PATCH 29/65] Resolved check for removed properties --- .../Repositories/Implement/ContentTypeRepositoryBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 3184c69dfe..6be07a4c3d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -328,7 +328,7 @@ AND umbracoNode.id <> @id", // We check if the entity's own PropertyTypes has been modified and then also check // any of the property groups PropertyTypes has been modified. // This specifically tells us if any property type collections have changed. - if (entity.IsPropertyDirty("PropertyTypes") || entity.PropertyGroups.Any(x => x.IsPropertyDirty("PropertyTypes"))) + if (entity.IsPropertyDirty("PropertyTypes") || entity.PropertyTypes.Any(pt => pt.IsDirty())) { var dbPropertyTypes = Database.Fetch("WHERE contentTypeId = @Id", new { entity.Id }); var dbPropertyTypeAlias = dbPropertyTypes.Select(x => x.Id); From a51d241dc223f2bf52ebc25279b22ceb32f5a309 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 6 Nov 2018 15:37:18 +0100 Subject: [PATCH 30/65] Fix missing "opens dialog dots" for menu items created using the MenuItemList helper methods --- src/Umbraco.Web/Models/Trees/MenuItemList.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/Models/Trees/MenuItemList.cs b/src/Umbraco.Web/Models/Trees/MenuItemList.cs index b34f0b4444..70b35e25bd 100644 --- a/src/Umbraco.Web/Models/Trees/MenuItemList.cs +++ b/src/Umbraco.Web/Models/Trees/MenuItemList.cs @@ -44,10 +44,11 @@ namespace Umbraco.Web.Models.Trees /// /// /// The text to display for the menu item, will default to the IAction alias if not specified - public MenuItem Add(string name, bool hasSeparator = false) + /// Whether or not this action opens a dialog + public MenuItem Add(string name, bool hasSeparator = false, bool opensDialog = false) where T : IAction { - var item = CreateMenuItem(name, hasSeparator); + var item = CreateMenuItem(name, hasSeparator, opensDialog); if (item != null) { Add(item); @@ -62,11 +63,11 @@ namespace Umbraco.Web.Models.Trees /// /// /// The used to localize the action name based on it's alias - /// + /// Whether or not this action opens a dialog public MenuItem Add(ILocalizedTextService textService, bool hasSeparator = false, bool opensDialog = false) where T : IAction { - var item = CreateMenuItem(textService, hasSeparator); + var item = CreateMenuItem(textService, hasSeparator, opensDialog); if (item != null) { Add(item); @@ -75,14 +76,15 @@ namespace Umbraco.Web.Models.Trees return null; } - internal MenuItem CreateMenuItem(string name, bool hasSeparator = false) + internal MenuItem CreateMenuItem(string name, bool hasSeparator = false, bool opensDialog = false) where T : IAction { var item = Current.Actions.GetAction(); if (item == null) return null; var menuItem = new MenuItem(item, name) { - SeperatorBefore = hasSeparator + SeperatorBefore = hasSeparator, + OpensDialog = opensDialog }; return menuItem; From c860a6b574123beb0a94b6c300c7cd07b2837773 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 9 Dec 2018 14:58:52 +0100 Subject: [PATCH 31/65] Pick members instead of entering username and password --- .../src/common/resources/content.resource.js | 14 ++-- src/Umbraco.Web.UI.Client/src/less/hacks.less | 1 - .../content/content.protect.controller.js | 53 +++++++++++++-- .../src/views/content/protect.html | 30 +++++---- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 6 +- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 8 +-- .../Umbraco/config/lang/en_us.xml | 8 +-- src/Umbraco.Web/Editors/ContentController.cs | 64 ++++++++----------- .../Models/ContentEditing/PublicAccess.cs | 7 +- 9 files changed, 110 insertions(+), 81 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index b7ea16ea19..681a629ed0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -990,27 +990,25 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * * * @param {Int} contentId The content Id - * @param {String} userName The name of the user that should have access (if using specific user protection) - * @param {String} password The password for the user that should have access (if using specific user protection) * @param {Array} roles The roles that should have access (if using role based protection) + * @param {Array} memberIds The IDs of the members that should have access (if using member based protection) * @param {Int} loginPageId The Id of the login page * @param {Int} errorPageId The Id of the error page * @returns {Promise} resourcePromise object containing the public access protection * */ - updatePublicAccess: function (contentId, userName, password, roles, loginPageId, errorPageId) { + updatePublicAccess: function (contentId, roles, memberIds, loginPageId, errorPageId) { var publicAccess = { contentId: contentId, loginPageId: loginPageId, errorPageId: errorPageId }; - if (userName && password) { - publicAccess.userName = userName; - publicAccess.password = password; - } - else if (angular.isArray(roles) && roles.length) { + if (angular.isArray(roles) && roles.length) { publicAccess.roles = roles; } + else if (angular.isArray(memberIds) && memberIds.length) { + publicAccess.memberIds = memberIds; + } else { throw "must supply either userName/password or roles"; } diff --git a/src/Umbraco.Web.UI.Client/src/less/hacks.less b/src/Umbraco.Web.UI.Client/src/less/hacks.less index cd32c64782..0bbb89a250 100644 --- a/src/Umbraco.Web.UI.Client/src/less/hacks.less +++ b/src/Umbraco.Web.UI.Client/src/less/hacks.less @@ -106,7 +106,6 @@ iframe, .content-column-body { display: flex; flex-wrap: nowrap; flex-direction: row; - justify-content: center; align-items: flex-start; margin-top: 15px; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js index 4504ebd372..ace37d4ac5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js @@ -18,6 +18,8 @@ vm.pickErrorPage = pickErrorPage; vm.pickGroup = pickGroup; vm.removeGroup = removeGroup; + vm.pickMember = pickMember; + vm.removeMember = removeMember; vm.removeProtection = removeProtection; vm.removeProtectionConfirm = removeProtectionConfirm; @@ -29,24 +31,25 @@ // get the current public access protection contentResource.getPublicAccess(id).then(function (publicAccess) { + vm.loading = false; + // init the current settings for public access (if any) vm.loginPage = publicAccess.loginPage; vm.errorPage = publicAccess.errorPage; - vm.userName = publicAccess.userName; - vm.roles = publicAccess.roles; + vm.roles = publicAccess.roles || []; + vm.members = publicAccess.members || []; vm.canRemove = true; - if (vm.userName) { - vm.type = "user"; + if (vm.members.length) { + vm.type = "member"; next(); } - else if (vm.roles) { + else if (vm.roles.length) { vm.type = "role"; next(); } else { vm.canRemove = false; - vm.loading = false; } }); } @@ -89,7 +92,8 @@ function save() { vm.buttonState = "busy"; var roles = _.map(vm.groups, function (group) { return group.name; }); - contentResource.updatePublicAccess(id, vm.userName, vm.password, roles, vm.loginPage.id, vm.errorPage.id).then( + var memberIds = _.map(vm.members, function (member) { return member.id; }); + contentResource.updatePublicAccess(id, roles, memberIds, vm.loginPage.id, vm.errorPage.id).then( function () { localizationService.localize("publicAccess_paIsProtected", [$scope.currentNode.name]).then(function (value) { vm.success = { @@ -143,6 +147,41 @@ vm.groups = _.without(vm.groups, group); } + function pickMember() { + navigationService.allowHideDialog(false); + // TODO: once editorService has a memberPicker method, use that instead + editorService.treePicker({ + multiPicker: true, + entityType: "Member", + section: "member", + treeAlias: "member", + filter: function (i) { + return i.metaData.isContainer; + }, + filterCssClass: "not-allowed", + submit: function (model) { + if (model.selection && model.selection.length) { + _.each(model.selection, + function (member) { + if (!_.find(vm.members, function (m) { return m.id === member.id })) { + vm.members.push(member); + } + }); + } + editorService.close(); + navigationService.allowHideDialog(true); + }, + close: function () { + editorService.close(); + navigationService.allowHideDialog(true); + } + }); + } + + function removeMember(member) { + vm.members = _.without(vm.members, member); + } + function pickLoginPage() { pickPage(vm.loginPage); } diff --git a/src/Umbraco.Web.UI.Client/src/views/content/protect.html b/src/Umbraco.Web.UI.Client/src/views/content/protect.html index 4b2bf54b5c..f179f1c63c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/protect.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/protect.html @@ -20,11 +20,11 @@
- + -
@@ -40,16 +40,18 @@
-
-

Set the login and password for this page

+
+

Select the members that should have access to this page

- - - - - - - + + + + Add +
@@ -72,7 +74,7 @@
-
+

Select the pages that contain login form and error messages

diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index f30b4aa7bc..c0377e16ee 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -886,9 +886,9 @@ Mange hilsner fra Umbraco robotten %0%?]]> Vælg siderne der indeholder log ind-formularer og fejlmeddelelser %0%]]> - %0%]]> - Enkel brugerbeskyttelse - Hvis du blot ønsker at opsætte simpel beskyttelse ved hjælp af et enkelt login og kodeord + %0%]]> + Adgang til enkelte medlemmer + Hvis du ønsker at give adgang til enkelte medlemmer Udgivelsen kunne ikke udgives da publiceringsdato er sat diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 49111e9eab..f61186a98f 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1137,10 +1137,10 @@ To manage your website, simply open the Umbraco back office and start adding con Remove protection... %0%?]]> Select the pages that contain login form and error messages - %0%]]> - %0%]]> - Single user protection - If you just want to setup simple protection using a single login and password + %0%]]> + %0%]]> + Specific members protection + If you wish to grant access to specific members Remove protection... %0%?]]> Select the pages that contain login form and error messages - %0%]]> - %0%]]> - Single user protection - If you just want to setup simple protection using a single login and password + %0%]]> + %0%]]> + Specific members protection + If you wish to grant access to specific members Insufficient user permissions to publish all descendant documents diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index b7a33c3ab8..015354f0c6 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -2173,9 +2173,12 @@ namespace Umbraco.Web.Editors // unwrap the current public access setup for the client // - this API method is the single point of entry for both "modes" of public access (single user and role based) - var userName = entry.Rules - .FirstOrDefault(rule => rule.RuleType == Constants.Conventions.PublicAccess.MemberUsernameRuleType) - ?.RuleValue; + var members = entry.Rules + .Where(rule => rule.RuleType == Constants.Conventions.PublicAccess.MemberUsernameRuleType) + .Select(rule => Services.MemberService.GetByUsername(rule.RuleValue)) + .Where(member => member != null) + .Select(Mapper.Map) + .ToArray(); var roles = entry.Rules .Where(rule => rule.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType) .Select(rule => rule.RuleValue) @@ -2183,7 +2186,7 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(HttpStatusCode.OK, new PublicAccess { - UserName = userName, + Members = members, Roles = roles, LoginPage = loginPageEntity != null ? Mapper.Map(loginPageEntity) : null, ErrorPage = errorPageEntity != null ? Mapper.Map(errorPageEntity) : null @@ -2193,9 +2196,9 @@ namespace Umbraco.Web.Editors // set up public access using role based access [EnsureUserPermissionForContent("contentId", ActionProtect.ActionLetter)] [HttpPost] - public HttpResponseMessage PostPublicAccess(int contentId, [FromUri]string[] roles, int loginPageId, int errorPageId) + public HttpResponseMessage PostPublicAccess(int contentId, [FromUri]string[] roles, [FromUri]int[] memberIds, int loginPageId, int errorPageId) { - if (roles == null || roles.Any() == false) + if ((roles == null || roles.Any() == false) && (userIds == null || userIds.Any() == false)) { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest)); } @@ -2208,14 +2211,23 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest)); } + var isRoleBased = roles != null && roles.Any(); + var candidateRuleValues = isRoleBased + ? roles + : memberIds.Select(id => Services.MemberService.GetById(id)?.Username).Where(s => s != null).ToArray(); + var newRuleType = isRoleBased + ? Constants.Conventions.PublicAccess.MemberRoleRuleType + : Constants.Conventions.PublicAccess.MemberUsernameRuleType; + var entry = Services.PublicAccessService.GetEntryForContent(content); if (entry == null) { entry = new PublicAccessEntry(content, loginPage, errorPage, new List()); - foreach (var role in roles) + + foreach (var role in candidateRuleValues) { - entry.AddRule(role, Constants.Conventions.PublicAccess.MemberRoleRuleType); + entry.AddRule(role, newRuleType); } } else @@ -2225,12 +2237,12 @@ namespace Umbraco.Web.Editors var currentRules = entry.Rules.ToArray(); var obsoleteRules = currentRules.Where(rule => - rule.RuleType != Constants.Conventions.PublicAccess.MemberRoleRuleType - || roles.Contains(rule.RuleValue) == false + rule.RuleType != newRuleType + || candidateRuleValues.Contains(rule.RuleValue) == false ); - var newRoles = roles.Where(group => + var newRuleValues = candidateRuleValues.Where(group => currentRules.Any(rule => - rule.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType + rule.RuleType == newRuleType && rule.RuleValue == group ) == false ); @@ -2238,9 +2250,9 @@ namespace Umbraco.Web.Editors { entry.RemoveRule(rule); } - foreach (var role in newRoles) + foreach (var role in newRuleValues) { - entry.AddRule(role, Constants.Conventions.PublicAccess.MemberRoleRuleType); + entry.AddRule(role, newRuleType); } } @@ -2249,30 +2261,6 @@ namespace Umbraco.Web.Editors : Request.CreateResponse(HttpStatusCode.InternalServerError); } - // set up public access using username and password - [EnsureUserPermissionForContent("contentId", ActionProtect.ActionLetter)] - [HttpPost] - public HttpResponseMessage PostPublicAccess(int contentId, string userName, string password, int loginPageId, int errorPageId) - { - // TODO KJAC: validate password regex - if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password)) - { - throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest)); - } - - var content = Services.ContentService.GetById(contentId); - var loginPage = Services.ContentService.GetById(loginPageId); - var errorPage = Services.ContentService.GetById(errorPageId); - if (content == null || loginPage == null || errorPage == null) - { - throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest)); - } - - // TODO KJAC: implement - - return Request.CreateResponse(HttpStatusCode.OK); - } - [EnsureUserPermissionForContent("contentId", ActionProtect.ActionLetter)] [HttpPost] public HttpResponseMessage RemovePublicAccess(int contentId) diff --git a/src/Umbraco.Web/Models/ContentEditing/PublicAccess.cs b/src/Umbraco.Web/Models/ContentEditing/PublicAccess.cs index 68236d5934..b8035c9f25 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PublicAccess.cs +++ b/src/Umbraco.Web/Models/ContentEditing/PublicAccess.cs @@ -5,8 +5,8 @@ namespace Umbraco.Web.Models.ContentEditing [DataContract(Name = "publicAccess", Namespace = "")] public class PublicAccess { - [DataMember(Name = "userName")] - public string UserName { get; set; } + //[DataMember(Name = "userName")] + //public string UserName { get; set; } [DataMember(Name = "roles")] public string[] Roles { get; set; } @@ -16,5 +16,8 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "errorPage")] public EntityBasic ErrorPage { get; set; } + + [DataMember(Name = "members")] + public MemberDisplay[] Members { get; set; } } } From a8b0ede770a5d68fc44c7f593c86a94c2a2d41c7 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 9 Dec 2018 15:03:35 +0100 Subject: [PATCH 32/65] Whoops forgot a file --- src/Umbraco.Web/Editors/ContentController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 015354f0c6..e245883ed7 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -2198,7 +2198,7 @@ namespace Umbraco.Web.Editors [HttpPost] public HttpResponseMessage PostPublicAccess(int contentId, [FromUri]string[] roles, [FromUri]int[] memberIds, int loginPageId, int errorPageId) { - if ((roles == null || roles.Any() == false) && (userIds == null || userIds.Any() == false)) + if ((roles == null || roles.Any() == false) && (memberIds == null || memberIds.Any() == false)) { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest)); } From c00b3e283dd1a2adc48c07d6ac72b51be2c06623 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 28 Nov 2018 12:37:22 +0100 Subject: [PATCH 33/65] Add dirty tracking for slider and datetime --- .../propertyeditors/datepicker/datepicker.controller.js | 1 + .../src/views/propertyeditors/slider/slider.controller.js | 5 +++-- .../src/views/propertyeditors/slider/slider.html | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js index 704fccbde1..62099734fb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js @@ -131,6 +131,7 @@ function dateTimePickerController($scope, notificationsService, assetsService, a else { $scope.model.value = null; } + angularHelper.getCurrentForm($scope).$setDirty(); } /** Sets the value of the date picker control adn associated viewModel objects based on the model value */ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js index c8813ee4c0..515eddf0a3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js @@ -1,4 +1,4 @@ -function sliderController($scope) { +function sliderController($scope, angularHelper) { let sliderRef = null; @@ -14,13 +14,14 @@ function setModelValue(values) { $scope.model.value = values ? values.toString() : null; + angularHelper.getCurrentForm($scope).$setDirty(); } $scope.setup = function(slider) { sliderRef = slider; }; - $scope.end = function(values) { + $scope.change = function (values) { setModelValue(values); }; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.html index c147b30b23..ea0c2ef7bc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.html @@ -5,7 +5,7 @@ ng-model="sliderValue" options="sliderOptions" on-setup="setup(slider)" - on-end="end(values)"> + on-change="change(values)">
From 8ff697ad98ae9bc53569061fe18603a127650a24 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 26 Nov 2018 22:47:42 +0100 Subject: [PATCH 34/65] Fix the flickering source name and the close button when moving items in the media tree. --- .../src/views/media/media.move.controller.js | 8 ++++---- src/Umbraco.Web.UI.Client/src/views/media/move.html | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.move.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.move.controller.js index c5ff80b7fb..8bb1dc3ac8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/media.move.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/media/media.move.controller.js @@ -3,7 +3,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.MoveController", function ($scope, userService, eventsService, mediaResource, appState, treeService, navigationService) { $scope.dialogTreeApi = {}; - var node = $scope.currentNode; + $scope.source = _.clone($scope.currentNode); $scope.treeModel = { hideHeader: false @@ -13,8 +13,8 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.MoveController", }); function treeLoadedHandler(args) { - if (node && node.path) { - $scope.dialogTreeApi.syncTree({ path: node.path, activate: false }); + if ($scope.source && $scope.source.path) { + $scope.dialogTreeApi.syncTree({ path: $scope.source.path, activate: false }); } } @@ -55,7 +55,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.MoveController", $scope.move = function () { $scope.busy = true; - mediaResource.move({ parentId: $scope.target.id, id: node.id }) + mediaResource.move({ parentId: $scope.target.id, id: $scope.source.id }) .then(function (path) { $scope.busy = false; $scope.error = false; diff --git a/src/Umbraco.Web.UI.Client/src/views/media/move.html b/src/Umbraco.Web.UI.Client/src/views/media/move.html index 6d93eb4e22..451acbdb16 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/move.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/move.html @@ -11,14 +11,14 @@
- {{currentNode.name}} was moved underneath {{target.name}} + {{source.name}} was moved underneath {{target.name}}
- +

Choose where to move - {{currentNode.name}} + {{source.name}} to in the tree structure below

From 939b320971485396e25cdb1582e9c70875149bcc Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 25 Nov 2018 13:23:27 +0100 Subject: [PATCH 35/65] Fix the OK button for move and close success messages --- src/Umbraco.Web.UI.Client/src/views/content/copy.html | 2 +- src/Umbraco.Web.UI.Client/src/views/content/move.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/copy.html b/src/Umbraco.Web.UI.Client/src/views/content/copy.html index 9fff73f1cc..8f6d5d3e6d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/copy.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/copy.html @@ -14,7 +14,7 @@ {{source.name}} was copied to {{target.name}}
- +

diff --git a/src/Umbraco.Web.UI.Client/src/views/content/move.html b/src/Umbraco.Web.UI.Client/src/views/content/move.html index 2607343a0d..b655917f6f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/move.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/move.html @@ -13,7 +13,7 @@

{{source.name}} was moved underneath {{target.name}}
- +

From c2a8378b899d22dd97398d0281f1f3f26a83289b Mon Sep 17 00:00:00 2001 From: Jannik Anker Date: Thu, 6 Dec 2018 11:03:08 +0100 Subject: [PATCH 36/65] V8, Getting Started: typo in URL Using HHTTPS as protocol just doesn't work :-/ --- .github/V8_GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/V8_GETTING_STARTED.md b/.github/V8_GETTING_STARTED.md index 87ddbb166a..1cc33bb126 100644 --- a/.github/V8_GETTING_STARTED.md +++ b/.github/V8_GETTING_STARTED.md @@ -33,5 +33,5 @@ We recommend running the site with the Visual Studio since you'll be able to rem We are keeping track of [known issues and limitations here](http://issues.umbraco.org/issue/U4-11279). These line items will eventually be turned into actual tasks to be worked on. Feel free to help us keep this list updated if you find issues and even help fix some of these items. If there is a particular item you'd like to help fix please mention this on the task and we'll create a sub task for the item to continue discussion there. -There's [a list of tasks for v8 that haven't been completed](hhttps://github.com/umbraco/Umbraco-CMS/labels/release%2F8.0.0). If you are interested in helping out with any of these please mention this on the task. This list will be constantly updated as we begin to document and design some of the other tasks that still need to get done. +There's [a list of tasks for v8 that haven't been completed](https://github.com/umbraco/Umbraco-CMS/labels/release%2F8.0.0). If you are interested in helping out with any of these please mention this on the task. This list will be constantly updated as we begin to document and design some of the other tasks that still need to get done. From 852b44af0e0cb74687bd06d5ec248951bbaafac2 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sat, 1 Dec 2018 12:16:22 +0100 Subject: [PATCH 37/65] Fix member roles assignment YSOD --- src/Umbraco.Core/Services/Implement/MemberService.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Services/Implement/MemberService.cs b/src/Umbraco.Core/Services/Implement/MemberService.cs index 5a644cfec1..e191493736 100644 --- a/src/Umbraco.Core/Services/Implement/MemberService.cs +++ b/src/Umbraco.Core/Services/Implement/MemberService.cs @@ -1033,7 +1033,7 @@ namespace Umbraco.Core.Services.Implement scope.WriteLock(Constants.Locks.MemberTree); var ids = _memberGroupRepository.GetMemberIds(usernames); _memberGroupRepository.AssignRoles(ids, roleNames); - scope.Events.Dispatch(AssignedRoles, this, new RolesEventArgs(ids, roleNames)); + scope.Events.Dispatch(AssignedRoles, this, new RolesEventArgs(ids, roleNames), nameof(AssignedRoles)); scope.Complete(); } } @@ -1050,7 +1050,7 @@ namespace Umbraco.Core.Services.Implement scope.WriteLock(Constants.Locks.MemberTree); var ids = _memberGroupRepository.GetMemberIds(usernames); _memberGroupRepository.DissociateRoles(ids, roleNames); - scope.Events.Dispatch(RemovedRoles, this, new RolesEventArgs(ids, roleNames)); + scope.Events.Dispatch(RemovedRoles, this, new RolesEventArgs(ids, roleNames), nameof(RemovedRoles)); scope.Complete(); } } @@ -1066,7 +1066,7 @@ namespace Umbraco.Core.Services.Implement { scope.WriteLock(Constants.Locks.MemberTree); _memberGroupRepository.AssignRoles(memberIds, roleNames); - scope.Events.Dispatch(AssignedRoles, this, new RolesEventArgs(memberIds, roleNames)); + scope.Events.Dispatch(AssignedRoles, this, new RolesEventArgs(memberIds, roleNames), nameof(AssignedRoles)); scope.Complete(); } } @@ -1082,7 +1082,7 @@ namespace Umbraco.Core.Services.Implement { scope.WriteLock(Constants.Locks.MemberTree); _memberGroupRepository.DissociateRoles(memberIds, roleNames); - scope.Events.Dispatch(RemovedRoles, this, new RolesEventArgs(memberIds, roleNames)); + scope.Events.Dispatch(RemovedRoles, this, new RolesEventArgs(memberIds, roleNames), nameof(RemovedRoles)); scope.Complete(); } } From bcd908e47ddb4f86674d8848dec95d094ef28975 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 10 Dec 2018 13:06:39 +1100 Subject: [PATCH 38/65] Changes term Indexer to Index and fixes a failing test --- src/Umbraco.Examine/ContentValueSetBuilder.cs | 8 +++--- .../ContentValueSetValidator.cs | 6 ++--- src/Umbraco.Examine/Umbraco.Examine.csproj | 6 ++--- ...ntentIndexer.cs => UmbracoContentIndex.cs} | 8 +++--- ...amineIndexer.cs => UmbracoExamineIndex.cs} | 8 +++--- .../UmbracoExamineIndexDiagnostics.cs | 20 +++++++------- ...MemberIndexer.cs => UmbracoMemberIndex.cs} | 6 ++--- .../Composing/TypeFinderTests.cs | 2 +- .../UmbracoExamine/IndexInitializer.cs | 6 ++--- src/Umbraco.Tests/UmbracoExamine/IndexTest.cs | 4 +-- .../UmbracoContentValueSetValidatorTests.cs | 26 +++++++++---------- .../Models/Mapping/EntityMapperProfile.cs | 10 +++---- .../GridPropertyIndexValueFactory.cs | 2 +- .../PropertyEditors/RichTextPropertyEditor.cs | 2 +- .../XmlPublishedCache/PublishedMediaCache.cs | 8 +++--- .../Search/UmbracoIndexesCreator.cs | 12 ++++----- 16 files changed, 67 insertions(+), 67 deletions(-) rename src/Umbraco.Examine/{UmbracoContentIndexer.cs => UmbracoContentIndex.cs} (97%) rename src/Umbraco.Examine/{UmbracoExamineIndexer.cs => UmbracoExamineIndex.cs} (98%) rename src/Umbraco.Examine/{UmbracoMemberIndexer.cs => UmbracoMemberIndex.cs} (95%) diff --git a/src/Umbraco.Examine/ContentValueSetBuilder.cs b/src/Umbraco.Examine/ContentValueSetBuilder.cs index 39ffab98d9..07e5e4565b 100644 --- a/src/Umbraco.Examine/ContentValueSetBuilder.cs +++ b/src/Umbraco.Examine/ContentValueSetBuilder.cs @@ -42,7 +42,7 @@ namespace Umbraco.Examine var values = new Dictionary> { {"icon", c.ContentType.Icon.Yield()}, - {UmbracoExamineIndexer.PublishedFieldName, new object[] {c.Published ? 1 : 0}}, //Always add invariant published value + {UmbracoExamineIndex.PublishedFieldName, new object[] {c.Published ? 1 : 0}}, //Always add invariant published value {"id", new object[] {c.Id}}, {"key", new object[] {c.Key}}, {"parentID", new object[] {c.Level > 1 ? c.ParentId : -1}}, @@ -61,12 +61,12 @@ namespace Umbraco.Examine {"writerName",(c.GetWriterProfile(_userService)?.Name ?? "??").Yield() }, {"writerID", new object[] {c.WriterId}}, {"template", new object[] {c.Template?.Id ?? 0}}, - {UmbracoContentIndexer.VariesByCultureFieldName, new object[] {0}}, + {UmbracoContentIndex.VariesByCultureFieldName, new object[] {0}}, }; if (isVariant) { - values[UmbracoContentIndexer.VariesByCultureFieldName] = new object[] { 1 }; + values[UmbracoContentIndex.VariesByCultureFieldName] = new object[] { 1 }; foreach (var culture in c.AvailableCultures) { @@ -76,7 +76,7 @@ namespace Umbraco.Examine values[$"nodeName_{lowerCulture}"] = PublishedValuesOnly ? c.GetPublishName(culture).Yield() : c.GetCultureName(culture).Yield(); - values[$"{UmbracoExamineIndexer.PublishedFieldName}_{lowerCulture}"] = (c.IsCulturePublished(culture) ? 1 : 0).Yield(); + values[$"{UmbracoExamineIndex.PublishedFieldName}_{lowerCulture}"] = (c.IsCulturePublished(culture) ? 1 : 0).Yield(); values[$"updateDate_{lowerCulture}"] = PublishedValuesOnly ? c.GetPublishDate(culture).Yield() : c.GetUpdateDate(culture).Yield(); diff --git a/src/Umbraco.Examine/ContentValueSetValidator.cs b/src/Umbraco.Examine/ContentValueSetValidator.cs index eabf15f672..d4f6ceb15f 100644 --- a/src/Umbraco.Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Examine/ContentValueSetValidator.cs @@ -92,18 +92,18 @@ namespace Umbraco.Examine //check for published content if (valueSet.Category == IndexTypes.Content && PublishedValuesOnly) { - if (!valueSet.Values.TryGetValue(UmbracoExamineIndexer.PublishedFieldName, out var published)) + if (!valueSet.Values.TryGetValue(UmbracoExamineIndex.PublishedFieldName, out var published)) return ValueSetValidationResult.Failed; if (!published[0].Equals(1)) return ValueSetValidationResult.Failed; //deal with variants, if there are unpublished variants than we need to remove them from the value set - if (valueSet.Values.TryGetValue(UmbracoContentIndexer.VariesByCultureFieldName, out var variesByCulture) + if (valueSet.Values.TryGetValue(UmbracoContentIndex.VariesByCultureFieldName, out var variesByCulture) && variesByCulture.Count > 0 && variesByCulture[0].Equals(1)) { //so this valueset is for a content that varies by culture, now check for non-published cultures and remove those values - foreach(var publishField in valueSet.Values.Where(x => x.Key.StartsWith($"{UmbracoExamineIndexer.PublishedFieldName}_")).ToList()) + foreach(var publishField in valueSet.Values.Where(x => x.Key.StartsWith($"{UmbracoExamineIndex.PublishedFieldName}_")).ToList()) { if (publishField.Value.Count <= 0 || !publishField.Value[0].Equals(1)) { diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 9194d8332d..0aedf6e754 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -83,12 +83,12 @@ - + - + - + Properties\SolutionInfo.cs diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndex.cs similarity index 97% rename from src/Umbraco.Examine/UmbracoContentIndexer.cs rename to src/Umbraco.Examine/UmbracoContentIndex.cs index 0501fe90c6..910941ea7b 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndex.cs @@ -21,7 +21,7 @@ namespace Umbraco.Examine /// /// An indexer for Umbraco content and media /// - public class UmbracoContentIndexer : UmbracoExamineIndexer + public class UmbracoContentIndex : UmbracoExamineIndex { public const string VariesByCultureFieldName = SpecialFieldPrefix + "VariesByCulture"; protected ILocalizationService LanguageService { get; } @@ -32,7 +32,7 @@ namespace Umbraco.Examine /// Constructor for configuration providers /// [EditorBrowsable(EditorBrowsableState.Never)] - public UmbracoContentIndexer() + public UmbracoContentIndex() { LanguageService = Current.Services.LocalizationService; @@ -50,7 +50,7 @@ namespace Umbraco.Examine /// /// /// - public UmbracoContentIndexer( + public UmbracoContentIndex( string name, IEnumerable fieldDefinitions, Directory luceneDirectory, @@ -167,7 +167,7 @@ namespace Umbraco.Examine //since the path is not valid we need to delete this item in case it exists in the index already and has now //been moved to an invalid parent. foreach (var i in group) - QueueIndexOperation(new IndexOperation(new ValueSet(i.Id), IndexOperationType.Delete)); + base.PerformDeleteFromIndex(i.Id, args => { /*noop*/ }); } else { diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndex.cs similarity index 98% rename from src/Umbraco.Examine/UmbracoExamineIndexer.cs rename to src/Umbraco.Examine/UmbracoExamineIndex.cs index 84c8a7d8c5..15f1a75955 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndex.cs @@ -23,7 +23,7 @@ namespace Umbraco.Examine /// An abstract provider containing the basic functionality to be able to query against /// Umbraco data. /// - public abstract class UmbracoExamineIndexer : LuceneIndex, IUmbracoIndexer, IIndexDiagnostics + public abstract class UmbracoExamineIndex : LuceneIndex, IUmbracoIndexer, IIndexDiagnostics { // note // wrapping all operations that end up calling base.SafelyProcessQueueItems in a safe call @@ -48,7 +48,7 @@ namespace Umbraco.Examine /// Constructor for config provider based indexes /// [EditorBrowsable(EditorBrowsableState.Never)] - protected UmbracoExamineIndexer() + protected UmbracoExamineIndex() : base() { ProfilingLogger = Current.ProfilingLogger; @@ -56,7 +56,7 @@ namespace Umbraco.Examine } /// - /// Create a new + /// Create a new /// /// /// @@ -65,7 +65,7 @@ namespace Umbraco.Examine /// /// /// - protected UmbracoExamineIndexer( + protected UmbracoExamineIndex( string name, IEnumerable fieldDefinitions, Directory luceneDirectory, diff --git a/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs b/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs index 0812d93931..227b52e085 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs @@ -9,10 +9,10 @@ namespace Umbraco.Examine { public class UmbracoExamineIndexDiagnostics : IIndexDiagnostics { - private readonly UmbracoExamineIndexer _index; + private readonly UmbracoExamineIndex _index; private readonly ILogger _logger; - public UmbracoExamineIndexDiagnostics(UmbracoExamineIndexer index, ILogger logger) + public UmbracoExamineIndexDiagnostics(UmbracoExamineIndex index, ILogger logger) { _index = index; _logger = logger; @@ -28,7 +28,7 @@ namespace Umbraco.Examine } catch (AlreadyClosedException) { - _logger.Warn(typeof(UmbracoContentIndexer), "Cannot get GetIndexDocumentCount, the writer is already closed"); + _logger.Warn(typeof(UmbracoContentIndex), "Cannot get GetIndexDocumentCount, the writer is already closed"); return 0; } } @@ -44,7 +44,7 @@ namespace Umbraco.Examine } catch (AlreadyClosedException) { - _logger.Warn(typeof(UmbracoContentIndexer), "Cannot get GetIndexFieldCount, the writer is already closed"); + _logger.Warn(typeof(UmbracoContentIndex), "Cannot get GetIndexFieldCount, the writer is already closed"); return 0; } } @@ -62,15 +62,15 @@ namespace Umbraco.Examine { var d = new Dictionary { - [nameof(UmbracoExamineIndexer.CommitCount)] = _index.CommitCount, - [nameof(UmbracoExamineIndexer.DefaultAnalyzer)] = _index.DefaultAnalyzer.GetType().Name, - [nameof(UmbracoExamineIndexer.DirectoryFactory)] = _index.DirectoryFactory, - [nameof(UmbracoExamineIndexer.EnableDefaultEventHandler)] = _index.EnableDefaultEventHandler, - [nameof(UmbracoExamineIndexer.LuceneIndexFolder)] = + [nameof(UmbracoExamineIndex.CommitCount)] = _index.CommitCount, + [nameof(UmbracoExamineIndex.DefaultAnalyzer)] = _index.DefaultAnalyzer.GetType().Name, + [nameof(UmbracoExamineIndex.DirectoryFactory)] = _index.DirectoryFactory, + [nameof(UmbracoExamineIndex.EnableDefaultEventHandler)] = _index.EnableDefaultEventHandler, + [nameof(UmbracoExamineIndex.LuceneIndexFolder)] = _index.LuceneIndexFolder == null ? string.Empty : _index.LuceneIndexFolder.ToString().ToLowerInvariant().TrimStart(IOHelper.MapPath(SystemDirectories.Root).ToLowerInvariant()).Replace("\\", "/").EnsureStartsWith('/'), - [nameof(UmbracoExamineIndexer.PublishedValuesOnly)] = _index.PublishedValuesOnly, + [nameof(UmbracoExamineIndex.PublishedValuesOnly)] = _index.PublishedValuesOnly, //There's too much info here //[nameof(UmbracoExamineIndexer.FieldDefinitionCollection)] = _index.FieldDefinitionCollection, }; diff --git a/src/Umbraco.Examine/UmbracoMemberIndexer.cs b/src/Umbraco.Examine/UmbracoMemberIndex.cs similarity index 95% rename from src/Umbraco.Examine/UmbracoMemberIndexer.cs rename to src/Umbraco.Examine/UmbracoMemberIndex.cs index 4943f49825..28b46f72dd 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndexer.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndex.cs @@ -22,13 +22,13 @@ namespace Umbraco.Examine /// /// Custom indexer for members /// - public class UmbracoMemberIndexer : UmbracoExamineIndexer + public class UmbracoMemberIndex : UmbracoExamineIndex { /// /// Constructor for config/provider based indexes /// [EditorBrowsable(EditorBrowsableState.Never)] - public UmbracoMemberIndexer() + public UmbracoMemberIndex() { } @@ -41,7 +41,7 @@ namespace Umbraco.Examine /// /// /// - public UmbracoMemberIndexer( + public UmbracoMemberIndex( string name, IEnumerable fieldDefinitions, Directory luceneDirectory, diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 955f6f94c8..9b52546dff 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -49,7 +49,7 @@ namespace Umbraco.Tests.Composing //typeof(TabPage).Assembly, typeof(System.Web.Mvc.ActionResult).Assembly, typeof(TypeFinder).Assembly, - typeof(global::Umbraco.Examine.UmbracoExamineIndexer).Assembly + typeof(global::Umbraco.Examine.UmbracoExamineIndex).Assembly }; } diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 1f849fc1ce..cbd335a6c4 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -146,7 +146,7 @@ namespace Umbraco.Tests.UmbracoExamine return mediaTypeServiceMock.Object; } - public static UmbracoContentIndexer GetUmbracoIndexer( + public static UmbracoContentIndex GetUmbracoIndexer( ProfilingLogger profilingLogger, Directory luceneDir, Analyzer analyzer = null, @@ -162,9 +162,9 @@ namespace Umbraco.Tests.UmbracoExamine if (validator == null) validator = new ContentValueSetValidator(true); - var i = new UmbracoContentIndexer( + var i = new UmbracoContentIndex( "testIndexer", - UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, + UmbracoExamineIndex.UmbracoIndexFieldDefinitions, luceneDir, analyzer, profilingLogger, diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index 29abfb9234..35e3524459 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -114,8 +114,8 @@ namespace Umbraco.Tests.UmbracoExamine Assert.AreEqual("value2", result.AllValues["grid.row1"][1]); Assert.IsTrue(result.Values.ContainsKey("grid")); Assert.AreEqual("value1 value2 ", result["grid"]); - Assert.IsTrue(result.Values.ContainsKey($"{UmbracoExamineIndexer.RawFieldPrefix}grid")); - Assert.AreEqual(json, result[$"{UmbracoExamineIndexer.RawFieldPrefix}grid"]); + Assert.IsTrue(result.Values.ContainsKey($"{UmbracoExamineIndex.RawFieldPrefix}grid")); + Assert.AreEqual(json, result[$"{UmbracoExamineIndex.RawFieldPrefix}grid"]); } } diff --git a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs index 934dd34503..8d7a446ccb 100644 --- a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs @@ -179,7 +179,7 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoExamineIndexer.PublishedFieldName] = 1 + [UmbracoExamineIndex.PublishedFieldName] = 1 })); Assert.AreEqual(ValueSetValidationResult.Valid, result); } @@ -213,7 +213,7 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoExamineIndexer.PublishedFieldName] = 0 + [UmbracoExamineIndex.PublishedFieldName] = 0 })); Assert.AreEqual(ValueSetValidationResult.Failed, result); @@ -222,7 +222,7 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoExamineIndexer.PublishedFieldName] = 1 + [UmbracoExamineIndex.PublishedFieldName] = 1 })); Assert.AreEqual(ValueSetValidationResult.Valid, result); } @@ -237,8 +237,8 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoContentIndexer.VariesByCultureFieldName] = 1, - [UmbracoExamineIndexer.PublishedFieldName] = 0 + [UmbracoContentIndex.VariesByCultureFieldName] = 1, + [UmbracoExamineIndex.PublishedFieldName] = 0 })); Assert.AreEqual(ValueSetValidationResult.Failed, result); @@ -247,8 +247,8 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoContentIndexer.VariesByCultureFieldName] = 1, - [UmbracoExamineIndexer.PublishedFieldName] = 1 + [UmbracoContentIndex.VariesByCultureFieldName] = 1, + [UmbracoExamineIndex.PublishedFieldName] = 1 })); Assert.AreEqual(ValueSetValidationResult.Valid, result); @@ -257,17 +257,17 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoContentIndexer.VariesByCultureFieldName] = 1, - [$"{UmbracoExamineIndexer.PublishedFieldName}_en-us"] = 1, + [UmbracoContentIndex.VariesByCultureFieldName] = 1, + [$"{UmbracoExamineIndex.PublishedFieldName}_en-us"] = 1, ["hello_en-us"] = "world", ["title_en-us"] = "my title", - [$"{UmbracoExamineIndexer.PublishedFieldName}_es-es"] = 0, + [$"{UmbracoExamineIndex.PublishedFieldName}_es-es"] = 0, ["hello_es-ES"] = "world", ["title_es-ES"] = "my title", - [UmbracoExamineIndexer.PublishedFieldName] = 1 + [UmbracoExamineIndex.PublishedFieldName] = 1 }); Assert.AreEqual(10, valueSet.Values.Count()); - Assert.IsTrue(valueSet.Values.ContainsKey($"{UmbracoExamineIndexer.PublishedFieldName}_es-es")); + Assert.IsTrue(valueSet.Values.ContainsKey($"{UmbracoExamineIndex.PublishedFieldName}_es-es")); Assert.IsTrue(valueSet.Values.ContainsKey("hello_es-ES")); Assert.IsTrue(valueSet.Values.ContainsKey("title_es-ES")); @@ -275,7 +275,7 @@ namespace Umbraco.Tests.UmbracoExamine Assert.AreEqual(ValueSetValidationResult.Filtered, result); Assert.AreEqual(7, valueSet.Values.Count()); //filtered to 7 values (removes es-es values) - Assert.IsFalse(valueSet.Values.ContainsKey($"{UmbracoExamineIndexer.PublishedFieldName}_es-es")); + Assert.IsFalse(valueSet.Values.ContainsKey($"{UmbracoExamineIndex.PublishedFieldName}_es-es")); Assert.IsFalse(valueSet.Values.ContainsKey("hello_es-ES")); Assert.IsFalse(valueSet.Values.ContainsKey("title_es-ES")); } diff --git a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs index 3e42178fbd..ea76293df5 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs @@ -123,15 +123,15 @@ namespace Umbraco.Web.Models.Mapping .AfterMap((src, dest) => { //get the icon if there is one - dest.Icon = src.Values.ContainsKey(UmbracoExamineIndexer.IconFieldName) - ? src.Values[UmbracoExamineIndexer.IconFieldName] + dest.Icon = src.Values.ContainsKey(UmbracoExamineIndex.IconFieldName) + ? src.Values[UmbracoExamineIndex.IconFieldName] : "icon-document"; dest.Name = src.Values.ContainsKey("nodeName") ? src.Values["nodeName"] : "[no name]"; - if (src.Values.ContainsKey(UmbracoExamineIndexer.NodeKeyFieldName)) + if (src.Values.ContainsKey(UmbracoExamineIndex.NodeKeyFieldName)) { Guid key; - if (Guid.TryParse(src.Values[UmbracoExamineIndexer.NodeKeyFieldName], out key)) + if (Guid.TryParse(src.Values[UmbracoExamineIndex.NodeKeyFieldName], out key)) { dest.Key = key; @@ -166,7 +166,7 @@ namespace Umbraco.Web.Models.Mapping dest.ParentId = -1; } } - dest.Path = src.Values.ContainsKey(UmbracoExamineIndexer.IndexPathFieldName) ? src.Values[UmbracoExamineIndexer.IndexPathFieldName] : ""; + dest.Path = src.Values.ContainsKey(UmbracoExamineIndex.IndexPathFieldName) ? src.Values[UmbracoExamineIndex.IndexPathFieldName] : ""; if (src.Values.ContainsKey(LuceneIndex.ItemTypeFieldName)) { diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValueFactory.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValueFactory.cs index c8235d1d11..83279ae975 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValueFactory.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValueFactory.cs @@ -59,7 +59,7 @@ namespace Umbraco.Web.PropertyEditors if (sb.Length > 0) { //First save the raw value to a raw field - result.Add(new KeyValuePair>($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new[] { rawVal })); + result.Add(new KeyValuePair>($"{UmbracoExamineIndex.RawFieldPrefix}{property.Alias}", new[] { rawVal })); //index the property with the combined/cleaned value result.Add(new KeyValuePair>(property.Alias, new[] { sb.ToString() })); diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 2deb8b8444..31b65c6357 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -104,7 +104,7 @@ namespace Umbraco.Web.PropertyEditors //index the stripped html values yield return new KeyValuePair>(property.Alias, new object[] { strVal.StripHtml() }); //store the raw value - yield return new KeyValuePair>($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new object[] { strVal }); + yield return new KeyValuePair>($"{UmbracoExamineIndex.RawFieldPrefix}{property.Alias}", new object[] { strVal }); } } } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index d681d9296e..ac6b425e27 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -108,7 +108,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // +(+parentID:-1) +__IndexType:media var criteria = searchProvider.CreateCriteria("media"); - var filter = criteria.ParentId(-1).Not().Field(UmbracoExamineIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); + var filter = criteria.ParentId(-1).Not().Field(UmbracoExamineIndex.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); var result = searchProvider.Search(filter.Compile()); if (result != null) @@ -294,7 +294,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // note that since the use of the wildcard, it automatically escapes it in Lucene. var criteria = searchProvider.CreateCriteria("media"); - var filter = criteria.Id(id.ToInvariantString()).Not().Field(UmbracoExamineIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); + var filter = criteria.Id(id.ToInvariantString()).Not().Field(UmbracoExamineIndex.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); var result = searchProvider.Search(filter.Compile()).FirstOrDefault(); if (result != null) return ConvertFromSearchResult(result); @@ -476,7 +476,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { //We are going to check for a special field however, that is because in some cases we store a 'Raw' //value in the index such as for xml/html. - var rawValue = dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(UmbracoExamineIndexer.RawFieldPrefix + alias)); + var rawValue = dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(UmbracoExamineIndex.RawFieldPrefix + alias)); return rawValue ?? dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); } @@ -509,7 +509,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache //first check in Examine as this is WAY faster var criteria = searchProvider.CreateCriteria("media"); - var filter = criteria.ParentId(parentId).Not().Field(UmbracoExamineIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); + var filter = criteria.ParentId(parentId).Not().Field(UmbracoExamineIndex.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); //the above filter will create a query like this, NOTE: That since the use of the wildcard, it automatically escapes it in Lucene. //+(+parentId:3113 -__Path:-1,-21,*) +__IndexType:media diff --git a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs index 623bd5b75f..f8b2c6ef8b 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs @@ -57,10 +57,10 @@ namespace Umbraco.Web.Search private IIndex CreateInternalIndex() { - var index = new UmbracoContentIndexer( + var index = new UmbracoContentIndex( Constants.UmbracoIndexes.InternalIndexName, //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes - UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, + UmbracoExamineIndex.UmbracoIndexFieldDefinitions, GetFileSystemLuceneDirectory(Constants.UmbracoIndexes.InternalIndexPath), new CultureInvariantWhitespaceAnalyzer(), ProfilingLogger, @@ -71,10 +71,10 @@ namespace Umbraco.Web.Search private IIndex CreateExternalIndex() { - var index = new UmbracoContentIndexer( + var index = new UmbracoContentIndex( Constants.UmbracoIndexes.ExternalIndexName, //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes - UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, + UmbracoExamineIndex.UmbracoIndexFieldDefinitions, GetFileSystemLuceneDirectory(Constants.UmbracoIndexes.ExternalIndexPath), new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30), ProfilingLogger, @@ -85,10 +85,10 @@ namespace Umbraco.Web.Search private IIndex CreateMemberIndex() { - var index = new UmbracoMemberIndexer( + var index = new UmbracoMemberIndex( Constants.UmbracoIndexes.MembersIndexName, //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes - UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, + UmbracoExamineIndex.UmbracoIndexFieldDefinitions, GetFileSystemLuceneDirectory(Constants.UmbracoIndexes.MembersIndexPath), new CultureInvariantWhitespaceAnalyzer(), ProfilingLogger, From a11b442cfdc3aa942d5bbd5fe9e7eddd324fda6b Mon Sep 17 00:00:00 2001 From: Jack Lawry Date: Thu, 6 Dec 2018 16:32:19 +0000 Subject: [PATCH 39/65] Update text --- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 4 ++-- src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index b2e02b1f9c..39f266a3bd 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1474,8 +1474,8 @@ To manage your website, simply open the Umbraco back office and start adding con Inherit tabs and properties from an existing document type. New tabs will be added to the current document type or merged if a tab with an identical name exists. This content type is used in a composition, and therefore cannot be composed itself. There are no content types available to use as a composition. - Available editors - Reuse + Create new + Use existing Editor settings Configuration Yes, delete diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index e4050d3a7b..1856f0f3f2 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1510,8 +1510,8 @@ To manage your website, simply open the Umbraco back office and start adding con Inherit tabs and properties from an existing document type. New tabs will be added to the current document type or merged if a tab with an identical name exists. This content type is used in a composition, and therefore cannot be composed itself. There are no content types available to use as a composition. - Available editors - Reuse + Create new + Use existing Editor settings Configuration Yes, delete From 98cc48c0d36c23429805630cc2502f49e5f8ec0e Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 10 Dec 2018 11:38:43 +0100 Subject: [PATCH 40/65] remove infinite editing backdrop when discarding changes --- .../components/editor/umbeditors.directive.js | 13 ++++++++----- .../src/common/services/editor.service.js | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js index c852228205..ac9b20fd66 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js @@ -203,11 +203,14 @@ })); evts.push(eventsService.on("appState.editors.close", function (name, args) { - removeEditor(args.editor); - })); - - evts.push(eventsService.on("appState.editors.closeAll", function (name, args) { - scope.editors = []; + // remove the closed editor + if(args && args.editor) { + removeEditor(args.editor); + } + // close all editors + if(args && args.editors.length === 0) { + scope.editors = []; + } })); //ensure to unregister from all events! diff --git a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js index 449470f54c..650a210784 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js @@ -98,7 +98,7 @@ editor: null }; - eventsService.emit("appState.editors.closeAll", args); + eventsService.emit("appState.editors.close", args); } /** From ca0cd8711e85759527116bde3717042081a0fb0c Mon Sep 17 00:00:00 2001 From: Claus Date: Mon, 10 Dec 2018 14:11:02 +0100 Subject: [PATCH 41/65] minor fixes. --- .../src/views/relationtypes/create.controller.js | 4 ++-- src/Umbraco.Web/Trees/RelationTypeTreeController.cs | 6 +++--- src/Umbraco.Web/Umbraco.Web.csproj | 10 ---------- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js index 8b1d82cf8f..2cef0bc5a4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/create.controller.js @@ -25,14 +25,14 @@ function RelationTypeCreateController($scope, $location, relationTypeResource, n function createRelationType() { if (formHelper.submitForm({ scope: $scope, formCtrl: this.createRelationTypeForm, statusMessage: "Creating relation type..." })) { - var node = $scope.dialogOptions.currentNode; + var node = $scope.currentNode; relationTypeResource.create(vm.relationType).then(function (data) { navigationService.hideMenu(); // Set the new item as active in the tree var currentPath = node.path ? node.path : "-1"; - navigationService.syncTree({ tree: "relationType", path: currentPath + "," + data, forceReload: true, activate: true }); + navigationService.syncTree({ tree: "relationTypes", path: currentPath + "," + data, forceReload: true, activate: true }); formHelper.resetForm({ scope: $scope }); diff --git a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs index 47a8d82808..1ce319b6ac 100644 --- a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs @@ -3,8 +3,8 @@ using System.Net.Http.Formatting; using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi.Filters; using Umbraco.Core; - using Umbraco.Core.Services; +using Umbraco.Web.Actions; namespace Umbraco.Web.Trees { @@ -21,7 +21,7 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { //Create the normal create action - menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)); + menu.Items.Add(Services.TextService.Localize("actions", ActionNew.ActionAlias)); //refresh action menu.Items.Add(new RefreshNode(Services.TextService, true)); @@ -32,7 +32,7 @@ namespace Umbraco.Web.Trees var relationType = Services.RelationService.GetRelationTypeById(int.Parse(id)); if (relationType == null) return new MenuItemCollection(); - menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)); + menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.ActionAlias)); return menu; } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a58871a774..4034dd2be6 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1242,13 +1242,6 @@ FeedProxy.aspx - - NewRelationType.aspx - ASPXCodeBehind - - - NewRelationType.aspx - insertMasterpageContent.aspx ASPXCodeBehind @@ -1320,9 +1313,6 @@ - - ASPXCodeBehind - ASPXCodeBehind From c61ac406bb6da0a8ad4e9899c65c956a0b09edbe Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 10 Dec 2018 14:20:57 +0100 Subject: [PATCH 42/65] fix fade animation when closing the last infinite editor --- .../common/directives/components/editor/umbeditors.directive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js index ac9b20fd66..e68c2bbc9a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js @@ -208,7 +208,7 @@ removeEditor(args.editor); } // close all editors - if(args && args.editors.length === 0) { + if(args && !args.editor && args.editors.length === 0) { scope.editors = []; } })); From e173ea6dac38ff905a068ccc49c23173157e1cb1 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 10 Dec 2018 15:30:56 +0100 Subject: [PATCH 43/65] Cleanup --- .../Implement/ContentTypeRepositoryBase.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 6be07a4c3d..9b62dbd6e3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -190,8 +190,8 @@ AND umbracoNode.nodeObjectType = @objectType", SortOrder = allowedContentType.SortOrder }); } - - + + //Insert Tabs foreach (var propertyGroup in entity.PropertyGroups) { @@ -331,11 +331,11 @@ AND umbracoNode.id <> @id", if (entity.IsPropertyDirty("PropertyTypes") || entity.PropertyTypes.Any(pt => pt.IsDirty())) { var dbPropertyTypes = Database.Fetch("WHERE contentTypeId = @Id", new { entity.Id }); - var dbPropertyTypeAlias = dbPropertyTypes.Select(x => x.Id); + var dbPropertyTypeIds = dbPropertyTypes.Select(x => x.Id); var entityPropertyTypes = entity.PropertyTypes.Where(x => x.HasIdentity).Select(x => x.Id); - var items = dbPropertyTypeAlias.Except(entityPropertyTypes); - foreach (var item in items) - DeletePropertyType(entity.Id, item); + var propertyTypeToDeleteIds = dbPropertyTypeIds.Except(entityPropertyTypes); + foreach (var propertyTypeId in propertyTypeToDeleteIds) + DeletePropertyType(entity.Id, propertyTypeId); } // Delete tabs ... by excepting entries from db with entries from collections. @@ -620,7 +620,7 @@ AND umbracoNode.id <> @id", var sqlDelete = Sql() .Delete() .WhereIn((System.Linq.Expressions.Expression>)(x => x.ContentKey), sqlSelect); - + Database.Execute(sqlDelete); } @@ -690,7 +690,7 @@ AND umbracoNode.id <> @id", //first clear out any existing names that might already exists under the default lang //there's 2x tables to update - //clear out the versionCultureVariation table + //clear out the versionCultureVariation table var sqlSelect = Sql().Select(x => x.Id) .From() .InnerJoin().On(x => x.Id, x => x.VersionId) From c4cf843b82e549a9808abbbd5613ff970667a7fb Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Mon, 10 Dec 2018 14:59:17 +0000 Subject: [PATCH 44/65] Adds in the Umbraco Forms Promo dashboard in via package.manifest so that Forms install can override when installed --- .../App_Plugins/UmbracoForms/package.manifest | 10 ++++++++++ src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 9 +++++---- src/Umbraco.Web.UI/config/Dashboard.config | 11 ----------- 3 files changed, 15 insertions(+), 15 deletions(-) create mode 100644 src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest diff --git a/src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest b/src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest new file mode 100644 index 0000000000..c7ed4a957a --- /dev/null +++ b/src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest @@ -0,0 +1,10 @@ +{ + "dashboards": [ + { + "name": "Install Umbraco Forms", + "alias": "installUmbracoForms", + "view": "views/dashboard/forms/formsdashboardintro.html", + "sections": [ "forms" ] + } + ] +} diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 2823908a92..130015ad53 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -231,6 +231,7 @@ + 404handlers.config @@ -452,10 +453,10 @@ $(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 - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v16.0\Web\Microsoft.Web.Publishing.Tasks.dll - - $(ProgramFiles32)\Microsoft Visual Studio\2019\Preview\MSBuild\Microsoft\VisualStudio\v16.0\Web\Microsoft.Web.Publishing.Tasks.dll + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v15.0\Web\Microsoft.Web.Publishing.Tasks.dll + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v16.0\Web\Microsoft.Web.Publishing.Tasks.dll + + $(ProgramFiles32)\Microsoft Visual Studio\2019\Preview\MSBuild\Microsoft\VisualStudio\v16.0\Web\Microsoft.Web.Publishing.Tasks.dll diff --git a/src/Umbraco.Web.UI/config/Dashboard.config b/src/Umbraco.Web.UI/config/Dashboard.config index fec6ab34ae..785fb61681 100644 --- a/src/Umbraco.Web.UI/config/Dashboard.config +++ b/src/Umbraco.Web.UI/config/Dashboard.config @@ -22,17 +22,6 @@ -
- - forms - - - - views/dashboard/forms/formsdashboardintro.html - - -
-
media From f10b86c9ec58018681d4df4e4a7a023da7c58dca Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 10 Dec 2018 17:07:45 +0100 Subject: [PATCH 45/65] Fix RelationMapperProfiler --- .../Models/Mapping/RelationMapperProfile.cs | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs index 1622fb907e..e31b1877d3 100644 --- a/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs @@ -11,18 +11,17 @@ namespace Umbraco.Web.Models.Mapping { // FROM IRelationType to RelationTypeDisplay CreateMap() - .ForMember(x => x.Icon, expression => expression.Ignore()) - .ForMember(x => x.Trashed, expression => expression.Ignore()) - .ForMember(x => x.Alias, expression => expression.Ignore()) - .ForMember(x => x.Path, expression => expression.Ignore()) - .ForMember(x => x.AdditionalData, expression => expression.Ignore()) - .ForMember(x => x.ChildObjectTypeName, expression => expression.Ignore()) - .ForMember(x => x.ParentObjectTypeName, expression => expression.Ignore()) - .ForMember(x => x.Relations, expression => expression.Ignore()) - .ForMember( - x => x.Udi, - expression => expression.MapFrom( - content => Udi.Create(Constants.UdiEntityType.RelationType, content.Key))) + .ForMember(dest => dest.Icon, opt => opt.Ignore()) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.Path, opt => opt.Ignore()) + .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) + .ForMember(dest => dest.ChildObjectTypeName, opt => opt.Ignore()) + .ForMember(dest => dest.ParentObjectTypeName, opt => opt.Ignore()) + .ForMember(dest => dest.Relations, opt => opt.Ignore()) + .ForMember(dest => dest.ParentId, opt => opt.Ignore()) + .ForMember(dest => dest.Notifications, opt => opt.Ignore()) + .ForMember(dest => dest.Udi, opt => opt.MapFrom(content => Udi.Create(Constants.UdiEntityType.RelationType, content.Key))) .AfterMap((src, dest) => { // Build up the path @@ -34,10 +33,15 @@ namespace Umbraco.Web.Models.Mapping }); // FROM IRelation to RelationDisplay - CreateMap(); + CreateMap() + .ForMember(dest => dest.ParentName, opt => opt.Ignore()) + .ForMember(dest => dest.ChildName, opt => opt.Ignore()); // FROM RelationTypeSave to IRelationType - CreateMap(); + CreateMap() + .ForMember(dest => dest.CreateDate, opt => opt.Ignore()) + .ForMember(dest => dest.UpdateDate, opt => opt.Ignore()) + .ForMember(dest => dest.DeleteDate, opt => opt.Ignore()); } } } From f9967e2d7d90733374746f8d0496d01d9034649b Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 10 Dec 2018 17:48:24 +0100 Subject: [PATCH 46/65] Fix e25e509d84 breaking tests, fix original issue --- src/Umbraco.Core/Models/ContentTypeBase.cs | 16 +++++++++------- src/Umbraco.Core/Models/PropertyGroup.cs | 7 +++++-- .../Implement/ContentTypeRepositoryBase.cs | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index caa63d7526..88b1179f6d 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -92,8 +92,8 @@ namespace Umbraco.Core.Models public readonly PropertyInfo AllowedAsRootSelector = ExpressionHelper.GetPropertyInfo(x => x.AllowedAsRoot); public readonly PropertyInfo IsContainerSelector = ExpressionHelper.GetPropertyInfo(x => x.IsContainer); public readonly PropertyInfo AllowedContentTypesSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedContentTypes); - public readonly PropertyInfo PropertyGroupCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyGroups); - public readonly PropertyInfo PropertyTypeCollectionSelector = ExpressionHelper.GetPropertyInfo>(x => x.PropertyTypes); + public readonly PropertyInfo PropertyGroupsSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyGroups); + public readonly PropertyInfo PropertyTypesSelector = ExpressionHelper.GetPropertyInfo>(x => x.PropertyTypes); public readonly PropertyInfo HasPropertyTypeBeenRemovedSelector = ExpressionHelper.GetPropertyInfo(x => x.HasPropertyTypeBeenRemoved); public readonly PropertyInfo VaryBy = ExpressionHelper.GetPropertyInfo(x => x.Variations); @@ -106,12 +106,12 @@ namespace Umbraco.Core.Models protected void PropertyGroupsChanged(object sender, NotifyCollectionChangedEventArgs e) { - OnPropertyChanged(Ps.Value.PropertyGroupCollectionSelector); + OnPropertyChanged(Ps.Value.PropertyGroupsSelector); } protected void PropertyTypesChanged(object sender, NotifyCollectionChangedEventArgs e) { - OnPropertyChanged(Ps.Value.PropertyTypeCollectionSelector); + OnPropertyChanged(Ps.Value.PropertyTypesSelector); } /// @@ -263,6 +263,8 @@ namespace Umbraco.Core.Models get => _noGroupPropertyTypes; set { + if (_noGroupPropertyTypes != null) + _noGroupPropertyTypes.CollectionChanged -= PropertyTypesChanged; _noGroupPropertyTypes = new PropertyTypeCollection(IsPublishing, value); _noGroupPropertyTypes.CollectionChanged += PropertyTypesChanged; PropertyTypesChanged(_noGroupPropertyTypes, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); @@ -376,7 +378,7 @@ namespace Umbraco.Core.Models if (!HasPropertyTypeBeenRemoved) { HasPropertyTypeBeenRemoved = true; - OnPropertyChanged(Ps.Value.PropertyTypeCollectionSelector); + OnPropertyChanged(Ps.Value.PropertyTypesSelector); } break; } @@ -388,7 +390,7 @@ namespace Umbraco.Core.Models if (!HasPropertyTypeBeenRemoved) { HasPropertyTypeBeenRemoved = true; - OnPropertyChanged(Ps.Value.PropertyTypeCollectionSelector); + OnPropertyChanged(Ps.Value.PropertyTypesSelector); } } } @@ -412,7 +414,7 @@ namespace Umbraco.Core.Models // actually remove the group PropertyGroups.RemoveItem(propertyGroupName); - OnPropertyChanged(Ps.Value.PropertyGroupCollectionSelector); + OnPropertyChanged(Ps.Value.PropertyGroupsSelector); } /// diff --git a/src/Umbraco.Core/Models/PropertyGroup.cs b/src/Umbraco.Core/Models/PropertyGroup.cs index 1d0b949932..595e8d1d6a 100644 --- a/src/Umbraco.Core/Models/PropertyGroup.cs +++ b/src/Umbraco.Core/Models/PropertyGroup.cs @@ -35,12 +35,12 @@ namespace Umbraco.Core.Models { public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); public readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder); - public readonly PropertyInfo PropertyTypeCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyTypes); + public readonly PropertyInfo PropertyTypes = ExpressionHelper.GetPropertyInfo(x => x.PropertyTypes); } private void PropertyTypesChanged(object sender, NotifyCollectionChangedEventArgs e) { - OnPropertyChanged(Ps.Value.PropertyTypeCollectionSelector); + OnPropertyChanged(Ps.Value.PropertyTypes); } /// @@ -76,6 +76,8 @@ namespace Umbraco.Core.Models get => _propertyTypes; set { + if (_propertyTypes != null) + _propertyTypes.CollectionChanged -= PropertyTypesChanged; _propertyTypes = value; // since we're adding this collection to this group, @@ -83,6 +85,7 @@ namespace Umbraco.Core.Models foreach (var propertyType in _propertyTypes) propertyType.PropertyGroupId = new Lazy(() => Id); + OnPropertyChanged(Ps.Value.PropertyTypes); _propertyTypes.CollectionChanged += PropertyTypesChanged; } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 9b62dbd6e3..44215b7f7e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -328,7 +328,7 @@ AND umbracoNode.id <> @id", // We check if the entity's own PropertyTypes has been modified and then also check // any of the property groups PropertyTypes has been modified. // This specifically tells us if any property type collections have changed. - if (entity.IsPropertyDirty("PropertyTypes") || entity.PropertyTypes.Any(pt => pt.IsDirty())) + if (entity.IsPropertyDirty("NoGroupPropertyTypes") || entity.PropertyGroups.Any(x => x.IsPropertyDirty("PropertyTypes"))) { var dbPropertyTypes = Database.Fetch("WHERE contentTypeId = @Id", new { entity.Id }); var dbPropertyTypeIds = dbPropertyTypes.Select(x => x.Id); From e91cf0039b9250645de6d9e7ca795df0bf56ae76 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 11 Dec 2018 12:32:51 +1100 Subject: [PATCH 47/65] Adds ability to filter the outgoing dashboard model --- src/Umbraco.Web/Editors/DashboardController.cs | 1 + src/Umbraco.Web/Editors/EditorModelEventManager.cs | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index 72b7acc9e7..d8cbc34938 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -118,6 +118,7 @@ namespace Umbraco.Web.Editors } [ValidateAngularAntiForgeryToken] + [OutgoingEditorModelEvent] public IEnumerable> GetDashboard(string section) { return _dashboards.GetDashboards(section, Security.CurrentUser); diff --git a/src/Umbraco.Web/Editors/EditorModelEventManager.cs b/src/Umbraco.Web/Editors/EditorModelEventManager.cs index e2a248cb88..2225f5c577 100644 --- a/src/Umbraco.Web/Editors/EditorModelEventManager.cs +++ b/src/Umbraco.Web/Editors/EditorModelEventManager.cs @@ -1,4 +1,5 @@ -using System.Web.Http.Filters; +using System.Collections.Generic; +using System.Web.Http.Filters; using Umbraco.Core.Events; using Umbraco.Web.Models.ContentEditing; @@ -13,6 +14,13 @@ namespace Umbraco.Web.Editors public static event TypedEventHandler> SendingMediaModel; public static event TypedEventHandler> SendingMemberModel; public static event TypedEventHandler> SendingUserModel; + public static event TypedEventHandler>>> SendingDashboardModel; + + private static void OnSendingDashboardModel(HttpActionExecutedContext sender, EditorModelEventArgs>> e) + { + var handler = SendingDashboardModel; + handler?.Invoke(sender, e); + } private static void OnSendingUserModel(HttpActionExecutedContext sender, EditorModelEventArgs e) { @@ -56,6 +64,9 @@ namespace Umbraco.Web.Editors if (e.Model is UserDisplay) OnSendingUserModel(sender, new EditorModelEventArgs(e)); + + if (e.Model is IEnumerable>) + OnSendingDashboardModel(sender, new EditorModelEventArgs>>(e)); } } } From 9b52fa215f0eb11f7daa96dbb21e47b8d1b15942 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 11 Dec 2018 12:53:15 +1100 Subject: [PATCH 48/65] Allows replacing the model with the outgoing event args --- .../Editors/EditorModelEventArgs.cs | 21 +++++++++++++++---- .../OutgoingEditorModelEventAttribute.cs | 11 +++++----- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web/Editors/EditorModelEventArgs.cs b/src/Umbraco.Web/Editors/EditorModelEventArgs.cs index 153a2d8786..daf262fce5 100644 --- a/src/Umbraco.Web/Editors/EditorModelEventArgs.cs +++ b/src/Umbraco.Web/Editors/EditorModelEventArgs.cs @@ -4,9 +4,13 @@ namespace Umbraco.Web.Editors { public sealed class EditorModelEventArgs : EditorModelEventArgs { + private readonly EditorModelEventArgs _baseArgs; + private T _model; + public EditorModelEventArgs(EditorModelEventArgs baseArgs) : base(baseArgs.Model, baseArgs.UmbracoContext) { + _baseArgs = baseArgs; Model = (T)baseArgs.Model; } @@ -16,7 +20,16 @@ namespace Umbraco.Web.Editors Model = model; } - public new T Model { get; private set; } + public new T Model + { + get => _model; + set + { + _model = value; + if (_baseArgs != null) + _baseArgs.Model = _model; + } + } } public class EditorModelEventArgs : EventArgs @@ -27,7 +40,7 @@ namespace Umbraco.Web.Editors UmbracoContext = umbracoContext; } - public object Model { get; private set; } - public UmbracoContext UmbracoContext { get; private set; } + public object Model { get; set; } + public UmbracoContext UmbracoContext { get; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs b/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs index ec32b61bca..8410891a5d 100644 --- a/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs @@ -19,16 +19,17 @@ namespace Umbraco.Web.WebApi.Filters var user = UmbracoContext.Current.Security.CurrentUser; if (user == null) return; - var objectContent = actionExecutedContext.Response.Content as ObjectContent; - if (objectContent != null) + if (actionExecutedContext.Response.Content is ObjectContent objectContent) { var model = objectContent.Value; if (model != null) { - EditorModelEventManager.EmitEvent(actionExecutedContext, new EditorModelEventArgs( - (dynamic)model, - UmbracoContext.Current)); + var args = new EditorModelEventArgs( + model, + UmbracoContext.Current); + EditorModelEventManager.EmitEvent(actionExecutedContext, args); + objectContent.Value = args.Model; } } From 27f9490837e3a457cb763adc06054f65bb6772d0 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 11 Dec 2018 10:14:18 +0100 Subject: [PATCH 49/65] fix docs build --- .../src/common/resources/relationtype.resource.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js index f17aae0a6b..7f13a46d2f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js @@ -35,7 +35,7 @@ function relationTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { /** * @ngdoc method * @name umbraco.resources.relationTypeResource#getRelationObjectTypes - * @methodof umbraco.resources.relationTypeResource + * @methodOf umbraco.resources.relationTypeResource * * @description * Gets a list of Umbraco object types which can be associated with a relation. @@ -54,7 +54,7 @@ function relationTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { /** * @ngdoc method * @name umbraco.resources.relationTypeResource#save - * @methodof umbraco.resources.relationTypeResource + * @methodOf umbraco.resources.relationTypeResource * * @description * Updates a relation type. @@ -74,7 +74,7 @@ function relationTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { /** * @ngdoc method * @name umbraco.resources.relationTypeResource#create - * @methodof umbraco.resources.relationTypeResource + * @methodOf umbraco.resources.relationTypeResource * * @description * Creates a new relation type. @@ -94,7 +94,7 @@ function relationTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { /** * @ngdoc method * @name umbraco.resources.relationTypeResource#deleteById - * @methodof umbraco.resources.relationTypeResource + * @methodOf umbraco.resources.relationTypeResource * * @description * Deletes a relation type with a given ID. From 0ae9d30373a06fbe030c41493cc00b90d3f48bfd Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 11 Dec 2018 09:43:47 +0000 Subject: [PATCH 50/65] Revert recent change to Forms dashboard back into XML config - now that we have a way to modify from Umbraco Forms component --- .../App_Plugins/UmbracoForms/package.manifest | 10 ---------- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 1 - src/Umbraco.Web.UI/config/Dashboard.config | 11 +++++++++++ 3 files changed, 11 insertions(+), 11 deletions(-) delete mode 100644 src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest diff --git a/src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest b/src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest deleted file mode 100644 index c7ed4a957a..0000000000 --- a/src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest +++ /dev/null @@ -1,10 +0,0 @@ -{ - "dashboards": [ - { - "name": "Install Umbraco Forms", - "alias": "installUmbracoForms", - "view": "views/dashboard/forms/formsdashboardintro.html", - "sections": [ "forms" ] - } - ] -} diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 130015ad53..5751e9155c 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -231,7 +231,6 @@ - 404handlers.config diff --git a/src/Umbraco.Web.UI/config/Dashboard.config b/src/Umbraco.Web.UI/config/Dashboard.config index 785fb61681..fec6ab34ae 100644 --- a/src/Umbraco.Web.UI/config/Dashboard.config +++ b/src/Umbraco.Web.UI/config/Dashboard.config @@ -22,6 +22,17 @@
+
+ + forms + + + + views/dashboard/forms/formsdashboardintro.html + + +
+
media From d06ccebf7c7d7c9c2987dbb2d86e97e877ee059a Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 11 Dec 2018 13:03:18 +0100 Subject: [PATCH 51/65] update docs for infinite editing --- .../overlays/umboverlay.directive.js | 216 ------------------ .../src/common/services/editor.service.js | 159 ++++++++++++- 2 files changed, 155 insertions(+), 220 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js index 9f9f1aa21e..99b89bf8cf 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js @@ -124,58 +124,6 @@ - -

Content Picker

-Opens a content picker.
-view: contentpicker - - - - - - - - - - - - - -
ParamTypeDetails
model.multiPickerBooleanPick one or multiple items
- - - - - - - - - - - - - -
ReturnsTypeDetails
model.selectionArrayArray of content objects
- - -

Icon Picker

-Opens an icon picker.
-view: iconpicker - - - - - - - - - - - - - -
ReturnsTypeDetails
model.iconStringThe icon class
-

Item Picker

Opens an item picker.
view: itempicker @@ -220,170 +168,6 @@ Opens an item picker.
-

Macro Picker

-Opens a media picker.
-view: macropicker - - - - - - - - - - - - - - - -
ParamTypeDetails
model.dialogDataObjectObject which contains array of allowedMacros. Set to null to allow all.
- - - - - - - - - - - - - - - - - - - - -
ReturnsTypeDetails
model.macroParamsArrayArray of macro params
model.selectedMacroObjectThe selected macro
- -

Media Picker

-Opens a media picker.
-view: mediapicker - - - - - - - - - - - - - - - - - - - - - - - - - -
ParamTypeDetails
model.multiPickerBooleanPick one or multiple items
model.onlyImagesBooleanOnly display files that have an image file-extension
model.disableFolderSelectBooleanDisable folder selection
- - - - - - - - - - - - - - - -
ReturnsTypeDetails
model.selectedImagesArrayArray of selected images
- -

Member Group Picker

-Opens a member group picker.
-view: membergrouppicker - - - - - - - - - - - - - - - -
ParamTypeDetails
model.multiPickerBooleanPick one or multiple items
- - - - - - - - - - - - - - - - - - - - -
ReturnsTypeDetails
model.selectedMemberGroupStringThe selected member group
model.selectedMemberGroups (multiPicker)ArrayThe selected member groups
- -

Member Picker

-Opens a member picker.
-view: memberpicker - - - - - - - - - - - - - - - -
ParamTypeDetails
model.multiPickerBooleanPick one or multiple items
- - - - - - - - - - - - - - -
ReturnsTypeDetails
model.selectionArrayArray of selected members/td> -
-

YSOD

Opens an overlay to show a custom YSOD.
view: ysod diff --git a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js index 650a210784..0dbd27b7a5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js @@ -4,6 +4,76 @@ * * @description * Added in Umbraco 8.0. Application-wide service for handling infinite editing. + * + * +

Markup example

+
+    
+ + + +
+
+ +

Controller example

+
+    (function () {
+
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+
+            vm.open = open;
+
+            function open() {
+                var mediaPickerOptions = {
+                    multiPicker: true,
+                    submit: function(model) {
+                        editorService.close();
+                    },
+                    close: function() {
+                        editorService.close();
+                    }
+                }
+                editorService.mediaPicker(mediaPickerOptions);
+            };
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+    })();
+
+ +

Custom view example

+
+    (function () {
+
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+
+            vm.open = open;
+
+            function open() {
+                var options = {
+                    view: "path/to/view.html"
+                    submit: function(model) {
+                        editorService.close();
+                    },
+                    close: function() {
+                        editorService.close();
+                    }
+                }
+                editorService.open(options);
+            };
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+    })();
+
*/ (function () { "use strict"; @@ -43,6 +113,10 @@ * * @description * Method to open a new editor in infinite editing + * + * @param {Object} editor rendering options + * @param {String} editor.view Path to view + * @param {String} editor.size Sets the size of the editor ("Small"). If nothing is set it will use full width. */ function open(editor) { @@ -108,8 +182,12 @@ * * @description * Opens a media editor in infinite editing, the submit callback returns the updated content item + * @param {Object} editor rendering options * @param {String} editor.id The id of the content item * @param {Boolean} editor.create Create new content item + * @param {Function} editor.submit Callback function when the publish and close button is clicked. Returns the editor model object + * @param {Function} editor.close Callback function when the close button is clicked. + * * @returns {Object} editor object */ function contentEditor(editor) { @@ -124,6 +202,12 @@ * * @description * Opens a content picker in infinite editing, the submit callback returns an array of selected items + * + * @param {Object} editor rendering options + * @param {Boolean} editor.multiPicker Pick one or multiple items + * @param {Function} editor.submit Callback function when the submit button is clicked. Returns the editor model object + * @param {Function} editor.close Callback function when the close button is clicked. + * * @returns {Object} editor object */ function contentPicker(editor) { @@ -218,11 +302,13 @@ * * @description * Opens an embed editor in infinite editing. + * @param {Object} editor rendering options + * @param {String} editor.icon The icon class + * @param {String} editor.color The color class * @param {Callback} editor.submit Saves, submits, and closes the editor * @param {Callback} editor.close Closes the editor * @returns {Object} editor object */ - function linkPicker(editor) { editor.view = "views/common/infiniteeditors/linkpicker/linkpicker.html"; editor.size = "small"; @@ -236,6 +322,7 @@ * * @description * Opens a media editor in infinite editing, the submit callback returns the updated media item + * @param {Object} editor rendering options * @param {String} editor.id The id of the media item * @param {Boolean} editor.create Create new media item * @param {Callback} editor.submit Saves, submits, and closes the editor @@ -254,6 +341,7 @@ * * @description * Opens a media picker in infinite editing, the submit callback returns an array of selected media items + * @param {Object} editor rendering options * @param {Boolean} editor.multiPicker Pick one or multiple items * @param {Boolean} editor.onlyImages Only display files that have an image file-extension * @param {Boolean} editor.disableFolderSelect Disable folder selection @@ -276,6 +364,7 @@ * * @description * Opens an icon picker in infinite editing, the submit callback returns the selected icon + * @param {Object} editor rendering options * @param {Callback} editor.submit Submits the editor * @param {Callback} editor.close Closes the editor * @returns {Object} editor object @@ -293,6 +382,7 @@ * * @description * Opens the document type editor in infinite editing, the submit callback returns the saved document type + * @param {Object} editor rendering options * @param {Callback} editor.submit Submits the editor * @param {Callback} editor.close Closes the editor * @returns {Object} editor object @@ -309,6 +399,7 @@ * * @description * Opens the media type editor in infinite editing, the submit callback returns the saved media type + * @param {Object} editor rendering options * @param {Callback} editor.submit Submits the editor * @param {Callback} editor.close Closes the editor * @returns {Object} editor object @@ -318,24 +409,75 @@ open(editor); } + /** + * @ngdoc method + * @name umbraco.services.editorService#queryBuilder + * @methodOf umbraco.services.editorService + * + * @description + * Opens the query builder in infinite editing, the submit callback returns the generted query + * @param {Object} editor rendering options + * @param {Callback} editor.submit Submits the editor + * @param {Callback} editor.close Closes the editor + * @returns {Object} editor object + */ function queryBuilder(editor) { editor.view = "views/common/infiniteeditors/querybuilder/querybuilder.html"; editor.size = "small"; open(editor); } + /** + * @ngdoc method + * @name umbraco.services.editorService#treePicker + * @methodOf umbraco.services.editorService + * + * @description + * Opens the query builder in infinite editing, the submit callback returns the generted query + * @param {Object} editor rendering options + * @param {String} options.section tree section to display + * @param {String} options.treeAlias specific tree to display + * @param {Boolean} options.multiPicker should the tree pick one or multiple items before returning + * @param {Callback} editor.submit Submits the editor + * @param {Callback} editor.close Closes the editor + * @returns {Object} editor object + */ function treePicker(editor) { editor.view = "views/common/infiniteeditors/treepicker/treepicker.html"; editor.size = "small"; open(editor); } + /** + * @ngdoc method + * @name umbraco.services.editorService#nodePermissions + * @methodOf umbraco.services.editorService + * + * @description + * Opens the an editor to set node permissions. + * @param {Object} editor rendering options + * @param {Callback} editor.submit Submits the editor + * @param {Callback} editor.close Closes the editor + * @returns {Object} editor object + */ function nodePermissions(editor) { editor.view = "views/common/infiniteeditors/nodepermissions/nodepermissions.html"; editor.size = "small"; open(editor); } + /** + * @ngdoc method + * @name umbraco.services.editorService#insertCodeSnippet + * @methodOf umbraco.services.editorService + * + * @description + * Open an editor to insert code snippets into the code editor + * @param {Object} editor rendering options + * @param {Callback} editor.submit Submits the editor + * @param {Callback} editor.close Closes the editor + * @returns {Object} editor object + */ function insertCodeSnippet(editor) { editor.view = "views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html"; editor.size = "small"; @@ -349,6 +491,7 @@ * * @description * Opens the user group picker in infinite editing, the submit callback returns an array of the selected user groups + * @param {Object} editor rendering options * @param {Callback} editor.submit Submits the editor * @param {Callback} editor.close Closes the editor * @returns {Object} editor object @@ -366,6 +509,7 @@ * * @description * Opens the user group picker in infinite editing, the submit callback returns the saved template + * @param {Object} editor rendering options * @param {String} editor.id The template id * @param {Callback} editor.submit Submits the editor * @param {Callback} editor.close Closes the editor @@ -382,7 +526,8 @@ * @methodOf umbraco.services.editorService * * @description - * Opens the section picker in infinite editing, the submit callback returns an array of the selected sections + * Opens the section picker in infinite editing, the submit callback returns an array of the selected sections¨ + * @param {Object} editor rendering options * @param {Callback} editor.submit Submits the editor * @param {Callback} editor.close Closes the editor * @returns {Object} editor object @@ -400,6 +545,7 @@ * * @description * Opens the insert field editor in infinite editing, the submit callback returns the code snippet + * @param {Object} editor rendering options * @param {Callback} editor.submit Submits the editor * @param {Callback} editor.close Closes the editor * @returns {Object} editor object @@ -417,6 +563,7 @@ * * @description * Opens the template sections editor in infinite editing, the submit callback returns the type to insert + * @param {Object} editor rendering options * @param {Callback} editor.submit Submits the editor * @param {Callback} editor.close Closes the editor * @returns {Object} editor object @@ -429,11 +576,12 @@ /** * @ngdoc method - * @name umbraco.services.editorService#sectionPicker + * @name umbraco.services.editorService#userPicker * @methodOf umbraco.services.editorService * * @description * Opens the section picker in infinite editing, the submit callback returns an array of the selected users + * @param {Object} editor rendering options * @param {Callback} editor.submit Submits the editor * @param {Callback} editor.close Closes the editor * @returns {Object} editor object @@ -452,6 +600,7 @@ * @description * Opens the section picker in infinite editing, the submit callback returns an array of the selected items * + * @param {Object} editor rendering options * @param {Array} editor.availableItems Array of available items. * @param {Array} editor.selectedItems Array of selected items. When passed in the selected items will be filtered from the available items. * @param {Boolean} editor.filter Set to false to hide the filter. @@ -485,12 +634,14 @@ /** * @ngdoc method - * @name umbraco.services.editorService#macroPicker + * @name umbraco.services.editorService#memberGroupPicker * @methodOf umbraco.services.editorService * * @description * Opens a member group picker in infinite editing. * + * @param {Object} editor rendering options + * @param {Object} editor.multiPicker Pick one or multiple items. * @param {Callback} editor.submit Submits the editor. * @param {Callback} editor.close Closes the editor. * @returns {Object} editor object From b19f9c81140991549118b7b7b5ee071c423e222f Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 11 Dec 2018 13:39:12 +0100 Subject: [PATCH 52/65] update styling on forms install dashboard --- src/Umbraco.Web.UI.Client/src/less/dashboards.less | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/dashboards.less b/src/Umbraco.Web.UI.Client/src/less/dashboards.less index 5fd0e25be1..cc13ad31fd 100644 --- a/src/Umbraco.Web.UI.Client/src/less/dashboards.less +++ b/src/Umbraco.Web.UI.Client/src/less/dashboards.less @@ -1,13 +1,14 @@ .umb-dashboards-forms-install { background: url('../img/forms/installer-background.png'); background-repeat: repeat-x; - position: relative; - top: -30px; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; padding-top: 30px; - box-shadow: inset 0px -40px 30px 25px rgba(255,255,255,1); - -moz-border-radius: 0px 0px 200px 200px; - -webkit-border-radius: 0px 0px 200px 200px; - border-radius: 0px 0px 200px 200px; + background-color: @white; + overflow: auto; small { font-size: 14px; From 0dff7680fd695088984cb93c8affcc20c7bdeffe Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 11 Dec 2018 14:02:54 +0100 Subject: [PATCH 53/65] Fix ContentTypeServiceTests (but, why???) --- .../Services/ContentTypeServiceTests.cs | 71 +++++++++++-------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index bc0854bdb7..8ea4856861 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -148,7 +148,7 @@ namespace Umbraco.Tests.Services //change the content type to be invariant, we will also update the name here to detect the copy changes doc.SetCultureName("Hello2", "en-US"); ServiceContext.ContentService.Save(doc); - contentType.Variations = ContentVariation.Nothing; + contentType.Variations = ContentVariation.Nothing; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get @@ -372,7 +372,7 @@ namespace Umbraco.Tests.Services doc2 = ServiceContext.ContentService.GetById(doc2.Id); //re-get //this will be null because the doc type was changed back to variant but it's property types don't get changed back - Assert.IsNull(doc.GetValue("title", "en-US")); + Assert.IsNull(doc.GetValue("title", "en-US")); Assert.IsNull(doc2.GetValue("title", "en-US")); } @@ -1714,50 +1714,65 @@ namespace Umbraco.Tests.Services // Arrange var service = ServiceContext.ContentTypeService; + // create 'page' content type with a 'Content_' group var page = MockedContentTypes.CreateSimpleContentType("page", "Page", null, false, "Content_"); + Assert.IsTrue(page.PropertyGroups.Contains("Content_")); + Assert.AreEqual(3, page.PropertyTypes.Count()); service.Save(page); + + // create 'contentPage' content type as a child of 'page' var contentPage = MockedContentTypes.CreateSimpleContentType("contentPage", "Content Page", page, true); - service.Save(contentPage); - var composition = MockedContentTypes.CreateMetaContentType(); - composition.AddPropertyGroup("Content"); - service.Save(composition); - //Adding Meta-composition to child doc type - contentPage.AddContentType(composition); + Assert.AreEqual(3, contentPage.PropertyTypes.Count()); service.Save(contentPage); - // Act - var propertyTypeOne = new PropertyType(Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext, "testTextbox") + // add 'Content' group to 'meta' content type + var meta = MockedContentTypes.CreateMetaContentType(); + meta.AddPropertyGroup("Content"); + Assert.AreEqual(2, meta.PropertyTypes.Count()); + service.Save(meta); + + // add 'meta' content type to 'contentPage' composition + contentPage.AddContentType(meta); + service.Save(contentPage); + + // add property 'prop1' to 'contentPage' group 'Content_' + var prop1 = new PropertyType(Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext, "testTextbox") { Name = "Test Textbox", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }; - var firstOneAdded = contentPage.AddPropertyType(propertyTypeOne, "Content_"); - var propertyTypeTwo = new PropertyType(Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext, "anotherTextbox") + var prop1Added = contentPage.AddPropertyType(prop1, "Content_"); + Assert.IsTrue(prop1Added); + + // add property 'prop2' to 'contentPage' group 'Content' + var prop2 = new PropertyType(Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext, "anotherTextbox") { Name = "Another Test Textbox", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }; - var secondOneAdded = contentPage.AddPropertyType(propertyTypeTwo, "Content"); + var prop2Added = contentPage.AddPropertyType(prop2, "Content"); + Assert.IsTrue(prop2Added); + + // save 'contentPage' content type service.Save(contentPage); - Assert.That(page.PropertyGroups.Contains("Content_"), Is.True); - var propertyGroup = page.PropertyGroups["Content_"]; - page.PropertyGroups.Add(new PropertyGroup(true) { Id = propertyGroup.Id, Name = "ContentTab", SortOrder = 0}); + var group = page.PropertyGroups["Content_"]; + group.Name = "ContentTab"; // rename the group service.Save(page); + Assert.AreEqual(3, page.PropertyTypes.Count()); - // Assert - Assert.That(firstOneAdded, Is.True); - Assert.That(secondOneAdded, Is.True); + // get 'contentPage' content type again + var contentPageAgain = service.Get("contentPage"); + Assert.IsNotNull(contentPageAgain); - var contentType = service.Get("contentPage"); - Assert.That(contentType, Is.Not.Null); + // assert that 'Content_' group is still there because we don't propagate renames + var findGroup = contentPageAgain.CompositionPropertyGroups.FirstOrDefault(x => x.Name == "Content_"); + Assert.IsNotNull(findGroup); - var compositionPropertyGroups = contentType.CompositionPropertyGroups; - - // now it is still 1, because we don't propagate renames anymore - Assert.That(compositionPropertyGroups.Count(x => x.Name.Equals("Content_")), Is.EqualTo(1)); - - var propertyTypeCount = contentType.PropertyTypes.Count(); - var compPropertyTypeCount = contentType.CompositionPropertyTypes.Count(); + // count all property types (local and composed) + var propertyTypeCount = contentPageAgain.PropertyTypes.Count(); Assert.That(propertyTypeCount, Is.EqualTo(5)); + + // count composed property types + var compPropertyTypeCount = contentPageAgain.CompositionPropertyTypes.Count(); Assert.That(compPropertyTypeCount, Is.EqualTo(10)); } From b0157d7116603c6ab6211ce329c4c70021309e1c Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 11 Dec 2018 14:08:44 +0000 Subject: [PATCH 54/65] We do not need to ship with these Umbraco Forms trees in the config file --- src/Umbraco.Web.UI/config/trees.config | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index 2b99f8751f..7f7aeca8a7 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -30,9 +30,5 @@ - - - - - + From 767cd93f08b83acc175aa2311ff29936260147ee Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 11 Dec 2018 14:09:44 +0000 Subject: [PATCH 55/65] Adds in some logic if we have no child trees for that application/section then we create a SingleTreeRoot --- .../Trees/ApplicationTreeController.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Trees/ApplicationTreeController.cs b/src/Umbraco.Web/Trees/ApplicationTreeController.cs index c1192b6909..d824f32f4b 100644 --- a/src/Umbraco.Web/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web/Trees/ApplicationTreeController.cs @@ -78,10 +78,20 @@ namespace Umbraco.Web.Trees } } - var multiTree = TreeRootNode.CreateMultiTreeRoot(collection); - multiTree.Name = Services.TextService.Localize("sections/" + application); + if(collection.Count > 0) + { + var multiTree = TreeRootNode.CreateMultiTreeRoot(collection); + multiTree.Name = Services.TextService.Localize("sections/" + application); - return multiTree; + return multiTree; + } + + //Otherwise its a application/section with no trees (aka a full screen app) + //For example we do not have a Forms tree definied in C# & can not attribute with [Tree(isSingleNodeTree:true0] + var rootId = Constants.System.Root.ToString(CultureInfo.InvariantCulture); + var section = Services.TextService.Localize("sections/" + application); + + return TreeRootNode.CreateSingleTreeRoot(rootId, null, null, section, TreeNodeCollection.Empty, true); } var rootNodeGroups = new List(); From e1743ae3ea2d8331b8f136d222c92f53c6d95f3f Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 12 Dec 2018 12:05:19 +1100 Subject: [PATCH 56/65] Changes IValueSetBuilder to not be strongly typed, no need for that --- src/Umbraco.Examine/IValueSetBuilder.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Examine/IValueSetBuilder.cs b/src/Umbraco.Examine/IValueSetBuilder.cs index 89aa907926..1c4890f404 100644 --- a/src/Umbraco.Examine/IValueSetBuilder.cs +++ b/src/Umbraco.Examine/IValueSetBuilder.cs @@ -5,18 +5,17 @@ using Umbraco.Core.Models; namespace Umbraco.Examine { /// - /// Creates a collection of to be indexed based on a collection of + /// Creates a collection of to be indexed based on a collection of /// - /// - public interface IValueSetBuilder - where TContent : IContentBase + /// + public interface IValueSetBuilder { /// - /// Creates a collection of to be indexed based on a collection of + /// Creates a collection of to be indexed based on a collection of /// /// /// - IEnumerable GetValueSets(params TContent[] content); + IEnumerable GetValueSets(params T[] content); } } From 15c7d7ce77b7c9c3e81340ec926b7fbc9da718c1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 12 Dec 2018 12:37:21 +1100 Subject: [PATCH 57/65] Creates a sub-generic interface for creating indexes --- src/Umbraco.Examine/IIndexCreator.cs | 13 +++++++++++++ src/Umbraco.Examine/IIndexDiagnostics.cs | 3 --- src/Umbraco.Examine/Umbraco.Examine.csproj | 1 + src/Umbraco.Web/Search/IUmbracoIndexesCreator.cs | 5 +++-- 4 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 src/Umbraco.Examine/IIndexCreator.cs diff --git a/src/Umbraco.Examine/IIndexCreator.cs b/src/Umbraco.Examine/IIndexCreator.cs new file mode 100644 index 0000000000..3b8f683990 --- /dev/null +++ b/src/Umbraco.Examine/IIndexCreator.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Examine; + +namespace Umbraco.Examine +{ + /// + /// Creates 's + /// + public interface IIndexCreator + { + IEnumerable Create(); + } +} diff --git a/src/Umbraco.Examine/IIndexDiagnostics.cs b/src/Umbraco.Examine/IIndexDiagnostics.cs index 04ca4a6ab9..29d530c2d0 100644 --- a/src/Umbraco.Examine/IIndexDiagnostics.cs +++ b/src/Umbraco.Examine/IIndexDiagnostics.cs @@ -1,12 +1,9 @@ using System; using System.Collections.Generic; -using Examine; using Umbraco.Core; namespace Umbraco.Examine { - - /// /// Exposes diagnostic information about an index /// diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 0aedf6e754..aa57707b2e 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -66,6 +66,7 @@ + diff --git a/src/Umbraco.Web/Search/IUmbracoIndexesCreator.cs b/src/Umbraco.Web/Search/IUmbracoIndexesCreator.cs index 58014597d2..d654e4effd 100644 --- a/src/Umbraco.Web/Search/IUmbracoIndexesCreator.cs +++ b/src/Umbraco.Web/Search/IUmbracoIndexesCreator.cs @@ -1,13 +1,14 @@ using System.Collections.Generic; using Examine; +using Umbraco.Examine; namespace Umbraco.Web.Search { + /// /// /// Used to create the Umbraco indexes /// - public interface IUmbracoIndexesCreator + public interface IUmbracoIndexesCreator : IIndexCreator { - IEnumerable Create(); } } From 7cb83373cae5c9961c7d88de8da5afc1fc385881 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 12 Dec 2018 12:46:58 +1100 Subject: [PATCH 58/65] Creates base class LuceneIndexCreator --- src/Umbraco.Examine/LuceneIndexCreator.cs | 39 +++++++++++++++++++ src/Umbraco.Examine/Umbraco.Examine.csproj | 1 + .../Search/UmbracoIndexesCreator.cs | 26 +++---------- 3 files changed, 46 insertions(+), 20 deletions(-) create mode 100644 src/Umbraco.Examine/LuceneIndexCreator.cs diff --git a/src/Umbraco.Examine/LuceneIndexCreator.cs b/src/Umbraco.Examine/LuceneIndexCreator.cs new file mode 100644 index 0000000000..c1fe7ae772 --- /dev/null +++ b/src/Umbraco.Examine/LuceneIndexCreator.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.IO; +using Examine; +using Lucene.Net.Store; +using Umbraco.Core.IO; + +namespace Umbraco.Examine +{ + /// + /// + /// Abstract class for creating Lucene based Indexes + /// + public abstract class LuceneIndexCreator : IIndexCreator + { + public abstract IEnumerable Create(); + + /// + /// Creates a file system based Lucene with the correct locking guidelines for Umbraco + /// + /// + /// + public virtual Lucene.Net.Store.Directory CreateFileSystemLuceneDirectory(string name) + { + //TODO: We should have a single AppSetting to be able to specify a default DirectoryFactory so we can have a single + //setting to configure all indexes that use this to easily swap the directory to Sync/%temp%/blog, etc... + + var dirInfo = new DirectoryInfo(Path.Combine(IOHelper.MapPath(SystemDirectories.Data), "TEMP", "ExamineIndexes", name)); + if (!dirInfo.Exists) + System.IO.Directory.CreateDirectory(dirInfo.FullName); + + var luceneDir = new SimpleFSDirectory(dirInfo); + //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the appdomain + //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock + //which simply checks the existence of the lock file + luceneDir.SetLockFactory(new NoPrefixSimpleFsLockFactory(dirInfo)); + return luceneDir; + } + } +} diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index aa57707b2e..66b1f09068 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -89,6 +89,7 @@ + Properties\SolutionInfo.cs diff --git a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs index f8b2c6ef8b..3723dedc48 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.Search /// /// Creates the indexes used by Umbraco /// - public class UmbracoIndexesCreator : IUmbracoIndexesCreator + public class UmbracoIndexesCreator : LuceneIndexCreator, IUmbracoIndexesCreator { //TODO: we should inject the different IValueSetValidator so devs can just register them instead of overriding this class? @@ -45,7 +45,7 @@ namespace Umbraco.Web.Search /// Creates the Umbraco indexes /// /// - public IEnumerable Create() + public override IEnumerable Create() { return new [] { @@ -61,7 +61,7 @@ namespace Umbraco.Web.Search Constants.UmbracoIndexes.InternalIndexName, //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes UmbracoExamineIndex.UmbracoIndexFieldDefinitions, - GetFileSystemLuceneDirectory(Constants.UmbracoIndexes.InternalIndexPath), + CreateFileSystemLuceneDirectory(Constants.UmbracoIndexes.InternalIndexPath), new CultureInvariantWhitespaceAnalyzer(), ProfilingLogger, LanguageService, @@ -75,7 +75,7 @@ namespace Umbraco.Web.Search Constants.UmbracoIndexes.ExternalIndexName, //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes UmbracoExamineIndex.UmbracoIndexFieldDefinitions, - GetFileSystemLuceneDirectory(Constants.UmbracoIndexes.ExternalIndexPath), + CreateFileSystemLuceneDirectory(Constants.UmbracoIndexes.ExternalIndexPath), new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30), ProfilingLogger, LanguageService, @@ -89,27 +89,13 @@ namespace Umbraco.Web.Search Constants.UmbracoIndexes.MembersIndexName, //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes UmbracoExamineIndex.UmbracoIndexFieldDefinitions, - GetFileSystemLuceneDirectory(Constants.UmbracoIndexes.MembersIndexPath), + CreateFileSystemLuceneDirectory(Constants.UmbracoIndexes.MembersIndexPath), new CultureInvariantWhitespaceAnalyzer(), ProfilingLogger, GetMemberValueSetValidator()); return index; } - - public virtual Lucene.Net.Store.Directory GetFileSystemLuceneDirectory(string name) - { - var dirInfo = new DirectoryInfo(Path.Combine(IOHelper.MapPath(SystemDirectories.Data), "TEMP", "ExamineIndexes", name)); - if (!dirInfo.Exists) - System.IO.Directory.CreateDirectory(dirInfo.FullName); - - var luceneDir = new SimpleFSDirectory(dirInfo); - //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the appdomain - //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock - //which simply checks the existence of the lock file - luceneDir.SetLockFactory(new NoPrefixSimpleFsLockFactory(dirInfo)); - return luceneDir; - } - + public virtual IContentValueSetValidator GetContentValueSetValidator() { return new ContentValueSetValidator(false, true, PublicAccessService); From b5da641d4823f96f3e8f18bbf8fcf54ad99e9548 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 12 Dec 2018 13:23:29 +1100 Subject: [PATCH 59/65] Uses the DefaultLockFactory to set the lock --- src/Umbraco.Examine/LuceneIndexCreator.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Examine/LuceneIndexCreator.cs b/src/Umbraco.Examine/LuceneIndexCreator.cs index c1fe7ae772..572de1e8a8 100644 --- a/src/Umbraco.Examine/LuceneIndexCreator.cs +++ b/src/Umbraco.Examine/LuceneIndexCreator.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.IO; using Examine; +using Examine.LuceneEngine.Directories; using Lucene.Net.Store; using Umbraco.Core.IO; @@ -29,10 +30,13 @@ namespace Umbraco.Examine System.IO.Directory.CreateDirectory(dirInfo.FullName); var luceneDir = new SimpleFSDirectory(dirInfo); + //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the appdomain //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock //which simply checks the existence of the lock file - luceneDir.SetLockFactory(new NoPrefixSimpleFsLockFactory(dirInfo)); + // The full syntax of this is: new NoPrefixSimpleFsLockFactory(dirInfo) + // however, we are setting the DefaultLockFactory in startup so we'll use that instead since it can be managed globally. + luceneDir.SetLockFactory(DirectoryFactory.DefaultLockFactory(dirInfo)); return luceneDir; } } From 85b25485da05f2fcc7e86b8e7b856f0756bfcd11 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 12 Dec 2018 13:34:54 +1100 Subject: [PATCH 60/65] removes trees from trees.Release.config --- src/Umbraco.Web.UI/config/trees.Release.config | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI/config/trees.Release.config b/src/Umbraco.Web.UI/config/trees.Release.config index 7b5a9e5e2a..bd75e97c38 100644 --- a/src/Umbraco.Web.UI/config/trees.Release.config +++ b/src/Umbraco.Web.UI/config/trees.Release.config @@ -30,9 +30,5 @@ - - - - - + From efb9b37f5daac33ef753c46f0a708d0f0a930c53 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 12 Dec 2018 09:27:03 +0100 Subject: [PATCH 61/65] Change from member IDs to usernames in member selections --- .../src/common/resources/content.resource.js | 8 ++--- .../content/content.protect.controller.js | 31 ++++++++++++++----- src/Umbraco.Web/Editors/ContentController.cs | 7 +++-- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 681a629ed0..75e1877be8 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -991,13 +991,13 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * * @param {Int} contentId The content Id * @param {Array} roles The roles that should have access (if using role based protection) - * @param {Array} memberIds The IDs of the members that should have access (if using member based protection) + * @param {Array} usernames The usernames of the members that should have access (if using member based protection) * @param {Int} loginPageId The Id of the login page * @param {Int} errorPageId The Id of the error page * @returns {Promise} resourcePromise object containing the public access protection * */ - updatePublicAccess: function (contentId, roles, memberIds, loginPageId, errorPageId) { + updatePublicAccess: function (contentId, roles, usernames, loginPageId, errorPageId) { var publicAccess = { contentId: contentId, loginPageId: loginPageId, @@ -1006,8 +1006,8 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { if (angular.isArray(roles) && roles.length) { publicAccess.roles = roles; } - else if (angular.isArray(memberIds) && memberIds.length) { - publicAccess.memberIds = memberIds; + else if (angular.isArray(usernames) && usernames.length) { + publicAccess.usernames = usernames; } else { throw "must supply either userName/password or roles"; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js index ace37d4ac5..c6b6cecd5d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function ContentProtectController($scope, $routeParams, contentResource, memberGroupResource, navigationService, localizationService, editorService) { + function ContentProtectController($scope, $q, contentResource, memberResource, memberGroupResource, navigationService, localizationService, editorService) { var vm = this; var id = $scope.currentNode.id; @@ -92,8 +92,8 @@ function save() { vm.buttonState = "busy"; var roles = _.map(vm.groups, function (group) { return group.name; }); - var memberIds = _.map(vm.members, function (member) { return member.id; }); - contentResource.updatePublicAccess(id, roles, memberIds, vm.loginPage.id, vm.errorPage.id).then( + var usernames = _.map(vm.members, function (member) { return member.username; }); + contentResource.updatePublicAccess(id, roles, usernames, vm.loginPage.id, vm.errorPage.id).then( function () { localizationService.localize("publicAccess_paIsProtected", [$scope.currentNode.name]).then(function (value) { vm.success = { @@ -161,15 +161,30 @@ filterCssClass: "not-allowed", submit: function (model) { if (model.selection && model.selection.length) { + var promises = []; + // get the selected member usernames _.each(model.selection, function (member) { - if (!_.find(vm.members, function (m) { return m.id === member.id })) { - vm.members.push(member); - } + // TODO: + // as-is we need to fetch all the picked members one at a time to get their usernames. + // when editorService has a memberPicker method, see if this can't be avoided - otherwise + // add a memberResource.getByKeys() method to do all this in one request + promises.push( + memberResource.getByKey(member.key).then(function(newMember) { + if (!_.find(vm.members, function (currentMember) { return currentMember.username === newMember.username })) { + vm.members.push(newMember); + } + }) + ); }); + editorService.close(); + navigationService.allowHideDialog(true); + // wait for all the member lookups to complete + vm.loading = true; + $q.all(promises).then(function() { + vm.loading = false; + }); } - editorService.close(); - navigationService.allowHideDialog(true); }, close: function () { editorService.close(); diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index e245883ed7..423680fac5 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -2173,6 +2173,7 @@ namespace Umbraco.Web.Editors // unwrap the current public access setup for the client // - this API method is the single point of entry for both "modes" of public access (single user and role based) + // TODO: support custom membership providers here var members = entry.Rules .Where(rule => rule.RuleType == Constants.Conventions.PublicAccess.MemberUsernameRuleType) .Select(rule => Services.MemberService.GetByUsername(rule.RuleValue)) @@ -2196,9 +2197,9 @@ namespace Umbraco.Web.Editors // set up public access using role based access [EnsureUserPermissionForContent("contentId", ActionProtect.ActionLetter)] [HttpPost] - public HttpResponseMessage PostPublicAccess(int contentId, [FromUri]string[] roles, [FromUri]int[] memberIds, int loginPageId, int errorPageId) + public HttpResponseMessage PostPublicAccess(int contentId, [FromUri]string[] roles, [FromUri]string[] usernames, int loginPageId, int errorPageId) { - if ((roles == null || roles.Any() == false) && (memberIds == null || memberIds.Any() == false)) + if ((roles == null || roles.Any() == false) && (usernames == null || usernames.Any() == false)) { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest)); } @@ -2214,7 +2215,7 @@ namespace Umbraco.Web.Editors var isRoleBased = roles != null && roles.Any(); var candidateRuleValues = isRoleBased ? roles - : memberIds.Select(id => Services.MemberService.GetById(id)?.Username).Where(s => s != null).ToArray(); + : usernames; var newRuleType = isRoleBased ? Constants.Conventions.PublicAccess.MemberRoleRuleType : Constants.Conventions.PublicAccess.MemberUsernameRuleType; From 35f0bf92f2efde5d3cf436c07e3ae08c034b117a Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 12 Dec 2018 09:55:35 +0100 Subject: [PATCH 62/65] Support custom membership providers (untested) --- src/Umbraco.Web/Editors/ContentController.cs | 33 ++++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 423680fac5..3f595f9cbc 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -8,6 +8,7 @@ using System.Text; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.ModelBinding; +using System.Web.Security; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Logging; @@ -2173,13 +2174,33 @@ namespace Umbraco.Web.Editors // unwrap the current public access setup for the client // - this API method is the single point of entry for both "modes" of public access (single user and role based) - // TODO: support custom membership providers here - var members = entry.Rules + var usernames = entry.Rules .Where(rule => rule.RuleType == Constants.Conventions.PublicAccess.MemberUsernameRuleType) - .Select(rule => Services.MemberService.GetByUsername(rule.RuleValue)) - .Where(member => member != null) - .Select(Mapper.Map) - .ToArray(); + .Select(rule => rule.RuleValue).ToArray(); + + MemberDisplay[] members; + switch (Services.MemberService.GetMembershipScenario()) + { + case MembershipScenario.NativeUmbraco: + members = usernames + .Select(username => Services.MemberService.GetByUsername(username)) + .Where(member => member != null) + .Select(Mapper.Map) + .ToArray(); + break; + // TODO: test support custom membership providers + case MembershipScenario.CustomProviderWithUmbracoLink: + case MembershipScenario.StandaloneCustomProvider: + default: + var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); + members = usernames + .Select(username => provider.GetUser(username, false)) + .Where(membershipUser => membershipUser != null) + .Select(Mapper.Map) + .ToArray(); + break; + } + var roles = entry.Rules .Where(rule => rule.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType) .Select(rule => rule.RuleValue) From 22b78ec617aaa404c385a70220ff0b0cd793bd52 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 12 Dec 2018 11:18:16 +0100 Subject: [PATCH 63/65] Make sure at least one member is selected --- .../src/views/content/content.protect.controller.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js index c6b6cecd5d..988cd10739 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js @@ -86,6 +86,9 @@ if (vm.type === "role") { return vm.groups && vm.groups.length > 0; } + if (vm.type === "member") { + return vm.members && vm.members.length > 0; + } return true; } From e4f9c2916d52028bd15061a0f5ba42a3bf6c58cd Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 12 Dec 2018 14:27:20 +0100 Subject: [PATCH 64/65] Use "groups", not "roles" + return richer group models from API --- .../src/common/resources/content.resource.js | 8 ++--- .../content/content.protect.controller.js | 27 +++++++++-------- .../src/views/content/protect.html | 16 +++++----- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 8 ++--- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 8 ++--- .../Umbraco/config/lang/en_us.xml | 8 ++--- src/Umbraco.Web/Editors/ContentController.cs | 29 ++++++++++--------- .../Models/ContentEditing/PublicAccess.cs | 7 ++--- 8 files changed, 55 insertions(+), 56 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 75e1877be8..b807a4dc31 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -990,21 +990,21 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * * * @param {Int} contentId The content Id - * @param {Array} roles The roles that should have access (if using role based protection) + * @param {Array} groups The names of the groups that should have access (if using group based protection) * @param {Array} usernames The usernames of the members that should have access (if using member based protection) * @param {Int} loginPageId The Id of the login page * @param {Int} errorPageId The Id of the error page * @returns {Promise} resourcePromise object containing the public access protection * */ - updatePublicAccess: function (contentId, roles, usernames, loginPageId, errorPageId) { + updatePublicAccess: function (contentId, groups, usernames, loginPageId, errorPageId) { var publicAccess = { contentId: contentId, loginPageId: loginPageId, errorPageId: errorPageId }; - if (angular.isArray(roles) && roles.length) { - publicAccess.roles = roles; + if (angular.isArray(groups) && groups.length) { + publicAccess.groups = groups; } else if (angular.isArray(usernames) && usernames.length) { publicAccess.usernames = usernames; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js index 988cd10739..8d80f308ab 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js @@ -36,7 +36,7 @@ // init the current settings for public access (if any) vm.loginPage = publicAccess.loginPage; vm.errorPage = publicAccess.errorPage; - vm.roles = publicAccess.roles || []; + vm.groups = publicAccess.groups || []; vm.members = publicAccess.members || []; vm.canRemove = true; @@ -44,8 +44,8 @@ vm.type = "member"; next(); } - else if (vm.roles.length) { - vm.type = "role"; + else if (vm.groups.length) { + vm.type = "group"; next(); } else { @@ -55,16 +55,14 @@ } function next() { - if (vm.type === "role") { + if (vm.type === "group") { vm.loading = true; - // Get all member groups + // get all existing member groups for lookup upon selection + // NOTE: if/when member groups support infinite editing, we can't rely on using a cached lookup list of valid groups anymore memberGroupResource.getGroups().then(function (groups) { vm.step = vm.type; vm.allGroups = groups; vm.hasGroups = groups.length > 0; - vm.groups = _.filter(groups, function(group) { - return _.contains(vm.roles, group.name); - }); vm.loading = false; }); } @@ -83,7 +81,7 @@ if (!vm.loginPage || !vm.errorPage) { return false; } - if (vm.type === "role") { + if (vm.type === "group") { return vm.groups && vm.groups.length > 0; } if (vm.type === "member") { @@ -94,9 +92,9 @@ function save() { vm.buttonState = "busy"; - var roles = _.map(vm.groups, function (group) { return group.name; }); + var groups = _.map(vm.groups, function (group) { return group.name; }); var usernames = _.map(vm.members, function (member) { return member.username; }); - contentResource.updatePublicAccess(id, roles, usernames, vm.loginPage.id, vm.errorPage.id).then( + contentResource.updatePublicAccess(id, groups, usernames, vm.loginPage.id, vm.errorPage.id).then( function () { localizationService.localize("publicAccess_paIsProtected", [$scope.currentNode.name]).then(function (value) { vm.success = { @@ -130,9 +128,10 @@ ? model.selectedMemberGroups : [model.selectedMemberGroup]; _.each(selectedGroupIds, - function(groupId) { + function (groupId) { + // find the group in the lookup list and add it if it isn't already var group = _.find(vm.allGroups, function(g) { return g.id === parseInt(groupId); }); - if (group && !_.contains(vm.groups, group)) { + if (group && !_.find(vm.groups, function (g) { return g.id === group.id })) { vm.groups.push(group); } }); @@ -147,7 +146,7 @@ } function removeGroup(group) { - vm.groups = _.without(vm.groups, group); + vm.groups = _.reject(vm.groups, function(g) { return g.id === group.id }); } function pickMember() { diff --git a/src/Umbraco.Web.UI.Client/src/views/content/protect.html b/src/Umbraco.Web.UI.Client/src/views/content/protect.html index f179f1c63c..ae4a15e8c1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/protect.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/protect.html @@ -29,11 +29,11 @@
- + -
@@ -55,12 +55,12 @@ -
-

You need to create a membergroup before you can use role-based authentication

+
+

You need to create a member group before you can use group based authentication

-
-

Pick the roles who have access to this page

+
+

Select the groups that should have access to this page

Indsæt, men fjern formattering som ikke bør være på en webside (Anbefales) - Rollebaseret beskyttelse - Hvis du ønsker at kontrollere adgang til siden ved hjælp af rollebaseret godkendelse via Umbracos medlemsgrupper. - Du skal oprette en medlemsgruppe før du kan bruge rollebaseret godkendelse + Gruppebaseret beskyttelse + Hvis du ønsker at give adgang til alle medlemmer af specifikke medlemsgrupper + Du skal oprette en medlemsgruppe før du kan bruge gruppebaseret beskyttelse Fejlside Brugt når folk er logget ind, men ingen adgang %0% skal beskyttes]]> @@ -885,7 +885,7 @@ Mange hilsner fra Umbraco robotten Fjern beskyttelse... %0%?]]> Vælg siderne der indeholder log ind-formularer og fejlmeddelelser - %0%]]> + %0%]]> %0%]]> Adgang til enkelte medlemmer Hvis du ønsker at give adgang til enkelte medlemmer diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index f61186a98f..51ea51685f 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1124,9 +1124,9 @@ To manage your website, simply open the Umbraco back office and start adding con Paste, but remove formatting (Recommended) - Role based protection - If you wish to control access to the page using role-based authentication, using Umbraco's member groups - You need to create a membergroup before you can use role-based authentication + Group based protection + If you want to grant access to all members of specific member groups + You need to create a member group before you can use group based authentication Error Page Used when people are logged on, but do not have access %0%]]> @@ -1137,7 +1137,7 @@ To manage your website, simply open the Umbraco back office and start adding con Remove protection... %0%?]]> Select the pages that contain login form and error messages - %0%]]> + %0%]]> %0%]]> Specific members protection If you wish to grant access to specific members diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 1953a351be..b5efba5a32 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1146,9 +1146,9 @@ To manage your website, simply open the Umbraco back office and start adding con Paste, but remove formatting (Recommended) - Role based protection - If you wish to control access to the page using role-based authentication, using Umbraco's member groups - You need to create a membergroup before you can use role-based authentication + Group based protection + If you want to grant access to all members of specific member groups + You need to create a member group before you can use group based authentication Error Page Used when people are logged on, but do not have access %0%]]> @@ -1159,7 +1159,7 @@ To manage your website, simply open the Umbraco back office and start adding con Remove protection... %0%?]]> Select the pages that contain login form and error messages - %0%]]> + %0%]]> %0%]]> Specific members protection If you wish to grant access to specific members diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 3f595f9cbc..d1cb15e76d 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -2201,15 +2201,18 @@ namespace Umbraco.Web.Editors break; } - var roles = entry.Rules + var allGroups = Services.MemberGroupService.GetAll().ToArray(); + var groups = entry.Rules .Where(rule => rule.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType) - .Select(rule => rule.RuleValue) + .Select(rule => allGroups.FirstOrDefault(g => g.Name == rule.RuleValue)) + .Where(memberGroup => memberGroup != null) + .Select(Mapper.Map) .ToArray(); return Request.CreateResponse(HttpStatusCode.OK, new PublicAccess { Members = members, - Roles = roles, + Groups = groups, LoginPage = loginPageEntity != null ? Mapper.Map(loginPageEntity) : null, ErrorPage = errorPageEntity != null ? Mapper.Map(errorPageEntity) : null }); @@ -2218,9 +2221,9 @@ namespace Umbraco.Web.Editors // set up public access using role based access [EnsureUserPermissionForContent("contentId", ActionProtect.ActionLetter)] [HttpPost] - public HttpResponseMessage PostPublicAccess(int contentId, [FromUri]string[] roles, [FromUri]string[] usernames, int loginPageId, int errorPageId) + public HttpResponseMessage PostPublicAccess(int contentId, [FromUri]string[] groups, [FromUri]string[] usernames, int loginPageId, int errorPageId) { - if ((roles == null || roles.Any() == false) && (usernames == null || usernames.Any() == false)) + if ((groups == null || groups.Any() == false) && (usernames == null || usernames.Any() == false)) { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest)); } @@ -2233,11 +2236,11 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest)); } - var isRoleBased = roles != null && roles.Any(); - var candidateRuleValues = isRoleBased - ? roles + var isGroupBased = groups != null && groups.Any(); + var candidateRuleValues = isGroupBased + ? groups : usernames; - var newRuleType = isRoleBased + var newRuleType = isGroupBased ? Constants.Conventions.PublicAccess.MemberRoleRuleType : Constants.Conventions.PublicAccess.MemberUsernameRuleType; @@ -2247,9 +2250,9 @@ namespace Umbraco.Web.Editors { entry = new PublicAccessEntry(content, loginPage, errorPage, new List()); - foreach (var role in candidateRuleValues) + foreach (var ruleValue in candidateRuleValues) { - entry.AddRule(role, newRuleType); + entry.AddRule(ruleValue, newRuleType); } } else @@ -2272,9 +2275,9 @@ namespace Umbraco.Web.Editors { entry.RemoveRule(rule); } - foreach (var role in newRuleValues) + foreach (var ruleValue in newRuleValues) { - entry.AddRule(role, newRuleType); + entry.AddRule(ruleValue, newRuleType); } } diff --git a/src/Umbraco.Web/Models/ContentEditing/PublicAccess.cs b/src/Umbraco.Web/Models/ContentEditing/PublicAccess.cs index b8035c9f25..dcf2dcae92 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PublicAccess.cs +++ b/src/Umbraco.Web/Models/ContentEditing/PublicAccess.cs @@ -5,11 +5,8 @@ namespace Umbraco.Web.Models.ContentEditing [DataContract(Name = "publicAccess", Namespace = "")] public class PublicAccess { - //[DataMember(Name = "userName")] - //public string UserName { get; set; } - - [DataMember(Name = "roles")] - public string[] Roles { get; set; } + [DataMember(Name = "groups")] + public MemberGroupDisplay[] Groups { get; set; } [DataMember(Name = "loginPage")] public EntityBasic LoginPage { get; set; } From 1ba41986d74ce3c4b7c9b2ba9a66e67bd0eec110 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 12 Dec 2018 15:35:34 +0100 Subject: [PATCH 65/65] Make sure the public access supports member based access --- .../Services/PublicAccessServiceExtensions.cs | 17 ++++++++++++----- src/Umbraco.Web/Routing/PublishedRouter.cs | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs b/src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs index 12db4daf40..b0dc979ebf 100644 --- a/src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs +++ b/src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Web.Security; +using Umbraco.Core.Models; namespace Umbraco.Core.Services { @@ -41,7 +42,7 @@ namespace Umbraco.Core.Services return hasChange; } - public static bool HasAccess(this IPublicAccessService publicAccessService, int documentId, IContentService contentService, IEnumerable currentMemberRoles) + public static bool HasAccess(this IPublicAccessService publicAccessService, int documentId, IContentService contentService, string username, IEnumerable currentMemberRoles) { var content = contentService.GetById(documentId); if (content == null) return true; @@ -49,8 +50,7 @@ namespace Umbraco.Core.Services var entry = publicAccessService.GetEntryForContent(content); if (entry == null) return true; - return entry.Rules.Any(x => x.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType - && currentMemberRoles.Contains(x.RuleValue)); + return HasAccess(entry, username, currentMemberRoles); } public static bool HasAccess(this IPublicAccessService publicAccessService, string path, MembershipUser member, RoleProvider roleProvider) @@ -77,8 +77,15 @@ namespace Umbraco.Core.Services var roles = rolesCallback(username); - return entry.Rules.Any(x => x.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType - && roles.Contains(x.RuleValue)); + return HasAccess(entry, username, roles); + } + + private static bool HasAccess(PublicAccessEntry entry, string username, IEnumerable roles) + { + return entry.Rules.Any(x => + (x.RuleType == Constants.Conventions.PublicAccess.MemberUsernameRuleType && username.Equals(x.RuleValue, StringComparison.OrdinalIgnoreCase)) + || (x.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType && roles.Contains(x.RuleValue)) + ); } } } diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index 06c23406ab..1122aaa11a 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -637,7 +637,7 @@ namespace Umbraco.Web.Routing if (loginPageId != request.PublishedContent.Id) request.PublishedContent = request.UmbracoContext.PublishedSnapshot.Content.GetById(loginPageId); } - else if (_services.PublicAccessService.HasAccess(request.PublishedContent.Id, _services.ContentService, GetRolesForLogin(membershipHelper.CurrentUserName)) == false) + else if (_services.PublicAccessService.HasAccess(request.PublishedContent.Id, _services.ContentService, membershipHelper.CurrentUserName, GetRolesForLogin(membershipHelper.CurrentUserName)) == false) { _logger.Debug("EnsurePublishedContentAccess: Current member has not access, redirect to error page"); var errorPageId = publicAccessAttempt.Result.NoAccessNodeId;