@foreach (var item in Model)
{
- bool attrForceLeft = item.ForceLeft;
- bool attrForceRight = item.ForceRight;
+
@{
var partialViewName = "blockgrid/Components/" + item.Content.ContentType.Alias;
From 5ce7cd39b91ab7aa4066d5ff1b72164f5a65808f Mon Sep 17 00:00:00 2001
From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
Date: Thu, 17 Nov 2022 09:43:57 +0100
Subject: [PATCH 04/24] V10: AllowedUploadFiles appsetting not working (#13408)
* Add new Settings
* Use new settings instead of old ones
* Implement AllowedUploadedFiles value to be copied to AllowedUplayedFileExtensions
* Obsolete old settings
* Rename DisallowedUploadFileExtensions
* Implement same fix for DisallowedUploadFiles
* Use new settings for backoffice server variables
* Update the correct setting
Co-authored-by: Zeegaan
---
.../ContentSettingsExtensions.cs | 6 ++---
.../Configuration/Models/ContentSettings.cs | 13 ++++++++++
.../UmbracoBuilder.Configuration.cs | 26 +++++++++++++++++++
.../Controllers/BackOfficeServerVariables.cs | 4 +--
4 files changed, 44 insertions(+), 5 deletions(-)
diff --git a/src/Umbraco.Core/Configuration/ContentSettingsExtensions.cs b/src/Umbraco.Core/Configuration/ContentSettingsExtensions.cs
index 315cee4627..23a67f3267 100644
--- a/src/Umbraco.Core/Configuration/ContentSettingsExtensions.cs
+++ b/src/Umbraco.Core/Configuration/ContentSettingsExtensions.cs
@@ -10,9 +10,9 @@ public static class ContentSettingsExtensions
/// Allow upload if extension is whitelisted OR if there is no whitelist and extension is NOT blacklisted.
///
public static bool IsFileAllowedForUpload(this ContentSettings contentSettings, string extension) =>
- contentSettings.AllowedUploadFiles.Any(x => x.InvariantEquals(extension)) ||
- (contentSettings.AllowedUploadFiles.Any() == false &&
- contentSettings.DisallowedUploadFiles.Any(x => x.InvariantEquals(extension)) == false);
+ contentSettings.AllowedUploadedFileExtensions.Any(x => x.InvariantEquals(extension)) ||
+ (contentSettings.AllowedUploadedFileExtensions.Any() == false &&
+ contentSettings.DisallowedUploadedFileExtensions.Any(x => x.InvariantEquals(extension)) == false);
///
/// Gets the auto-fill configuration for a specified property alias.
diff --git a/src/Umbraco.Core/Configuration/Models/ContentSettings.cs b/src/Umbraco.Core/Configuration/Models/ContentSettings.cs
index f4f3040b79..4014930a5c 100644
--- a/src/Umbraco.Core/Configuration/Models/ContentSettings.cs
+++ b/src/Umbraco.Core/Configuration/Models/ContentSettings.cs
@@ -196,11 +196,13 @@ public class ContentSettings
/// Gets or sets a value for the collection of file extensions that are disallowed for upload.
///
[DefaultValue(StaticDisallowedUploadFiles)]
+ [Obsolete("Please use DisAllowedUploadedFileExtensions instead, scheduled for removal in V13")]
public IEnumerable DisallowedUploadFiles { get; set; } = StaticDisallowedUploadFiles.Split(',');
///
/// Gets or sets a value for the collection of file extensions that are allowed for upload.
///
+ [Obsolete("Please use AllowedUploadedFileExtensions instead, scheduled for removal in V13")]
public IEnumerable AllowedUploadFiles { get; set; } = Array.Empty();
///
@@ -249,4 +251,15 @@ public class ContentSettings
///
[DefaultValue(StaticAllowEditInvariantFromNonDefault)]
public bool AllowEditInvariantFromNonDefault { get; set; } = StaticAllowEditInvariantFromNonDefault;
+
+ ///
+ /// Gets or sets a value for the collection of file extensions that are allowed for upload.
+ ///
+ public string[] AllowedUploadedFileExtensions { get; set; } = Array.Empty();
+
+ ///
+ /// Gets or sets a value for the collection of file extensions that are disallowed for upload.
+ ///
+ [DefaultValue(StaticDisallowedUploadFiles)]
+ public string[] DisallowedUploadedFileExtensions { get; set; } = StaticDisallowedUploadFiles.Split(',');
}
diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs
index 3be3815afa..28fc05907e 100644
--- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs
+++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs
@@ -117,6 +117,32 @@ public static partial class UmbracoBuilderExtensions
}
});
+ // TODO: Remove this in V13
+ // This is to avoid a breaking change in ContentSettings, if the old AllowedFileUploads has a value, and the new
+ // AllowedFileUploadExtensions does not, copy the value over, if the new has a value, use that instead.
+ builder.Services.Configure(settings =>
+ {
+ // We have to use Config.GetSection().Get, as the GetSection.GetValue simply cannot retrieve a string array
+ var allowedUploadedFileExtensionsValue = builder.Config.GetSection($"{Constants.Configuration.ConfigContent}:{nameof(ContentSettings.AllowedUploadedFileExtensions)}").Get();
+ var allowedUploadFilesValue = builder.Config.GetSection($"{Constants.Configuration.ConfigContent}:{nameof(ContentSettings.AllowedUploadFiles)}").Get();
+
+ if (allowedUploadedFileExtensionsValue is null && allowedUploadFilesValue is not null)
+ {
+ settings.AllowedUploadedFileExtensions = allowedUploadFilesValue;
+ }
+ });
+
+ // TODO: Remove this in V13
+ builder.Services.Configure(settings =>
+ {
+ var disallowedUploadedFileExtensionsValue = builder.Config.GetSection($"{Constants.Configuration.ConfigContent}:{nameof(ContentSettings.DisallowedUploadedFileExtensions)}").Get();
+ var disallowedUploadFilesValue = builder.Config.GetSection($"{Constants.Configuration.ConfigContent}:{nameof(ContentSettings.DisallowedUploadFiles)}").Get();
+
+ if (disallowedUploadedFileExtensionsValue is null && disallowedUploadFilesValue is not null)
+ {
+ settings.DisallowedUploadedFileExtensions = disallowedUploadFilesValue;
+ }
+ });
return builder;
}
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs
index c8a3c710ec..768fe382f9 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs
@@ -543,11 +543,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
},
{
"disallowedUploadFiles",
- string.Join(",", _contentSettings.DisallowedUploadFiles)
+ string.Join(",", _contentSettings.DisallowedUploadedFileExtensions)
},
{
"allowedUploadFiles",
- string.Join(",", _contentSettings.AllowedUploadFiles)
+ string.Join(",", _contentSettings.AllowedUploadedFileExtensions)
},
{
"maxFileSize",
From f18209f61670c7684ff76c539c609aca1452cf19 Mon Sep 17 00:00:00 2001
From: Mole
Date: Thu, 17 Nov 2022 13:54:14 +0100
Subject: [PATCH 05/24] V11: Add compatibility suppressions (#13425)
* Add suprressions
* Revert "Add suprressions"
This reverts commit 1c924ee967375870b6d86be9e9480ced0e22f8c7.
* Update CompatibilitySuppressions.xml
Co-authored-by: Ronald Barendse
---
.../CompatibilitySuppressions.xml | 28 +++++++++++++++++++
.../CompatibilitySuppressions.xml | 28 +++++++++++++++++++
2 files changed, 56 insertions(+)
diff --git a/src/Umbraco.Core/CompatibilitySuppressions.xml b/src/Umbraco.Core/CompatibilitySuppressions.xml
index e34b117b9a..f580fa9458 100644
--- a/src/Umbraco.Core/CompatibilitySuppressions.xml
+++ b/src/Umbraco.Core/CompatibilitySuppressions.xml
@@ -1,5 +1,33 @@
+
+ CP0002
+ M:Umbraco.Cms.Core.Models.Blocks.BlockGridItem.get_ForceLeft
+ lib/net7.0/Umbraco.Core.dll
+ lib/net7.0/Umbraco.Core.dll
+ true
+
+
+ CP0002
+ M:Umbraco.Cms.Core.Models.Blocks.BlockGridItem.get_ForceRight
+ lib/net7.0/Umbraco.Core.dll
+ lib/net7.0/Umbraco.Core.dll
+ true
+
+
+ CP0002
+ M:Umbraco.Cms.Core.Models.Blocks.BlockGridItem.set_ForceLeft(System.Boolean)
+ lib/net7.0/Umbraco.Core.dll
+ lib/net7.0/Umbraco.Core.dll
+ true
+
+
+ CP0002
+ M:Umbraco.Cms.Core.Models.Blocks.BlockGridItem.set_ForceRight(System.Boolean)
+ lib/net7.0/Umbraco.Core.dll
+ lib/net7.0/Umbraco.Core.dll
+ true
+
CP0006
M:Umbraco.Cms.Core.Deploy.IDataTypeConfigurationConnector.FromArtifact(Umbraco.Cms.Core.Models.IDataType,System.String,Umbraco.Cms.Core.Deploy.IContextCache)
diff --git a/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml b/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml
index 2095a0c798..f48f361244 100644
--- a/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml
+++ b/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml
@@ -1,5 +1,33 @@
+
+ CP0002
+ M:Umbraco.Cms.Core.Models.Blocks.BlockGridLayoutItem.get_ForceLeft
+ lib/net7.0/Umbraco.Infrastructure.dll
+ lib/net7.0/Umbraco.Infrastructure.dll
+ true
+
+
+ CP0002
+ M:Umbraco.Cms.Core.Models.Blocks.BlockGridLayoutItem.get_ForceRight
+ lib/net7.0/Umbraco.Infrastructure.dll
+ lib/net7.0/Umbraco.Infrastructure.dll
+ true
+
+
+ CP0002
+ M:Umbraco.Cms.Core.Models.Blocks.BlockGridLayoutItem.set_ForceLeft(System.Boolean)
+ lib/net7.0/Umbraco.Infrastructure.dll
+ lib/net7.0/Umbraco.Infrastructure.dll
+ true
+
+
+ CP0002
+ M:Umbraco.Cms.Core.Models.Blocks.BlockGridLayoutItem.set_ForceRight(System.Boolean)
+ lib/net7.0/Umbraco.Infrastructure.dll
+ lib/net7.0/Umbraco.Infrastructure.dll
+ true
+
CP0006
M:Umbraco.Cms.Core.Deploy.IGridCellValueConnector.GetValue(Umbraco.Cms.Core.Models.GridValue.GridControl,System.Collections.Generic.ICollection{Umbraco.Cms.Core.Deploy.ArtifactDependency},Umbraco.Cms.Core.Deploy.IContextCache)
From 5e40b3f0d6ad8ac6b51f2e9024dcf8a7bbe3b61a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 25 Oct 2022 14:43:54 +0000
Subject: [PATCH 06/24] Bump async from 2.6.3 to 2.6.4 in
/src/Umbraco.Web.UI.Client
Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4)
---
updated-dependencies:
- dependency-name: async
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
---
src/Umbraco.Web.UI.Client/package-lock.json | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json
index 23ed392f82..eae26f620b 100644
--- a/src/Umbraco.Web.UI.Client/package-lock.json
+++ b/src/Umbraco.Web.UI.Client/package-lock.json
@@ -3333,9 +3333,9 @@
}
},
"node_modules/async": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
- "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+ "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"dev": true,
"dependencies": {
"lodash": "^4.17.14"
@@ -19845,9 +19845,9 @@
"dev": true
},
"async": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
- "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+ "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"dev": true,
"requires": {
"lodash": "^4.17.14"
From 44d1c2d7226ef78434c349d5bcfc8214b253ab15 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 19 Oct 2022 13:38:14 +0000
Subject: [PATCH 07/24] Bump eventsource from 1.1.0 to 1.1.2 in
/src/Umbraco.Web.UI.Client
Bumps [eventsource](https://github.com/EventSource/eventsource) from 1.1.0 to 1.1.2.
- [Release notes](https://github.com/EventSource/eventsource/releases)
- [Changelog](https://github.com/EventSource/eventsource/blob/master/HISTORY.md)
- [Commits](https://github.com/EventSource/eventsource/compare/v1.1.0...v1.1.2)
---
updated-dependencies:
- dependency-name: eventsource
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
---
src/Umbraco.Web.UI.Client/package-lock.json | 68 +++------------------
1 file changed, 10 insertions(+), 58 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json
index eae26f620b..b0ce2b8315 100644
--- a/src/Umbraco.Web.UI.Client/package-lock.json
+++ b/src/Umbraco.Web.UI.Client/package-lock.json
@@ -6660,12 +6660,9 @@
"dev": true
},
"node_modules/eventsource": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
- "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
- "dependencies": {
- "original": "^1.0.0"
- },
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.2.tgz",
+ "integrity": "sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA==",
"engines": {
"node": ">=0.12.0"
}
@@ -12746,14 +12743,6 @@
"readable-stream": "^2.0.1"
}
},
- "node_modules/original": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
- "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
- "dependencies": {
- "url-parse": "^1.4.3"
- }
- },
"node_modules/os-filter-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz",
@@ -14015,11 +14004,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/querystringify": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
- "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
- },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -14454,7 +14438,8 @@
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
- "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
+ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+ "dev": true
},
"node_modules/resolve": {
"version": "1.20.0",
@@ -16619,15 +16604,6 @@
"deprecated": "Please see https://github.com/lydell/urix#deprecated",
"dev": true
},
- "node_modules/url-parse": {
- "version": "1.5.10",
- "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
- "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
- "dependencies": {
- "querystringify": "^2.1.1",
- "requires-port": "^1.0.0"
- }
- },
"node_modules/url-parse-lax": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
@@ -22556,12 +22532,9 @@
"dev": true
},
"eventsource": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
- "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
- "requires": {
- "original": "^1.0.0"
- }
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.2.tgz",
+ "integrity": "sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA=="
},
"exec-buffer": {
"version": "3.2.0",
@@ -27292,14 +27265,6 @@
"readable-stream": "^2.0.1"
}
},
- "original": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
- "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
- "requires": {
- "url-parse": "^1.4.3"
- }
- },
"os-filter-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz",
@@ -28176,11 +28141,6 @@
"strict-uri-encode": "^1.0.0"
}
},
- "querystringify": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
- "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
- },
"queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -28519,7 +28479,8 @@
"requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
- "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
+ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+ "dev": true
},
"resolve": {
"version": "1.20.0",
@@ -30251,15 +30212,6 @@
"integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
"dev": true
},
- "url-parse": {
- "version": "1.5.10",
- "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
- "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
- "requires": {
- "querystringify": "^2.1.1",
- "requires-port": "^1.0.0"
- }
- },
"url-parse-lax": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
From f7a314cd6bbb84481138276c0661fa4a535b8b56 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 25 Oct 2022 14:43:36 +0000
Subject: [PATCH 08/24] Bump minimist from 1.2.5 to 1.2.7 in
/src/Umbraco.Web.UI.Client
Bumps [minimist](https://github.com/minimistjs/minimist) from 1.2.5 to 1.2.7.
- [Release notes](https://github.com/minimistjs/minimist/releases)
- [Changelog](https://github.com/minimistjs/minimist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/minimistjs/minimist/compare/v1.2.5...v1.2.7)
---
updated-dependencies:
- dependency-name: minimist
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
---
src/Umbraco.Web.UI.Client/package-lock.json | 40 ++++++---------------
1 file changed, 10 insertions(+), 30 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json
index b0ce2b8315..416e47efca 100644
--- a/src/Umbraco.Web.UI.Client/package-lock.json
+++ b/src/Umbraco.Web.UI.Client/package-lock.json
@@ -11864,10 +11864,13 @@
}
},
"node_modules/minimist": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
- "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
- "dev": true
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
+ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
"node_modules/minimize": {
"version": "2.2.0",
@@ -12382,7 +12385,6 @@
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
"dev": true,
- "optional": true,
"dependencies": {
"boolbase": "^1.0.0"
},
@@ -13764,18 +13766,6 @@
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"dev": true
},
- "node_modules/postcss-svgo/node_modules/nth-check": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
- "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
- "dev": true,
- "dependencies": {
- "boolbase": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/fb55/nth-check?sponsor=1"
- }
- },
"node_modules/postcss-svgo/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -26579,9 +26569,9 @@
}
},
"minimist": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
- "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
+ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
"dev": true
},
"minimize": {
@@ -26992,7 +26982,6 @@
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
"dev": true,
- "optional": true,
"requires": {
"boolbase": "^1.0.0"
}
@@ -27955,15 +27944,6 @@
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"dev": true
},
- "nth-check": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
- "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
- "dev": true,
- "requires": {
- "boolbase": "^1.0.0"
- }
- },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
From 695b5bd57ab14ff5f0a72b0da93b40c528f59b6d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 17 Nov 2022 14:07:42 +0000
Subject: [PATCH 09/24] Bump minimatch from 3.0.4 to 3.1.2 in
/src/Umbraco.Web.UI.Client
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2.
- [Release notes](https://github.com/isaacs/minimatch/releases)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2)
---
updated-dependencies:
- dependency-name: minimatch
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
---
src/Umbraco.Web.UI.Client/package-lock.json | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json
index 416e47efca..96e6ddfba8 100644
--- a/src/Umbraco.Web.UI.Client/package-lock.json
+++ b/src/Umbraco.Web.UI.Client/package-lock.json
@@ -11852,9 +11852,9 @@
}
},
"node_modules/minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
@@ -26560,9 +26560,9 @@
"optional": true
},
"minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
From 65479706a9e3ee57283af01ed95eb3ed1d4f8697 Mon Sep 17 00:00:00 2001
From: Anders Bjerner
Date: Tue, 8 Nov 2022 09:22:23 +0100
Subject: [PATCH 10/24] NullOrWhiteSpaceAsNull should be nullable
---
src/Umbraco.Core/Extensions/StringExtensions.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Umbraco.Core/Extensions/StringExtensions.cs b/src/Umbraco.Core/Extensions/StringExtensions.cs
index d30ef9ccba..eb1a125c5f 100644
--- a/src/Umbraco.Core/Extensions/StringExtensions.cs
+++ b/src/Umbraco.Core/Extensions/StringExtensions.cs
@@ -1239,7 +1239,7 @@ public static class StringExtensions
///
/// Turns an null-or-whitespace string into a null string.
///
- public static string? NullOrWhiteSpaceAsNull(this string text)
+ public static string? NullOrWhiteSpaceAsNull(this string? text)
=> string.IsNullOrWhiteSpace(text) ? null : text;
///
From 1196a4d07b07e2e5fb58e76f73276b39e9a27b6b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20Lyngs=C3=B8?=
Date: Mon, 21 Nov 2022 10:41:57 +0100
Subject: [PATCH 11/24] Block Grid Editor sorting directive (#13391)
* make sure area border is on top of block views.
* rename class to avoid confusion
* change witch UI goes on top on hover
* Description informing all blocks are allowed when none is configured.
* add 'When empty'
* Sort mode
* ability to switch out property actions
* enter and exit sortmode from property actions
* gridsortblock
* rename block class to use sortblock
* Sort mode styling
* remove unused css selector
* fixing style for inline-creat button to appear above and not when hovering contextbar
* work on block grid inline editor
* use uui-button + enable installing demo blocks when its not the first dataType of this kind.
* improvements to inline editing POC
* update title of area config overlay editor
* reset columnSpan if no column span options is defined.
* Inline editing
* remove html comment
* remove code for transfer of stylesheets
* ability to hide label from directive
* inline editing using slots to render the umb-property in light dom
* remove property editor proxies when moving a block to a new area/block/context
* minor adjustments to custom views
* use individual slots for each area.
* Inline editing
* a little smaller rte min-height
* fire Custom focus/blur event for Block Grid Editor to catch for focus imitation
* disable inline editing prevalue field when custom view is set
* Fix scroll parent block into view
* initial work on sorter directive
* remove mediaBlock controller
* initial notes and structure
* further concept work
* remove consol log
* CSS for getting bigger areas
* removal of the forceLeft/forceRight code
* proven concept
* fix grid space detection. vertical/horizontal
* clean up and notes
* move into inner containers as well
* use last available index pr default
* boundary selector, for improved choise of dropping into an area
* hide last inline create button when dragging around
* remove console.log
* removal of forced placement in css
* default config and clean up
* notes
* bring back removed code
* show area ui when in dragging mode
* more specific selector
* drop allowance + clean up
* notes and clean up
* auto scroll
* turn --umb-block-grid--dragging-mode into conditional CSS Custom Property
* auto scroll
* refactoring
* fix condition mistake
* scope.config.resolveVerticalDirection
* wrap up simple setDragImage solution
* bring back vm.notifyVisualUpdate and clean up
* make draggableSelector optional, fallback to element
* implement umb-block-grid-sorter for Area PreValue editor
* remove sortableJS dependency
* remove sortableJs from dependencies
* wups, bring back the comma
* removed sortablejs from package-lock
* finished implementation of sorter for PreValue Block Areas
* fix for FireFox shadowDom issue, contains temprorary code.
* stop auto scroll
* make full thing dragable
* fix firefox issue (applying translateZ)
* comment
* make block fit in context columns
* revert element to where it came from if sync could not succeed + clean up
* ensure block does not push the amount of columns, this occourse when dragging item around.
* take horizontalPlaceAfter into account
* implement horizontalPlaceAfter in Areas Prevalue editor
* clean up dependencies
* Shift related el to first in row or last in row when there is no horizontal room
* clean up and correct calculation
* remove unused attribute
* revert to using el.getBoundingClientRect(), as the config.draggableSelector is not available for the placeholder item.
* bind model via dedicated binding to ensure it stay connected with the source model
* Update src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-editor.html
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
* fix eslint issues
* ensure missingColumnWidth is above 0
* Do not allow dragging something thats not found in the model.
* remove as this is not an error.
* update to Flexbox solution
* as the complex model does not change we can use single way binding
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
---
.../gulp/tasks/dependencies.js | 5 -
src/Umbraco.Web.UI.Client/package-lock.json | 11 -
src/Umbraco.Web.UI.Client/package.json | 1 -
.../gridinlineblock.editor.html | 8 +-
.../blockgrid/blockgridui.less | 34 +-
...ckconfiguration.area.overlay.controller.js | 2 +-
...blockgrid.blockconfiguration.controller.js | 1 -
...d.blockconfiguration.overlay.controller.js | 2 +-
.../prevalue/umb-block-grid-area-editor.html | 27 +-
.../prevalue/umb-block-grid-area-editor.less | 3 +
...bBlockGridAreaAllowanceEditor.component.js | 2 +-
.../umbBlockGridAreaEditor.component.js | 139 +++-
.../umbBlockGridColumnEditor.component.js | 10 +-
.../blockgrid/umb-block-grid-entries.html | 5 +-
.../umbBlockGridPropertyEditor.component.js | 32 +-
.../umbblockgridentries.component.js | 569 ++++----------
.../blockgrid/umbblockgridentry.component.js | 2 +-
.../umbblockgridsortable.directive.js | 706 ++++++++++++++++++
.../umbraco-blockgridlayout-flexbox.css | 9 +-
.../blockgrid/umbraco-blockgridlayout.css | 4 +-
.../test/config/karma.conf.js | 1 -
21 files changed, 1040 insertions(+), 533 deletions(-)
create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridsortable.directive.js
diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js b/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js
index c1eab78b21..2f711245e3 100644
--- a/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js
+++ b/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js
@@ -291,11 +291,6 @@ function dependencies() {
"./node_modules/@umbraco-ui/uui-css/dist/uui-text.css"
],
"base": "./node_modules/@umbraco-ui"
- },
- {
- "name": "sortablejs",
- "src": ["./node_modules/sortablejs/Sortable.min.js"],
- "base": "./node_modules/sortablejs"
}
];
diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json
index 96e6ddfba8..dad86ecd05 100644
--- a/src/Umbraco.Web.UI.Client/package-lock.json
+++ b/src/Umbraco.Web.UI.Client/package-lock.json
@@ -38,7 +38,6 @@
"moment": "2.29.4",
"ng-file-upload": "12.2.13",
"nouislider": "15.6.1",
- "sortablejs": "1.15.0",
"spectrum-colorpicker2": "2.0.9",
"tinymce": "6.2.0",
"typeahead.js": "0.11.1",
@@ -15166,11 +15165,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/sortablejs": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz",
- "integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w=="
- },
"node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -29051,11 +29045,6 @@
"sort-keys": "^1.0.0"
}
},
- "sortablejs": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz",
- "integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w=="
- },
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json
index 3f13149e8f..e632cf1e5d 100644
--- a/src/Umbraco.Web.UI.Client/package.json
+++ b/src/Umbraco.Web.UI.Client/package.json
@@ -49,7 +49,6 @@
"moment": "2.29.4",
"ng-file-upload": "12.2.13",
"nouislider": "15.6.1",
- "sortablejs": "1.15.0",
"spectrum-colorpicker2": "2.0.9",
"tinymce": "6.2.0",
"typeahead.js": "0.11.1",
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.html
index 3a67a1be88..71c4da4178 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.html
@@ -71,7 +71,11 @@
min-height: 48px;
height: auto;
}
-
+
+ .blockelement-gridinlineblock-editor > slot {
+ --umb-block-grid--inline-editor--pointer-events--condition: var(--umb-block-grid--dragging-mode) none;
+ pointer-events: var(--umb-block-grid--inline-editor--pointer-events--condition, auto);
+ }
@@ -100,6 +104,6 @@
-
+
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less
index b6205d73ac..c037631d46 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less
@@ -188,7 +188,8 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti
&.--active {
/** Avoid displaying hover when dragging-mode */
- --umb-block-grid--block-ui-opacity: calc(1 - var(--umb-block-grid--dragging-mode, 0));
+ --umb-block-grid--block-ui-opacity--condition: var(--umb-block-grid--dragging-mode) 0;
+ --umb-block-grid--block-ui-opacity: var(--umb-block-grid--block-ui-opacity--code, 1);
> .umb-block-grid__block--context {
opacity: var(--umb-block-grid--block-ui-opacity);
@@ -412,7 +413,9 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl
z-index: 1; /** overwritten for the first one of an area. */
/** Avoid showing inline-create in dragging-mode */
- opacity: calc(1 - var(--umb-block-grid--dragging-mode, 0));
+
+ --umb-block-grid__block--inline-create-button-display--condition: var(--umb-block-grid--dragging-mode) none;
+ display: var(--umb-block-grid__block--inline-create-button-display--condition);
}
.umb-block-grid__block--inline-create-button.--above {
left: 0;
@@ -420,7 +423,7 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl
top: calc(var(--umb-block-grid--row-gap, 0px) * -0.5);
}
-.umb-block-grid__layout-item:first-of-type .umb-block-grid__block--inline-create-button.--above {
+.umb-block-grid__layout-item:first-of-type > .umb-block-grid__block--inline-create-button.--above {
/* Do not use row-gap if the first one. */
top: 0;
}
@@ -454,6 +457,10 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl
/* Move inline create button slightly up, to avoid collision with others*/
margin-bottom: -7px;
margin-top: -13px;
+
+ /** Avoid showing last-inline-create in dragging-mode */
+ --umb-block-grid__block--last-inline-create-button-display--condition: var(--umb-block-grid--dragging-mode) none;
+ display: var(--umb-block-grid__block--last-inline-create-button-display--condition);
}
@@ -553,6 +560,7 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl
.umb-block-grid__area-actions {
grid-column: span var(--umb-block-grid--area-column-span);
+ flex-grow: 1;
opacity: calc(var(--umb-block-grid--hint-area-ui, 0) * .5 + var(--umb-block-grid--show-area-ui, 0));
transition: opacity 120ms;
@@ -590,10 +598,12 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl
}
}
+
.umb-block-grid__layout-item-placeholder {
background: transparent;
border-radius: 3px;
+ box-sizing: border-box;
border: solid 1px;
border-color: rgba(@blueDark, .5);
border-radius: 3px;
@@ -601,9 +611,11 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl
height: 100%;
}
+
.umb-block-grid__layout-item-placeholder > * {
display: none;
}
+
.umb-block-grid__layout-item-placeholder::before {
content: '';
position:absolute;
@@ -611,6 +623,7 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl
inset: 0;
border-radius: 3px;
background-color: white;
+ pointer-events:none;
}
.umb-block-grid__layout-item-placeholder::after {
content: '';
@@ -618,6 +631,7 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl
z-index:1;
inset: 0;
border-radius: 3px;
+ pointer-events:none;
transition: background-color 240ms ease-in;
animation: umb-block-grid__placeholder__pulse 400ms ease-in-out alternate infinite;
@@ -627,10 +641,14 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl
}
}
-
.umb-block-grid__area {
position: relative;
- --umb-block-grid--show-area-ui: 0;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+
+ --umb-block-grid__area--show-area-ui--condition: var(--umb-block-grid--dragging-mode) 1;
+ --umb-block-grid--show-area-ui: var(--umb-block-grid__area--show-area-ui--condition, 0);
}
.umb-block-grid__area:focus,
.umb-block-grid__area:focus-within,
@@ -654,10 +672,9 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl
/* Moved slightly in to align with the inline-create button, which is moved slightly in to avoid collision with other create buttons. */
top:2px;
bottom: 2px;
- /** Avoid displaying highlight when in dragging-mode */
- opacity: calc(1 - var(--umb-block-grid--dragging-mode, 0));
border-color: @blueDark;
box-shadow: 0 0 0 1px rgba(255, 255, 255, .7), inset 0 0 0 1px rgba(255, 255, 255, .7);
+
}
.umb-block-grid__area:has( .umb-block-grid__layout-item-placeholder )::after {
opacity: 1;
@@ -674,6 +691,9 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl
100% { border-color: rgba(@blueDark, 0.66); }
}
}
+.umb-block-grid__area > ng-form {
+ display: contents;
+}
.umb-block-grid__scalebox-backdrop {
position: absolute;
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.controller.js
index f20f14f227..1c2f7e1c2b 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.controller.js
@@ -25,7 +25,7 @@
value: {min:vm.area.minAllowed, max:vm.area.maxAllowed}
}
- unsubscribe.push($scope.$watch('vm.area.alias', (newVal, oldVal) => {
+ unsubscribe.push($scope.$watch('vm.area.alias', (newVal) => {
$scope.model.updateTitle();
if($scope.blockGridBlockConfigurationAreaForm.alias) {
$scope.blockGridBlockConfigurationAreaForm.alias.$setValidity("alias", $scope.model.otherAreaAliases.indexOf(newVal) === -1);
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.controller.js
index 84a64a7669..3c005663e8 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.controller.js
@@ -241,7 +241,6 @@
infiniteMode: true,
noTemplate: true,
isElement: true,
- noTemplate: true,
submit: function (model) {
loadElementTypes().then( function () {
callback(model.documentTypeKey);
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.overlay.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.overlay.controller.js
index 0bb9f6e703..11a0972309 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.overlay.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.overlay.controller.js
@@ -93,7 +93,7 @@
var elementTypeId = elementType.id;
const editor = {
id: elementTypeId,
- submit: function (model) {
+ submit: function () {
editorService.close();
},
close: function () {
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-editor.html
index 64a7909b7a..7ee2ee6344 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-editor.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-editor.html
@@ -2,33 +2,40 @@
-
+
-
-
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-editor.less
index bf66c34661..4f5d45c67c 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-editor.less
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-editor.less
@@ -217,6 +217,9 @@ Grid part:
display: flex;
align-items: center;
justify-content: center;
+
+ height:50px;
+ width:100%;
color: @ui-action-discreet-type;
font-weight: bold;
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaAllowanceEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaAllowanceEditor.component.js
index 693cdcc223..9729ddd85b 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaAllowanceEditor.component.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaAllowanceEditor.component.js
@@ -27,7 +27,7 @@
}
});
- function BlockGridAreaAllowanceController($scope, $element, assetsService, localizationService, editorService) {
+ function BlockGridAreaAllowanceController($scope) {
var unsubscribe = [];
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaEditor.component.js
index 9126a6cc54..3b9b6b4bea 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaEditor.component.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaEditor.component.js
@@ -1,6 +1,44 @@
(function () {
"use strict";
+
+ // Utils:
+
+ function getInterpolatedIndexOfPositionInWeightMap(target, weights) {
+ const map = [0];
+ weights.reduce((a, b, i) => { return map[i+1] = a+b; }, 0);
+ const foundValue = map.reduce((a, b) => {
+ let aDiff = Math.abs(a - target);
+ let bDiff = Math.abs(b - target);
+
+ if (aDiff === bDiff) {
+ return a < b ? a : b;
+ } else {
+ return bDiff < aDiff ? b : a;
+ }
+ })
+ const foundIndex = map.indexOf(foundValue);
+ const targetDiff = (target-foundValue);
+ let interpolatedIndex = foundIndex;
+ if (targetDiff < 0 && foundIndex === 0) {
+ // Don't adjust.
+ } else if (targetDiff > 0 && foundIndex === map.length-1) {
+ // Don't adjust.
+ } else {
+ const foundInterpolationWeight = weights[targetDiff >= 0 ? foundIndex : foundIndex-1];
+ interpolatedIndex += foundInterpolationWeight === 0 ? interpolatedIndex : (targetDiff/foundInterpolationWeight)
+ }
+ return interpolatedIndex;
+ }
+
+ function getAccumulatedValueOfIndex(index, weights) {
+ let i = 0, len = Math.min(index, weights.length), calc = 0;
+ while(i
modelEntry.key === el.dataset.areaKey,
+ querySelectModelToElement: (container, modelEntry) => container.querySelector(`[data-area-key='${modelEntry.key}']`),
+ itemHasNestedContainersResolver: () => false,// We never have nested in this case.
+ containerSelector: ".umb-block-grid-area-editor__grid-wrapper",
+ itemSelector: ".umb-block-grid-area-editor__area",
+ placeholderClass: "umb-block-grid-area-editor__area-placeholder",
+ onSync: onSortSync
}
- const gridContainerEl = $element[0].querySelector('.umb-block-grid-area-editor__grid-wrapper');
+ function onSortSync() {
+ $scope.$evalAsync();
+ setDirty();
+ }
- const sortable = Sortable.create(gridContainerEl, {
- sort: true, // sorting inside list
- animation: 150, // ms, animation speed moving items when sorting, `0` — without animation
- easing: "cubic-bezier(1, 0, 0, 1)", // Easing for animation. Defaults to null. See https://easings.net/ for examples.
- cancel: '',
- draggable: ".umb-block-grid-area-editor__area", // Specifies which items inside the element should be draggable
- ghostClass: "umb-block-grid-area-editor__area-placeholder",
- onAdd: function (evt) {
- _sync(evt);
- $scope.$evalAsync();
- },
- onUpdate: function (evt) {
- _sync(evt);
- $scope.$evalAsync();
+ function resolveVerticalDirection(data) {
+
+ /** We need some data about the grid to figure out if there is room to be placed next to the found element */
+ const approvedContainerComputedStyles = getComputedStyle(data.containerElement);
+ const gridColumnGap = Number(approvedContainerComputedStyles.columnGap.split("px")[0]) || 0;
+ const gridColumnNumber = vm.rootLayoutColumns;
+
+ const foundElColumns = parseInt(data.relatedElement.dataset.colSpan, 10);
+ const currentElementColumns = data.item.columnSpan;
+
+ if(currentElementColumns >= gridColumnNumber) {
+ return true;
}
- });
-
- // TODO: setDirty if sort has happend.
+
+ // Get grid template:
+ const approvedContainerGridColumns = approvedContainerComputedStyles.gridTemplateColumns.trim().split("px").map(x => Number(x)).filter(n => n > 0).map((n, i, list) => list.length === i ? n : n + gridColumnGap);
+
+ // ensure all columns are there.
+ // This will also ensure handling non-css-grid mode,
+ // use container width divided by amount of columns( or the item width divided by its amount of columnSpan)
+ let amountOfColumnsInWeightMap = approvedContainerGridColumns.length;
+ const amountOfUnknownColumns = gridColumnNumber-amountOfColumnsInWeightMap;
+ if(amountOfUnknownColumns > 0) {
+ let accumulatedValue = getAccumulatedValueOfIndex(amountOfColumnsInWeightMap, approvedContainerGridColumns) || 0;
+ const layoutWidth = data.containerRect.width;
+ const missingColumnWidth = (layoutWidth-accumulatedValue)/amountOfUnknownColumns;
+ if(missingColumnWidth > 0) {
+ while(amountOfColumnsInWeightMap++ < gridColumnNumber) {
+ approvedContainerGridColumns.push(missingColumnWidth);
+ }
+ }
+ }
+
+ let offsetPlacement = 0;
+ /* If placeholder is in this same line, we want to assume that it will offset the placement of the found element,
+ which provides more potential space for the item to drop at.
+ This is relevant in this calculation where we look at the space to determine if its a vertical or horizontal drop in relation to the found element.
+ */
+ if(data.placeholderIsInThisRow && data.elementRect.left < data.relatedRect.left) {
+ offsetPlacement = -(data.elementRect.width + gridColumnGap);
+ }
+
+ const relatedStartX = Math.max(data.relatedRect.left - data.containerRect.left + offsetPlacement, 0);
+ const relatedStartCol = Math.round(getInterpolatedIndexOfPositionInWeightMap(relatedStartX, approvedContainerGridColumns));
+
+ // If the found related element does not have enough room after which for the current element, then we go vertical mode:
+ return (relatedStartCol + (data.horizontalPlaceAfter ? foundElColumns : 0) + currentElementColumns > gridColumnNumber);
+
+ }
+
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridColumnEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridColumnEditor.component.js
index 864f479009..1dcbc5b2da 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridColumnEditor.component.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridColumnEditor.component.js
@@ -25,9 +25,7 @@
}
});
- function BlockGridColumnController($scope) {
-
- //var unsubscribe = [];
+ function BlockGridColumnController() {
var vm = this;
@@ -58,12 +56,6 @@
}
}
- /*$scope.$on("$destroy", function () {
- for (const subscription of unsubscribe) {
- subscription();
- }
- });*/
-
}
})();
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html
index 4aed670ffd..ef02d28318 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html
@@ -2,9 +2,10 @@
rect.left - modifier && x < rect.right + modifier && y > rect.top - modifier && y < rect.bottom + modifier);
+ function getAccumulatedValueOfIndex(index, weights) {
+ let i = 0, len = Math.min(index, weights.length), calc = 0;
+ while(i area.key === vm.areaKey);
@@ -90,10 +94,6 @@
unsubscribe.push($scope.$watch('vm.entries', onLocalAmountOfBlocksChanged, true));
};
- unsubscribe.push($scope.$watch("layoutColumns", (newVal, oldVal) => {
- vm.layoutColumnsInt = parseInt(vm.layoutColumns, 10);
- }));
-
function onLocalAmountOfBlocksChanged() {
if (vm.entriesForm && vm.areaConfig) {
@@ -153,440 +153,159 @@
}
}
-
- vm.notifyVisualUpdate = function () {
+ // Used by umb block grid entries component, to trigger other blocks to update.
+ vm.notifyVisualUpdate = notifyVisualUpdate;
+ function notifyVisualUpdate() {
$scope.$broadcast("blockGridEditorVisualUpdate", {areaKey: vm.areaKey});
}
- vm.acceptBlock = function(contentTypeKey) {
- return vm.blockEditorApi.internal.isElementTypeKeyAllowedAt(vm.parentBlock, vm.areaKey, contentTypeKey);
+ function removeAllContainedPropertyEditorProxies() {
+ currentContainedPropertyEditorProxies.forEach(slotName => {
+ removePropertyEditorProxies(slotName);
+ });
+ }
+ function removePropertyEditorProxies(slotName) {
+ const event = new CustomEvent("UmbBlockGrid_RemoveProperty", {composed: true, bubbles: true, detail: {'slotName': slotName}});
+ $element[0].dispatchEvent(event);
}
- vm.getLayoutEntryByIndex = function(index) {
- return vm.blockEditorApi.internal.getLayoutEntryByIndex(vm.parentBlock, vm.areaKey, index);
+
+
+
+ function resolveVerticalDirection(data) {
+
+ /** We need some data about the grid to figure out if there is room to be placed next to the found element */
+ const approvedContainerComputedStyles = getComputedStyle(data.containerElement);
+ const gridColumnGap = Number(approvedContainerComputedStyles.columnGap.split("px")[0]) || 0;
+ const gridColumnNumber = parseInt(approvedContainerComputedStyles.getPropertyValue("--umb-block-grid--grid-columns"), 10);
+
+
+ const foundElColumns = parseInt(data.relatedElement.dataset.colSpan, 10);
+ const currentElementColumns = data.item.columnSpan;
+
+ if(currentElementColumns >= gridColumnNumber) {
+ return true;
+ }
+
+ // Get grid template:
+ const approvedContainerGridColumns = approvedContainerComputedStyles.gridTemplateColumns.trim().split("px").map(x => Number(x)).filter(n => n > 0).map((n, i, list) => list.length === i ? n : n + gridColumnGap);
+
+ // ensure all columns are there.
+ // This will also ensure handling non-css-grid mode,
+ // use container width divided by amount of columns( or the item width divided by its amount of columnSpan)
+ let amountOfColumnsInWeightMap = approvedContainerGridColumns.length;
+ const amountOfUnknownColumns = gridColumnNumber-amountOfColumnsInWeightMap;
+ if(amountOfUnknownColumns > 0) {
+ let accumulatedValue = getAccumulatedValueOfIndex(amountOfColumnsInWeightMap, approvedContainerGridColumns) || 0;
+ const layoutWidth = data.containerRect.width;
+ const missingColumnWidth = (layoutWidth-accumulatedValue)/amountOfUnknownColumns;
+ if(missingColumnWidth > 0) {
+ while(amountOfColumnsInWeightMap++ < gridColumnNumber) {
+ approvedContainerGridColumns.push(missingColumnWidth);
+ }
+ }
+ }
+
+ let offsetPlacement = 0;
+ /* If placeholder is in this same line, we want to assume that it will offset the placement of the found element,
+ which provides more potential space for the item to drop at.
+ This is relevant in this calculation where we look at the space to determine if its a vertical or horizontal drop in relation to the found element.
+ */
+ if(data.placeholderIsInThisRow && data.elementRect.left < data.relatedRect.left) {
+ offsetPlacement = -(data.elementRect.width + gridColumnGap);
+ }
+
+ const relatedStartX = Math.max(data.relatedRect.left - data.containerRect.left + offsetPlacement, 0);
+ const relatedStartCol = Math.round(getInterpolatedIndexOfPositionInWeightMap(relatedStartX, approvedContainerGridColumns));
+
+ // If the found related element does not have enough room after which for the current element, then we go vertical mode:
+ return (relatedStartCol + (data.horizontalPlaceAfter ? foundElColumns : 0) + currentElementColumns > gridColumnNumber);
}
- vm.showNotAllowed = function() {
+
+
+
+ function initializeSorter() {
+ vm.sorterOptions = {
+ resolveVerticalDirection: resolveVerticalDirection,
+ dataTransferResolver: (dataTransfer, item) => {dataTransfer.setData("text/plain", item.$block.label)}, // (Optional) Append OS data to the moved item.
+ compareElementToModel: (el, modelEntry) => modelEntry.contentUdi === el.dataset.elementUdi,
+ querySelectModelToElement: (container, modelEntry) => container.querySelector(`[data-element-udi='${modelEntry.contentUdi}']`),
+ itemHasNestedContainersResolver: (foundEl) => foundEl.classList.contains('--has-areas'), // (Optional) improve performance for recognizing if an items has inner containers.
+ identifier: "BlockGridEditor_"+vm.blockEditorApi.internal.uniqueEditorKey,
+ boundarySelector: ".umb-block-grid__area", // (Optional) Used for extended boundary between containers.
+ containerSelector: ".umb-block-grid__layout-container", // Used for connecting with others
+ itemSelector: ".umb-block-grid__layout-item",
+ draggableSelector: ".umb-block-grid__block--view",
+ placeholderClass: "umb-block-grid__layout-item-placeholder",
+ ghostClass: "umb-block-grid__layout-item-ghost",
+ onStart: onSortStart,
+ onEnd: onSortEnd,
+ onSync: onSortSync,
+ onDisallowed: onSortDisallowed,
+ onAllowed: onSortAllowed,
+ onRequestDrop: onSortRequestDrop
+ }
+ }
+
+ function onSortStart(data) {
+ // Gather containedPropertyEditorProxies from this element.
+ currentContainedPropertyEditorProxies = Array.from(data.element.querySelectorAll('slot[data-is-property-editor-proxy]')).map(x => x.getAttribute('name'));
+ vm.blockEditorApi.internal.startDraggingMode();
+ }
+
+ function onSortEnd() {
+ vm.blockEditorApi.internal.exitDraggingMode();
+ currentContainedPropertyEditorProxies = [];
+ notifyVisualUpdate();
+ $scope.$evalAsync();
+ }
+
+ function onSortSync(data) {
+ if (data.fromController !== data.toController) {
+ removeAllContainedPropertyEditorProxies();
+
+ const contextColumns = vm.blockEditorApi.internal.getContextColumns(vm.parentBlock, vm.areaKey);
+
+ // if colSpan is lower than contextColumns, and we do have some columnSpanOptions:
+ if (data.item.columnSpan < contextColumns && data.item.$block.config.columnSpanOptions.length > 0) {
+ // then check if the colSpan is a columnSpanOption, if NOT then reset to contextColumns.
+ const found = data.item.$block.config.columnSpanOptions.find(option => option.columnSpan === data.item.columnSpan);
+ if(!found) {
+ data.item.columnSpan = contextColumns;
+ }
+ } else {
+ data.item.columnSpan = contextColumns;
+ }
+ }
+ $scope.$evalAsync();
+ vm.blockEditorApi.internal.setDirty();
+ }
+
+ function onSortDisallowed() {
vm.showNotAllowedUI = true;
$scope.$evalAsync();
}
- vm.hideNotAllowed = function() {
+ function onSortAllowed() {
vm.showNotAllowedUI = false;
$scope.$evalAsync();
}
-
- var revertIndicateDroppableTimeout;
- vm.revertIndicateDroppable = function() {
- revertIndicateDroppableTimeout = $timeout(() => {
- vm.droppableIndication = false;
- }, 2000);
- }
- vm.indicateDroppable = function() {
- if (revertIndicateDroppableTimeout) {
- $timeout.cancel(revertIndicateDroppableTimeout);
- revertIndicateDroppableTimeout = null;
- }
- vm.droppableIndication = true;
- $scope.$evalAsync();
+ function onSortRequestDrop(data) {
+ return vm.blockEditorApi.internal.isElementTypeKeyAllowedAt(vm.parentBlock, vm.areaKey, data.item.$block.config.contentElementTypeKey);
}
- function initializeSortable() {
- const gridLayoutContainerEl = $element[0].querySelector('.umb-block-grid__layout-container');
- var _lastIndicationContainerVM = null;
- var targetRect = null;
- var relatedEl = null;
- var ghostEl = null;
- var ghostRect = null;
- var dragX = 0;
- var dragY = 0;
- var dragOffsetX = 0;
- var approvedContainerEl = null;
- // Setup DOM method for communication between sortables:
- gridLayoutContainerEl['Sortable:controller'] = () => {
- return vm;
- };
- var nextSibling;
- function _removePropertyProxy(eventTarget, slotName) {
- const event = new CustomEvent("UmbBlockGrid_RemoveProperty", {composed: true, bubbles: true, detail: {'slotName': slotName}});
- eventTarget.dispatchEvent(event);
+
+ $scope.$on('$destroy', function () {
+ for (const subscription of unsubscribe) {
+ subscription();
}
-
- // Borrowed concept from, its not identical as more has been implemented: https://github.com/SortableJS/angular-legacy-sortablejs/blob/master/angular-legacy-sortable.js
- function _sync(evt) {
-
- const oldIndex = evt.oldIndex,
- newIndex = evt.newIndex;
-
- // If not the same gridLayoutContainerEl, then test for transfer option:
- if (gridLayoutContainerEl !== evt.from) {
- const fromCtrl = evt.from['Sortable:controller']();
- const prevEntries = fromCtrl.entries;
- const syncEntry = prevEntries[oldIndex];
-
- // Make sure Property Editor Proxies are destroyed, as we need to establish new when moving context:
-
-
- // unregister all property editor proxies via events:
- fromCtrl.containedPropertyEditorProxies.forEach(slotName => {
- _removePropertyProxy(evt.from, slotName);
- });
-
- // Perform the transfer:
- if (Sortable.active && Sortable.active.lastPullMode === 'clone') {
- syncEntry = Utilities.copy(syncEntry);
- prevEntries.splice(Sortable.utils.index(evt.clone, sortable.options.draggable), 0, prevEntries.splice(oldIndex, 1)[0]);
- }
- else {
- prevEntries.splice(oldIndex, 1);
- }
- vm.entries.splice(newIndex, 0, syncEntry);
-
- const contextColumns = vm.blockEditorApi.internal.getContextColumns(vm.parentBlock, vm.areaKey);
-
- // if colSpan is lower than contextColumns, and we do have some columnSpanOptions:
- if (syncEntry.columnSpan < contextColumns && syncEntry.$block.config.columnSpanOptions.length > 0) {
- // then check if the colSpan is a columnSpanOption, if NOT then reset to contextColumns.
- const found = syncEntry.$block.config.columnSpanOptions.find(option => option.columnSpan === syncEntry.columnSpan);
- if(!found) {
- syncEntry.columnSpan = contextColumns;
- }
- } else {
- syncEntry.columnSpan = contextColumns;
- }
-
- }
- else {
- vm.entries.splice(newIndex, 0, vm.entries.splice(oldIndex, 1)[0]);
- }
- }
-
- function _indication(contextVM, movingEl) {
-
- // Remove old indication:
- if(_lastIndicationContainerVM !== contextVM && _lastIndicationContainerVM !== null) {
- _lastIndicationContainerVM.hideNotAllowed();
- _lastIndicationContainerVM.revertIndicateDroppable();
- }
- _lastIndicationContainerVM = contextVM;
-
- if(contextVM.acceptBlock(movingEl.dataset.contentElementTypeKey) === true) {
- _lastIndicationContainerVM.hideNotAllowed();
- _lastIndicationContainerVM.indicateDroppable();// This block is accepted so we will indicate a good drop.
- return true;
- }
-
- contextVM.showNotAllowed();// This block is not accepted to we will indicate that its not allowed.
-
- return false;
- }
-
- function _moveGhostElement() {
-
- rqaId = null;
- if(!ghostEl) {
- return;
- }
- if(!approvedContainerEl) {
- console.error("Cancel cause had no approvedContainerEl", approvedContainerEl)
- return;
- }
-
- ghostRect = ghostEl.getBoundingClientRect();
-
- const insideGhost = isWithinRect(dragX, dragY, ghostRect);
- if (insideGhost) {
- return;
- }
-
- var approvedContainerRect = approvedContainerEl.getBoundingClientRect();
-
- const approvedContainerHasItems = approvedContainerEl.querySelector('.umb-block-grid__layout-item:not(.umb-block-grid__layout-item-placeholder)');
- if(!approvedContainerHasItems && isWithinRect(dragX, dragY, approvedContainerRect, 20) || approvedContainerHasItems && isWithinRect(dragX, dragY, approvedContainerRect, -10)) {
- // we are good...
- } else {
- var parentContainer = approvedContainerEl.parentNode.closest('.umb-block-grid__layout-container');
- if(parentContainer) {
-
- if(parentContainer['Sortable:controller']().sortGroupIdentifier === vm.sortGroupIdentifier) {
- approvedContainerEl = parentContainer;
- approvedContainerRect = approvedContainerEl.getBoundingClientRect();
- }
- }
- }
-
- // gather elements on the same row.
- let elementInSameRow = [];
- const containerElements = Array.from(approvedContainerEl.children);
- for (const el of containerElements) {
- const elRect = el.getBoundingClientRect();
- // gather elements on the same row.
- if(dragY >= elRect.top && dragY <= elRect.bottom && el !== ghostEl) {
- elementInSameRow.push({el: el, rect:elRect});
- }
- }
-
- let lastDistance = 99999;
- let foundRelatedEl = null;
- let placeAfter = false;
- elementInSameRow.forEach( sameRow => {
- const centerX = (sameRow.rect.left + (sameRow.rect.width*.5));
- let distance = Math.abs(dragX - centerX);
- if(distance < lastDistance) {
- foundRelatedEl = sameRow.el;
- lastDistance = Math.abs(distance);
- placeAfter = dragX > centerX;
- }
- });
-
- if (foundRelatedEl === ghostEl) {
- return;
- }
-
- if (foundRelatedEl) {
-
-
- let newIndex = containerElements.indexOf(foundRelatedEl);
-
- const foundRelatedElRect = foundRelatedEl.getBoundingClientRect();
-
- // Ghost is already on same line and we are not hovering the related element?
- const ghostCenterY = ghostRect.top + (ghostRect.height*.5);
- const isInsideFoundRelated = isWithinRect(dragX, dragY, foundRelatedElRect, 0);
-
-
- if (isInsideFoundRelated && foundRelatedEl.classList.contains('--has-areas')) {
- // If mouse is on top of an area, then make that the new approvedContainer?
- const blockView = foundRelatedEl.querySelector('.umb-block-grid__block--view');
- const subLayouts = blockView.querySelectorAll('.umb-block-grid__layout-container');
- for (const subLayout of subLayouts) {
- const subLayoutRect = subLayout.getBoundingClientRect();
- const hasItems = subLayout.querySelector('.umb-block-grid__layout-item:not(.umb-block-grid__layout-item-placeholder)');
- // gather elements on the same row.
- if(!hasItems && isWithinRect(dragX, dragY, subLayoutRect, 20) || hasItems && isWithinRect(dragX, dragY, subLayoutRect, -10)) {
-
- var subVm = subLayout['Sortable:controller']();
- if(subVm.sortGroupIdentifier === vm.sortGroupIdentifier) {
- approvedContainerEl = subLayout;
- _moveGhostElement();
- return;
- }
- }
- }
- }
-
- if (ghostCenterY > foundRelatedElRect.top && ghostCenterY < foundRelatedElRect.bottom && !isInsideFoundRelated) {
- return;
- }
-
- const containerVM = approvedContainerEl['Sortable:controller']();
- if(_indication(containerVM, ghostEl) === false) {
- return;
- }
-
- let verticalDirection = false;
-
- // TODO: move calculations out so they can be persisted a bit longer?
- //const approvedContainerRect = approvedContainerEl.getBoundingClientRect();
- const approvedContainerComputedStyles = getComputedStyle(approvedContainerEl);
- const gridColumnNumber = parseInt(approvedContainerComputedStyles.getPropertyValue("--umb-block-grid--grid-columns"), 10);
-
- const relatedColumns = parseInt(foundRelatedEl.dataset.colSpan, 10);
- const ghostColumns = parseInt(ghostEl.dataset.colSpan, 10);
-
- // Get grid template:
- const approvedContainerGridColumns = approvedContainerComputedStyles.gridTemplateColumns.trim().split("px").map(x => Number(x)).filter(n => n > 0);
-
- // ensure all columns are there.
- // This will also ensure handling non-css-grid mode,
- // use container width divided by amount of columns( or the item width divided by its amount of columnSpan)
- let amountOfColumnsInWeightMap = approvedContainerGridColumns.length;
- const amountOfUnknownColumns = gridColumnNumber-amountOfColumnsInWeightMap;
- if(amountOfUnknownColumns > 0) {
- let accumulatedValue = getAccumulatedValueOfIndex(amountOfColumnsInWeightMap, approvedContainerGridColumns) || 0;
- const layoutWidth = approvedContainerRect.width;
- const missingColumnWidth = (layoutWidth-accumulatedValue)/amountOfUnknownColumns;
- while(amountOfColumnsInWeightMap++ < gridColumnNumber) {
- approvedContainerGridColumns.push(missingColumnWidth);
- }
- }
-
-
- const relatedStartX = foundRelatedElRect.left - approvedContainerRect.left;
- const relatedStartCol = Math.round(getInterpolatedIndexOfPositionInWeightMap(relatedStartX, approvedContainerGridColumns));
-
- if(relatedStartCol + relatedColumns + ghostColumns > gridColumnNumber) {
- verticalDirection = true;
- }
-
- if (verticalDirection) {
- placeAfter = (dragY > foundRelatedElRect.top + (foundRelatedElRect.height*.5));
- }
-
-
- const nextEl = containerElements[(placeAfter ? newIndex+1 : newIndex)];
- if (nextEl) {
- approvedContainerEl.insertBefore(ghostEl, nextEl);
- } else {
- approvedContainerEl.appendChild(ghostEl);
- }
-
- return;
- }
-
- // If above or below container, we will go first or last.
- const containerVM = approvedContainerEl['Sortable:controller']();
- if(_indication(containerVM, ghostEl) === false) {
- return;
- }
- if(dragY < approvedContainerRect.top) {
- const firstEl = containerElements[0];
- if (firstEl) {
- approvedContainerEl.insertBefore(ghostEl, firstEl);
- } else {
- approvedContainerEl.appendChild(ghostEl);
- }
- } else if(dragY > approvedContainerRect.bottom) {
- approvedContainerEl.appendChild(ghostEl);
- }
- }
-
- var rqaId = null
- function _onDragMove(evt) {
-
- const clientX = (evt.touches ? evt.touches[0] : evt).clientX;
- const clientY = (evt.touches ? evt.touches[1] : evt).clientY;
- if(vm.movingLayoutEntry && targetRect && ghostRect && clientX !== 0 && clientY !== 0) {
-
- if(dragX === clientX && dragY === clientY) {
- return;
- }
- dragX = clientX;
- dragY = clientY;
-
- ghostRect = ghostEl.getBoundingClientRect();
-
- const insideGhost = isWithinRect(dragX, dragY, ghostRect, 0);
-
- if (!insideGhost) {
- if(rqaId === null) {
- rqaId = requestAnimationFrame(_moveGhostElement);
- }
- }
- }
- }
-
- vm.sortGroupIdentifier = "BlockGridEditor_"+vm.blockEditorApi.internal.uniqueEditorKey;
-
- const sortable = Sortable.create(gridLayoutContainerEl, {
- group: vm.sortGroupIdentifier,
- sort: true,
- animation: 0,
- cancel: '',
- draggable: ".umb-block-grid__layout-item",
- ghostClass: "umb-block-grid__layout-item-placeholder",
- swapThreshold: .4,
- dragoverBubble: true,
- emptyInsertThreshold: 40,
-
- scrollSensitivity: 50,
- scrollSpeed: 16,
- scroll: true,
- forceAutoScrollFallback: true,
-
- onStart: function (evt) {
-
- // TODO: This does not work correctly jet with SortableJS. With the replacement we should be able to call this before DOM is changed.
- vm.blockEditorApi.internal.startDraggingMode();
-
- nextSibling = evt.from === evt.item.parentNode ? evt.item.nextSibling : evt.clone.nextSibling;
-
- var contextVM = vm;
- if (gridLayoutContainerEl !== evt.to) {
- contextVM = evt.to['Sortable:controller']();
- }
-
- approvedContainerEl = evt.to;
-
- const oldIndex = evt.oldIndex;
- vm.movingLayoutEntry = contextVM.getLayoutEntryByIndex(oldIndex);
-
- ghostEl = evt.item;
- vm.containedPropertyEditorProxies = Array.from(ghostEl.querySelectorAll('slot[data-is-property-editor-proxy]')).map(x => x.getAttribute('name'));
-
- targetRect = evt.to.getBoundingClientRect();
- ghostRect = ghostEl.getBoundingClientRect();
-
- const clientX = (evt.originalEvent.touches ? evt.originalEvent.touches[0] : evt.originalEvent).clientX;
- dragOffsetX = clientX - ghostRect.left;
-
- window.addEventListener('drag', _onDragMove);
- window.addEventListener('dragover', _onDragMove);
-
- $scope.$evalAsync();
- },
- // Called by any change to the list (add / update / remove)
- onMove: function (evt) {
- relatedEl = evt.related;
- targetRect = evt.to.getBoundingClientRect();
- ghostRect = evt.draggedRect;
-
- // Disable SortableJS from handling the drop, instead we will use our own.
- return false;
- },
- // When an change actually was made, after drop has occurred:
- onSort: function (evt) {
- vm.blockEditorApi.internal.setDirty();
- },
-
- onAdd: function (evt) {
- _sync(evt);
- $scope.$evalAsync();
- },
- onUpdate: function (evt) {
- _sync(evt);
- $scope.$evalAsync();
- },
- onEnd: function(evt) {
- if(rqaId !== null) {
- cancelAnimationFrame(rqaId);
- }
- window.removeEventListener('drag', _onDragMove);
- window.removeEventListener('dragover', _onDragMove);
- vm.blockEditorApi.internal.exitDraggingMode();
-
- // ensure not-allowed indication is removed.
- if(_lastIndicationContainerVM) {
- _lastIndicationContainerVM.hideNotAllowed();
- _lastIndicationContainerVM.revertIndicateDroppable();
- _lastIndicationContainerVM = null;
- }
-
- approvedContainerEl = null;
- vm.movingLayoutEntry = null;
- targetRect = null;
- ghostRect = null;
- ghostEl = null;
- relatedEl = null;
- vm.containedPropertyEditorProxies = [];
-
- vm.notifyVisualUpdate();
- }
- });
-
- $scope.$on('$destroy', function () {
- sortable.destroy();
- for (const subscription of unsubscribe) {
- subscription();
- }
- });
-
- };
+ });
}
})();
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentry.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentry.component.js
index abc1ec5016..28c4aa6847 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentry.component.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentry.component.js
@@ -159,7 +159,7 @@
$scope.$evalAsync();
}
- unsubscribe.push($scope.$watch("depth", (newVal, oldVal) => {
+ unsubscribe.push($scope.$watch("depth", () => {
vm.childDepth = parseInt(vm.depth) + 1;
}));
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridsortable.directive.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridsortable.directive.js
new file mode 100644
index 0000000000..fc973ce490
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridsortable.directive.js
@@ -0,0 +1,706 @@
+(function () {
+ 'use strict';
+
+ function isWithinRect(x, y, rect, modifier) {
+ return (x > rect.left - modifier && x < rect.right + modifier && y > rect.top - modifier && y < rect.bottom + modifier);
+ }
+
+
+ function getParentScrollElement(el, includeSelf) {
+ // skip to window
+ if (!el || !el.getBoundingClientRect) return null;
+ var elem = el;
+ var gotSelf = false;
+
+ while(elem) {
+
+ // we don't need to get elem css if it isn't even overflowing in the first place (performance)
+ if (elem.clientWidth < elem.scrollWidth || elem.clientHeight < elem.scrollHeight) {
+ var elemCSS = getComputedStyle(elem);
+
+ if (
+ elem.clientHeight < elem.scrollHeight && (elemCSS.overflowY == 'auto' || elemCSS.overflowY == 'scroll')
+ ||
+ elem.clientWidth < elem.scrollWidth && (elemCSS.overflowX == 'auto' || elemCSS.overflowX == 'scroll')
+ ) {
+ if (!elem.getBoundingClientRect || elem === document.body) return null;
+ if (gotSelf || includeSelf) return elem;
+ gotSelf = true;
+ }
+ }
+
+ if(elem.parentNode === document) {
+ return null;
+ } else if(elem.parentNode instanceof DocumentFragment) {
+ elem = elem.parentNode.host;
+ } else {
+ elem = elem.parentNode;
+ }
+ }
+
+ return null;
+ }
+
+
+
+
+ const DefaultConfig = {
+ compareElementToModel: (el, modelEntry) => modelEntry.contentUdi === el.dataset.elementUdi,
+ querySelectModelToElement: (container, modelEntry) => container.querySelector(`[data-element-udi='${modelEntry.contentUdi}']`),
+ identifier: "UmbBlockGridSorter",
+ containerSelector: "ol", // To find container and to connect with others.
+ ignorerSelector: "a, img, iframe",
+ itemSelector: "li",
+ placeholderClass: "umb-drag-placeholder"
+ }
+
+
+
+ function UmbBlockGridSorter() {
+
+ function link(scope, element) {
+
+
+ let observer = new MutationObserver(function(mutations) {
+ mutations.forEach(function(mutation) {
+ mutation.addedNodes.forEach(function(addedNode) {
+ if (addedNode.matches && addedNode.matches(scope.config.itemSelector)) {
+ setupItem(addedNode);
+ }
+ });
+ mutation.removedNodes.forEach(function(removedNode) {
+ if (removedNode.matches && removedNode.matches(scope.config.itemSelector)) {
+ destroyItem(removedNode);
+ }
+ });
+ });
+ });
+
+
+ let vm = {};
+
+ const config = {...DefaultConfig, ...scope.config};
+
+ vm.identifier = config.identifier;
+
+ let scrollElement = null;
+
+ let containerEl = config.containerSelector ? element[0].closest(config.containerSelector) : element[0];
+ if (!containerEl) {
+ console.error("Could not initialize umb block grid sorter.", element[0])
+ return;
+ }
+
+
+
+ function init() {
+
+ containerEl['umbBlockGridSorter:vm'] = () => {
+ return vm;
+ };
+ containerEl.addEventListener('dragover', preventDragOver);
+
+ observer.observe(containerEl, {childList: true, subtree: false});
+ }
+ init();
+
+ function preventDragOver(e) {
+ e.preventDefault()
+ }
+
+ function setupItem(element) {
+
+ setupIgnorerElements(element);
+
+ element.draggable = true;
+ element.addEventListener('dragstart', handleDragStart);
+ }
+
+ function destroyItem(element) {
+
+ destroyIgnorerElements(element);
+
+ element.removeEventListener('dragstart', handleDragStart);
+ }
+
+ function setupIgnorerElements(element) {
+ config.ignorerSelector.split(',').forEach(function (criteria) {
+ element.querySelectorAll(criteria.trim()).forEach(setupPreventEvent);
+ });
+ }
+ function destroyIgnorerElements(element) {
+ config.ignorerSelector.split(',').forEach(function (criteria) {
+ element.querySelectorAll(criteria.trim()).forEach(destroyPreventEvent);
+ });
+ }
+ function setupPreventEvent(element) {
+ element.draggable = false
+ }
+ function destroyPreventEvent(element) {
+ element.removeAttribute('draggable');
+ }
+
+
+
+
+
+ let currentContainerElement = containerEl;
+ let currentContainerVM = vm;
+
+ let rqaId = null;
+ let currentItem = null;
+ let currentElement = null;
+ let currentDragElement = null;
+ let currentDragRect = null;
+ let dragX = 0;
+ let dragY = 0;
+
+ function handleDragStart(event) {
+
+ if(currentElement) {
+ handleDragEnd();
+ }
+
+ event.stopPropagation();
+ event.dataTransfer.effectAllowed = "move";// copyMove when we enhance the drag with clipboard data.
+ event.dataTransfer.dropEffect = "none";// visual feedback when dropped.
+
+ if(!scrollElement) {
+ scrollElement = getParentScrollElement(containerEl, true);
+ }
+
+ const element = event.target.closest(config.itemSelector);
+
+ currentElement = element;
+ currentDragElement = config.draggableSelector ? currentElement.querySelector(config.draggableSelector) : currentElement;
+ currentDragRect = currentDragElement.getBoundingClientRect();
+ currentItem = vm.getItemOfElement(currentElement);
+ if(!currentItem) {
+ console.error("Could not find item related to this element.");
+ return;
+ }
+
+ currentElement.style.transform = 'translateZ(0)';// Solves problem with FireFox and ShadowDom in the drag-image.
+
+ if (config.dataTransferResolver) {
+ config.dataTransferResolver(event.dataTransfer, currentItem);
+ }
+
+ if (config.onStart) {
+ config.onStart({item: currentItem, element: currentElement});
+ }
+
+ window.addEventListener('dragover', handleDragMove);
+ window.addEventListener('dragend', handleDragEnd);
+
+ // We must wait one frame before changing the look of the block.
+ rqaId = requestAnimationFrame(() => {// It should be okay to use the same refId, as the move does not or is okay not to happen on first frame/drag-move.
+ rqaId = null;
+ currentElement.style.transform = '';
+ currentElement.classList.add(config.placeholderClass);
+ });
+
+ }
+
+ function handleDragEnd() {
+
+ if(!currentElement) {
+ return;
+ }
+
+ window.removeEventListener('dragover', handleDragMove);
+ window.removeEventListener('dragend', handleDragEnd);
+ currentElement.style.transform = '';
+ currentElement.classList.remove(config.placeholderClass);
+
+ stopAutoScroll();
+ removeAllowIndication();
+
+ if(currentContainerVM.sync(currentElement, vm) === false) {
+ // Sync could not succeed, might be because item is not allowed here.
+
+ // Lets move the Element back to where it came from:
+ const movingItemIndex = scope.model.indexOf(currentItem);
+ if(movingItemIndex < scope.model.length-1) {
+ const afterItem = scope.model[movingItemIndex+1];
+ const afterEl = config.querySelectModelToElement(containerEl, afterItem);
+ containerEl.insertBefore(currentElement, afterEl);
+ } else {
+ containerEl.appendChild(currentElement);
+ }
+
+ currentContainerVM = vm;
+ }
+
+ if (config.onEnd) {
+ config.onEnd({item: currentItem, element: currentElement});
+ }
+
+ if(rqaId) {
+ cancelAnimationFrame(rqaId);
+ }
+
+ currentContainerElement = containerEl;
+ currentContainerVM = vm;
+
+ rqaId = null
+ currentItem = null;
+ currentElement = null;
+ currentDragElement = null;
+ currentDragRect = null;
+ dragX = 0;
+ dragY = 0;
+
+ }
+
+ function handleDragMove(event) {
+
+ if(!currentElement) {
+ return;
+ }
+
+ const clientX = (event.touches ? event.touches[0] : event).clientX;
+ const clientY = (event.touches ? event.touches[1] : event).clientY;
+ if(clientX !== 0 && clientY !== 0) {
+
+
+ if(dragX === clientX && dragY === clientY) {
+ return;
+ }
+ dragX = clientX;
+ dragY = clientY;
+
+ handleAutoScroll(dragX, dragY);
+
+ currentDragRect = currentDragElement.getBoundingClientRect();
+ const insideCurrentRect = isWithinRect(dragX, dragY, currentDragRect, 0);
+ if (!insideCurrentRect) {
+ if(rqaId === null) {
+ rqaId = requestAnimationFrame(moveCurrentElement);
+ }
+ }
+ }
+ }
+
+
+
+ function moveCurrentElement() {
+
+ rqaId = null;
+ if(!currentElement) {
+ return;
+ }
+
+ const currentElementRect = currentElement.getBoundingClientRect();
+ const insideCurrentRect = isWithinRect(dragX, dragY, currentElementRect);
+ if (insideCurrentRect) {
+ return;
+ }
+
+ // If we have a boundarySelector, try it, if we didn't get anything fall back to currentContainerElement.
+ var currentBoundaryElement = (config.boundarySelector ? currentContainerElement.closest(config.boundarySelector) : currentContainerElement) || currentContainerElement;
+
+ var currentBoundaryRect = currentBoundaryElement.getBoundingClientRect();
+
+ const currentContainerHasItems = currentContainerVM.hasOtherItemsThan(currentItem);
+
+ // if empty we will be move likely to accept an item (add 20px to the bounding box)
+ // If we have items we must be 10 within the container to accept the move.
+ const offsetEdge = currentContainerHasItems ? -10 : 20;
+ if(!isWithinRect(dragX, dragY, currentBoundaryRect, offsetEdge)) {
+ // we are outside the current container boundary, so lets see if there is a parent we can move.
+ var parentContainer = currentContainerElement.parentNode.closest(config.containerSelector);
+ if(parentContainer) {
+ const parentContainerVM = parentContainer['umbBlockGridSorter:vm']();
+ if(parentContainerVM.identifier === vm.identifier) {
+ currentContainerElement = parentContainer;
+ currentContainerVM = parentContainerVM;
+ }
+ }
+ }
+
+
+
+
+ // We want to retrieve the children of the container, every time to ensure we got the right order and index
+ const orderedContainerElements = Array.from(currentContainerElement.children);
+
+ var currentContainerRect = currentContainerElement.getBoundingClientRect();
+
+
+ // gather elements on the same row.
+ let elementsInSameRow = [];
+ let placeholderIsInThisRow = false;
+ for (const el of orderedContainerElements) {
+ const elRect = el.getBoundingClientRect();
+ // gather elements on the same row.
+ if(dragY >= elRect.top && dragY <= elRect.bottom) {
+
+ const dragElement = config.draggableSelector ? el.querySelector(config.draggableSelector) : el;
+ const dragElementRect = dragElement.getBoundingClientRect();
+ if(el !== currentElement) {
+ elementsInSameRow.push({el:el, dragRect:dragElementRect});
+ } else {
+ placeholderIsInThisRow = true;
+ }
+ }
+ }
+
+ let lastDistance = 99999;
+ let foundEl = null;
+ let foundElDragRect = null;
+ let placeAfter = false;
+ elementsInSameRow.forEach( sameRow => {
+ const centerX = (sameRow.dragRect.left + (sameRow.dragRect.width*.5));
+ let distance = Math.abs(dragX - centerX);
+ if(distance < lastDistance) {
+ foundEl = sameRow.el;
+ foundElDragRect = sameRow.dragRect;
+ lastDistance = Math.abs(distance);
+ placeAfter = dragX > centerX;
+ }
+ });
+
+ // If we are on top or closest to our self, we should not do anything.
+ if (foundEl === currentElement) {
+ return;
+ }
+
+ if (foundEl) {
+
+ const isInsideFound = isWithinRect(dragX, dragY, foundElDragRect, 0);
+
+ // If we are inside the found element, lets look for sub containers.
+ // use the itemHasNestedContainersResolver, if not configured fallback to looking for the existence of a container via DOM.
+ if (isInsideFound &&
+ config.itemHasNestedContainersResolver ? config.itemHasNestedContainersResolver(foundEl) : foundEl.querySelector(config.containerSelector)) {
+
+ // Find all sub containers:
+ const subLayouts = foundEl.querySelectorAll(config.containerSelector);
+ for (const subLayoutEl of subLayouts) {
+
+ // Use boundary element or fallback to container element.
+ var subBoundaryElement = (config.boundarySelector ? subLayoutEl.closest(config.boundarySelector) : subLayoutEl) || subLayoutEl;
+ var subBoundaryRect = subBoundaryElement.getBoundingClientRect();
+
+ const subContainerHasItems = subLayoutEl.querySelector(config.itemSelector+':not(.'+config.placeholderClass+')');
+ // gather elements on the same row.
+ const subOffsetEdge = subContainerHasItems ? -10 : 20;
+ if(isWithinRect(dragX, dragY, subBoundaryRect, subOffsetEdge)) {
+
+ var subVm = subLayoutEl['umbBlockGridSorter:vm']();
+ if(subVm.identifier === vm.identifier) {
+ currentContainerElement = subLayoutEl;
+ currentContainerVM = subVm;
+ moveCurrentElement();
+ return;
+ }
+ }
+ }
+ }
+
+
+ // Indication if drop is good:
+ if(updateAllowIndication(currentContainerVM, currentItem) === false) {
+ return;
+ }
+
+ let verticalDirection = scope.config.resolveVerticalDirection ? scope.config.resolveVerticalDirection({
+ containerElement: currentContainerElement,
+ containerRect: currentContainerRect,
+ item: currentItem,
+ element: currentElement,
+ elementRect: currentElementRect,
+ relatedElement: foundEl,
+ relatedRect: foundElDragRect,
+ placeholderIsInThisRow: placeholderIsInThisRow,
+ horizontalPlaceAfter: placeAfter
+ }) : true;
+
+ if (verticalDirection) {
+ placeAfter = (dragY > foundElDragRect.top + (foundElDragRect.height*.5));
+ }
+
+ if(verticalDirection) {
+
+ let el;
+ if(placeAfter === false) {
+ let lastLeft = foundElDragRect.left;
+ elementsInSameRow.findIndex((x) => {
+ if(x.dragRect.left < lastLeft) {
+ lastLeft = x.dragRect.left;
+ el = x.el;
+ }
+ });
+ } else {
+ let lastRight = foundElDragRect.right;
+ elementsInSameRow.findIndex((x) => {
+ if(x.dragRect.right > lastRight) {
+ lastRight = x.dragRect.right;
+ el = x.el;
+ }
+ });
+ }
+ if(el) {
+ foundEl = el;
+ }
+ }
+
+ const foundElIndex = orderedContainerElements.indexOf(foundEl);
+ const placeAt = (placeAfter ? foundElIndex+1 : foundElIndex);
+
+ move(orderedContainerElements, placeAt);
+
+ return;
+ }
+ // We skipped the above part cause we are above or below container:
+
+ // Indication if drop is good:
+ if(updateAllowIndication(currentContainerVM, currentItem) === false) {
+ return;
+ }
+
+ if(dragY < currentContainerRect.top) {
+ move(orderedContainerElements, 0);
+ } else if(dragY > currentContainerRect.bottom) {
+ move(orderedContainerElements, -1);
+ }
+ }
+
+ function move(orderedContainerElements, newElIndex) {
+
+ newElIndex = newElIndex === -1 ? orderedContainerElements.length : newElIndex;
+
+ const placeBeforeElement = orderedContainerElements[newElIndex];
+ if (placeBeforeElement) {
+ // We do not need to move this, if the element to be placed before is it self.
+ if(placeBeforeElement !== currentElement) {
+ currentContainerElement.insertBefore(currentElement, placeBeforeElement);
+ }
+ } else {
+ currentContainerElement.appendChild(currentElement);
+ }
+
+ if(config.onChange) {
+ config.onChange();
+ }
+ }
+
+
+ /** Removes an element from container and returns its items-data entry */
+ vm.getItemOfElement = function (element) {
+ if(!element) {
+ return null;
+ }
+ return scope.model.find(entry => config.compareElementToModel(element, entry));
+ }
+ vm.removeItem = function (item) {
+ if(!item) {
+ return null;
+ }
+ const oldIndex = scope.model.indexOf(item);
+ if(oldIndex !== -1) {
+ return scope.model.splice(oldIndex, 1)[0];
+ }
+ return null;
+ }
+
+ vm.hasOtherItemsThan = function(item) {
+ return scope.model.filter(x => x !== item).length > 0;
+ }
+
+ vm.sync = function(element, fromVm) {
+
+ const movingItem = fromVm.getItemOfElement(element);
+ if(!movingItem) {
+ console.error("Could not find item of sync item")
+ return false;
+ }
+ if(vm.notifyRequestDrop({item: movingItem}) === false) {
+ return false;
+ }
+ if(fromVm.removeItem(movingItem) === null) {
+ console.error("Sync could not remove item")
+ return false;
+ }
+
+ /** Find next element, to then find the index of that element in items-data, to use as a safe reference to where the item will go in our items-data.
+ * This enables the container to contain various other elements and as well having these elements change while sorting is occurring.
+ */
+
+ // find next valid element (This assumes the next element in DOM is presented in items-data, aka. only moving one item between each sync)
+ let nextEl;
+ let loopEl = element;
+ while((loopEl = loopEl.nextElementSibling)) {
+ if(loopEl.matches && loopEl.matches(config.itemSelector)) {
+ nextEl = loopEl;
+ break;
+ }
+ }
+
+ let newIndex = scope.model.length;
+ if(nextEl) {
+ // We had a reference element, we want to get the index of it.
+ // This is problem if a item is being moved forward?
+ newIndex = scope.model.findIndex(entry => config.compareElementToModel(nextEl, entry));
+ }
+
+ scope.model.splice(newIndex, 0, movingItem);
+
+ const eventData = {item: movingItem, fromController:fromVm, toController:vm};
+ if(fromVm !== vm) {
+ fromVm.notifySync(eventData);
+ }
+ vm.notifySync(eventData);
+
+ return true;
+ }
+
+
+
+
+
+
+
+ var _lastIndicationContainerVM = null;
+ function updateAllowIndication(contextVM, item) {
+
+ // Remove old indication:
+ if(_lastIndicationContainerVM !== null && _lastIndicationContainerVM !== contextVM) {
+ _lastIndicationContainerVM.notifyAllowed();
+ }
+ _lastIndicationContainerVM = contextVM;
+
+ if(contextVM.notifyRequestDrop({item: item}) === true) {
+ contextVM.notifyAllowed();
+ return true;
+ }
+
+ contextVM.notifyDisallowed();// This block is not accepted to we will indicate that its not allowed.
+ return false;
+ }
+ function removeAllowIndication() {
+
+ // Remove old indication:
+ if(_lastIndicationContainerVM !== null) {
+ _lastIndicationContainerVM.notifyAllowed();
+ }
+ _lastIndicationContainerVM = null;
+ }
+
+
+
+
+
+ let autoScrollRAF;
+ let autoScrollEl;
+ const autoScrollSensitivity = 50;
+ const autoScrollSpeed = 16;
+ let autoScrollX = 0;
+ let autoScrollY = 0;
+
+ function handleAutoScroll(clientX, clientY) {
+
+ let scrollRect = null;
+ if (scrollElement) {
+ autoScrollEl = scrollElement;
+ scrollRect = scrollElement.getBoundingClientRect();
+ } else {
+ autoScrollEl = document.scrollingElement || document.documentElement;
+ scrollRect = {top: 0,
+ left: 0,
+ bottom: window.innerHeight,
+ right: window.innerWidth,
+ height: window.innerHeight,
+ width: window.innerWidth
+ }
+ }
+
+ const scrollWidth = autoScrollEl.scrollWidth;
+ const scrollHeight = autoScrollEl.scrollHeight;
+ const canScrollX = scrollRect.width < scrollWidth;
+ const canScrollY = scrollRect.height < scrollHeight;
+ const scrollPosX = autoScrollEl.scrollLeft;
+ const scrollPosY = autoScrollEl.scrollTop;
+
+ cancelAnimationFrame(autoScrollRAF);
+
+ if(canScrollX || canScrollY) {
+
+ autoScrollX = (Math.abs(scrollRect.right - clientX) <= autoScrollSensitivity && scrollPosX + scrollRect.width < scrollWidth) - (Math.abs(scrollRect.left - clientX) <= autoScrollSensitivity && !!scrollPosX);
+ autoScrollY = (Math.abs(scrollRect.bottom - clientY) <= autoScrollSensitivity && scrollPosY + scrollRect.height < scrollHeight) - (Math.abs(scrollRect.top - clientY) <= autoScrollSensitivity && !!scrollPosY);
+ autoScrollRAF = requestAnimationFrame(performAutoScroll);
+ }
+ }
+ function performAutoScroll() {
+ autoScrollEl.scrollLeft += autoScrollX * autoScrollSpeed;
+ autoScrollEl.scrollTop += autoScrollY * autoScrollSpeed;
+ autoScrollRAF = requestAnimationFrame(performAutoScroll);
+ }
+ function stopAutoScroll() {
+ cancelAnimationFrame(autoScrollRAF);
+ autoScrollRAF = null;
+ }
+
+
+
+
+ vm.notifySync = function(data) {
+ if(config.onSync) {
+ config.onSync(data);
+ }
+ }
+ vm.notifyDisallowed = function() {
+ if(config.onDisallowed) {
+ config.onDisallowed();
+ }
+ }
+ vm.notifyAllowed = function() {
+ if(config.onAllowed) {
+ config.onAllowed();
+ }
+ }
+ vm.notifyRequestDrop = function(data) {
+ if(config.onRequestDrop) {
+ return config.onRequestDrop(data);
+ }
+ return true;
+ }
+
+
+
+ scope.$on('$destroy', () => {
+
+ if(currentElement) {
+ handleDragEnd()
+ }
+
+ _lastIndicationContainerVM = null;
+
+ containerEl['umbBlockGridSorter:vm'] = null
+ containerEl.removeEventListener('dragover', preventDragOver);
+
+ observer.disconnect();
+ observer = null;
+ containerEl = null;
+ scrollElement = null;
+ vm = null;
+ });
+ }
+
+ var directive = {
+ restrict: 'A',
+ scope: {
+ config: '=umbBlockGridSorter',
+ model: '=umbBlockGridSorterModel'
+ },
+ link: link
+ };
+
+ return directive;
+
+ }
+
+ angular.module('umbraco.directives').directive('umbBlockGridSorter', UmbBlockGridSorter);
+
+})();
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbraco-blockgridlayout-flexbox.css b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbraco-blockgridlayout-flexbox.css
index a095fb438c..5dcc95a61d 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbraco-blockgridlayout-flexbox.css
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbraco-blockgridlayout-flexbox.css
@@ -1,14 +1,15 @@
/** Example of how a grid layout stylehseet could be done with Flex box: */
-
.umb-block-grid__layout-container {
position: relative;
display: flex;
flex-wrap: wrap;
+ gap: var(--umb-block-grid--row-gap, 0) var(--umb-block-grid--column-gap, 0);
}
.umb-block-grid__layout-item {
+ position: relative;
--umb-block-grid__layout-item-calc: calc(var(--umb-block-grid--item-column-span) / var(--umb-block-grid--grid-columns));
- width: calc(var(--umb-block-grid__layout-item-calc) * 100%);
+ width: calc(var(--umb-block-grid__layout-item-calc) * 100% - (1 - var(--umb-block-grid__layout-item-calc)) * var(--umb-block-grid--column-gap, 0px));
}
@@ -17,10 +18,12 @@
display: flex;
flex-wrap: wrap;
width: 100%;
+ gap: var(--umb-block-grid--areas-row-gap, 0) var(--umb-block-grid--areas-column-gap, 0);
}
.umb-block-grid__area {
+ position: relative;
--umb-block-grid__area-calc: calc(var(--umb-block-grid--area-column-span) / var(--umb-block-grid--area-grid-columns, 1));
- width: calc(var(--umb-block-grid__area-calc) * 100%);
+ width: calc(var(--umb-block-grid__area-calc) * 100% - (1 - var(--umb-block-grid__area-calc)) * var(--umb-block-grid--areas-column-gap, 0px));
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbraco-blockgridlayout.css b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbraco-blockgridlayout.css
index f6fe596b5c..60e0c4e96a 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbraco-blockgridlayout.css
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbraco-blockgridlayout.css
@@ -29,13 +29,13 @@
.umb-block-grid__area {
position: relative;
/* For small devices we scale columnSpan by three, to make everything bigger than 1/3 take full width: */
- grid-column-end: span min(calc(var(--umb-block-grid--item-column-span, 1) * 3), var(--umb-block-grid--grid-columns));
+ grid-column-end: span min(calc(var(--umb-block-grid--area-column-span, 1) * 3), var(--umb-block-grid--grid-columns));
grid-row: span var(--umb-block-grid--area-row-span, 1);
}
@media (min-width:1024px) {
.umb-block-grid__layout-item {
- grid-column-end: span var(--umb-block-grid--item-column-span, 1);
+ grid-column-end: span min(var(--umb-block-grid--item-column-span, 1), var(--umb-block-grid--grid-columns));
}
.umb-block-grid__area {
grid-column-end: span var(--umb-block-grid--area-column-span, 1);
diff --git a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js
index 89101d2f1d..6619754315 100644
--- a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js
+++ b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js
@@ -29,7 +29,6 @@ module.exports = function (config) {
'lib/umbraco/Extensions.js',
'node_modules/lazyload-js/LazyLoad.min.js',
'node_modules/angular-dynamic-locale/dist/tmhDynamicLocale.min.js',
- 'node_modules/sortablejs/Sortable.min.js',
//app bootstrap and loader
'test/config/app.unit.js',
From 72bdebf419182ead5d48b97aa2a71beb66479555 Mon Sep 17 00:00:00 2001
From: Jacob Overgaard <752371+iovergaard@users.noreply.github.com>
Date: Fri, 18 Nov 2022 15:06:24 +0100
Subject: [PATCH 12/24] v11: Umbraco Marketplace replaces packages repo
(#13371)
* add lang keys for marketplace
* remove old 'repo' page and deprecate related services
* add new view for Umbraco Marketplace
* optimise margin/padding for other tabs
* mark Our Repository constants as obsolete
* improve css path to iframe slightly with more aliases and classnames
* remove style qs
* update URL of Marketplace
* add ng-controller with utitlities for future PostMessage API
* rename marketplace loaded function
* remove iframe postmessage logic for time being
* add handling of dynamic querystring params
* assume url does not change
* Added support for additional parameters for marketplace
* Update src/JsonSchema/AppSettings.cs
Fix styling issue
Co-authored-by: Ronald Barendse
* Update src/Umbraco.Core/Configuration/Models/MarketplaceSettings.cs
Fix styling issue
Co-authored-by: Ronald Barendse
* Update src/Umbraco.Core/Configuration/Models/MarketplaceSettings.cs
Make comment more descriptive
Co-authored-by: Ronald Barendse
* Update src/Umbraco.Core/Constants-Marketplace.cs
Fix styling issue
Co-authored-by: Ronald Barendse
Co-authored-by: Bjarke Berg
Co-authored-by: Ronald Barendse
---
src/JsonSchema/AppSettings.cs | 2 +
.../Models/ActiveDirectorySettings.cs | 3 +-
.../Models/MarketplaceSettings.cs | 16 +
src/Umbraco.Core/Constants-Configuration.cs | 1 +
src/Umbraco.Core/Constants-Marketplace.cs | 12 +
.../Constants-PackageRepository.cs | 1 +
.../UmbracoBuilder.Configuration.cs | 1 +
.../EmbeddedResources/Lang/da.xml | 1 +
.../EmbeddedResources/Lang/en.xml | 1 +
.../EmbeddedResources/Lang/en_us.xml | 1 +
.../Controllers/BackOfficeServerVariables.cs | 71 ++++
.../ourpackagerrepository.resource.js | 3 +-
.../src/less/components/umb-packages.less | 38 +++
.../editor/umb-editor-container.html | 2 +-
.../editor/umb-editor-sub-views.html | 25 +-
.../src/views/packages/overview.controller.js | 176 +++++-----
.../packages/views/marketplace.controller.js | 18 +
.../src/views/packages/views/marketplace.html | 13 +
.../views/packages/views/repo.controller.js | 265 ---------------
.../src/views/packages/views/repo.html | 318 ------------------
20 files changed, 279 insertions(+), 689 deletions(-)
create mode 100644 src/Umbraco.Core/Configuration/Models/MarketplaceSettings.cs
create mode 100644 src/Umbraco.Core/Constants-Marketplace.cs
create mode 100644 src/Umbraco.Web.UI.Client/src/views/packages/views/marketplace.controller.js
create mode 100644 src/Umbraco.Web.UI.Client/src/views/packages/views/marketplace.html
delete mode 100644 src/Umbraco.Web.UI.Client/src/views/packages/views/repo.controller.js
delete mode 100644 src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html
diff --git a/src/JsonSchema/AppSettings.cs b/src/JsonSchema/AppSettings.cs
index f9c7c224be..6f53541df4 100644
--- a/src/JsonSchema/AppSettings.cs
+++ b/src/JsonSchema/AppSettings.cs
@@ -75,5 +75,7 @@ internal class AppSettings
public DataTypesSettings? DataTypes { get; set; }
public LicensesSettings? Licenses { get; set; }
+
+ public MarketplaceSettings? Marketplace { get; set; }
}
}
diff --git a/src/Umbraco.Core/Configuration/Models/ActiveDirectorySettings.cs b/src/Umbraco.Core/Configuration/Models/ActiveDirectorySettings.cs
index 3373b7a778..3a3f90de89 100644
--- a/src/Umbraco.Core/Configuration/Models/ActiveDirectorySettings.cs
+++ b/src/Umbraco.Core/Configuration/Models/ActiveDirectorySettings.cs
@@ -7,7 +7,8 @@ namespace Umbraco.Cms.Core.Configuration.Models;
/// Typed configuration options for active directory settings.
///
[UmbracoOptions(Constants.Configuration.ConfigActiveDirectory)]
-[Obsolete("This is not used anymore. Will be removed in Umbraco 12")]public class ActiveDirectorySettings
+[Obsolete("This is not used anymore. Will be removed in Umbraco 12")]
+public class ActiveDirectorySettings
{
///
/// Gets or sets a value for the Active Directory domain.
diff --git a/src/Umbraco.Core/Configuration/Models/MarketplaceSettings.cs b/src/Umbraco.Core/Configuration/Models/MarketplaceSettings.cs
new file mode 100644
index 0000000000..73f7da185f
--- /dev/null
+++ b/src/Umbraco.Core/Configuration/Models/MarketplaceSettings.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Umbraco.
+// See LICENSE for more details.
+
+namespace Umbraco.Cms.Core.Configuration.Models;
+
+///
+/// Configuration options for the Marketplace.
+///
+[UmbracoOptions(Constants.Configuration.ConfigMarketplace)]
+public class MarketplaceSettings
+{
+ ///
+ /// Gets or sets the additional parameters that are sent to the Marketplace.
+ ///
+ public Dictionary AdditionalParameters { get; set; } = new ();
+}
diff --git a/src/Umbraco.Core/Constants-Configuration.cs b/src/Umbraco.Core/Constants-Configuration.cs
index bc4c29cfa6..656bcd1cdf 100644
--- a/src/Umbraco.Core/Constants-Configuration.cs
+++ b/src/Umbraco.Core/Constants-Configuration.cs
@@ -27,6 +27,7 @@ public static partial class Constants
public const string ConfigHostingDebug = ConfigHostingPrefix + "Debug";
public const string ConfigCustomErrorsMode = ConfigCustomErrorsPrefix + "Mode";
public const string ConfigActiveDirectory = ConfigPrefix + "ActiveDirectory";
+ public const string ConfigMarketplace = ConfigPrefix + "Marketplace";
public const string ConfigLegacyPasswordMigration = ConfigPrefix + "LegacyPasswordMigration";
public const string ConfigContent = ConfigPrefix + "Content";
public const string ConfigCoreDebug = ConfigCorePrefix + "Debug";
diff --git a/src/Umbraco.Core/Constants-Marketplace.cs b/src/Umbraco.Core/Constants-Marketplace.cs
new file mode 100644
index 0000000000..6d5abb38e7
--- /dev/null
+++ b/src/Umbraco.Core/Constants-Marketplace.cs
@@ -0,0 +1,12 @@
+namespace Umbraco.Cms.Core;
+
+public static partial class Constants
+{
+ ///
+ /// Defines the constants used for the Umbraco Marketplace.
+ ///
+ public static class Marketplace
+ {
+ public const string Url = "https://dev.marketplace.umbraco.com";
+ }
+}
diff --git a/src/Umbraco.Core/Constants-PackageRepository.cs b/src/Umbraco.Core/Constants-PackageRepository.cs
index 96746adb49..ef2e6fdd48 100644
--- a/src/Umbraco.Core/Constants-PackageRepository.cs
+++ b/src/Umbraco.Core/Constants-PackageRepository.cs
@@ -5,6 +5,7 @@ public static partial class Constants
///
/// Defines the constants used for the Umbraco package repository
///
+ [Obsolete("This is no longer used and will be removed in Umbraco 13")]
public static class PackageRepository
{
public const string RestApiBaseUrl = "https://our.umbraco.com/webapi/packages/v1";
diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs
index 28fc05907e..92e10c7b1c 100644
--- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs
+++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs
@@ -50,6 +50,7 @@ public static partial class UmbracoBuilderExtensions
builder
.AddUmbracoOptions()
.AddUmbracoOptions()
+ .AddUmbracoOptions()
.AddUmbracoOptions()
.AddUmbracoOptions()
.AddUmbracoOptions()
diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml
index 1c23c53e38..32317e57d0 100644
--- a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml
+++ b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml
@@ -1263,6 +1263,7 @@ Mange hilsner fra Umbraco robotten
Mediearkiv
Medlemmer
Pakker
+ Marketplace
Nyhedsbreve
Indstillinger
Statistik
diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml
index 3cd7f0d40a..f3bdf620d4 100644
--- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml
+++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml
@@ -1477,6 +1477,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
Members
Newsletters
Packages
+ Marketplace
Settings
Statistics
Translation
diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml
index a20075769a..c19642d351 100644
--- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml
+++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml
@@ -1515,6 +1515,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
Media
Members
Packages
+ Marketplace
Settings
Translation
Users
diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs
index 768fe382f9..d6231a5bef 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs
@@ -1,4 +1,5 @@
using System.Runtime.Serialization;
+using System.Web;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
@@ -54,6 +55,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
private MemberPasswordConfigurationSettings _memberPasswordConfigurationSettings;
private DataTypesSettings _dataTypesSettings;
private readonly ITempDataDictionaryFactory _tempDataDictionaryFactory;
+ private MarketplaceSettings _marketplaceSettings;
[Obsolete("Use constructor that takes IOptionsMontior, scheduled for removal in V12")]
public BackOfficeServerVariables(
@@ -139,6 +141,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
}
+ [Obsolete("Use constructor that takes IOptionsMonitor, scheduled for removal in V13")]
public BackOfficeServerVariables(
LinkGenerator linkGenerator,
IRuntimeState runtimeState,
@@ -159,6 +162,52 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
IOptionsMonitor memberPasswordConfigurationSettings,
IOptionsMonitor dataTypesSettings,
ITempDataDictionaryFactory tempDataDictionaryFactory)
+ : this(
+ linkGenerator,
+ runtimeState,
+ features,
+ globalSettings,
+ umbracoVersion,
+ contentSettings,
+ httpContextAccessor,
+ treeCollection,
+ hostingEnvironment,
+ runtimeSettings,
+ securitySettings,
+ runtimeMinifier,
+ externalLogins,
+ imageUrlGenerator,
+ previewRoutes,
+ emailSender,
+ memberPasswordConfigurationSettings,
+ dataTypesSettings,
+ tempDataDictionaryFactory,
+ StaticServiceProvider.Instance.GetRequiredService>()
+ )
+ {
+ }
+
+ public BackOfficeServerVariables(
+ LinkGenerator linkGenerator,
+ IRuntimeState runtimeState,
+ UmbracoFeatures features,
+ IOptionsMonitor globalSettings,
+ IUmbracoVersion umbracoVersion,
+ IOptionsMonitor contentSettings,
+ IHttpContextAccessor httpContextAccessor,
+ TreeCollection treeCollection,
+ IHostingEnvironment hostingEnvironment,
+ IOptionsMonitor runtimeSettings,
+ IOptionsMonitor securitySettings,
+ IRuntimeMinifier runtimeMinifier,
+ IBackOfficeExternalLoginProviders externalLogins,
+ IImageUrlGenerator imageUrlGenerator,
+ PreviewRoutes previewRoutes,
+ IEmailSender emailSender,
+ IOptionsMonitor memberPasswordConfigurationSettings,
+ IOptionsMonitor dataTypesSettings,
+ ITempDataDictionaryFactory tempDataDictionaryFactory,
+ IOptionsMonitor marketplaceSettings)
{
_linkGenerator = linkGenerator;
_runtimeState = runtimeState;
@@ -179,6 +228,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
_tempDataDictionaryFactory = tempDataDictionaryFactory;
_memberPasswordConfigurationSettings = memberPasswordConfigurationSettings.CurrentValue;
_dataTypesSettings = dataTypesSettings.CurrentValue;
+ _marketplaceSettings = marketplaceSettings.CurrentValue;
globalSettings.OnChange(x => _globalSettings = x);
contentSettings.OnChange(x => _contentSettings = x);
@@ -186,6 +236,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
securitySettings.OnChange(x => _securitySettings = x);
dataTypesSettings.OnChange(x => _dataTypesSettings = x);
memberPasswordConfigurationSettings.OnChange(x => _memberPasswordConfigurationSettings = x);
+ marketplaceSettings.OnChange(x => _marketplaceSettings = x);
}
///
@@ -298,6 +349,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{"gridConfig", _linkGenerator.GetPathByAction(nameof(BackOfficeController.GetGridConfig), backOfficeControllerName, new { area = Constants.Web.Mvc.BackOfficeArea })},
// TODO: This is ultra confusing! this same key is used for different things, when returning the full app when authenticated it is this URL but when not auth'd it's actually the ServerVariables address
{"serverVarsJs", _linkGenerator.GetPathByAction(nameof(BackOfficeController.Application), backOfficeControllerName, new { area = Constants.Web.Mvc.BackOfficeArea })},
+ {"marketplaceUrl", GetMarketplaceUrl()},
//API URLs
{
"packagesRestApiBaseUrl", Constants.PackageRepository.RestApiBaseUrl
@@ -621,6 +673,25 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
return defaultVals;
}
+ private string GetMarketplaceUrl()
+ {
+ var uriBuilder = new UriBuilder(Constants.Marketplace.Url);
+
+ var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+
+ query["umbversion"] = _runtimeState.SemanticVersion.ToSemanticStringWithoutBuild();
+ query["style"] = "backoffice";
+
+ foreach (var kvp in _marketplaceSettings.AdditionalParameters)
+ {
+ query[kvp.Key] = kvp.Value;
+ }
+
+ uriBuilder.Query = query.ToString();
+
+ return uriBuilder.ToString();
+ }
+
[DataContract]
private class PluginTree
{
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/ourpackagerrepository.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/ourpackagerrepository.resource.js
index 430f05c2c4..60ceebb207 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/ourpackagerrepository.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/ourpackagerrepository.resource.js
@@ -2,8 +2,9 @@
* @ngdoc service
* @name umbraco.resources.ourPackageRepositoryResource
* @description handles data for package installations
+ * @deprecated This resource is deprecated and will be removed in future versions. Umbraco no longer supports the Our Umbraco repository.
**/
-function ourPackageRepositoryResource($q, $http, umbDataFormatter, umbRequestHelper) {
+function ourPackageRepositoryResource($http, umbRequestHelper) {
var baseurl = Umbraco.Sys.ServerVariables.umbracoUrls.packagesRestApiBaseUrl;
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less
index 015f37741a..d93f931c1b 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less
@@ -1,3 +1,41 @@
+[data-element="editor-packages"] {
+ .umb-pane {
+ height: 100%;
+ margin: 0;
+
+ .umb-pane-content,
+ .umb-editor-sub-views {
+ height: 100%;
+
+ .umb-editor-sub-view {
+ padding: 20px;
+ }
+
+ .sub-view-Marketplace {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+
+ .umb-editor-sub-view__content {
+ height: 100%;
+ }
+ }
+ }
+ }
+}
+
+.umb-marketplace-view-wrapper {
+ height: 100%;
+ display: flex;
+ align-items: stretch;
+}
+
+.umb-marketplace-view {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+}
+
.umb-packages-view-title {
font-size: 20px;
font-weight: bold;
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html
index aa1765f365..2def207c70 100644
--- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html
+++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html
@@ -6,6 +6,6 @@
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html
index a0f56ee883..56c7a9cf48 100644
--- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html
+++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html
@@ -1,17 +1,12 @@