From 8af1e2bcf318bf655a71c48220b6879dca06b238 Mon Sep 17 00:00:00 2001 From: Richard Ockerby Date: Sat, 30 May 2020 23:18:56 +0100 Subject: [PATCH 01/42] If `httpContext.GetCurrentRequestIpAddress` is called with a spoofed HTTP context then IP returns null --- src/Umbraco.Core/Compose/AuditEventsComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Compose/AuditEventsComponent.cs b/src/Umbraco.Core/Compose/AuditEventsComponent.cs index 453fd6314a..033b46c13f 100644 --- a/src/Umbraco.Core/Compose/AuditEventsComponent.cs +++ b/src/Umbraco.Core/Compose/AuditEventsComponent.cs @@ -68,7 +68,7 @@ namespace Umbraco.Core.Compose { var httpContext = HttpContext.Current == null ? (HttpContextBase) null : new HttpContextWrapper(HttpContext.Current); var ip = httpContext.GetCurrentRequestIpAddress(); - if (ip.ToLowerInvariant().StartsWith("unknown")) ip = ""; + if (ip == null || ip.ToLowerInvariant().StartsWith("unknown")) ip = ""; return ip; } } From 4266e2b1d159b342389c49fa4c7f3001814b554f Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 13 May 2020 20:45:57 +0200 Subject: [PATCH 02/42] Fix "Edit: undefined" in content editor title --- .../components/editor/umbeditorcontentheader.directive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontentheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontentheader.directive.js index 7bd812e321..9b8f92d7c3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontentheader.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontentheader.directive.js @@ -27,7 +27,7 @@ var title = data[2] + ": "; if (!scope.isNew) { scope.a11yMessage += " " + scope.editor.content.name; - title += scope.content.name; + title += scope.editor.content.name; } else { var name = editorState.current.contentTypeName; scope.a11yMessage += " " + name; From 3680f22321c77b8732fbc4d94c26e399d19e6cdd Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 10 Jun 2020 14:33:32 +0100 Subject: [PATCH 03/42] Allows unpublished content to render a macro HTML in the RTE --- src/Umbraco.Web/Editors/MacroRenderingController.cs | 2 +- src/Umbraco.Web/IUmbracoComponentRenderer.cs | 13 +++++++++++++ src/Umbraco.Web/UmbracoComponentRenderer.cs | 9 +++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/MacroRenderingController.cs b/src/Umbraco.Web/Editors/MacroRenderingController.cs index 7aabff6822..33a58328b9 100644 --- a/src/Umbraco.Web/Editors/MacroRenderingController.cs +++ b/src/Umbraco.Web/Editors/MacroRenderingController.cs @@ -142,7 +142,7 @@ namespace Umbraco.Web.Editors //need to create a specific content result formatted as HTML since this controller has been configured //with only json formatters. result.Content = new StringContent( - _componentRenderer.RenderMacro(pageId, m.Alias, macroParams).ToString(), + _componentRenderer.RenderMacroForContent(publishedContent, m.Alias, macroParams).ToString(), Encoding.UTF8, "text/html"); diff --git a/src/Umbraco.Web/IUmbracoComponentRenderer.cs b/src/Umbraco.Web/IUmbracoComponentRenderer.cs index 4dc9036e6b..f05afe3f6b 100644 --- a/src/Umbraco.Web/IUmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/IUmbracoComponentRenderer.cs @@ -42,5 +42,18 @@ namespace Umbraco.Web /// The parameters. /// IHtmlString RenderMacro(int contentId, string alias, IDictionary parameters); + + /// + /// Renders the macro with the specified alias, passing in the specified parameters. + /// + /// An IPublishedContent to use for the context for the macro rendering + /// The alias. + /// The parameters. + /// A raw HTML string of the macro output + /// + /// Currently only used when the node is unpublished and unable to get the contentId item from the + /// content cache as its unpublished. This deals with taking in a preview/draft version of the content node + /// + IHtmlString RenderMacroForContent(IPublishedContent content, string alias, IDictionary parameters); } } diff --git a/src/Umbraco.Web/UmbracoComponentRenderer.cs b/src/Umbraco.Web/UmbracoComponentRenderer.cs index 0373c73724..aa4cbefe7b 100644 --- a/src/Umbraco.Web/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/UmbracoComponentRenderer.cs @@ -102,6 +102,15 @@ namespace Umbraco.Web return RenderMacro(content, alias, parameters); } + + public IHtmlString RenderMacroForContent(IPublishedContent content, string alias, IDictionary parameters) + { + if(content == null) + throw new InvalidOperationException("Cannot render a macro, IPublishedContent is null"); + + return RenderMacro(content, alias, parameters); + } + /// /// Renders the macro with the specified alias, passing in the specified parameters. /// From 1347b973f1c8617d372592236eab101507dd4179 Mon Sep 17 00:00:00 2001 From: Rachel Breeze Date: Wed, 10 Jun 2020 16:20:44 +0100 Subject: [PATCH 04/42] V8/tmplogin (#8203) --- .../application/umblogin.directive.js | 1 + .../components/application/umb-login.html | 78 +++++++++---------- 2 files changed, 38 insertions(+), 41 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js index 6dd740e08b..c2b298ad24 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js @@ -274,6 +274,7 @@ vm.showEmailResetConfirmation = false; if (vm.requestPasswordResetForm.$invalid) { + vm.errorMsg = 'Email address cannot be empty'; return; } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html index 098d69960d..9787c57940 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html @@ -23,7 +23,7 @@ {{vm.invitedUserPasswordModel.passwordPolicyText}} - + Your new password cannot be blank! Minimum {{vm.invitedUserPasswordModel.passwordPolicies.minPasswordLength}} characters {{inviteUserPasswordForm.password.errorMsg}} @@ -33,7 +33,7 @@
- + Required The confirmed password doesn't match the new password! @@ -41,11 +41,10 @@
- +
@@ -58,11 +57,10 @@ - +
@@ -70,19 +68,18 @@
+ ngf-select + ng-model="vm.avatarFile.filesHolder" + ngf-change="vm.changeAvatar($files, $event)" + ngf-multiple="false" + ngf-pattern="{{vm.avatarFile.acceptedFileTypes}}" + ngf-max-size="{{ vm.avatarFile.maxFileSize }}"> - + @@ -94,11 +91,10 @@

- +
@@ -153,7 +149,7 @@
- +
@@ -188,21 +184,21 @@

-
+
-
-
{{errorMsg}}
+
+
+ An email with password reset instructions will be sent to the specified address if it matched our records +

+
@@ -231,16 +227,16 @@
-
-
{{vm.errorMsg}}
+
+
-
+

Your new password has been set and you may now use it to log in. +

-
Return to login form @@ -251,7 +247,7 @@
- {{error}} +

{{error}}

From 349a46c311fef19fe7a5896b4aa08bb3a2b1a699 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Thu, 11 Jun 2020 19:14:46 +0200 Subject: [PATCH 05/42] Adjust property editor settings (#8251) Co-authored-by: Bjarne Fyrstenborg --- .../less/components/umb-group-builder.less | 1 - .../propertysettings/propertysettings.html | 26 +++++++------------ .../macros/infiniteeditors/parameter.html | 25 ++++++++---------- 3 files changed, 21 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less index ac7a2c63ce..6eded29b8b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less @@ -527,7 +527,6 @@ input.umb-group-builder__group-sort-value { text-align: left; display: flex; align-items: center; - max-width: calc(100% - 48px); min-height: 80px; color: @ui-action-discreet-type; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html index 1b28e84607..af9295f1ed 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html @@ -54,34 +54,32 @@
- +
- -
@@ -108,8 +106,7 @@ placeholder="@validation_mandatoryMessage" ng-model="model.property.validation.mandatoryMessage" ng-if="model.property.validation.mandatory" - ng-keypress="vm.submitOnEnter($event)"> - + ng-keypress="vm.submitOnEnter($event)" />
@@ -155,8 +151,7 @@ + on-click="vm.toggleAllowSegmentVariants()">
@@ -195,7 +190,6 @@ on-click="vm.toggleIsSensitiveData()"> -
diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/infiniteeditors/parameter.html b/src/Umbraco.Web.UI.Client/src/views/macros/infiniteeditors/parameter.html index 5a18ddf2cb..8a915be57b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/infiniteeditors/parameter.html +++ b/src/Umbraco.Web.UI.Client/src/views/macros/infiniteeditors/parameter.html @@ -41,25 +41,22 @@
- +
- +
+ {{ model.parameter.dataTypeName }} + {{ model.parameter.editor }} +
+
From c4456c53a054069e1d37c8813b07f02f39bfcfd3 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Fri, 12 Jun 2020 15:37:05 +0200 Subject: [PATCH 06/42] v8: Fix disappearing image cropper and grid content when using rollback (#5792) --- .../infiniteeditors/rollback/rollback.controller.js | 12 +++++++++++- .../common/infiniteeditors/rollback/rollback.html | 12 ++++++------ src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 3 +++ src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 4 +++- src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 3 +++ 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/rollback/rollback.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/rollback/rollback.controller.js index 24ed971de0..08a26ec340 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/rollback/rollback.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/rollback/rollback.controller.js @@ -68,14 +68,20 @@ if(version && version.versionId) { + vm.loading = true; + const culture = $scope.model.node.variants.length > 1 ? vm.currentVersion.language.culture : null; contentResource.getRollbackVersion(version.versionId, culture) - .then(function(data){ + .then(function(data) { vm.previousVersion = data; vm.previousVersion.versionId = version.versionId; createDiff(vm.currentVersion, vm.previousVersion); + + vm.loading = false; vm.rollbackButtonDisabled = false; + }, function () { + vm.loading = false; }); } else { @@ -118,6 +124,10 @@ tab.properties.forEach((property, propertyIndex) => { var oldProperty = previousVersion.tabs[tabIndex].properties[propertyIndex]; + // copy existing properties, so it doesn't manipulate existing properties on page + oldProperty = angular.copy(oldProperty); + property = angular.copy(property); + // we have to make properties storing values as object into strings (Grid, nested content, etc.) if(property.value instanceof Object) { property.value = JSON.stringify(property.value, null, 1); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/rollback/rollback.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/rollback/rollback.html index e292a94606..49408d69c3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/rollback/rollback.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/rollback/rollback.html @@ -31,8 +31,8 @@
-
-

{{vm.currentVersion.name}} (Created: {{vm.currentVersion.createDate}})

+
Current version
+

{{vm.currentVersion.name}} (Created: {{vm.currentVersion.createDate}})

- + From 2fd4512d3ae6ecfb4a977d7d480679384169938a Mon Sep 17 00:00:00 2001 From: Richard Thompson Date: Thu, 18 Jun 2020 10:55:01 +0100 Subject: [PATCH 14/42] Spelling correction Fixed a typo. --- .../directives/components/application/umbtour.directive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js index 6f98dbca6e..92869f39bd 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js @@ -11,7 +11,7 @@ You can easily add you own tours to the Help-drawer or show and start tours from anywhere in the Umbraco backoffice. To see a real world example of a custom tour implementation, install The Starter Kit in Umbraco 7.8

Extending the help drawer with custom tours

-The easiet way to add new tours to Umbraco is through the Help-drawer. All it requires is a my-tour.json file. +The easiest way to add new tours to Umbraco is through the Help-drawer. All it requires is a my-tour.json file. Place the file in App_Plugins/{MyPackage}/backoffice/tours/{my-tour}.json and it will automatically be picked up by Umbraco and shown in the Help-drawer. From 378118236345b7d3008935d43319d1be755320fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 19 Jun 2020 09:41:19 +0200 Subject: [PATCH 15/42] fix of npm run unit, plus begin able to test code that uses promises. --- src/Umbraco.Web.UI.Client/gulpfile.js | 4 ++-- src/Umbraco.Web.UI.Client/package.json | 6 ++++-- src/Umbraco.Web.UI.Client/test/config/karma.conf.js | 13 ++++++++++++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js index 542d45c479..d272c77397 100644 --- a/src/Umbraco.Web.UI.Client/gulpfile.js +++ b/src/Umbraco.Web.UI.Client/gulpfile.js @@ -31,6 +31,6 @@ exports.build = series(parallel(dependencies, js, less, views), testUnit); exports.dev = series(setDevelopmentMode, parallel(dependencies, js, less, views), watchTask); exports.watch = series(watchTask); // -exports.runTests = series(setTestMode, parallel(js, testUnit)); -exports.runUnit = series(setTestMode, parallel(js, runUnitTestServer), watchTask); +exports.runTests = series(setTestMode, series(js, testUnit)); +exports.runUnit = series(setTestMode, series(js, runUnitTestServer), watchTask); exports.testE2e = series(setTestMode, parallel(testE2e)); diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index c298f063a7..1b0af95e13 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -2,7 +2,7 @@ "private": true, "scripts": { "test": "gulp runTests", - "unit": "gulp testUnit", + "unit": "gulp runUnit", "e2e": "gulp testE2e", "build": "gulp build", "dev": "gulp dev", @@ -39,7 +39,7 @@ "moment": "2.22.2", "ng-file-upload": "12.2.13", "nouislider": "14.1.1", - "npm": "6.13.6", + "npm": "^6.14.0", "signalr": "2.4.0", "spectrum-colorpicker": "1.8.0", "tinymce": "4.9.10", @@ -71,7 +71,9 @@ "gulp-wrap": "0.15.0", "gulp-wrap-js": "0.4.1", "jasmine-core": "3.5.0", + "jasmine-promise-matchers": "^2.6.0", "karma": "4.4.1", + "karma-chrome-launcher": "^3.1.0", "karma-jasmine": "2.0.1", "karma-junit-reporter": "2.0.1", "karma-phantomjs-launcher": "1.0.4", 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 4e3a78144b..5883f59be8 100644 --- a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js +++ b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js @@ -10,6 +10,9 @@ module.exports = function (config) { // list of files / patterns to load in the browser files: [ + // Jasmine plugins + 'node_modules/jasmine-promise-matchers/dist/jasmine-promise-matchers.js', + //libraries 'node_modules/jquery/dist/jquery.min.js', 'node_modules/angular/angular.js', @@ -98,7 +101,14 @@ module.exports = function (config) { // - PhantomJS // - IE (only Windows) // CLI --browsers Chrome,Firefox,Safari - browsers: ['PhantomJS'], + browsers: ['ChromeHeadless'], + + customLaunchers: { + ChromeDebugging: { + base: 'Chrome', + flags: ['--remote-debugging-port=9333'] + } + }, // allow waiting a bit longer, some machines require this @@ -115,6 +125,7 @@ module.exports = function (config) { plugins: [ require('karma-jasmine'), require('karma-phantomjs-launcher'), + require('karma-chrome-launcher'), require('karma-junit-reporter'), require('karma-spec-reporter') From 0886ada39c4651fcd0a8bec5bce4358972791c45 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Fri, 19 Jun 2020 11:41:16 +0200 Subject: [PATCH 16/42] Properly HTML encode text in helper methods (#6545) --- .../PublishedContent/IndexedArrayItem.cs | 293 +++++++++++++++++- src/Umbraco.Web/HtmlHelperRenderExtensions.cs | 77 +++-- src/Umbraco.Web/HtmlStringUtilities.cs | 14 +- src/Umbraco.Web/Mvc/HtmlTagWrapperTextNode.cs | 2 +- src/Umbraco.Web/PublishedContentExtensions.cs | 149 ++++++++- 5 files changed, 489 insertions(+), 46 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IndexedArrayItem.cs b/src/Umbraco.Core/Models/PublishedContent/IndexedArrayItem.cs index 5dc42cc542..f7d13fcf90 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IndexedArrayItem.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IndexedArrayItem.cs @@ -2,168 +2,441 @@ namespace Umbraco.Core.Models.PublishedContent { + /// + /// Represents an item in an array that stores its own index and the total count. + /// + /// The type of the content. public class IndexedArrayItem { + /// + /// Initializes a new instance of the class. + /// + /// The content. + /// The index. public IndexedArrayItem(TContent content, int index) { Content = content; Index = index; } + /// + /// Gets the content. + /// + /// + /// The content. + /// public TContent Content { get; } + /// + /// Gets the index. + /// + /// + /// The index. + /// public int Index { get; } + /// + /// Gets the total count. + /// + /// + /// The total count. + /// public int TotalCount { get; internal set; } + /// + /// Determines whether this item is the first. + /// + /// + /// true if this item is the first; otherwise, false. + /// public bool IsFirst() { return Index == 0; } + /// + /// If this item is the first, the HTML encoded will be returned; otherwise, . + /// + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsFirst(string valueIfTrue) { return IsFirst(valueIfTrue, string.Empty); } + /// + /// If this item is the first, the HTML encoded will be returned; otherwise, . + /// + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsFirst(string valueIfTrue, string valueIfFalse) { - return new HtmlString(IsFirst() ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(IsFirst() ? valueIfTrue : valueIfFalse)); } + /// + /// Determines whether this item is not the first. + /// + /// + /// true if this item is not the first; otherwise, false. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public bool IsNotFirst() { return IsFirst() == false; } + /// + /// If this item is not the first, the HTML encoded will be returned; otherwise, . + /// + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsNotFirst(string valueIfTrue) { return IsNotFirst(valueIfTrue, string.Empty); } + /// + /// If this item is not the first, the HTML encoded will be returned; otherwise, . + /// + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsNotFirst(string valueIfTrue, string valueIfFalse) { - return new HtmlString(IsNotFirst() ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(IsNotFirst() ? valueIfTrue : valueIfFalse)); } + /// + /// Determines whether this item is at the specified . + /// + /// The index. + /// + /// true if this item is at the specified ; otherwise, false. + /// public bool IsIndex(int index) { return Index == index; } + /// + /// If this item is at the specified , the HTML encoded will be returned; otherwise, . + /// + /// The index. + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsIndex(int index, string valueIfTrue) { return IsIndex(index, valueIfTrue, string.Empty); } + /// + /// If this item is at the specified , the HTML encoded will be returned; otherwise, . + /// + /// The index. + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsIndex(int index, string valueIfTrue, string valueIfFalse) { - return new HtmlString(IsIndex(index) ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(IsIndex(index) ? valueIfTrue : valueIfFalse)); } + /// + /// Determines whether this item is at an index that can be divided by the specified . + /// + /// The modulus. + /// + /// true if this item is at an index that can be divided by the specified ; otherwise, false. + /// public bool IsModZero(int modulus) { return Index % modulus == 0; } + /// + /// If this item is at an index that can be divided by the specified , the HTML encoded will be returned; otherwise, . + /// + /// The modulus. + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsModZero(int modulus, string valueIfTrue) { return IsModZero(modulus, valueIfTrue, string.Empty); } + /// + /// If this item is at an index that can be divided by the specified , the HTML encoded will be returned; otherwise, . + /// + /// The modulus. + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsModZero(int modulus, string valueIfTrue, string valueIfFalse) { - return new HtmlString(IsModZero(modulus) ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(IsModZero(modulus) ? valueIfTrue : valueIfFalse)); } + /// + /// Determines whether this item is not at an index that can be divided by the specified . + /// + /// The modulus. + /// + /// true if this item is not at an index that can be divided by the specified ; otherwise, false. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public bool IsNotModZero(int modulus) { return IsModZero(modulus) == false; } + /// + /// If this item is not at an index that can be divided by the specified , the HTML encoded will be returned; otherwise, . + /// + /// The modulus. + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsNotModZero(int modulus, string valueIfTrue) { return IsNotModZero(modulus, valueIfTrue, string.Empty); } + /// + /// If this item is not at an index that can be divided by the specified , the HTML encoded will be returned; otherwise, . + /// + /// The modulus. + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsNotModZero(int modulus, string valueIfTrue, string valueIfFalse) { - return new HtmlString(IsNotModZero(modulus) ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(IsNotModZero(modulus) ? valueIfTrue : valueIfFalse)); } + /// + /// Determines whether this item is not at the specified . + /// + /// The index. + /// + /// true if this item is not at the specified ; otherwise, false. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public bool IsNotIndex(int index) { return IsIndex(index) == false; } + /// + /// If this item is not at the specified , the HTML encoded will be returned; otherwise, . + /// + /// The index. + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsNotIndex(int index, string valueIfTrue) { return IsNotIndex(index, valueIfTrue, string.Empty); } + /// + /// If this item is at the specified , the HTML encoded will be returned; otherwise, . + /// + /// The index. + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsNotIndex(int index, string valueIfTrue, string valueIfFalse) { - return new HtmlString(IsNotIndex(index) ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(IsNotIndex(index) ? valueIfTrue : valueIfFalse)); } + /// + /// Determines whether this item is the last. + /// + /// + /// true if this item is the last; otherwise, false. + /// public bool IsLast() { return Index == TotalCount - 1; } + /// + /// If this item is the last, the HTML encoded will be returned; otherwise, . + /// + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsLast(string valueIfTrue) { return IsLast(valueIfTrue, string.Empty); } + /// + /// If this item is the last, the HTML encoded will be returned; otherwise, . + /// + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsLast(string valueIfTrue, string valueIfFalse) { - return new HtmlString(IsLast() ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(IsLast() ? valueIfTrue : valueIfFalse)); } + /// + /// Determines whether this item is not the last. + /// + /// + /// true if this item is not the last; otherwise, false. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public bool IsNotLast() { return IsLast() == false; } + /// + /// If this item is not the last, the HTML encoded will be returned; otherwise, . + /// + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsNotLast(string valueIfTrue) { return IsNotLast(valueIfTrue, string.Empty); } + /// + /// If this item is not the last, the HTML encoded will be returned; otherwise, . + /// + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsNotLast(string valueIfTrue, string valueIfFalse) { - return new HtmlString(IsNotLast() ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(IsNotLast() ? valueIfTrue : valueIfFalse)); } + /// + /// Determines whether this item is at an even index. + /// + /// + /// true if this item is at an even index; otherwise, false. + /// public bool IsEven() { return Index % 2 == 0; } + /// + /// If this item is at an even index, the HTML encoded will be returned; otherwise, . + /// + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsEven(string valueIfTrue) { return IsEven(valueIfTrue, string.Empty); } + /// + /// If this item is at an even index, the HTML encoded will be returned; otherwise, . + /// + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsEven(string valueIfTrue, string valueIfFalse) { - return new HtmlString(IsEven() ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(IsEven() ? valueIfTrue : valueIfFalse)); } + /// + /// Determines whether this item is at an odd index. + /// + /// + /// true if this item is at an odd index; otherwise, false. + /// public bool IsOdd() { return Index % 2 == 1; } + /// + /// If this item is at an odd index, the HTML encoded will be returned; otherwise, . + /// + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsOdd(string valueIfTrue) { return IsOdd(valueIfTrue, string.Empty); } + /// + /// If this item is at an odd index, the HTML encoded will be returned; otherwise, . + /// + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public HtmlString IsOdd(string valueIfTrue, string valueIfFalse) { - return new HtmlString(IsOdd() ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(IsOdd() ? valueIfTrue : valueIfFalse)); } } } diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index ebe5e08f89..4b1de86bcf 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -253,12 +253,12 @@ namespace Umbraco.Web } /// - /// Helper method to create a new form to execute in the Umbraco request pipeline against a locally declared controller + /// Helper method to create a new form to execute in the Umbraco request pipeline against a locally declared controller. /// - /// - /// - /// - /// + /// The HTML helper. + /// Name of the action. + /// Name of the controller. + /// The method. /// public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, FormMethod method) { @@ -315,9 +315,9 @@ namespace Umbraco.Web /// /// public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, - object additionalRouteVals, - object htmlAttributes, - FormMethod method) + object additionalRouteVals, + object htmlAttributes, + FormMethod method) { return html.BeginUmbracoForm(action, controllerName, additionalRouteVals, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes), method); } @@ -332,8 +332,8 @@ namespace Umbraco.Web /// /// public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, - object additionalRouteVals, - object htmlAttributes) + object additionalRouteVals, + object htmlAttributes) { return html.BeginUmbracoForm(action, controllerName, additionalRouteVals, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } @@ -349,9 +349,9 @@ namespace Umbraco.Web /// /// public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, - object additionalRouteVals, - IDictionary htmlAttributes, - FormMethod method) + object additionalRouteVals, + IDictionary htmlAttributes, + FormMethod method) { if (action == null) throw new ArgumentNullException(nameof(action)); if (string.IsNullOrWhiteSpace(action)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(action)); @@ -371,8 +371,8 @@ namespace Umbraco.Web /// /// public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, - object additionalRouteVals, - IDictionary htmlAttributes) + object additionalRouteVals, + IDictionary htmlAttributes) { if (action == null) throw new ArgumentNullException(nameof(action)); if (string.IsNullOrWhiteSpace(action)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(action)); @@ -840,19 +840,32 @@ namespace Umbraco.Web #region If /// - /// If the test is true, the string valueIfTrue will be returned, otherwise the valueIfFalse will be returned. + /// If is true, the HTML encoded will be returned; otherwise, . /// - public static IHtmlString If(this HtmlHelper html, bool test, string valueIfTrue, string valueIfFalse) + /// The HTML helper. + /// If set to true returns ; otherwise, . + /// The value if true. + /// + /// The HTML encoded value. + /// + public static IHtmlString If(this HtmlHelper html, bool test, string valueIfTrue) { - return test ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); + return If(html, test, valueIfTrue, string.Empty); } /// - /// If the test is true, the string valueIfTrue will be returned, otherwise the valueIfFalse will be returned. + /// If is true, the HTML encoded will be returned; otherwise, . /// - public static IHtmlString If(this HtmlHelper html, bool test, string valueIfTrue) + /// The HTML helper. + /// If set to true returns ; otherwise, . + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + public static IHtmlString If(this HtmlHelper html, bool test, string valueIfTrue, string valueIfFalse) { - return test ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); + return new HtmlString(HttpUtility.HtmlEncode(test ? valueIfTrue : valueIfFalse)); } #endregion @@ -862,16 +875,32 @@ namespace Umbraco.Web private static readonly HtmlStringUtilities StringUtilities = new HtmlStringUtilities(); /// - /// Replaces text line breaks with HTML line breaks + /// Replaces text line breaks with HTML line breaks. /// - /// + /// The HTML helper. /// The text. - /// The text with text line breaks replaced with HTML line breaks (
)
+ /// + /// The text with text line breaks replaced with HTML line breaks (<br />). + /// + [Obsolete("This method doesn't HTML encode the text. Use ReplaceLineBreaks instead.")] public static IHtmlString ReplaceLineBreaksForHtml(this HtmlHelper helper, string text) { return StringUtilities.ReplaceLineBreaksForHtml(text); } + /// + /// HTML encodes the text and replaces text line breaks with HTML line breaks. + /// + /// The HTML helper. + /// The text. + /// + /// The HTML encoded text with text line breaks replaced with HTML line breaks (<br />). + /// + public static IHtmlString ReplaceLineBreaks(this HtmlHelper helper, string text) + { + return StringUtilities.ReplaceLineBreaks(text); + } + /// /// Generates a hash based on the text string passed in. This method will detect the /// security requirements (is FIPS enabled) and return an appropriate hash. diff --git a/src/Umbraco.Web/HtmlStringUtilities.cs b/src/Umbraco.Web/HtmlStringUtilities.cs index a8cbb70019..7df3e69bcb 100644 --- a/src/Umbraco.Web/HtmlStringUtilities.cs +++ b/src/Umbraco.Web/HtmlStringUtilities.cs @@ -16,21 +16,25 @@ namespace Umbraco.Web public sealed class HtmlStringUtilities { /// - /// Replaces text line breaks with HTML line breaks + /// Replaces text line breaks with HTML line breaks. /// /// The text. - /// The text with text line breaks replaced with HTML line breaks (<br />). + /// + /// The text with text line breaks replaced with HTML line breaks (<br />). + /// [Obsolete("This method doesn't HTML encode the text. Use ReplaceLineBreaks instead.")] public HtmlString ReplaceLineBreaksForHtml(string text) { - return new HtmlString(text.Replace("\r\n", @"
").Replace("\n", @"
").Replace("\r", @"
")); + return new HtmlString(text.Replace("\r\n", @"
").Replace("\n", @"
").Replace("\r", @"
")); } - + /// /// HTML encodes the text and replaces text line breaks with HTML line breaks. /// /// The text. - /// The HTML encoded text with text line breaks replaced with HTML line breaks (<br />). + /// + /// The HTML encoded text with text line breaks replaced with HTML line breaks (<br />). + /// public IHtmlString ReplaceLineBreaks(string text) { var value = HttpUtility.HtmlEncode(text)? diff --git a/src/Umbraco.Web/Mvc/HtmlTagWrapperTextNode.cs b/src/Umbraco.Web/Mvc/HtmlTagWrapperTextNode.cs index 43bfedde86..1085c2a279 100644 --- a/src/Umbraco.Web/Mvc/HtmlTagWrapperTextNode.cs +++ b/src/Umbraco.Web/Mvc/HtmlTagWrapperTextNode.cs @@ -12,7 +12,7 @@ namespace Umbraco.Web.Mvc public void WriteToHtmlTextWriter(System.Web.UI.HtmlTextWriter html) { - html.Write(Content); + html.WriteEncodedText(Content); } } } diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 750ffa4be6..73148b667a 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -287,29 +287,82 @@ namespace Umbraco.Web return content.Id == other.Id; } + /// + /// If the specified is equal to , the HTML encoded will be returned; otherwise, . + /// + /// The content. + /// The other content. + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public static HtmlString IsEqual(this IPublishedContent content, IPublishedContent other, string valueIfTrue) { return content.IsEqual(other, valueIfTrue, string.Empty); } + /// + /// If the specified is equal to , the HTML encoded will be returned; otherwise, . + /// + /// The content. + /// The other content. + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public static HtmlString IsEqual(this IPublishedContent content, IPublishedContent other, string valueIfTrue, string valueIfFalse) { - return new HtmlString(content.IsEqual(other) ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(content.IsEqual(other) ? valueIfTrue : valueIfFalse)); } + /// + /// If the specified is not equal to , the HTML encoded will be returned; otherwise, . + /// + /// The content. + /// The other content. + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public static bool IsNotEqual(this IPublishedContent content, IPublishedContent other) { return content.IsEqual(other) == false; } + /// + /// If the specified is not equal to , the HTML encoded will be returned; otherwise, . + /// + /// The content. + /// The other content. + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public static HtmlString IsNotEqual(this IPublishedContent content, IPublishedContent other, string valueIfTrue) { return content.IsNotEqual(other, valueIfTrue, string.Empty); } + /// + /// If the specified is not equal to , the HTML encoded will be returned; otherwise, . + /// + /// The content. + /// The other content. + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public static HtmlString IsNotEqual(this IPublishedContent content, IPublishedContent other, string valueIfTrue, string valueIfFalse) { - return new HtmlString(content.IsNotEqual(other) ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(content.IsNotEqual(other) ? valueIfTrue : valueIfFalse)); } #endregion @@ -321,14 +374,35 @@ namespace Umbraco.Web return other.Level < content.Level && content.Path.InvariantStartsWith(other.Path.EnsureEndsWith(',')); } + /// + /// If the specified is a decendant of , the HTML encoded will be returned; otherwise, . + /// + /// The content. + /// The other content. + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public static HtmlString IsDescendant(this IPublishedContent content, IPublishedContent other, string valueIfTrue) { return content.IsDescendant(other, valueIfTrue, string.Empty); } + /// + /// If the specified is a decendant of , the HTML encoded will be returned; otherwise, . + /// + /// The content. + /// The other content. + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public static HtmlString IsDescendant(this IPublishedContent content, IPublishedContent other, string valueIfTrue, string valueIfFalse) { - return new HtmlString(content.IsDescendant(other) ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(content.IsDescendant(other) ? valueIfTrue : valueIfFalse)); } public static bool IsDescendantOrSelf(this IPublishedContent content, IPublishedContent other) @@ -336,14 +410,35 @@ namespace Umbraco.Web return content.Path.InvariantEquals(other.Path) || content.IsDescendant(other); } + /// + /// If the specified is a decendant of or are the same, the HTML encoded will be returned; otherwise, . + /// + /// The content. + /// The other content. + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public static HtmlString IsDescendantOrSelf(this IPublishedContent content, IPublishedContent other, string valueIfTrue) { return content.IsDescendantOrSelf(other, valueIfTrue, string.Empty); } + /// + /// If the specified is a decendant of or are the same, the HTML encoded will be returned; otherwise, . + /// + /// The content. + /// The other content. + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public static HtmlString IsDescendantOrSelf(this IPublishedContent content, IPublishedContent other, string valueIfTrue, string valueIfFalse) { - return new HtmlString(content.IsDescendantOrSelf(other) ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(content.IsDescendantOrSelf(other) ? valueIfTrue : valueIfFalse)); } public static bool IsAncestor(this IPublishedContent content, IPublishedContent other) @@ -351,14 +446,35 @@ namespace Umbraco.Web return content.Level < other.Level && other.Path.InvariantStartsWith(content.Path.EnsureEndsWith(',')); } + /// + /// If the specified is an ancestor of , the HTML encoded will be returned; otherwise, . + /// + /// The content. + /// The other content. + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public static HtmlString IsAncestor(this IPublishedContent content, IPublishedContent other, string valueIfTrue) { return content.IsAncestor(other, valueIfTrue, string.Empty); } + /// + /// If the specified is an ancestor of , the HTML encoded will be returned; otherwise, . + /// + /// The content. + /// The other content. + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public static HtmlString IsAncestor(this IPublishedContent content, IPublishedContent other, string valueIfTrue, string valueIfFalse) { - return new HtmlString(content.IsAncestor(other) ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(content.IsAncestor(other) ? valueIfTrue : valueIfFalse)); } public static bool IsAncestorOrSelf(this IPublishedContent content, IPublishedContent other) @@ -366,14 +482,35 @@ namespace Umbraco.Web return other.Path.InvariantEquals(content.Path) || content.IsAncestor(other); } + /// + /// If the specified is an ancestor of or are the same, the HTML encoded will be returned; otherwise, . + /// + /// The content. + /// The other content. + /// The value if true. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public static HtmlString IsAncestorOrSelf(this IPublishedContent content, IPublishedContent other, string valueIfTrue) { return content.IsAncestorOrSelf(other, valueIfTrue, string.Empty); } + /// + /// If the specified is an ancestor of or are the same, the HTML encoded will be returned; otherwise, . + /// + /// The content. + /// The other content. + /// The value if true. + /// The value if false. + /// + /// The HTML encoded value. + /// + // TODO: This method should be removed or moved to an extension method on HtmlHelper. public static HtmlString IsAncestorOrSelf(this IPublishedContent content, IPublishedContent other, string valueIfTrue, string valueIfFalse) { - return new HtmlString(content.IsAncestorOrSelf(other) ? valueIfTrue : valueIfFalse); + return new HtmlString(HttpUtility.HtmlEncode(content.IsAncestorOrSelf(other) ? valueIfTrue : valueIfFalse)); } #endregion From 591575b47de87ce64c963ac85066ed6572660171 Mon Sep 17 00:00:00 2001 From: patrickdemooij9 Date: Fri, 19 Jun 2020 19:10:16 +0200 Subject: [PATCH 17/42] Added content apps for document types (#8187) * Added content apps for document types * PR Feedback updates * Updated the javascript code to use Utilities * Replace tolowercase comparison with localeCompare --- .../Manifest/ManifestContentAppFactory.cs | 4 + .../views/documenttypes/edit.controller.js | 79 ++++++------------- .../ContentEditorContentAppFactory.cs | 8 +- .../ContentInfoContentAppFactory.cs | 2 +- .../ContentTypeDesignContentAppFactory.cs | 33 ++++++++ .../ContentTypeListViewContentAppFactory.cs | 33 ++++++++ ...ContentTypePermissionsContentAppFactory.cs | 33 ++++++++ .../ContentTypeTemplatesContentAppFactory.cs | 33 ++++++++ .../ContentApps/ListViewContentAppFactory.cs | 4 +- .../ContentEditing/DocumentTypeDisplay.cs | 4 + .../Models/Mapping/CommonMapper.cs | 3 +- .../Mapping/ContentTypeMapDefinition.cs | 5 +- src/Umbraco.Web/Runtime/WebInitialComposer.cs | 6 +- src/Umbraco.Web/Umbraco.Web.csproj | 4 + 14 files changed, 182 insertions(+), 69 deletions(-) create mode 100644 src/Umbraco.Web/ContentApps/ContentTypeDesignContentAppFactory.cs create mode 100644 src/Umbraco.Web/ContentApps/ContentTypeListViewContentAppFactory.cs create mode 100644 src/Umbraco.Web/ContentApps/ContentTypePermissionsContentAppFactory.cs create mode 100644 src/Umbraco.Web/ContentApps/ContentTypeTemplatesContentAppFactory.cs diff --git a/src/Umbraco.Core/Manifest/ManifestContentAppFactory.cs b/src/Umbraco.Core/Manifest/ManifestContentAppFactory.cs index 788310d7a1..e0b63d1069 100644 --- a/src/Umbraco.Core/Manifest/ManifestContentAppFactory.cs +++ b/src/Umbraco.Core/Manifest/ManifestContentAppFactory.cs @@ -62,6 +62,10 @@ namespace Umbraco.Core.Manifest partA = "member"; partB = member.ContentType.Alias; break; + case IContentType contentType: + partA = "contentType"; + partB = contentType.Alias; + break; default: return null; diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js index 42dba5f580..ad042842f5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js @@ -38,7 +38,7 @@ vm.page.loading = false; vm.page.saveButtonState = "init"; vm.page.navigation = []; - + var labelKeys = [ "general_design", "general_listView", @@ -92,33 +92,6 @@ vm.labels.addTemplate = values[13]; vm.labels.allowCultureVariants = values[14]; - var buttons = [ - { - "name": vm.labels.design, - "alias": "design", - "icon": "icon-document-dashed-line", - "view": "views/documenttypes/views/design/design.html" - }, - { - "name": vm.labels.listview, - "alias": "listView", - "icon": "icon-list", - "view": "views/documenttypes/views/listview/listview.html" - }, - { - "name": vm.labels.permissions, - "alias": "permissions", - "icon": "icon-keychain", - "view": "views/documenttypes/views/permissions/permissions.html" - }, - { - "name": vm.labels.templates, - "alias": "templates", - "icon": "icon-layout", - "view": "views/documenttypes/views/templates/templates.html" - } - ]; - vm.page.keyboardShortcutsOverview = [ { "name": vm.labels.sections, @@ -187,9 +160,6 @@ ] } ]; - - loadButtons(buttons); - }); contentTypeHelper.checkModelsBuilderStatus().then(function (result) { @@ -279,25 +249,25 @@ contentTypeResource.getById(documentTypeId).then(function (dt) { init(dt); // we don't need to sync the tree in infinite mode - if(!infiniteMode) { + if (!infiniteMode) { syncTreeNode(vm.contentType, dt.path, true); } vm.page.loading = false; }); } - function loadButtons(buttons) { + function loadButtons() { + vm.page.navigation = vm.contentType.apps; - angular.forEach(buttons, - function (val, index) { + if (disableTemplates === true) { + Utilities.forEach(vm.contentType.apps, + (app, index) => { + if (app.alias === "templates") { + vm.page.navigation.splice(index, 1); + } + }); + } - if (disableTemplates === true && val.alias === "templates") { - buttons.splice(index, 1); - } - - }); - - vm.page.navigation = buttons; initializeActiveNavigationPanel(); } @@ -307,16 +277,14 @@ var initialViewSetFromRouteParams = false; var view = $routeParams.view; if (view) { - var viewPath = "views/documenttypes/views/" + view + "/" + view + ".html"; for (var i = 0; i < vm.page.navigation.length; i++) { - if (vm.page.navigation[i].view === viewPath) { + if (vm.page.navigation[i].alias.localeCompare(view, undefined, { sensitivity: 'accent' }) === 0) { vm.page.navigation[i].active = true; initialViewSetFromRouteParams = true; break; } } } - if (initialViewSetFromRouteParams === false) { vm.page.navigation[0].active = true; } @@ -379,17 +347,17 @@ }).then(function (data) { //success // we don't need to sync the tree in infinite mode - if(!infiniteMode) { + if (!infiniteMode) { syncTreeNode(vm.contentType, data.path); } // emit event var args = { documentType: vm.contentType }; eventsService.emit("editors.documentType.saved", args); - + vm.page.saveButtonState = "success"; - if(infiniteMode && $scope.model.submit) { + if (infiniteMode && $scope.model.submit) { $scope.model.documentTypeAlias = vm.contentType.alias; $scope.model.submit($scope.model); } @@ -446,10 +414,12 @@ // convert icons for content type convertLegacyIcons(contentType); - //set a shared state - editorState.set(contentType); - vm.contentType = contentType; + + //set a shared state + editorState.set(vm.contentType); + + loadButtons(); } /** Syncs the template alias for new doc types before saving if a template is to be created */ @@ -525,11 +495,10 @@ if (treeExists) { navigationService.syncTree({ tree: "templates", path: [], forceReload: true }) .then(function (syncArgs) { - navigationService.reloadNode(syncArgs.node) - } - ); + navigationService.reloadNode(syncArgs.node); + }); } - }); + }); } })); diff --git a/src/Umbraco.Web/ContentApps/ContentEditorContentAppFactory.cs b/src/Umbraco.Web/ContentApps/ContentEditorContentAppFactory.cs index add7e2f16a..9f6924cd54 100644 --- a/src/Umbraco.Web/ContentApps/ContentEditorContentAppFactory.cs +++ b/src/Umbraco.Web/ContentApps/ContentEditorContentAppFactory.cs @@ -30,9 +30,6 @@ namespace Umbraco.Web.ContentApps Weight = Weight }); - case IContent _: - return null; - case IMedia media when !media.ContentType.IsContainer || media.Properties.Count > 0: return _mediaApp ?? (_mediaApp = new ContentApp { @@ -43,9 +40,6 @@ namespace Umbraco.Web.ContentApps Weight = Weight }); - case IMedia _: - return null; - case IMember _: return _memberApp ?? (_memberApp = new ContentApp { @@ -57,7 +51,7 @@ namespace Umbraco.Web.ContentApps }); default: - throw new NotSupportedException($"Object type {o.GetType()} is not supported here."); + return null; } } } diff --git a/src/Umbraco.Web/ContentApps/ContentInfoContentAppFactory.cs b/src/Umbraco.Web/ContentApps/ContentInfoContentAppFactory.cs index fac03c43d0..abad7f4bf8 100644 --- a/src/Umbraco.Web/ContentApps/ContentInfoContentAppFactory.cs +++ b/src/Umbraco.Web/ContentApps/ContentInfoContentAppFactory.cs @@ -49,7 +49,7 @@ namespace Umbraco.Web.ContentApps }); default: - throw new NotSupportedException($"Object type {o.GetType()} is not supported here."); + return null; } } } diff --git a/src/Umbraco.Web/ContentApps/ContentTypeDesignContentAppFactory.cs b/src/Umbraco.Web/ContentApps/ContentTypeDesignContentAppFactory.cs new file mode 100644 index 0000000000..1aa0770aeb --- /dev/null +++ b/src/Umbraco.Web/ContentApps/ContentTypeDesignContentAppFactory.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Models.ContentEditing; +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Web.ContentApps +{ + internal class ContentTypeDesignContentAppFactory : IContentAppFactory + { + private const int Weight = -200; + + private ContentApp _contentTypeApp; + + public ContentApp GetContentAppFor(object source, IEnumerable userGroups) + { + switch (source) + { + case IContentType _: + return _contentTypeApp ?? (_contentTypeApp = new ContentApp() + { + Alias = "design", + Name = "Design", + Icon = "icon-document-dashed-line", + View = "views/documenttypes/views/design/design.html", + Weight = Weight + }); + default: + return null; + } + } + } +} diff --git a/src/Umbraco.Web/ContentApps/ContentTypeListViewContentAppFactory.cs b/src/Umbraco.Web/ContentApps/ContentTypeListViewContentAppFactory.cs new file mode 100644 index 0000000000..3b618c64f7 --- /dev/null +++ b/src/Umbraco.Web/ContentApps/ContentTypeListViewContentAppFactory.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Models.ContentEditing; +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Web.ContentApps +{ + internal class ContentTypeListViewContentAppFactory : IContentAppFactory + { + private const int Weight = -180; + + private ContentApp _contentTypeApp; + + public ContentApp GetContentAppFor(object source, IEnumerable userGroups) + { + switch (source) + { + case IContentType _: + return _contentTypeApp ?? (_contentTypeApp = new ContentApp() + { + Alias = "listView", + Name = "List view", + Icon = "icon-list", + View = "views/documenttypes/views/listview/listview.html", + Weight = Weight + }); + default: + return null; + } + } + } +} diff --git a/src/Umbraco.Web/ContentApps/ContentTypePermissionsContentAppFactory.cs b/src/Umbraco.Web/ContentApps/ContentTypePermissionsContentAppFactory.cs new file mode 100644 index 0000000000..0b07515068 --- /dev/null +++ b/src/Umbraco.Web/ContentApps/ContentTypePermissionsContentAppFactory.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Models.ContentEditing; +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Web.ContentApps +{ + internal class ContentTypePermissionsContentAppFactory : IContentAppFactory + { + private const int Weight = -160; + + private ContentApp _contentTypeApp; + + public ContentApp GetContentAppFor(object source, IEnumerable userGroups) + { + switch (source) + { + case IContentType _: + return _contentTypeApp ?? (_contentTypeApp = new ContentApp() + { + Alias = "permissions", + Name = "Permissions", + Icon = "icon-keychain", + View = "views/documenttypes/views/permissions/permissions.html", + Weight = Weight + }); + default: + return null; + } + } + } +} diff --git a/src/Umbraco.Web/ContentApps/ContentTypeTemplatesContentAppFactory.cs b/src/Umbraco.Web/ContentApps/ContentTypeTemplatesContentAppFactory.cs new file mode 100644 index 0000000000..0b656518d8 --- /dev/null +++ b/src/Umbraco.Web/ContentApps/ContentTypeTemplatesContentAppFactory.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Models.ContentEditing; +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Web.ContentApps +{ + internal class ContentTypeTemplatesContentAppFactory : IContentAppFactory + { + private const int Weight = -140; + + private ContentApp _contentTypeApp; + + public ContentApp GetContentAppFor(object source, IEnumerable userGroups) + { + switch (source) + { + case IContentType _: + return _contentTypeApp ?? (_contentTypeApp = new ContentApp() + { + Alias = "templates", + Name = "Templates", + Icon = "icon-layout", + View = "views/documenttypes/views/templates/templates.html", + Weight = Weight + }); + default: + return null; + } + } + } +} diff --git a/src/Umbraco.Web/ContentApps/ListViewContentAppFactory.cs b/src/Umbraco.Web/ContentApps/ListViewContentAppFactory.cs index bf6184197f..7d9ab43ac9 100644 --- a/src/Umbraco.Web/ContentApps/ListViewContentAppFactory.cs +++ b/src/Umbraco.Web/ContentApps/ListViewContentAppFactory.cs @@ -45,10 +45,8 @@ namespace Umbraco.Web.ContentApps entityType = "media"; dtdId = Core.Constants.DataTypes.DefaultMediaListView; break; - case IMember member: - return null; default: - throw new NotSupportedException($"Object type {o.GetType()} is not supported here."); + return null; } return CreateContentApp(_dataTypeService, _propertyEditors, entityType, contentTypeAlias, dtdId); diff --git a/src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs index 9467033aec..8aaa5c8af1 100644 --- a/src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Runtime.Serialization; +using Umbraco.Core.Models.ContentEditing; namespace Umbraco.Web.Models.ContentEditing { @@ -27,5 +28,8 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "allowSegmentVariant")] public bool AllowSegmentVariant { get; set; } + [DataMember(Name = "apps")] + public IEnumerable ContentApps { get; set; } + } } diff --git a/src/Umbraco.Web/Models/Mapping/CommonMapper.cs b/src/Umbraco.Web/Models/Mapping/CommonMapper.cs index 7bf4a94b1c..1276a2a405 100644 --- a/src/Umbraco.Web/Models/Mapping/CommonMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/CommonMapper.cs @@ -7,6 +7,7 @@ using Umbraco.Core; using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Models.ContentEditing; +using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; using Umbraco.Web.ContentApps; @@ -81,7 +82,7 @@ namespace Umbraco.Web.Models.Mapping return urlHelper.GetUmbracoApiService(controller => controller.GetTreeNode(source.Key.ToString("N"), null)); } - public IEnumerable GetContentApps(IContentBase source) + public IEnumerable GetContentApps(IUmbracoEntity source) { var apps = _contentAppDefinitions.GetContentAppsFor(source).ToArray(); diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs index c767e9aaf3..35b844ce95 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs @@ -17,6 +17,7 @@ namespace Umbraco.Web.Models.Mapping ///
internal class ContentTypeMapDefinition : IMapDefinition { + private readonly CommonMapper _commonMapper; private readonly PropertyEditorCollection _propertyEditors; private readonly IDataTypeService _dataTypeService; private readonly IFileService _fileService; @@ -25,10 +26,11 @@ namespace Umbraco.Web.Models.Mapping private readonly IMemberTypeService _memberTypeService; private readonly ILogger _logger; - public ContentTypeMapDefinition(PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, IFileService fileService, + public ContentTypeMapDefinition(CommonMapper commonMapper, PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, IFileService fileService, IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, ILogger logger) { + _commonMapper = commonMapper; _propertyEditors = propertyEditors; _dataTypeService = dataTypeService; _fileService = fileService; @@ -122,6 +124,7 @@ namespace Umbraco.Web.Models.Mapping target.AllowCultureVariant = source.VariesByCulture(); target.AllowSegmentVariant = source.VariesBySegment(); + target.ContentApps = _commonMapper.GetContentApps(source); //sync templates target.AllowedTemplates = context.MapEnumerable(source.AllowedTemplates); diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index c631aac5e3..60521e6d90 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -238,7 +238,11 @@ namespace Umbraco.Web.Runtime composition.ContentApps() .Append() .Append() - .Append(); + .Append() + .Append() + .Append() + .Append() + .Append(); // register back office sections in the order we want them rendered composition.Sections() diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c06ec574c5..b1cbff0fef 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -135,6 +135,10 @@ + + + + From 7210cf45d2d410478aa1a174738e3d8f04dbb052 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jun 2020 11:08:19 +0200 Subject: [PATCH 18/42] Bump angular from 1.7.9 to 1.8.0 in /src/Umbraco.Web.UI.Client (#8312) Bumps [angular](https://github.com/angular/angular.js) from 1.7.9 to 1.8.0. - [Release notes](https://github.com/angular/angular.js/releases) - [Changelog](https://github.com/angular/angular.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/angular/angular.js/compare/v1.7.9...v1.8.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/Umbraco.Web.UI.Client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index c298f063a7..95e91e61fa 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "ace-builds": "1.4.2", - "angular": "1.7.9", + "angular": "1.8.0", "angular-animate": "1.7.5", "angular-aria": "1.7.9", "angular-chart.js": "^1.1.1", From 5cb56042249ea825449f60f9200a2c5ae4ec5b26 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 23 Jun 2020 11:55:54 +0200 Subject: [PATCH 19/42] Update to the latest jQuery --- src/Umbraco.Web.UI.Client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 95e91e61fa..167bf53eb8 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -32,7 +32,7 @@ "diff": "3.5.0", "flatpickr": "4.5.2", "font-awesome": "4.7.0", - "jquery": "^3.4.1", + "jquery": "^3.5.1", "jquery-ui-dist": "1.12.1", "jquery-ui-touch-punch": "0.2.3", "lazyload-js": "1.0.0", From 611e7d3f6961b4d86573aa40b3721f754d5b6436 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Tue, 23 Jun 2020 13:17:56 +0200 Subject: [PATCH 20/42] Media picker folder create loader (#8306) * Format document * Use umb-loader component instead since this wrap the loader so it doesn't cause overflow on animation Co-authored-by: Bjarne Fyrstenborg --- .../mediapicker/mediapicker.html | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html index e1a89061b6..51cbf0269a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html @@ -20,15 +20,14 @@
@@ -42,9 +43,8 @@ - - + + {{ item.comment }} - - +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/urllist/urllist.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/urllist/urllist.html index 864ad213a4..65c8c2364c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/urllist/urllist.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/urllist/urllist.html @@ -2,10 +2,9 @@ -
\ No newline at end of file + From e19f375c84fb02ffe1c387af2a193d21d7d40919 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Tue, 23 Jun 2020 17:24:01 +0200 Subject: [PATCH 22/42] Update slider to handle merging of overlapping tooltips (#8074) --- src/Umbraco.Web.UI.Client/package.json | 2 +- .../components/umbrangeslider.directive.js | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 167bf53eb8..816d4e5090 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -38,7 +38,7 @@ "lazyload-js": "1.0.0", "moment": "2.22.2", "ng-file-upload": "12.2.13", - "nouislider": "14.1.1", + "nouislider": "14.4.0", "npm": "6.13.6", "signalr": "2.4.0", "spectrum-colorpicker": "1.8.0", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js index 21a1f181a6..e467522c84 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js @@ -118,6 +118,8 @@ For extra details about options and events take a look here: https://refreshless // create new slider noUiSlider.create(sliderInstance, options); + mergeTooltips(sliderInstance, 15, ' - '); + if (ctrl.onSetup) { ctrl.onSetup({ slider: sliderInstance @@ -200,6 +202,91 @@ For extra details about options and events take a look here: https://refreshless } } + // Merging overlapping tooltips: https://refreshless.com/nouislider/examples/#section-merging-tooltips + + /** + * @param slider HtmlElement with an initialized slider + * @param threshold Minimum proximity (in percentages) to merge tooltips + * @param separator String joining tooltips + */ + function mergeTooltips(slider, threshold, separator) { + + var textIsRtl = getComputedStyle(slider).direction === 'rtl'; + var isRtl = slider.noUiSlider.options.direction === 'rtl'; + var isVertical = slider.noUiSlider.options.orientation === 'vertical'; + var tooltips = slider.noUiSlider.getTooltips(); + var origins = slider.noUiSlider.getOrigins(); + + // Move tooltips into the origin element. The default stylesheet handles this. + tooltips.forEach(function (tooltip, index) { + if (tooltip) { + origins[index].appendChild(tooltip); + } + }); + + slider.noUiSlider.on('update', function (values, handle, unencoded, tap, positions) { + + var pools = [[]]; + var poolPositions = [[]]; + var poolValues = [[]]; + var atPool = 0; + + // Assign the first tooltip to the first pool, if the tooltip is configured + if (tooltips[0]) { + pools[0][0] = 0; + poolPositions[0][0] = positions[0]; + poolValues[0][0] = values[0]; + } + + for (var i = 1; i < positions.length; i++) { + if (!tooltips[i] || (positions[i] - positions[i - 1]) > threshold) { + atPool++; + pools[atPool] = []; + poolValues[atPool] = []; + poolPositions[atPool] = []; + } + + if (tooltips[i]) { + pools[atPool].push(i); + poolValues[atPool].push(values[i]); + poolPositions[atPool].push(positions[i]); + } + } + + pools.forEach(function (pool, poolIndex) { + var handlesInPool = pool.length; + + for (var j = 0; j < handlesInPool; j++) { + var handleNumber = pool[j]; + + if (j === handlesInPool - 1) { + var offset = 0; + + poolPositions[poolIndex].forEach(function (value) { + offset += 1000 - 10 * value; + }); + + var direction = isVertical ? 'bottom' : 'right'; + var last = isRtl ? 0 : handlesInPool - 1; + var lastOffset = 1000 - 10 * poolPositions[poolIndex][last]; + offset = (textIsRtl && !isVertical ? 100 : 0) + (offset / handlesInPool) - lastOffset; + + // Filter to unique values + var tooltipValues = poolValues[poolIndex].filter((v, i, a) => a.indexOf(v) === i); + + // Center this tooltip over the affected handles + tooltips[handleNumber].innerHTML = tooltipValues.join(separator); + tooltips[handleNumber].style.display = 'block'; + tooltips[handleNumber].style[direction] = offset + '%'; + } else { + // Hide this tooltip + tooltips[handleNumber].style.display = 'none'; + } + } + }); + }); + } + } angular.module('umbraco.directives').component('umbRangeSlider', umbRangeSlider); From 5ce9a3e74f3b8470430298db5f00e4212b95619e Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 13 May 2020 20:22:14 +0200 Subject: [PATCH 23/42] Reload property preview when changing property type --- .../property/umbpropertyeditor.directive.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js index 5eac7e5e24..8aaa4ff5ec 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js @@ -33,8 +33,15 @@ function umbPropEditor(umbPropEditorHelper) { scope.model.alias = Math.random().toString(36).slice(2); } - scope.propertyEditorView = umbPropEditorHelper.getViewPath(scope.model.view, scope.isPreValue); - + var unbindWatcher = scope.$watch("model.view", + function() { + scope.propertyEditorView = umbPropEditorHelper.getViewPath(scope.model.view, scope.isPreValue); + } + ); + + scope.$on("$destroy", function () { + unbindWatcher(); + }); } }; }; From 7b0468fb39bc2ee28f1ad5110f8a33a524d4a844 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 13 May 2020 20:57:36 +0200 Subject: [PATCH 24/42] Prevent property previews from having focus --- .../src/views/components/umb-groups-builder.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html index 0e1b2b4973..c46981a6cb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html @@ -240,7 +240,7 @@ - + Date: Fri, 22 May 2020 16:14:40 +0200 Subject: [PATCH 25/42] adds row immediately if only one layout is allowed --- .../src/views/propertyeditors/grid/grid.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html index 5048c479bb..0628766638 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html @@ -251,7 +251,9 @@ - From 9723c81d2721c57c6dfff5e38692c929d947b6eb Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 24 May 2020 13:53:48 +0200 Subject: [PATCH 30/42] Use element type description as tooltip in the Nested Content "Add content" dialog --- .../itempicker/itempicker.controller.js | 25 +++++++++++++++++++ .../overlays/itempicker/itempicker.html | 6 ++++- .../nestedcontent/nestedcontent.controller.js | 3 ++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.controller.js index 74a677b7c6..17184ae9a3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.controller.js @@ -19,6 +19,31 @@ function ItemPickerOverlay($scope, localizationService) { $scope.submitForm($scope.model); }; + $scope.tooltip = { + show: false, + event: null + }; + + $scope.showTooltip = function(item, $event) { + if (!item.tooltip) { + $scope.mouseLeave(); + return; + } + $scope.tooltip = { + show: true, + event: $event, + text: item.tooltip + }; + } + + $scope.hideTooltip = function () { + $scope.tooltip = { + show: false, + event: null, + text: null + }; + } + onInit(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.html index 715dc12a07..ff2ce6379f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/itempicker/itempicker.html @@ -38,9 +38,13 @@ + + + {{ tooltip.text }} + diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js index 066cbd6c17..fad68acbee 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js @@ -228,7 +228,8 @@ vm.overlayMenu.availableItems.push({ alias: scaffold.contentTypeAlias, name: scaffold.contentTypeName, - icon: iconHelper.convertFromLegacyIcon(scaffold.icon) + icon: iconHelper.convertFromLegacyIcon(scaffold.icon), + tooltip: scaffold.documentType.description }); }); From a70f95b4e563e8468f033c675d2537728bb8217c Mon Sep 17 00:00:00 2001 From: Jan Skovgaard <1932158+BatJan@users.noreply.github.com> Date: Tue, 23 Jun 2020 18:12:24 +0200 Subject: [PATCH 31/42] Sort dialog: Accessibility optimizations (#8156) --- src/Umbraco.Web.UI.Client/src/less/main.less | 8 +++- .../src/views/content/sort.html | 42 +++++++++---------- .../src/views/media/sort.html | 30 +++++++------ 3 files changed, 41 insertions(+), 39 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less index b34f313435..ee8dafebc2 100644 --- a/src/Umbraco.Web.UI.Client/src/less/main.less +++ b/src/Umbraco.Web.UI.Client/src/less/main.less @@ -347,10 +347,16 @@ label:not([for]) { border: none !important; } -table thead a { +table thead a, +table thead button { color: @gray-2; } +table thead button:hover, +table thead button:focus{ + text-decoration: underline; +} + /* UI interactions */ .umb-table tbody.ui-sortable tr diff --git a/src/Umbraco.Web.UI.Client/src/views/content/sort.html b/src/Umbraco.Web.UI.Client/src/views/content/sort.html index 471f3de956..e6cc58c8be 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/sort.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/sort.html @@ -1,37 +1,35 @@
- +