| Name |
-
+
{{part.value}}
{{part.value}}
{{part.value}}
@@ -64,7 +64,7 @@
| {{property.label}} |
-
+
{{part.value}}
{{part.value}}
{{part.value}}
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
index a876b1a6b7..59ce327d4d 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
@@ -1094,10 +1094,13 @@ Mange hilsner fra Umbraco robotten
Brugerdefineret
+ Ændringer
+ Oprettet
Vælg en version at sammenligne med den nuværende version
Nuværende version
Rød tekst vil ikke blive vist i den valgte version. Grøn betyder tilføjet]]>
Dokument tilbagerullet
+ Vælg en version at sammenligne med den nuværende version
Her vises den valgte version som html. Hvis du ønsker at se forskellen mellem de 2 versioner på samme tid, brug 'diff'-oversigten
Tilbagerulning til
Vælg version
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
index e96ef754c4..0150a5542d 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
@@ -1336,10 +1336,12 @@ To manage your website, simply open the Umbraco back office and start adding con
User defined
- Select a version to compare with the current version
+ Changes
+ Created
Current version
Red text will not be shown in the selected version. , green means added]]>
Document has been rolled back
+ Select a version to compare with the current version
This displays the selected version as HTML, if you wish to see the difference between 2 versions at the same time, use the diff view
Rollback to
Select version
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
index 2bb6495977..6700818ee0 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
@@ -1345,10 +1345,13 @@ To manage your website, simply open the Umbraco back office and start adding con
User defined
+ Changes
+ Created
Select a version to compare with the current version
Current version
Red text will not be shown in the selected version. , green means added]]>
Document has been rolled back
+ Select a version to compare with the current version
This displays the selected version as HTML, if you wish to see the difference between 2 versions at the same time, use the diff view
Rollback to
Select version
From 9be4775acfe63d53a8a4bc1557ea7a5268301138 Mon Sep 17 00:00:00 2001
From: Sebastiaan Janssen
Date: Fri, 12 Jun 2020 15:46:06 +0200
Subject: [PATCH 07/42] Make sure to use the new Utilities class
---
.../common/infiniteeditors/rollback/rollback.controller.js | 4 ++--
1 file changed, 2 insertions(+), 2 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 08a26ec340..fb8b649b55 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
@@ -125,8 +125,8 @@
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);
+ oldProperty = Utilities.copy(oldProperty);
+ property = Utilities.copy(property);
// we have to make properties storing values as object into strings (Grid, nested content, etc.)
if(property.value instanceof Object) {
From fda7251aedba177bf0add71ad8bdc1ddc0c1910c Mon Sep 17 00:00:00 2001
From: Bjarne Fyrstenborg
Date: Fri, 12 Jun 2020 16:01:19 +0200
Subject: [PATCH 08/42] Upload media and select media type (#8043)
---
.../upload/umbfiledropzone.directive.js | 43 ++++++++++---------
.../components/upload/umb-file-dropzone.html | 7 ---
.../propertyeditors/grid/grid.controller.js | 35 +++++++--------
3 files changed, 41 insertions(+), 44 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfiledropzone.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfiledropzone.directive.js
index 3fb1b3eb36..7f405eb28c 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfiledropzone.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbfiledropzone.directive.js
@@ -20,7 +20,7 @@ TODO
angular.module("umbraco.directives")
.directive('umbFileDropzone',
- function($timeout, Upload, localizationService, umbRequestHelper) {
+ function ($timeout, Upload, localizationService, umbRequestHelper, overlayService) {
return {
restrict: 'E',
replace: true,
@@ -119,9 +119,8 @@ angular.module("umbraco.directives")
//auto-clear the done queue after 3 secs
var currentLength = scope.done.length;
$timeout(function() {
- scope.done.splice(0, currentLength);
- },
- 3000);
+ scope.done.splice(0, currentLength);
+ }, 3000);
}
}
@@ -197,30 +196,34 @@ angular.module("umbraco.directives")
}
function _chooseMediaType() {
- scope.mediatypepickerOverlay = {
- view: "mediatypepicker",
- title: "Choose media type",
- acceptedMediatypes: scope.acceptedMediatypes,
- hideSubmitButton: true,
- show: true,
- submit: function(model) {
- scope.contentTypeAlias = model.selectedType.alias;
- scope.mediatypepickerOverlay.show = false;
- scope.mediatypepickerOverlay = null;
- _processQueueItem();
- },
- close: function(oldModel) {
- scope.queue.map(function(file) {
+ const dialog = {
+ view: "itempicker",
+ filter: scope.acceptedMediatypes.length > 15,
+ availableItems: scope.acceptedMediatypes,
+ submit: function (model) {
+ scope.contentTypeAlias = model.selectedItem.alias;
+ _processQueueItem();
+
+ overlayService.close();
+ },
+ close: function () {
+
+ scope.queue.map(function (file) {
file.uploadStatus = "error";
file.serverErrorMessage = "Cannot upload this file, no mediatype selected";
scope.rejected.push(file);
});
scope.queue = [];
- scope.mediatypepickerOverlay.show = false;
- scope.mediatypepickerOverlay = null;
+
+ overlayService.close();
}
};
+
+ localizationService.localize("defaultdialogs_selectMediaType").then(value => {
+ dialog.title = value;
+ overlayService.open(dialog);
+ });
}
scope.handleFiles = function(files, event) {
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-file-dropzone.html b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-file-dropzone.html
index 1a0086e836..b079b72be4 100644
--- a/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-file-dropzone.html
+++ b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-file-dropzone.html
@@ -104,11 +104,4 @@
-
-
-
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js
index 2568f62cf4..a0826f7a96 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js
@@ -318,25 +318,26 @@ angular.module("umbraco")
// Add items overlay menu
// *********************************************
$scope.openEditorOverlay = function (event, area, index, key) {
- var title = "";
- localizationService.localize("grid_insertControl").then(function (value) {
- title = value;
- overlayService.open({
- view: "itempicker",
- filter: area.$allowedEditors.length > 15,
- title: title,
- availableItems: area.$allowedEditors,
- event: event,
- submit: function (model) {
- if (model.selectedItem) {
- $scope.addControl(model.selectedItem, area, index);
- overlayService.close();
- }
- },
- close: function () {
+
+ const dialog = {
+ view: "itempicker",
+ filter: area.$allowedEditors.length > 15,
+ availableItems: area.$allowedEditors,
+ event: event,
+ submit: function (model) {
+ if (model.selectedItem) {
+ $scope.addControl(model.selectedItem, area, index);
overlayService.close();
}
- });
+ },
+ close: function () {
+ overlayService.close();
+ }
+ };
+
+ localizationService.localize("grid_insertControl").then(value => {
+ dialog.title = value;
+ overlayService.open(dialog);
});
};
From 567d7176a51d07515441b5393cec5d09ad2a0275 Mon Sep 17 00:00:00 2001
From: Sebastiaan Janssen
Date: Mon, 15 Jun 2020 11:05:40 +0200
Subject: [PATCH 09/42] Importing a doctype would never assign a value to
UploadedFiles, turns out we don't need UploadedFiles on the model
Additionally added a check to see if temp files were left behind and delete them to prevent errors when the same file has just been uploaded
---
.../Editors/ContentTypeController.cs | 27 ++++++++++++-------
.../Models/ContentTypeImportModel.cs | 5 +---
2 files changed, 18 insertions(+), 14 deletions(-)
diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs
index 9c248f186b..4e1417fb03 100644
--- a/src/Umbraco.Web/Editors/ContentTypeController.cs
+++ b/src/Umbraco.Web/Editors/ContentTypeController.cs
@@ -551,7 +551,6 @@ namespace Umbraco.Web.Editors
}
[HttpPost]
- [FileUploadCleanupFilter(false)]
public async Task Upload()
{
if (Request.Content.IsMimeMultipartContent() == false)
@@ -577,20 +576,28 @@ namespace Umbraco.Web.Editors
var fileName = file.Headers.ContentDisposition.FileName.Trim('\"');
var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower();
- // renaming the file because MultipartFormDataStreamProvider has created a random fileName instead of using the name from the
- // content-disposition for more than 6 years now. Creating a CustomMultipartDataStreamProvider deriving from MultipartFormDataStreamProvider
- // seems like a cleaner option, but I'm not sure where to put it and renaming only takes one line of code.
- System.IO.File.Move(result.FileData[0].LocalFileName, root + "\\" + fileName);
+ var destFileName = root + "\\" + fileName;
+ try
+ {
+ // due to a bug before 8.7.0 we didn't delete temp files, so we need to make sure to delete before
+ // moving else you get errors and the upload fails without a message in the UI (there's a JS error)
+ if(System.IO.File.Exists(destFileName))
+ System.IO.File.Delete(destFileName);
+
+ // renaming the file because MultipartFormDataStreamProvider has created a random fileName instead of using the name from the
+ // content-disposition for more than 6 years now. Creating a CustomMultipartDataStreamProvider deriving from MultipartFormDataStreamProvider
+ // seems like a cleaner option, but I'm not sure where to put it and renaming only takes one line of code.
+ System.IO.File.Move(result.FileData[0].LocalFileName, destFileName);
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(ex, "Error uploading udt file to App_Data: {File}", destFileName);
+ }
if (ext.InvariantEquals("udt"))
{
model.TempFileName = Path.Combine(root, fileName);
- model.UploadedFiles.Add(new ContentPropertyFile
- {
- TempFilePath = model.TempFileName
- });
-
var xd = new XmlDocument
{
XmlResolver = null
diff --git a/src/Umbraco.Web/Models/ContentTypeImportModel.cs b/src/Umbraco.Web/Models/ContentTypeImportModel.cs
index f6f9a5926d..fbe3e920f8 100644
--- a/src/Umbraco.Web/Models/ContentTypeImportModel.cs
+++ b/src/Umbraco.Web/Models/ContentTypeImportModel.cs
@@ -1,12 +1,11 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
-using Umbraco.Core.Models.Editors;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Models
{
[DataContract(Name = "contentTypeImportModel")]
- public class ContentTypeImportModel : INotificationModel, IHaveUploadedFiles
+ public class ContentTypeImportModel : INotificationModel
{
[DataMember(Name = "alias")]
public string Alias { get; set; }
@@ -19,7 +18,5 @@ namespace Umbraco.Web.Models
[DataMember(Name = "tempFileName")]
public string TempFileName { get; set; }
-
- public List UploadedFiles => new List();
}
}
From 35751e257fae1a4a89b697af962807d509b0dbad Mon Sep 17 00:00:00 2001
From: Bjarne Fyrstenborg
Date: Mon, 15 Jun 2020 11:48:22 +0200
Subject: [PATCH 10/42] Ensure SVG image is shown in preview (#8007)
Co-authored-by: Bjarne Fyrstenborg
---
.../src/less/components/umb-property-file-upload.less | 2 +-
src/Umbraco.Web.UI.Client/src/less/property-editors.less | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less
index 9550acfb1b..39b2f4002e 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less
@@ -1,7 +1,7 @@
.umb-property-file-upload {
+ .umb-property-editor--limit-width();
.umb-upload-button-big {
- max-width: (@propertyEditorLimitedWidth - 40);
display: block;
padding: 20px;
opacity: 1;
diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less
index b5870b8dce..a3bdbb0996 100644
--- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less
+++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less
@@ -724,6 +724,7 @@
// --------------------------------------------------
.umb-fileupload {
display: flex;
+ flex-direction: column;
}
.umb-fileupload .preview {
From 57d08c964a0196c4426a1b4b1a338ce79cc0aa5f Mon Sep 17 00:00:00 2001
From: Marc Goodson
Date: Wed, 20 May 2020 16:39:55 +0100
Subject: [PATCH 11/42] Broadcast 'formSubmitting' event when Macro Parameter
Overlay is submitted
Some property editors - eg MultiUrlPicker listen out for the broadcast of the 'formSubmitting' event before setting their value of $scope.model.value... if these editors are used as Macro Parameters they do not save their value. This commit adds the broadcasting of the event when the 'submit' button is called and any parameters have been set.
---
.../common/infiniteeditors/macropicker/macropicker.controller.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js
index ba577045aa..40338f2dca 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js
@@ -22,6 +22,7 @@ function MacroPickerController($scope, entityResource, macroResource, umbPropEdi
if ($scope.wizardStep === "macroSelect") {
editParams(true);
} else {
+ $scope.$broadcast("formSubmitting", { scope: $scope });
$scope.model.submit($scope.model);
}
};
From fe90f98515fca46c769a52da1947f10b562edf6b Mon Sep 17 00:00:00 2001
From: Bjarne Fyrstenborg
Date: Wed, 17 Jun 2020 12:56:41 +0200
Subject: [PATCH 12/42] Add id attribute so click on property label focus in
input (#8078)
Co-authored-by: Bjarne Fyrstenborg
---
.../src/views/propertyeditors/decimal/decimal.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/decimal/decimal.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/decimal/decimal.html
index d5560c8433..ac2fdabf3d 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/decimal/decimal.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/decimal/decimal.html
@@ -7,6 +7,7 @@
ng-model="model.value"
ng-required="model.validation.mandatory"
aria-required="{{model.validation.mandatory}}"
+ id="{{model.alias}}"
val-server="value"
fix-number min="{{model.config.min}}" max="{{model.config.max}}" step="{{model.config.step}}" />
From 4f70f0e8c56b126e67589105439c186f9c8f6618 Mon Sep 17 00:00:00 2001
From: Bjarne Fyrstenborg
Date: Thu, 18 Jun 2020 00:07:26 +0200
Subject: [PATCH 13/42] Use button element for add-button in macro partial view
---
.../src/views/macros/views/settings.html | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html b/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html
index 7706734327..eada93f497 100644
--- a/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html
+++ b/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html
@@ -21,15 +21,15 @@
on-edit="model.openViewPicker()"
on-remove="model.removeMacroView()">
+
-
+
+
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 @@
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 @@
| |