From 1279fd234fcc7972da99a8d42070d8857cda2377 Mon Sep 17 00:00:00 2001 From: Heather Floyd Date: Fri, 11 Sep 2015 19:43:41 -0400 Subject: [PATCH 01/72] Checking for possible Null Ref Exception in Textstring.cshtml grid renderer --- .../Views/Partials/Grid/Editors/TextString.cshtml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml index 0cac4eb1ff..285fc3c029 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml @@ -6,7 +6,11 @@ string markup = Model.editor.config.markup.ToString(); markup = markup.Replace("#value#", Model.value.ToString()); - markup = markup.Replace("#style#", Model.editor.config.style.ToString()); + + if (Model.editor.config.style != null) + { + markup = markup.Replace("#style#", Model.editor.config.style.ToString()); + } @Html.Raw(markup) From 9001cd8a6f2aaf530f1a3f7ea6d3498c67cc6356 Mon Sep 17 00:00:00 2001 From: bjarnef Date: Thu, 14 Apr 2016 23:50:43 +0200 Subject: [PATCH 02/72] Sort data types array by name A-Z --- .../umbraco/developer/Packages/editPackage.aspx.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs index ceae3d5659..043882af6c 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs @@ -162,6 +162,13 @@ namespace umbraco.presentation.developer.packages /*Data types */ cms.businesslogic.datatype.DataTypeDefinition[] umbDataType = cms.businesslogic.datatype.DataTypeDefinition.GetAll(); + + // sort array by name + Array.Sort(umbDataType, delegate(cms.businesslogic.datatype.DataTypeDefinition umbDataType1, cms.businesslogic.datatype.DataTypeDefinition umbDataType2) + { + return umbDataType1.Text.CompareTo(umbDataType2.Text); + }); + foreach (cms.businesslogic.datatype.DataTypeDefinition umbDtd in umbDataType) { From 50f8f185031593c2f89f6c98eb2abf347da5c4b5 Mon Sep 17 00:00:00 2001 From: Jesper Hauge Date: Mon, 20 Jun 2016 21:53:40 +0200 Subject: [PATCH 03/72] Fixes U4-8616 --- .../src/common/services/assets.service.js | 125 +++++++++++------- 1 file changed, 76 insertions(+), 49 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js index ff54c5c222..0d1ed30721 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js @@ -44,7 +44,8 @@ angular.module('umbraco.services') .factory('assetsService', function ($q, $log, angularHelper, umbRequestHelper, $rootScope, $http) { var initAssetsLoaded = false; - var appendRnd = function (url) { + + function appendRnd (url) { //if we don't have a global umbraco obj yet, the app is bootstrapping if (!Umbraco.Sys.ServerVariables.application) { return url; @@ -77,6 +78,7 @@ angular.module('umbraco.services') return this.loadedAssets[path]; } }, + /** Internal method. This is called when the application is loading and the user is already authenticated, or once the user is authenticated. There's a few assets the need to be loaded for the application to function but these assets require authentication to load. @@ -192,7 +194,7 @@ angular.module('umbraco.services') * @methodOf umbraco.services.assetsService * * @description - * Injects a collection of files, this can be ONLY js files + * Injects a collection of css and js files * * * @param {Array} pathArray string array of paths to the files to load @@ -206,62 +208,87 @@ angular.module('umbraco.services') throw "pathArray must be an array"; } + // Check to see if there's anything to load, resolve promise if not var nonEmpty = _.reject(pathArray, function (item) { return item === undefined || item === ""; }); - - - //don't load anything if there's nothing to load - if (nonEmpty.length > 0) { - var promises = []; - var assets = []; - - //compile a list of promises - //blocking - _.each(nonEmpty, function (path) { - - path = convertVirtualPath(path); - - var asset = service._getAssetPromise(path); - //if not previously loaded, add to list of promises - if (asset.state !== "loaded") { - if (asset.state === "new") { - asset.state = "loading"; - assets.push(asset); - } - - //we need to always push to the promises collection to monitor correct - //execution - promises.push(asset.deferred.promise); - } - }); - - - //gives a central monitoring of all assets to load - promise = $q.all(promises); - - _.each(assets, function (asset) { - LazyLoad.js(appendRnd(asset.path), function () { - asset.state = "loaded"; - if (!scope) { - asset.deferred.resolve(true); - } - else { - angularHelper.safeApply(scope, function () { - asset.deferred.resolve(true); - }); - } - }); - }); - } - else { - //return and resolve + if (nonEmpty.length === 0) { var deferred = $q.defer(); promise = deferred.promise; deferred.resolve(true); + return promise; } + //compile a list of promises + //blocking + var promises = []; + var assets = []; + _.each(nonEmpty, function (path) { + path = convertVirtualPath(path); + var asset = service._getAssetPromise(path); + //if not previously loaded, add to list of promises + if (asset.state !== "loaded") { + if (asset.state === "new") { + asset.state = "loading"; + assets.push(asset); + } + //we need to always push to the promises collection to monitor correct + //execution + promises.push(asset.deferred.promise); + } + }); + + //gives a central monitoring of all assets to load + promise = $q.all(promises); + + // Split into css and js asset arrays, and use LazyLoad on each array + var cssAssets = _.filter(assets, + function (asset) { + return asset.path.match(/(\.css$|\.css\?)/ig); + }); + var jsAssets = _.filter(assets, + function (asset) { + return asset.path.match(/(\.js$|\.js\?)/ig); + }); + + if (cssAssets.length > 0) { + var cssPaths = _.map(cssAssets, function (asset) { return appendRnd(asset.path) }); + LazyLoad.css(cssPaths, + function () { + _.each(cssAssets, + function (asset) { + asset.state = "loaded"; + if (!scope) { + asset.deferred.resolve(true); + return; + } + angularHelper.safeApply(scope, + function () { + asset.deferred.resolve(true); + }); + }); + }); + } + + if (jsAssets.length > 0) { + var jsPaths = _.map(jsAssets, function (asset) { return appendRnd(asset.path) }); + LazyLoad.js(jsPaths, + function () { + _.each(jsAssets, + function (asset) { + asset.state = "loaded"; + if (!scope) { + asset.deferred.resolve(true); + return; + } + angularHelper.safeApply(scope, + function () { + asset.deferred.resolve(true); + }); + }); + }); + } return promise; } }; From aac84516270f0679e678d69b69a654144097c8dc Mon Sep 17 00:00:00 2001 From: Lars-Erik Aabech Date: Sun, 11 Sep 2016 21:36:10 +0200 Subject: [PATCH 04/72] Removing HttpContext dependency from GridValueConverter. Adding configuration for IOHelper when testing to remove usage of HttpContext. --- src/Umbraco.Core/IO/IOHelper.cs | 6 +++++- .../PropertyEditors/ValueConverters/GridValueConverter.cs | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/IO/IOHelper.cs b/src/Umbraco.Core/IO/IOHelper.cs index 286acf0285..ef41c9210b 100644 --- a/src/Umbraco.Core/IO/IOHelper.cs +++ b/src/Umbraco.Core/IO/IOHelper.cs @@ -13,7 +13,9 @@ using Umbraco.Core.Logging; namespace Umbraco.Core.IO { public static class IOHelper - { + { + public static bool IAmUnitTestingSoNeverUseHttpContextEver = false; + private static string _rootDir = ""; // static compiled regex for faster performance @@ -97,6 +99,8 @@ namespace Umbraco.Core.IO public static string MapPath(string path, bool useHttpContext) { + useHttpContext = useHttpContext && IAmUnitTestingSoNeverUseHttpContextEver == false; + // Check if the path is already mapped if ((path.Length >= 2 && path[1] == Path.VolumeSeparatorChar) || path.StartsWith(@"\\")) //UNC Paths start with "\\". If the site is running off a network drive mapped paths will look like "\\Whatever\Boo\Bar" diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs index b3db026c89..3ec08b0c22 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs @@ -44,8 +44,8 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters var gridConfig = UmbracoConfig.For.GridConfig( ApplicationContext.Current.ProfilingLogger.Logger, ApplicationContext.Current.ApplicationCache.RuntimeCache, - new DirectoryInfo(HttpContext.Current.Server.MapPath(SystemDirectories.AppPlugins)), - new DirectoryInfo(HttpContext.Current.Server.MapPath(SystemDirectories.Config)), + new DirectoryInfo(IOHelper.MapPath(SystemDirectories.AppPlugins)), + new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Config)), HttpContext.Current.IsDebuggingEnabled); var sections = GetArray(obj, "sections"); From d382fcc9194df1d9a5a779ec948095645b42ba86 Mon Sep 17 00:00:00 2001 From: Lars-Erik Aabech Date: Tue, 13 Sep 2016 23:56:00 +0200 Subject: [PATCH 05/72] Issue with isHosted in TypeFinder when unit testing. --- src/Umbraco.Core/TypeFinder.cs | 4 +++- src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/TypeFinder.cs b/src/Umbraco.Core/TypeFinder.cs index f970cf225b..75b05fd2ba 100644 --- a/src/Umbraco.Core/TypeFinder.cs +++ b/src/Umbraco.Core/TypeFinder.cs @@ -26,6 +26,8 @@ namespace Umbraco.Core /// public static class TypeFinder { + public static bool IAmUnitTestingSoNeverUseHttpContextEver = false; + private static volatile HashSet _localFilteredAssemblyCache = null; private static readonly object LocalFilteredAssemblyCacheLocker = new object(); @@ -52,7 +54,7 @@ namespace Umbraco.Core HashSet assemblies = null; try { - var isHosted = HttpContext.Current != null; + var isHosted = HttpContext.Current != null && IAmUnitTestingSoNeverUseHttpContextEver == false; try { diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 28649457dd..28075632db 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -375,7 +375,7 @@ namespace Umbraco.Tests.TestHelpers return ctx; } - protected FakeHttpContextFactory GetHttpContextFactory(string url, RouteData routeData = null) + protected virtual FakeHttpContextFactory GetHttpContextFactory(string url, RouteData routeData = null) { var factory = routeData != null ? new FakeHttpContextFactory(url, routeData) From 361ef2a4750ad5d8226e2787d2d178ba97df39a5 Mon Sep 17 00:00:00 2001 From: Lars-Erik Aabech Date: Tue, 13 Sep 2016 23:57:31 +0200 Subject: [PATCH 06/72] Opening up GetHttpContextFactory for extending (actually in prev commit, but messed up) --- src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 28075632db..cbcdfeb848 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -381,7 +381,6 @@ namespace Umbraco.Tests.TestHelpers ? new FakeHttpContextFactory(url, routeData) : new FakeHttpContextFactory(url); - //set the state helper StateHelper.HttpContext = factory.HttpContext; From 88fd5a96237ce04f03aaab1f88127b11182448d4 Mon Sep 17 00:00:00 2001 From: Stefano Chiodino Date: Thu, 3 Nov 2016 11:40:20 +0000 Subject: [PATCH 07/72] Remove background-color on preview that was interfering with the website style --- src/Umbraco.Web.UI.Client/src/less/canvas-designer.less | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less index 52c4052975..e0527aefcb 100644 --- a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less +++ b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less @@ -21,7 +21,6 @@ body { font-size: 14px; line-height: 20px; color: #343434; - background-color: #F5F5F5; -webkit-transition: all 0.2s ease-in-out; -moz-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; From 4cab1c89b4aad264dfa7ff407dbddef8f07bffac Mon Sep 17 00:00:00 2001 From: Stefano Chiodino Date: Thu, 3 Nov 2016 16:02:10 +0000 Subject: [PATCH 08/72] U4-7721 Specify margin 0 for buttons On Safari input elemnts have 1px margin which use to make customise and install buttons not aligned --- src/Umbraco.Web.UI.Client/src/less/buttons.less | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/less/buttons.less b/src/Umbraco.Web.UI.Client/src/less/buttons.less index 84b96292c2..b699438d69 100644 --- a/src/Umbraco.Web.UI.Client/src/less/buttons.less +++ b/src/Umbraco.Web.UI.Client/src/less/buttons.less @@ -253,6 +253,9 @@ input[type="submit"].btn { *padding-top: 1px; *padding-bottom: 1px; } + + // Safari defaults to 1px for input. Ref U4-7721. + margin: 0px; } From 7cf8cd3518a95ea0b557e05a2bec38613e58d045 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Sat, 4 Mar 2017 18:19:44 +0100 Subject: [PATCH 09/72] Fix font size caret --- .../src/less/components/umb-grid.less | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less index 4b63bfb1a7..0a306255c5 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less @@ -649,9 +649,19 @@ clear: both; } -.umb-grid .mce-btn button { - padding: 8px 6px; - line-height: inherit; +.umb-grid .mce-btn { + button { + padding-top: 8px; + padding-bottom: 8px; + padding-left: 6px; + line-height: inherit; + } + + &:not(.mce-menubtn) { + button { + padding-right: 6px; + } + } } .umb-grid .mce-toolbar { From be1172d6042f1ef61b0dd3b80fac86091e269a1d Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Sat, 4 Mar 2017 18:20:03 +0100 Subject: [PATCH 10/72] Add icon for font select --- .../src/views/propertyeditors/rte/rte.prevalues.controller.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js index 702d19a509..65a62f599c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js @@ -76,6 +76,7 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController", icon.isCustom = false; break; case "styleselect": + case "fontsizeselect": icon.name = "icon-list"; icon.isCustom = true; break; From 7367c8ef674009ed771a33b217055a37ce690436 Mon Sep 17 00:00:00 2001 From: Nik Date: Sun, 30 Jul 2017 02:09:38 +0100 Subject: [PATCH 11/72] Re-do min max number of items in the grid after rebasing --- .../views/propertyeditors/grid/dialogs/rowconfig.html | 8 ++++++-- .../src/views/propertyeditors/grid/grid.controller.js | 11 +++++++++-- .../src/views/propertyeditors/grid/grid.html | 2 +- .../views/propertyeditors/grid/grid.prevalues.html | 4 +++- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 3 ++- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 2 ++ src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml | 2 ++ src/Umbraco.Web.UI/umbraco/config/lang/es.xml | 3 +++ src/Umbraco.Web.UI/umbraco/config/lang/nl.xml | 4 +++- 9 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.html index 421aaf0d40..381db5a38c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.html @@ -48,14 +48,18 @@ - {{currentCell.grid}} + {{currentCell.grid}} + + + + - + Delete 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 bb1da209bf..a282334cb5 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 @@ -81,6 +81,7 @@ angular.module("umbraco") var notIncludedRte = []; var cancelMove = false; + var startingArea; $scope.sortableOptionsCell = { distance: 10, @@ -112,9 +113,11 @@ angular.module("umbraco") }, over: function (event, ui) { - var allowedEditors = $(event.target).scope().area.allowed; + var area = $(event.target).scope().area; + var allowedEditors = area.allowed; - if ($.inArray(ui.item.scope().control.editor.alias, allowedEditors) < 0 && allowedEditors) { + if (($.inArray(ui.item.scope().control.editor.alias, allowedEditors) < 0 && allowedEditors) || + (startingArea != area && area.maxItems != '' && area.maxItems > 0 && area.maxItems < area.controls.length + 1)) { $scope.$apply(function () { $(event.target).scope().area.dropNotAllowed = true; @@ -168,6 +171,10 @@ angular.module("umbraco") start: function (e, ui) { + //Get the starting area for reference + var area = $(e.target).scope().area; + startingArea = area; + // fade out control when sorting ui.item.context.style.display = "block"; ui.item.context.style.opacity = "0.5"; 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 4ddca47489..c2513b9760 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 @@ -224,7 +224,7 @@ -
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.html index 06bdfe2e99..fa2da2f9e2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.html @@ -65,7 +65,9 @@ ng-repeat="area in layout.areas | filter:zeroWidthFilter" ng-style="{width: percentage(area.grid) + '%', 'max-width': '100%'}"> -
+
+

{{area.maxItems}}

+
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index c25f3ce562..3cf1b2f6a5 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -830,7 +830,8 @@ Mange hilsner fra Umbraco robotten Tillad alle editorer Tillad alle rækkekonfigurationer - + Maksimalt emner + Efterlad blank eller sat til 0 ubegrænset for Sæt som standard Vælg ekstra Vælg standard diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 52c0779733..9c8e4cfeb4 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -1248,6 +1248,8 @@ To manage your website, simply open the Umbraco back office and start adding con Allow all editors Allow all row configurations + Leave blank or set to 0 for unlimited + Maximum items Set as default Choose extra Choose default 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 f2ea5ad011..cca9621b81 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -1241,6 +1241,8 @@ To manage your website, simply open the Umbraco back office and start adding con Allow all editors Allow all row configurations + Maximum items + Leave blank or set to 0 for unlimited Set as default Choose extra Choose default diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/es.xml b/src/Umbraco.Web.UI/umbraco/config/lang/es.xml index ad0c3ecf30..3ccaee7061 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/es.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/es.xml @@ -790,6 +790,9 @@ Permitir todos los controles de edición Permitir todas las configuraciones de fila + Artículos máximos + Laat dit leeg of is ingesteld op -1 voor onbeperkt + Dejar en blanco o se establece en 0 para ilimitada Campo opcional diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml index 0d7faa9f14..3966803e24 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml @@ -1080,7 +1080,9 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Alle editors toelaten Alle rijconfiguraties toelaten - Instellen als standaard + Maximale artikelen + Laat dit leeg of is ingesteld op -1 voor onbeperkt + Instellen als standaard Kies extra Kies standaard zijn toegevoegd From 32d5a0a57c2076f15a4e4d177116ea2e74d1685a Mon Sep 17 00:00:00 2001 From: Bitmapped Date: Wed, 30 Aug 2017 20:04:30 -0400 Subject: [PATCH 12/72] Add method for configuring external logins so things can be granularly overriden. --- src/Umbraco.Web/UmbracoDefaultOwinStartup.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs index 5773d88f73..31159a91b2 100644 --- a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs +++ b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs @@ -28,6 +28,7 @@ namespace Umbraco.Web ConfigureServices(app); ConfigureMiddleware(app); + ConfigureExternalLogins(app); } /// @@ -68,6 +69,15 @@ namespace Umbraco.Web Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider()); } + /// + /// Configure external oAuth login providers + /// + /// + protected virtual void ConfigureExternalLogins(IAppBuilder app) + { + // Code for configuring external login/oAuth providers goes here. + } + /// /// Raised when the middleware has been configured /// From 65db5396cf325989cebab0ff677a84ee241ebe27 Mon Sep 17 00:00:00 2001 From: Bitmapped Date: Wed, 30 Aug 2017 20:06:20 -0400 Subject: [PATCH 13/72] Spelling fix --- src/Umbraco.Web/UmbracoDefaultOwinStartup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs index 31159a91b2..e78c82ccbd 100644 --- a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs +++ b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs @@ -70,12 +70,12 @@ namespace Umbraco.Web } /// - /// Configure external oAuth login providers + /// Configure external/OAuth login providers /// /// protected virtual void ConfigureExternalLogins(IAppBuilder app) { - // Code for configuring external login/oAuth providers goes here. + // Code for configuring external login/OAuth providers goes here. } /// From 606abd8ac7f32c9a7278fed58939827ccece94c0 Mon Sep 17 00:00:00 2001 From: Bitmapped Date: Wed, 6 Sep 2017 00:43:27 -0400 Subject: [PATCH 14/72] Restructure per discussion in U4-10369 --- src/Umbraco.Web/UmbracoDefaultOwinStartup.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs index e78c82ccbd..342b8daa16 100644 --- a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs +++ b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs @@ -28,7 +28,6 @@ namespace Umbraco.Web ConfigureServices(app); ConfigureMiddleware(app); - ConfigureExternalLogins(app); } /// @@ -47,12 +46,11 @@ namespace Umbraco.Web /// protected virtual void ConfigureMiddleware(IAppBuilder app) { - //Ensure owin is configured for Umbraco back office authentication. If you have any front-end OWIN - // cookie configuration, this must be declared after it. + + // Configure OWIN for authentication. + ConfigureUmbracoAuthentication(app); + app - .UseUmbracoBackOfficeCookieAuthentication(ApplicationContext, PipelineStage.Authenticate) - .UseUmbracoBackOfficeExternalCookieAuthentication(ApplicationContext, PipelineStage.Authenticate) - .UseUmbracoPreviewAuthentication(ApplicationContext, PipelineStage.Authorize) .UseSignalR() .FinalizeMiddlewareConfiguration(); } @@ -73,9 +71,14 @@ namespace Umbraco.Web /// Configure external/OAuth login providers /// /// - protected virtual void ConfigureExternalLogins(IAppBuilder app) + protected virtual void ConfigureUmbracoAuthentication(IAppBuilder app) { - // Code for configuring external login/OAuth providers goes here. + // Ensure owin is configured for Umbraco back office authentication. + // Front-end OWIN cookie configuration must be declared after this code. + app + .UseUmbracoBackOfficeCookieAuthentication(ApplicationContext, PipelineStage.Authenticate) + .UseUmbracoBackOfficeExternalCookieAuthentication(ApplicationContext, PipelineStage.Authenticate) + .UseUmbracoPreviewAuthentication(ApplicationContext, PipelineStage.Authorize); } /// From b363a1711a15dd5b126d373c87304b42b4a5829a Mon Sep 17 00:00:00 2001 From: erikarenhill Date: Thu, 28 Sep 2017 15:52:13 +0200 Subject: [PATCH 15/72] U4-10478: Bugfix where it would throw an IndexOutOfBounds when passing a word shorter than … to Truncate. Now checks whether the input wanted an elipsis and also if it reached the end. --- .../Web/Mvc/HtmlStringUtilitiesTests.cs | 35 +++++++++++++++++++ src/Umbraco.Web/HtmlStringUtilities.cs | 10 ++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs b/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs index c84d578d14..cdca8645f3 100644 --- a/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs @@ -22,5 +22,40 @@ namespace Umbraco.Tests.Web.Mvc var expected = "

hello world

hello world
hello world
hello world
hello world

"; Assert.AreEqual(expected, output); } + + [Test] + public void TruncateWithElipsis() + { + var output = _htmlStringUtilities.Truncate("hello world", 5, true, false).ToString(); + var expected = "hello…"; + Assert.AreEqual(expected, output); + } + + [Test] + public void TruncateWithoutElipsis() + { + var output = _htmlStringUtilities.Truncate("hello world", 5, false, false).ToString(); + var expected = "hello"; + Assert.AreEqual(expected, output); + } + + [Test] + public void TruncateShorterWordThanHellip() + { + //http://issues.umbraco.org/issue/U4-10478 + var output = _htmlStringUtilities.Truncate("hi", 5, true, false).ToString(); + var expected = "hi"; + Assert.AreEqual(expected, output); + } + + [Test] + public void TruncateAndRemoveSpaceBetweenHellipAndWord() + { + var output = _htmlStringUtilities.Truncate("hello world", 6 /* hello plus space */, true, false).ToString(); + var expected = "hello…"; + Assert.AreEqual(expected, output); + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Web/HtmlStringUtilities.cs b/src/Umbraco.Web/HtmlStringUtilities.cs index c4ae1c4eac..2caeb012bc 100644 --- a/src/Umbraco.Web/HtmlStringUtilities.cs +++ b/src/Umbraco.Web/HtmlStringUtilities.cs @@ -92,6 +92,8 @@ namespace Umbraco.Web using (var outputms = new MemoryStream()) { + bool lengthReached = false; + using (var outputtw = new StreamWriter(outputms)) { using (var ms = new MemoryStream()) @@ -106,7 +108,6 @@ namespace Umbraco.Web using (TextReader tr = new StreamReader(ms)) { bool isInsideElement = false, - lengthReached = false, insideTagSpaceEncountered = false, isTagClose = false; @@ -254,10 +255,15 @@ namespace Umbraco.Web //Check to see if there is an empty char between the hellip and the output string //if there is, remove it - if (string.IsNullOrWhiteSpace(firstTrim) == false) + if (addElipsis && lengthReached && string.IsNullOrWhiteSpace(firstTrim) == false) { result = firstTrim[firstTrim.Length - hellip.Length - 1] == ' ' ? firstTrim.Remove(firstTrim.Length - hellip.Length - 1, 1) : firstTrim; } + else + { + result = firstTrim; + } + return new HtmlString(result); } } From 581f15b1725334de6042dc6cc99603b81721d5ed Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Sun, 1 Oct 2017 12:05:40 +0200 Subject: [PATCH 16/72] Manually applying change from U4-10478 7.7.1 HtmlStringUtilities Truncate throws an IndexOutOfRange when input word is shorter than "…" and crash the entire site to 7.6.9 too. --- .../Web/Mvc/HtmlStringUtilitiesTests.cs | 33 +++++++++++++++++++ src/Umbraco.Web/HtmlStringUtilities.cs | 8 +++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs b/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs index c84d578d14..90c710dda7 100644 --- a/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs @@ -22,5 +22,38 @@ namespace Umbraco.Tests.Web.Mvc var expected = "

hello world

hello world
hello world
hello world
hello world

"; Assert.AreEqual(expected, output); } + + [Test] + public void TruncateWithElipsis() + { + var output = _htmlStringUtilities.Truncate("hello world", 5, true, false).ToString(); + var expected = "hello…"; + Assert.AreEqual(expected, output); + } + + [Test] + public void TruncateWithoutElipsis() + { + var output = _htmlStringUtilities.Truncate("hello world", 5, false, false).ToString(); + var expected = "hello"; + Assert.AreEqual(expected, output); + } + + [Test] + public void TruncateShorterWordThanHellip() + { + //http://issues.umbraco.org/issue/U4-10478 + var output = _htmlStringUtilities.Truncate("hi", 5, true, false).ToString(); + var expected = "hi"; + Assert.AreEqual(expected, output); + } + + [Test] + public void TruncateAndRemoveSpaceBetweenHellipAndWord() + { + var output = _htmlStringUtilities.Truncate("hello world", 6 /* hello plus space */, true, false).ToString(); + var expected = "hello…"; + Assert.AreEqual(expected, output); + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/HtmlStringUtilities.cs b/src/Umbraco.Web/HtmlStringUtilities.cs index c4ae1c4eac..32c11274d2 100644 --- a/src/Umbraco.Web/HtmlStringUtilities.cs +++ b/src/Umbraco.Web/HtmlStringUtilities.cs @@ -92,6 +92,7 @@ namespace Umbraco.Web using (var outputms = new MemoryStream()) { + bool lengthReached = false; using (var outputtw = new StreamWriter(outputms)) { using (var ms = new MemoryStream()) @@ -106,7 +107,6 @@ namespace Umbraco.Web using (TextReader tr = new StreamReader(ms)) { bool isInsideElement = false, - lengthReached = false, insideTagSpaceEncountered = false, isTagClose = false; @@ -254,10 +254,14 @@ namespace Umbraco.Web //Check to see if there is an empty char between the hellip and the output string //if there is, remove it - if (string.IsNullOrWhiteSpace(firstTrim) == false) + if (addElipsis && lengthReached && string.IsNullOrWhiteSpace(firstTrim) == false) { result = firstTrim[firstTrim.Length - hellip.Length - 1] == ' ' ? firstTrim.Remove(firstTrim.Length - hellip.Length - 1, 1) : firstTrim; } + else + { + result = firstTrim; + } return new HtmlString(result); } } From 6de3b920eb44e1f2ce49d8637c8ea13e30bce879 Mon Sep 17 00:00:00 2001 From: keroberts Date: Thu, 5 Oct 2017 14:04:02 +0100 Subject: [PATCH 17/72] Fix Typo in Email Property Editor --- .../src/views/propertyeditors/email/email.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html index 8455571c42..315a7f15f3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html @@ -8,6 +8,6 @@ val-server="value" /> Required - Invalid email + Invalid email From 7b3f7f4ad47528540143f7598b22d34ee5a1962d Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 6 Oct 2017 11:47:25 +1100 Subject: [PATCH 18/72] U4-10503 Umbraco plugins cache files should be stored in the same local temp location as the umbraco xml cache file --- .../Configuration/GlobalSettings.cs | 35 ++++++++++++++----- ...ntentXmlStorage.cs => LocalTempStorage.cs} | 2 +- src/Umbraco.Core/IO/SystemFiles.cs | 8 ++--- src/Umbraco.Core/PluginManager.cs | 22 ++++++++++-- src/Umbraco.Core/Umbraco.Core.csproj | 2 +- 5 files changed, 51 insertions(+), 18 deletions(-) rename src/Umbraco.Core/Configuration/{ContentXmlStorage.cs => LocalTempStorage.cs} (75%) diff --git a/src/Umbraco.Core/Configuration/GlobalSettings.cs b/src/Umbraco.Core/Configuration/GlobalSettings.cs index 02f3322ec9..395502d1b2 100644 --- a/src/Umbraco.Core/Configuration/GlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/GlobalSettings.cs @@ -538,24 +538,41 @@ namespace Umbraco.Core.Configuration internal static bool ContentCacheXmlStoredInCodeGen { - get { return ContentCacheXmlStorageLocation == ContentXmlStorage.AspNetTemp; } + get { return LocalTempStorageLocation == LocalTempStorage.AspNetTemp; } } - - internal static ContentXmlStorage ContentCacheXmlStorageLocation + + /// + /// This is the location type to store temporary files such as cache files or other localized files for a given machine + /// + /// + /// Currently used for the xml cache file and the plugin cache files + /// + internal static LocalTempStorage LocalTempStorageLocation { get - { + { + //there's a bunch of backwards compat config checks here.... + + //This is the current one + if (ConfigurationManager.AppSettings.ContainsKey("umbracoLocalTempStorage")) + { + return Enum.Parse(ConfigurationManager.AppSettings["umbracoLocalTempStorage"]); + } + + //This one is old if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLStorage")) { - return Enum.Parse(ConfigurationManager.AppSettings["umbracoContentXMLStorage"]); - } + return Enum.Parse(ConfigurationManager.AppSettings["umbracoContentXMLStorage"]); + } + + //This one is older if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLUseLocalTemp")) { return bool.Parse(ConfigurationManager.AppSettings["umbracoContentXMLUseLocalTemp"]) - ? ContentXmlStorage.AspNetTemp - : ContentXmlStorage.Default; + ? LocalTempStorage.AspNetTemp + : LocalTempStorage.Default; } - return ContentXmlStorage.Default; + return LocalTempStorage.Default; } } diff --git a/src/Umbraco.Core/Configuration/ContentXmlStorage.cs b/src/Umbraco.Core/Configuration/LocalTempStorage.cs similarity index 75% rename from src/Umbraco.Core/Configuration/ContentXmlStorage.cs rename to src/Umbraco.Core/Configuration/LocalTempStorage.cs index 7cbbc70675..d41f7d1925 100644 --- a/src/Umbraco.Core/Configuration/ContentXmlStorage.cs +++ b/src/Umbraco.Core/Configuration/LocalTempStorage.cs @@ -1,6 +1,6 @@ namespace Umbraco.Core.Configuration { - internal enum ContentXmlStorage + internal enum LocalTempStorage { Default, AspNetTemp, diff --git a/src/Umbraco.Core/IO/SystemFiles.cs b/src/Umbraco.Core/IO/SystemFiles.cs index 437ddd3ef7..412849d5b0 100644 --- a/src/Umbraco.Core/IO/SystemFiles.cs +++ b/src/Umbraco.Core/IO/SystemFiles.cs @@ -73,11 +73,11 @@ namespace Umbraco.Core.IO { get { - switch (GlobalSettings.ContentCacheXmlStorageLocation) + switch (GlobalSettings.LocalTempStorageLocation) { - case ContentXmlStorage.AspNetTemp: + case LocalTempStorage.AspNetTemp: return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco.config"); - case ContentXmlStorage.EnvironmentTemp: + case LocalTempStorage.EnvironmentTemp: var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1(); var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoXml", //include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back @@ -85,7 +85,7 @@ namespace Umbraco.Core.IO // utilizing an old path appDomainHash); return Path.Combine(cachePath, "umbraco.config"); - case ContentXmlStorage.Default: + case LocalTempStorage.Default: return IOHelper.ReturnPath("umbracoContentXML", "~/App_Data/umbraco.config"); default: throw new ArgumentOutOfRangeException(); diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index f90163d181..b6a72f74cf 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using System.Text; using System.Threading; +using System.Web; using System.Web.Compilation; using Umbraco.Core.Cache; using Umbraco.Core.IO; @@ -15,6 +16,7 @@ using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Profiling; using Umbraco.Core.PropertyEditors; using umbraco.interfaces; +using Umbraco.Core.Configuration; using File = System.IO.File; namespace Umbraco.Core @@ -426,9 +428,23 @@ namespace Umbraco.Core } private string GetPluginListFilePath() - { - var filename = "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list"; - return Path.Combine(_tempFolder, filename); + { + switch (GlobalSettings.LocalTempStorageLocation) + { + case LocalTempStorage.AspNetTemp: + return Path.Combine(HttpRuntime.CodegenDir, "umbraco-plugins.list"); + case LocalTempStorage.EnvironmentTemp: + var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1(); + var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoPlugins", + //include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back + // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not + // utilizing an old path + appDomainHash); + return Path.Combine(cachePath, "umbraco-plugins.list"); + case LocalTempStorage.Default: + default: + return Path.Combine(_tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list"); + } } private string GetPluginHashFilePath() diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index c4c1d58fdc..7de98685da 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -198,7 +198,7 @@ - + From bee75803f7932eec5ee295d969a2cde8a3aea51d Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 6 Oct 2017 11:51:27 +1100 Subject: [PATCH 19/72] Adds file exist checks --- src/Umbraco.Core/PluginManager.cs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index b6a72f74cf..9bcca8982a 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -82,21 +82,23 @@ namespace Umbraco.Core RequiresRescanning = (CachedAssembliesHash != CurrentAssembliesHash) || CachedAssembliesHash == string.Empty; //if they have changed, we need to write the new file if (RequiresRescanning) - { - // if the hash has changed, clear out the persisted list no matter what, this will force - // rescanning of all plugin types including lazy ones. - // http://issues.umbraco.org/issue/U4-4789 - File.Delete(pluginListFile); + { + // if the hash has changed, clear out the persisted list no matter what, this will force + // rescanning of all plugin types including lazy ones. + // http://issues.umbraco.org/issue/U4-4789 + if(File.Exists(pluginListFile)) + File.Delete(pluginListFile); WriteCachePluginsHash(); } } else - { - // if the hash has changed, clear out the persisted list no matter what, this will force - // rescanning of all plugin types including lazy ones. + { + // if the hash has changed, clear out the persisted list no matter what, this will force + // rescanning of all plugin types including lazy ones. // http://issues.umbraco.org/issue/U4-4789 - File.Delete(pluginListFile); + if (File.Exists(pluginListFile)) + File.Delete(pluginListFile); // always set to true if we're not detecting (generally only for testing) RequiresRescanning = true; From 901614711874e51ed7e27dc07c68c7550897ffdd Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 6 Oct 2017 12:11:20 +1100 Subject: [PATCH 20/72] Streamlines temp folder name so it's consistent, ensure the folder exists before writing to it --- src/Umbraco.Core/IO/SystemFiles.cs | 8 +++--- src/Umbraco.Core/PluginManager.cs | 43 +++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Core/IO/SystemFiles.cs b/src/Umbraco.Core/IO/SystemFiles.cs index 412849d5b0..ccf8ea5b2c 100644 --- a/src/Umbraco.Core/IO/SystemFiles.cs +++ b/src/Umbraco.Core/IO/SystemFiles.cs @@ -79,10 +79,10 @@ namespace Umbraco.Core.IO return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco.config"); case LocalTempStorage.EnvironmentTemp: var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1(); - var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoXml", - //include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back - // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not - // utilizing an old path + var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", + //include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back + // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not + // utilizing an old path appDomainHash); return Path.Combine(cachePath, "umbraco.config"); case LocalTempStorage.Default: diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index 9bcca8982a..0489093712 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -235,6 +235,14 @@ namespace Umbraco.Core private void WriteCachePluginsHash() { var filePath = GetPluginHashFilePath(); + + // be absolutely sure the folder exists + var folder = Path.GetDirectoryName(filePath); + if (folder == null) + throw new InvalidOperationException("The folder could not be determined for the file " + filePath); + if (Directory.Exists(folder) == false) + Directory.CreateDirectory(folder); + File.WriteAllText(filePath, CurrentAssembliesHash.ToString(), Encoding.UTF8); } @@ -434,10 +442,10 @@ namespace Umbraco.Core switch (GlobalSettings.LocalTempStorageLocation) { case LocalTempStorage.AspNetTemp: - return Path.Combine(HttpRuntime.CodegenDir, "umbraco-plugins.list"); + return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.list"); case LocalTempStorage.EnvironmentTemp: var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1(); - var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoPlugins", + var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", //include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not // utilizing an old path @@ -451,18 +459,35 @@ namespace Umbraco.Core private string GetPluginHashFilePath() { - var filename = "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash"; - return Path.Combine(_tempFolder, filename); + switch (GlobalSettings.LocalTempStorageLocation) + { + case LocalTempStorage.AspNetTemp: + return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.hash"); + case LocalTempStorage.EnvironmentTemp: + var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1(); + var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", + //include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back + // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not + // utilizing an old path + appDomainHash); + return Path.Combine(cachePath, "umbraco-plugins.hash"); + case LocalTempStorage.Default: + default: + return Path.Combine(_tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash"); + } } internal void WriteCache() { - // be absolutely sure - if (Directory.Exists(_tempFolder) == false) - Directory.CreateDirectory(_tempFolder); - - var filePath = GetPluginListFilePath(); + var filePath = GetPluginListFilePath(); + // be absolutely sure the folder exists + var folder = Path.GetDirectoryName(filePath); + if (folder == null) + throw new InvalidOperationException("The folder could not be determined for the file " + filePath); + if (Directory.Exists(folder) == false) + Directory.CreateDirectory(folder); + using (var stream = GetFileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout)) using (var writer = new StreamWriter(stream)) { From fe2b86b681455ac975b294652064b2718d4e2ba2 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 6 Oct 2017 14:38:07 +0200 Subject: [PATCH 21/72] Html encode nodenames to prevent XSS attacks. Fixes U4-10497 XSS Vulnerability in page name. --- src/Umbraco.Web.UI/umbraco/dialogs/Publish.aspx.cs | 2 +- .../umbraco.presentation/umbraco/dialogs/notifications.aspx.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/dialogs/Publish.aspx.cs b/src/Umbraco.Web.UI/umbraco/dialogs/Publish.aspx.cs index 7e09d0b425..ababea628a 100644 --- a/src/Umbraco.Web.UI/umbraco/dialogs/Publish.aspx.cs +++ b/src/Umbraco.Web.UI/umbraco/dialogs/Publish.aspx.cs @@ -30,7 +30,7 @@ namespace Umbraco.Web.UI.Umbraco.Dialogs } DocumentId = doc.Id; - PageName = doc.Name; + PageName = Server.HtmlEncode(doc.Name); DocumentPath = doc.Path; } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs index 70137da920..195ac15ec8 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs @@ -27,7 +27,7 @@ namespace umbraco.dialogs protected void Page_Load(object sender, EventArgs e) { Button1.Text = ui.Text("update"); - pane_form.Text = ui.Text("notifications", "editNotifications", node.Text, base.getUser()); + pane_form.Text = ui.Text("notifications", "editNotifications", Server.HtmlEncode(node.Text), base.getUser()); } #region Web Form Designer generated code From 5dde2efe0d2b3a47d17439e03acabb7ea2befb64 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 6 Oct 2017 15:14:41 +0200 Subject: [PATCH 22/72] U4-10506 Importing a specially crafted document type file can cause XXE attack --- .../umbraco/dialogs/importDocumenttype.aspx.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs index 27c1724bff..147e7604c1 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs @@ -70,10 +70,11 @@ namespace umbraco.presentation.umbraco.dialogs private void import_Click(object sender, EventArgs e) { var xd = new XmlDocument(); + xd.XmlResolver = null; xd.Load(tempFile.Value); var userId = base.getUser().Id; - + var element = XElement.Parse(xd.InnerXml); var importContentTypes = ApplicationContext.Current.Services.PackagingService.ImportContentTypes(element, userId); var contentType = importContentTypes.FirstOrDefault(); @@ -104,7 +105,8 @@ namespace umbraco.presentation.umbraco.dialogs documentTypeFile.PostedFile.SaveAs(fileName); var xd = new XmlDocument(); - xd.Load(fileName); + xd.XmlResolver = null; + xd.Load(fileName); dtName.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Name").FirstChild.Value; dtAlias.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Alias").FirstChild.Value; From 04e04ae47a37da86724143a47491850e8caeaded Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Oct 2017 14:40:23 +1100 Subject: [PATCH 23/72] Ensures that the local folder does not also get created when using the env temp storage location, this is also a tiny bit better for perfs --- src/Umbraco.Core/PluginManager.cs | 82 +++++++++++++++++++------------ 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index 0489093712..8aa87f0ef8 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -41,7 +41,8 @@ namespace Umbraco.Core private readonly IServiceProvider _serviceProvider; private readonly IRuntimeCacheProvider _runtimeCache; private readonly ProfilingLogger _logger; - private readonly string _tempFolder; + private string _pluginListFilePath; + private string _pluginHashFilePath; private readonly object _typesLock = new object(); private readonly Dictionary _types = new Dictionary(); @@ -67,12 +68,7 @@ namespace Umbraco.Core _serviceProvider = serviceProvider; _runtimeCache = runtimeCache; _logger = logger; - - // the temp folder where the cache file lives - _tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache"); - if (Directory.Exists(_tempFolder) == false) - Directory.CreateDirectory(_tempFolder); - + var pluginListFile = GetPluginListFilePath(); if (detectChanges) @@ -234,16 +230,8 @@ namespace Umbraco.Core ///
private void WriteCachePluginsHash() { - var filePath = GetPluginHashFilePath(); - - // be absolutely sure the folder exists - var folder = Path.GetDirectoryName(filePath); - if (folder == null) - throw new InvalidOperationException("The folder could not be determined for the file " + filePath); - if (Directory.Exists(folder) == false) - Directory.CreateDirectory(folder); - - File.WriteAllText(filePath, CurrentAssembliesHash.ToString(), Encoding.UTF8); + var filePath = GetPluginHashFilePath(); + File.WriteAllText(filePath, CurrentAssembliesHash, Encoding.UTF8); } /// @@ -438,11 +426,18 @@ namespace Umbraco.Core } private string GetPluginListFilePath() - { + { + //if it's already set then return it - we don't care about locking here + //if 2 threads do this at the same time it won't hurt + if (_pluginListFilePath != null) + return _pluginListFilePath; + + string pluginListFilePath; switch (GlobalSettings.LocalTempStorageLocation) { case LocalTempStorage.AspNetTemp: - return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.list"); + pluginListFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.list"); + break; case LocalTempStorage.EnvironmentTemp: var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1(); var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", @@ -450,19 +445,39 @@ namespace Umbraco.Core // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not // utilizing an old path appDomainHash); - return Path.Combine(cachePath, "umbraco-plugins.list"); + pluginListFilePath = Path.Combine(cachePath, "umbraco-plugins.list"); + break; case LocalTempStorage.Default: default: - return Path.Combine(_tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list"); + var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache"); + pluginListFilePath = Path.Combine(tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list"); + break; } + + //ensure the folder exists + var folder = Path.GetDirectoryName(pluginListFilePath); + if (folder == null) + throw new InvalidOperationException("The folder could not be determined for the file " + pluginListFilePath); + if (Directory.Exists(folder) == false) + Directory.CreateDirectory(folder); + + _pluginListFilePath = pluginListFilePath; + return _pluginListFilePath; } private string GetPluginHashFilePath() { + //if it's already set then return it - we don't care about locking here + //if 2 threads do this at the same time it won't hurt + if (_pluginHashFilePath != null) + return _pluginHashFilePath; + + string pluginHashFilePath; switch (GlobalSettings.LocalTempStorageLocation) { case LocalTempStorage.AspNetTemp: - return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.hash"); + pluginHashFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.hash"); + break; case LocalTempStorage.EnvironmentTemp: var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1(); var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", @@ -470,23 +485,28 @@ namespace Umbraco.Core // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not // utilizing an old path appDomainHash); - return Path.Combine(cachePath, "umbraco-plugins.hash"); + pluginHashFilePath = Path.Combine(cachePath, "umbraco-plugins.hash"); + break; case LocalTempStorage.Default: default: - return Path.Combine(_tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash"); + pluginHashFilePath = Path.Combine(_pluginListFilePath, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash"); + break; } + + //ensure the folder exists + var folder = Path.GetDirectoryName(pluginHashFilePath); + if (folder == null) + throw new InvalidOperationException("The folder could not be determined for the file " + pluginHashFilePath); + if (Directory.Exists(folder) == false) + Directory.CreateDirectory(folder); + + _pluginHashFilePath = pluginHashFilePath; + return _pluginHashFilePath; } internal void WriteCache() { var filePath = GetPluginListFilePath(); - - // be absolutely sure the folder exists - var folder = Path.GetDirectoryName(filePath); - if (folder == null) - throw new InvalidOperationException("The folder could not be determined for the file " + filePath); - if (Directory.Exists(folder) == false) - Directory.CreateDirectory(folder); using (var stream = GetFileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout)) using (var writer = new StreamWriter(stream)) From ce814163686d8e4736616cabb58c07371628c0ce Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Oct 2017 14:50:34 +1100 Subject: [PATCH 24/72] Ensures the DistCache file is stored in the same configured temp location as the plugin cache files --- src/Umbraco.Core/PluginManager.cs | 3 +- .../Sync/DatabaseServerMessenger.cs | 51 ++++++++++++++++--- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index 8aa87f0ef8..e31e5752c1 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -489,7 +489,8 @@ namespace Umbraco.Core break; case LocalTempStorage.Default: default: - pluginHashFilePath = Path.Combine(_pluginListFilePath, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash"); + var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache"); + pluginHashFilePath = Path.Combine(tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash"); break; } diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index 12cb465b50..98c57e67df 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -14,6 +14,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using umbraco.interfaces; +using Umbraco.Core.Configuration; using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Sync @@ -39,6 +40,7 @@ namespace Umbraco.Core.Sync private bool _syncing; private bool _released; private readonly ProfilingLogger _profilingLogger; + private string _distCacheFilePath; protected DatabaseServerMessengerOptions Options { get; private set; } protected ApplicationContext ApplicationContext { get { return _appContext; } } @@ -502,16 +504,51 @@ namespace Umbraco.Core.Sync /// Gets the sync file path for the local server. /// /// The sync file path for the local server. - private static string SyncFilePath + private string SyncFilePath { - get - { - var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/DistCache/" + NetworkHelper.FileSafeMachineName); - if (Directory.Exists(tempFolder) == false) - Directory.CreateDirectory(tempFolder); + get { return GetDistCacheFilePath(); } + } - return Path.Combine(tempFolder, HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt"); + private string GetDistCacheFilePath() + { + //if it's already set then return it - we don't care about locking here + //if 2 threads do this at the same time it won't hurt + if (_distCacheFilePath != null) + return _distCacheFilePath; + + var fileName = HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt"; + + string distCacheFilePath; + switch (GlobalSettings.LocalTempStorageLocation) + { + case LocalTempStorage.AspNetTemp: + distCacheFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData", fileName); + break; + case LocalTempStorage.EnvironmentTemp: + var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1(); + var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", + //include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back + // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not + // utilizing an old path + appDomainHash); + distCacheFilePath = Path.Combine(cachePath, fileName); + break; + case LocalTempStorage.Default: + default: + var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/DistCache"); + distCacheFilePath = Path.Combine(tempFolder, fileName); + break; } + + //ensure the folder exists + var folder = Path.GetDirectoryName(distCacheFilePath); + if (folder == null) + throw new InvalidOperationException("The folder could not be determined for the file " + distCacheFilePath); + if (Directory.Exists(folder) == false) + Directory.CreateDirectory(folder); + + _distCacheFilePath = distCacheFilePath; + return _distCacheFilePath; } #endregion From 422219ab21bfaa627ef6ec779cdf4bbfe4911344 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Oct 2017 16:01:22 +1100 Subject: [PATCH 25/72] Updates the cache file paths to be resolved behind a lazy so that there's proper locking in place for accessing the value, this also makes the code a little nicer. --- src/Umbraco.Core/PluginManager.cs | 78 +++++++------------ .../Sync/DatabaseServerMessenger.cs | 28 ++----- 2 files changed, 34 insertions(+), 72 deletions(-) diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index e31e5752c1..aafbc92b8b 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -41,8 +41,8 @@ namespace Umbraco.Core private readonly IServiceProvider _serviceProvider; private readonly IRuntimeCacheProvider _runtimeCache; private readonly ProfilingLogger _logger; - private string _pluginListFilePath; - private string _pluginHashFilePath; + private readonly Lazy _pluginListFilePath = new Lazy(GetPluginListFilePath); + private readonly Lazy _pluginHashFilePath = new Lazy(GetPluginHashFilePath); private readonly object _typesLock = new object(); private readonly Dictionary _types = new Dictionary(); @@ -69,8 +69,6 @@ namespace Umbraco.Core _runtimeCache = runtimeCache; _logger = logger; - var pluginListFile = GetPluginListFilePath(); - if (detectChanges) { //first check if the cached hash is string.Empty, if it is then we need @@ -82,8 +80,8 @@ namespace Umbraco.Core // if the hash has changed, clear out the persisted list no matter what, this will force // rescanning of all plugin types including lazy ones. // http://issues.umbraco.org/issue/U4-4789 - if(File.Exists(pluginListFile)) - File.Delete(pluginListFile); + if(File.Exists(_pluginListFilePath.Value)) + File.Delete(_pluginListFilePath.Value); WriteCachePluginsHash(); } @@ -93,8 +91,8 @@ namespace Umbraco.Core // if the hash has changed, clear out the persisted list no matter what, this will force // rescanning of all plugin types including lazy ones. // http://issues.umbraco.org/issue/U4-4789 - if (File.Exists(pluginListFile)) - File.Delete(pluginListFile); + if (File.Exists(_pluginListFilePath.Value)) + File.Delete(_pluginListFilePath.Value); // always set to true if we're not detecting (generally only for testing) RequiresRescanning = true; @@ -187,11 +185,10 @@ namespace Umbraco.Core { if (_cachedAssembliesHash != null) return _cachedAssembliesHash; + + if (File.Exists(_pluginHashFilePath.Value) == false) return string.Empty; - var filePath = GetPluginHashFilePath(); - if (File.Exists(filePath) == false) return string.Empty; - - var hash = File.ReadAllText(filePath, Encoding.UTF8); + var hash = File.ReadAllText(_pluginHashFilePath.Value, Encoding.UTF8); _cachedAssembliesHash = hash; return _cachedAssembliesHash; @@ -229,9 +226,8 @@ namespace Umbraco.Core /// Writes the assembly hash file. ///
private void WriteCachePluginsHash() - { - var filePath = GetPluginHashFilePath(); - File.WriteAllText(filePath, CurrentAssembliesHash, Encoding.UTF8); + { + File.WriteAllText(_pluginHashFilePath.Value, CurrentAssembliesHash, Encoding.UTF8); } /// @@ -351,8 +347,7 @@ namespace Umbraco.Core { try { - var filePath = GetPluginListFilePath(); - File.Delete(filePath); + File.Delete(_pluginListFilePath.Value); } catch { @@ -366,12 +361,11 @@ namespace Umbraco.Core internal Dictionary, IEnumerable> ReadCache() { var cache = new Dictionary, IEnumerable>(); - - var filePath = GetPluginListFilePath(); - if (File.Exists(filePath) == false) + + if (File.Exists(_pluginListFilePath.Value) == false) return cache; - using (var stream = GetFileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout)) + using (var stream = GetFileStream(_pluginListFilePath.Value, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout)) using (var reader = new StreamReader(stream)) { while (true) @@ -414,24 +408,17 @@ namespace Umbraco.Core /// Generally only used for resetting cache, for example during the install process. public void ClearPluginCache() { - var path = GetPluginListFilePath(); - if (File.Exists(path)) - File.Delete(path); - - path = GetPluginHashFilePath(); - if (File.Exists(path)) - File.Delete(path); + if (File.Exists(_pluginListFilePath.Value)) + File.Delete(_pluginListFilePath.Value); + + if (File.Exists(_pluginHashFilePath.Value)) + File.Delete(_pluginHashFilePath.Value); _runtimeCache.ClearCacheItem(CacheKey); } - private string GetPluginListFilePath() - { - //if it's already set then return it - we don't care about locking here - //if 2 threads do this at the same time it won't hurt - if (_pluginListFilePath != null) - return _pluginListFilePath; - + private static string GetPluginListFilePath() + { string pluginListFilePath; switch (GlobalSettings.LocalTempStorageLocation) { @@ -461,17 +448,11 @@ namespace Umbraco.Core if (Directory.Exists(folder) == false) Directory.CreateDirectory(folder); - _pluginListFilePath = pluginListFilePath; - return _pluginListFilePath; + return pluginListFilePath; } - private string GetPluginHashFilePath() + private static string GetPluginHashFilePath() { - //if it's already set then return it - we don't care about locking here - //if 2 threads do this at the same time it won't hurt - if (_pluginHashFilePath != null) - return _pluginHashFilePath; - string pluginHashFilePath; switch (GlobalSettings.LocalTempStorageLocation) { @@ -500,16 +481,13 @@ namespace Umbraco.Core throw new InvalidOperationException("The folder could not be determined for the file " + pluginHashFilePath); if (Directory.Exists(folder) == false) Directory.CreateDirectory(folder); - - _pluginHashFilePath = pluginHashFilePath; - return _pluginHashFilePath; + + return pluginHashFilePath; } internal void WriteCache() { - var filePath = GetPluginListFilePath(); - - using (var stream = GetFileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout)) + using (var stream = GetFileStream(_pluginListFilePath.Value, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout)) using (var writer = new StreamWriter(stream)) { foreach (var typeList in _types.Values) @@ -755,7 +733,7 @@ namespace Umbraco.Core // else proceed, typeList = new TypeList(baseType, attributeType); - var scan = RequiresRescanning || File.Exists(GetPluginListFilePath()) == false; + var scan = RequiresRescanning || File.Exists(_pluginListFilePath.Value) == false; if (scan) { diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index 98c57e67df..8d0fb54b1e 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -40,7 +40,7 @@ namespace Umbraco.Core.Sync private bool _syncing; private bool _released; private readonly ProfilingLogger _profilingLogger; - private string _distCacheFilePath; + private Lazy _distCacheFilePath = new Lazy(GetDistCacheFilePath); protected DatabaseServerMessengerOptions Options { get; private set; } protected ApplicationContext ApplicationContext { get { return _appContext; } } @@ -462,10 +462,9 @@ namespace Umbraco.Core.Sync /// private void ReadLastSynced() { - var path = SyncFilePath; - if (File.Exists(path) == false) return; + if (File.Exists(_distCacheFilePath.Value) == false) return; - var content = File.ReadAllText(path); + var content = File.ReadAllText(_distCacheFilePath.Value); int last; if (int.TryParse(content, out last)) _lastId = last; @@ -480,7 +479,7 @@ namespace Umbraco.Core.Sync /// private void SaveLastSynced(int id) { - File.WriteAllText(SyncFilePath, id.ToString(CultureInfo.InvariantCulture)); + File.WriteAllText(_distCacheFilePath.Value, id.ToString(CultureInfo.InvariantCulture)); _lastId = id; } @@ -500,22 +499,8 @@ namespace Umbraco.Core.Sync + "/D" + AppDomain.CurrentDomain.Id // eg 22 + "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique - /// - /// Gets the sync file path for the local server. - /// - /// The sync file path for the local server. - private string SyncFilePath + private static string GetDistCacheFilePath() { - get { return GetDistCacheFilePath(); } - } - - private string GetDistCacheFilePath() - { - //if it's already set then return it - we don't care about locking here - //if 2 threads do this at the same time it won't hurt - if (_distCacheFilePath != null) - return _distCacheFilePath; - var fileName = HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt"; string distCacheFilePath; @@ -547,8 +532,7 @@ namespace Umbraco.Core.Sync if (Directory.Exists(folder) == false) Directory.CreateDirectory(folder); - _distCacheFilePath = distCacheFilePath; - return _distCacheFilePath; + return distCacheFilePath; } #endregion From 7f44f4a0300eb5f872ff6c4c0a7761d95f6f0d3a Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Oct 2017 16:01:49 +1100 Subject: [PATCH 26/72] readonly field --- src/Umbraco.Core/Sync/DatabaseServerMessenger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index 8d0fb54b1e..6f1fc03281 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -40,7 +40,7 @@ namespace Umbraco.Core.Sync private bool _syncing; private bool _released; private readonly ProfilingLogger _profilingLogger; - private Lazy _distCacheFilePath = new Lazy(GetDistCacheFilePath); + private readonly Lazy _distCacheFilePath = new Lazy(GetDistCacheFilePath); protected DatabaseServerMessengerOptions Options { get; private set; } protected ApplicationContext ApplicationContext { get { return _appContext; } } From de17b6956403ab3ccf9eca05c4055f449f3052ab Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Oct 2017 16:07:55 +1100 Subject: [PATCH 27/72] U4-10452 Unable to create new User Groups programmatically - publicizes UserGroup --- src/Umbraco.Core/Models/Membership/UserGroup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Models/Membership/UserGroup.cs b/src/Umbraco.Core/Models/Membership/UserGroup.cs index 012e9b6cc2..7042c63ec1 100644 --- a/src/Umbraco.Core/Models/Membership/UserGroup.cs +++ b/src/Umbraco.Core/Models/Membership/UserGroup.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Models.Membership /// [Serializable] [DataContract(IsReference = true)] - internal class UserGroup : Entity, IUserGroup, IReadOnlyUserGroup + public class UserGroup : Entity, IUserGroup, IReadOnlyUserGroup { private int? _startContentId; private int? _startMediaId; From fbda4fc8d175cd7549a76007c704bec21d37582a Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Oct 2017 17:49:01 +1100 Subject: [PATCH 28/72] Fixes part of the failing tests --- src/Umbraco.Tests/Models/ContentTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index 4244188697..aa9fc35524 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -20,7 +20,7 @@ using Umbraco.Tests.TestHelpers.Entities; namespace Umbraco.Tests.Models { [TestFixture] - public class ContentTests : BaseUmbracoConfigurationTest + public class ContentTests : BaseUmbracoApplicationTest { [SetUp] public void Init() From 4b08c3a5c371a28cff2a0c451a9ac7c89cd365dc Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Oct 2017 18:20:07 +1100 Subject: [PATCH 29/72] Reset PluginManager on each tests, no reason not to, this should resolve the issue --- src/Umbraco.Core/PluginManager.cs | 35 +++++++------- .../TestHelpers/BaseUmbracoApplicationTest.cs | 48 +++++++------------ 2 files changed, 33 insertions(+), 50 deletions(-) diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index aafbc92b8b..aa318de287 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -76,22 +76,22 @@ namespace Umbraco.Core RequiresRescanning = (CachedAssembliesHash != CurrentAssembliesHash) || CachedAssembliesHash == string.Empty; //if they have changed, we need to write the new file if (RequiresRescanning) - { - // if the hash has changed, clear out the persisted list no matter what, this will force - // rescanning of all plugin types including lazy ones. - // http://issues.umbraco.org/issue/U4-4789 - if(File.Exists(_pluginListFilePath.Value)) + { + // if the hash has changed, clear out the persisted list no matter what, this will force + // rescanning of all plugin types including lazy ones. + // http://issues.umbraco.org/issue/U4-4789 + if(File.Exists(_pluginListFilePath.Value)) File.Delete(_pluginListFilePath.Value); WriteCachePluginsHash(); } } else - { - // if the hash has changed, clear out the persisted list no matter what, this will force - // rescanning of all plugin types including lazy ones. + { + // if the hash has changed, clear out the persisted list no matter what, this will force + // rescanning of all plugin types including lazy ones. // http://issues.umbraco.org/issue/U4-4789 - if (File.Exists(_pluginListFilePath.Value)) + if (File.Exists(_pluginListFilePath.Value)) File.Delete(_pluginListFilePath.Value); // always set to true if we're not detecting (generally only for testing) @@ -270,7 +270,7 @@ namespace Umbraco.Core } } } - return generator.GenerateHash(); + return generator.GenerateHash(); } } } @@ -310,7 +310,7 @@ namespace Umbraco.Core uniqInfos.Add(fileOrFolder.FullName); generator.AddFileSystemItem(fileOrFolder); } - return generator.GenerateHash(); + return generator.GenerateHash(); } } } @@ -343,7 +343,7 @@ namespace Umbraco.Core { return ReadCache(); } - catch + catch (Exception ex) { try { @@ -440,7 +440,7 @@ namespace Umbraco.Core pluginListFilePath = Path.Combine(tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list"); break; } - + //ensure the folder exists var folder = Path.GetDirectoryName(pluginListFilePath); if (folder == null) @@ -503,8 +503,7 @@ namespace Umbraco.Core internal void UpdateCache() { - // note - // at the moment we write the cache to disk every time we update it. ideally we defer the writing + // TODO: at the moment we write the cache to disk every time we update it. ideally we defer the writing // since all the updates are going to happen in a row when Umbraco starts. that being said, the // file is small enough, so it is not a priority. WriteCache(); @@ -517,13 +516,13 @@ namespace Umbraco.Core while (true) { try - { + { return new FileStream(path, fileMode, fileAccess, fileShare); } - catch + catch (Exception ex) { if (--attempts == 0) - throw; + throw; LogHelper.Debug(string.Format("Attempted to get filestream for file {0} failed, {1} attempts left, pausing for {2} milliseconds", path, attempts, pauseMilliseconds)); Thread.Sleep(pauseMilliseconds); diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index 49895ed453..6444638928 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -118,25 +118,12 @@ namespace Umbraco.Tests.TestHelpers }); } - /// - /// By default this returns false which means the plugin manager will not be reset so it doesn't need to re-scan - /// all of the assemblies. Inheritors can override this if plugin manager resetting is required, generally needs - /// to be set to true if the SetupPluginManager has been overridden. - /// - protected virtual bool PluginManagerResetRequired - { - get { return false; } - } - /// /// Inheritors can resset the plugin manager if they choose to on teardown /// protected virtual void ResetPluginManager() { - if (PluginManagerResetRequired) - { - PluginManager.Current = null; - } + PluginManager.Current = null; } protected virtual CacheHelper CreateCacheHelper() @@ -185,26 +172,23 @@ namespace Umbraco.Tests.TestHelpers ///
protected virtual void SetupPluginManager() { - if (PluginManager.Current == null || PluginManagerResetRequired) + PluginManager.Current = new PluginManager( + new ActivatorServiceProvider(), + CacheHelper.RuntimeCache, ProfilingLogger, false) { - PluginManager.Current = new PluginManager( - new ActivatorServiceProvider(), - CacheHelper.RuntimeCache, ProfilingLogger, false) + AssembliesToScan = new[] { - AssembliesToScan = new[] - { - Assembly.Load("Umbraco.Core"), - Assembly.Load("umbraco"), - Assembly.Load("Umbraco.Tests"), - Assembly.Load("businesslogic"), - Assembly.Load("cms"), - Assembly.Load("controls"), - Assembly.Load("umbraco.editorControls"), - Assembly.Load("umbraco.MacroEngines"), - Assembly.Load("umbraco.providers"), - } - }; - } + Assembly.Load("Umbraco.Core"), + Assembly.Load("umbraco"), + Assembly.Load("Umbraco.Tests"), + Assembly.Load("businesslogic"), + Assembly.Load("cms"), + Assembly.Load("controls"), + Assembly.Load("umbraco.editorControls"), + Assembly.Load("umbraco.MacroEngines"), + Assembly.Load("umbraco.providers"), + } + }; } ///
From cf97926f26ebc65689c0ba6d93cbee89eeda8665 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Oct 2017 19:00:00 +1100 Subject: [PATCH 30/72] trying another test fix --- src/Umbraco.Tests/UdiTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/UdiTests.cs b/src/Umbraco.Tests/UdiTests.cs index 75f4536fd4..82608a5513 100644 --- a/src/Umbraco.Tests/UdiTests.cs +++ b/src/Umbraco.Tests/UdiTests.cs @@ -7,11 +7,12 @@ using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Deploy; using Umbraco.Core.Serialization; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests { [TestFixture] - public class UdiTests + public class UdiTests : BaseUmbracoApplicationTest { [Test] public void StringUdiCtorTest() From 1cc3b3960188a77580e49d493b55cdffe1d4c869 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 11 Oct 2017 10:18:40 +0200 Subject: [PATCH 31/72] fixes: U4-10521 Creating new folders from the media picker no longer works --- .../src/less/components/umb-breadcrumbs.less | 7 +++++++ .../src/views/common/overlays/mediaPicker/mediapicker.html | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less index d25744a108..47ed97fde2 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less @@ -35,3 +35,10 @@ margin-right: 5px; color: @gray-7; } + +input.umb-breadcrumbs__add-ancestor { + height: 25px; + margin-top: -2px; + margin-left: 3px; + width: 100px; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html index 8c726f4a17..73b0915161 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html @@ -48,14 +48,14 @@ / -
  • +
  • Date: Thu, 12 Oct 2017 17:11:45 +1100 Subject: [PATCH 32/72] Fixes PostAddFolder --- src/Umbraco.Web/Editors/MediaController.cs | 136 ++++++++++-------- .../Models/ContentEditing/PostedFolder.cs | 17 +++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 3 files changed, 93 insertions(+), 61 deletions(-) create mode 100644 src/Umbraco.Web/Models/ContentEditing/PostedFolder.cs diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 0b1a697eae..1364a8e2af 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -599,12 +599,14 @@ namespace Umbraco.Web.Editors throw; } } - - [EnsureUserPermissionForMedia("folder.ParentId")] - public MediaItemDisplay PostAddFolder(EntityBasic folder) + + public MediaItemDisplay PostAddFolder(PostedFolder folder) { + var intParentId = GetParentIdAsInt(folder.ParentId, validatePermissions:true); + var mediaService = ApplicationContext.Services.MediaService; - var f = mediaService.CreateMedia(folder.Name, folder.ParentId, Constants.Conventions.MediaTypes.Folder); + + var f = mediaService.CreateMedia(folder.Name, intParentId, Constants.Conventions.MediaTypes.Folder); mediaService.Save(f, Security.CurrentUser.Id); return Mapper.Map(f); @@ -636,66 +638,15 @@ namespace Umbraco.Web.Editors if (result.FileData.Count == 0) { return Request.CreateResponse(HttpStatusCode.NotFound); - } - + } + //get the string json from the request - int parentId; bool entityFound; GuidUdi parentUdi; - string currentFolderId = result.FormData["currentFolder"]; - // test for udi - if (GuidUdi.TryParse(currentFolderId, out parentUdi)) - { - currentFolderId = parentUdi.Guid.ToString(); - } - - if (int.TryParse(currentFolderId, out parentId) == false) - { - // if a guid then try to look up the entity - Guid idGuid; - if (Guid.TryParse(currentFolderId, out idGuid)) - { - var entity = Services.EntityService.GetByKey(idGuid); - if (entity != null) - { - entityFound = true; - parentId = entity.Id; - } - else - { - throw new EntityNotFoundException(currentFolderId, "The passed id doesn't exist"); - } - } - else - { - return Request.CreateValidationErrorResponse("The request was not formatted correctly, the currentFolder is not an integer or Guid"); - } - - if (entityFound == false) - { - return Request.CreateValidationErrorResponse("The request was not formatted correctly, the currentFolder is not an integer or Guid"); - } - } - - - //ensure the user has access to this folder by parent id! - if (CheckPermissions( - new Dictionary(), - Security.CurrentUser, - Services.MediaService, - Services.EntityService, - parentId) == false) - { - return Request.CreateResponse( - HttpStatusCode.Forbidden, - new SimpleNotificationModel(new Notification( - Services.TextService.Localize("speechBubbles/operationFailedHeader"), - Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"), - SpeechBubbleIcon.Warning))); - } - + string currentFolderId = result.FormData["currentFolder"]; + int parentId = GetParentIdAsInt(currentFolderId, validatePermissions: true); + var tempFiles = new PostedFiles(); var mediaService = ApplicationContext.Services.MediaService; - - + //in case we pass a path with a folder in it, we will create it and upload media to it. if (result.FormData.ContainsKey("path")) { @@ -830,6 +781,69 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(HttpStatusCode.OK, tempFiles); } + /// + /// Given a parent id which could be a GUID, UDI or an INT, this will resolve the INT + /// + /// + /// + /// If true, this will check if the current user has access to the resolved integer parent id + /// and if that check fails an unauthorized exception will occur + /// + /// + private int GetParentIdAsInt(string parentId, bool validatePermissions) + { + int intParentId; + GuidUdi parentUdi; + + // test for udi + if (GuidUdi.TryParse(parentId, out parentUdi)) + { + parentId = parentUdi.Guid.ToString(); + } + + //if it's not an INT then we'll check for GUID + if (int.TryParse(parentId, out intParentId) == false) + { + // if a guid then try to look up the entity + Guid idGuid; + if (Guid.TryParse(parentId, out idGuid)) + { + var entity = Services.EntityService.GetByKey(idGuid); + if (entity != null) + { + intParentId = entity.Id; + } + else + { + throw new EntityNotFoundException(parentId, "The passed id doesn't exist"); + } + } + else + { + throw new HttpResponseException( + Request.CreateValidationErrorResponse("The request was not formatted correctly, the parentId is not an integer, Guid or UDI")); + } + } + + //ensure the user has access to this folder by parent id! + if (validatePermissions && CheckPermissions( + new Dictionary(), + Security.CurrentUser, + Services.MediaService, + Services.EntityService, + intParentId) == false) + { + throw new HttpResponseException(Request.CreateResponse( + HttpStatusCode.Forbidden, + new SimpleNotificationModel(new Notification( + Services.TextService.Localize("speechBubbles/operationFailedHeader"), + Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"), + SpeechBubbleIcon.Warning)))); + } + + return intParentId; + } + /// /// Ensures the item can be moved/copied to the new location /// diff --git a/src/Umbraco.Web/Models/ContentEditing/PostedFolder.cs b/src/Umbraco.Web/Models/ContentEditing/PostedFolder.cs new file mode 100644 index 0000000000..35cd908787 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/PostedFolder.cs @@ -0,0 +1,17 @@ +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + /// + /// Used to create a folder with the MediaController + /// + [DataContract] + public class PostedFolder + { + [DataMember(Name = "parentId")] + public string ParentId { get; set; } + + [DataMember(Name = "name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c86b86db30..014d204d95 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -392,6 +392,7 @@ + From d5447106a686edf748cc34eec0ec34c1ba387c81 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 12 Oct 2017 18:23:19 +1100 Subject: [PATCH 33/72] U4-10504 Previewing content results in the same PrepareDocument (expensive) procedure to be executed 3 times --- .../canvasdesigner.controller.js | 14 ++++++++++-- .../components/content/edit.controller.js | 4 ++-- src/Umbraco.Web/Editors/ContentController.cs | 22 +------------------ 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.controller.js b/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.controller.js index d23fdcbf84..cc45df2ce2 100644 --- a/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.controller.js +++ b/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.controller.js @@ -7,12 +7,22 @@ var app = angular.module("Umbraco.canvasdesigner", ['colorpicker', 'ui.slider', .controller("Umbraco.canvasdesignerController", function ($scope, $http, $window, $timeout, $location, dialogService) { + var isInit = $location.search().init; + if (isInit === "true") { + //do not continue, this is the first load of this new window, if this is passed in it means it's been + //initialized by the content editor and then the content editor will actually re-load this window without + //this flag. This is a required trick to get around chrome popup mgr. We don't want to double load preview.aspx + //since that will double prepare the preview documents + return; + } + $scope.isOpen = false; $scope.frameLoaded = false; $scope.enableCanvasdesigner = 0; $scope.googleFontFamilies = {}; - $scope.pageId = $location.search().id; - $scope.pageUrl = "../dialogs/Preview.aspx?id=" + $location.search().id; + var pageId = $location.search().id; + $scope.pageId = pageId; + $scope.pageUrl = "../dialogs/Preview.aspx?id=" + pageId; $scope.valueAreLoaded = false; $scope.devices = [ { name: "desktop", css: "desktop", icon: "icon-display", title: "Desktop" }, diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 7d60a83b89..1a59c701d7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -208,9 +208,9 @@ if (!$scope.busy) { // Chromes popup blocker will kick in if a window is opened - // outwith the initial scoped request. This trick will fix that. + // without the initial scoped request. This trick will fix that. // - var previewWindow = $window.open('preview/?id=' + content.id, 'umbpreview'); + var previewWindow = $window.open('preview/?init=true&id=' + content.id, 'umbpreview'); // Build the correct path so both /#/ and #/ work. var redirect = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/preview/?id=' + content.id; diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 511b33f812..d53ee84e7c 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -643,9 +643,7 @@ namespace Umbraco.Web.Editors ShowMessageForPublishStatus(publishStatus.Result, display); break; } - - UpdatePreviewContext(contentItem.PersistedContent.Id); - + //If the item is new and the operation was cancelled, we need to return a different // status code so the UI can handle it since it won't be able to redirect since there // is no Id to redirect to! @@ -875,24 +873,6 @@ namespace Umbraco.Web.Editors } } - /// - /// Checks if the user is currently in preview mode and if so will update the preview content for this item - /// - /// - private void UpdatePreviewContext(int contentId) - { - var previewId = Request.GetPreviewCookieValue(); - if (previewId.IsNullOrWhiteSpace()) return; - Guid id; - if (Guid.TryParse(previewId, out id)) - { - var d = new Document(contentId); - var pc = new PreviewContent(UmbracoUser, id, false); - pc.PrepareDocument(UmbracoUser, d, true); - pc.SavePreviewSet(); - } - } - /// /// Maps the dto property values to the persisted model /// From 13055ad1f2e4771d3744d051e48dbc7b802c12d6 Mon Sep 17 00:00:00 2001 From: Claus Date: Thu, 12 Oct 2017 11:35:08 +0200 Subject: [PATCH 34/72] U4-10524 Show warning in UI for any picker that is referencing a trashed or deleted item fixing the trash flag not getting mapped via automapper. fixing up the ui for mediapicker. fixing a small ui issue with the contentpicker and trashed content. --- .../src/less/property-editors.less | 153 ++++++------ .../contentpicker/contentpicker.controller.js | 13 +- .../mediapicker/mediapicker.controller.js | 58 +++-- .../mediapicker/mediapicker.html | 70 +++--- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 65 +++--- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 217 +++++++++--------- .../umbraco/config/lang/en_us.xml | 5 + .../Models/ContentEditing/EntityBasic.cs | 3 - .../Models/Mapping/EntityModelMapper.cs | 7 +- 9 files changed, 315 insertions(+), 276 deletions(-) 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 8b10b2f91b..1a999b9c1c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -126,21 +126,21 @@ ul.color-picker li a { // Media picker // -------------------------------------------------- .umb-mediapicker .add-link { - display: inline-block; - height: 120px; - width: 120px; - text-align: center; - color: @gray-8; - border: 2px @gray-8 dashed; - line-height: 120px; - text-decoration: none; + display: flex; + justify-content:center; + align-items:center; + width: 120px; + text-align: center; + color: @gray-8; + border: 2px @gray-8 dashed; + text-decoration: none; - transition: all 150ms ease-in-out; + transition: all 150ms ease-in-out; - &:hover { - color: @turquoise-d1; - border-color: @turquoise; - } + &:hover { + color: @turquoise-d1; + border-color: @turquoise; + } } .umb-mediapicker .picked-image { @@ -207,11 +207,10 @@ ul.color-picker li a { .umb-mediapicker .umb-sortable-thumbnails li { flex-direction: column; - margin: 0; + margin: 0 5px 0 0; padding: 5px; -} - - +} + .umb-sortable-thumbnails li:hover a { display: flex; justify-content: center; @@ -219,16 +218,20 @@ ul.color-picker li a { } .umb-sortable-thumbnails li img { - max-width:100%; - max-height:100%; - margin:auto; - display:block; - background-image: url(../img/checkered-background.png); + max-width:100%; + max-height:100%; + margin:auto; + display:block; + background-image: url(../img/checkered-background.png); +} + +.umb-sortable-thumbnails li img.trashed { + opacity:0.3; } -.umb-sortable-thumbnails li img.noScale{ - max-width: none !important; - max-height: none !important; +.umb-sortable-thumbnails li img.noScale { + max-width: none !important; + max-height: none !important; } .umb-sortable-thumbnails .umb-icon-holder { @@ -254,8 +257,8 @@ ul.color-picker li a { } .umb-sortable-thumbnails li:hover .umb-sortable-thumbnails__actions { - opacity: 1; - visibility: visible; + opacity: 1; + visibility: visible; } .umb-sortable-thumbnails .umb-sortable-thumbnails__action { @@ -285,27 +288,27 @@ ul.color-picker li a { // ------------------------------------------------- .umb-cropper{ - position: relative; + position: relative; } .umb-cropper img, .umb-cropper-gravity img{ - position: relative; - max-width: 100%; - height: auto; - top: 0; - left: 0; + position: relative; + max-width: 100%; + height: auto; + top: 0; + left: 0; } .umb-cropper img { - max-width: none; + max-width: none; } .umb-cropper .overlay, .umb-cropper-gravity .overlay { - top: 0; - left: 0; - cursor: move; - z-index: @zindexCropperOverlay; - position: absolute; + top: 0; + left: 0; + cursor: move; + z-index: @zindexCropperOverlay; + position: absolute; } .umb-cropper .viewport{ @@ -317,43 +320,43 @@ ul.color-picker li a { } .umb-cropper-gravity .viewport{ - overflow: hidden; - position: relative; - width: 100%; - height: 100%; + overflow: hidden; + position: relative; + width: 100%; + height: 100%; } .umb-cropper .viewport:after { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: @zindexCropperOverlay - 1; - -moz-opacity: .75; - opacity: .75; - filter: alpha(opacity=7); - -webkit-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); - -moz-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); - box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: @zindexCropperOverlay - 1; + -moz-opacity: .75; + opacity: .75; + filter: alpha(opacity=7); + -webkit-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); + -moz-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); + box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); } .umb-cropper-gravity .overlay{ - width: 14px; - height: 14px; - text-align: center; - border-radius: 20px; - background: @turquoise; - border: 3px solid @white; - opacity: 0.8; + width: 14px; + height: 14px; + text-align: center; + border-radius: 20px; + background: @turquoise; + border: 3px solid @white; + opacity: 0.8; } .umb-cropper-gravity .overlay i { - font-size: 26px; - line-height: 26px; - opacity: 0.8 !important; + font-size: 26px; + line-height: 26px; + opacity: 0.8 !important; } .umb-cropper .crop-container { @@ -361,16 +364,16 @@ ul.color-picker li a { } .umb-cropper .crop-slider { - padding: 10px; - border-top: 1px solid @gray-10; - margin-top: 10px; - display: flex; - align-items: center; - justify-content: center; - flex-wrap: wrap; - @media (min-width: 769px) { + padding: 10px; + border-top: 1px solid @gray-10; + margin-top: 10px; + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + @media (min-width: 769px) { padding: 10px 50px 10px 50px; - } + } } .umb-cropper .crop-slider i { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 9b3316ec1a..ec83988719 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -1,7 +1,7 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it -function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, miniEditorHelper) { +function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, miniEditorHelper, localizationService) { var unsubscribe; @@ -54,7 +54,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper setSortingState($scope.renderModel); }); - } + } $scope.renderModel = []; @@ -154,7 +154,6 @@ function contentPickerController($scope, entityResource, editorState, iconHelper } } - if ($routeParams.section === "settings" && $routeParams.tree === "documentTypes") { //if the content-picker is being rendered inside the document-type editor, we don't need to process the startnode query dialogOptions.startNodeId = -1; @@ -287,8 +286,12 @@ function contentPickerController($scope, entityResource, editorState, iconHelper entityResource.getUrl(entity.id, entityType).then(function(data){ // update url angular.forEach($scope.renderModel, function(item){ - if(item.id === entity.id) { - item.url = data; + if (item.id === entity.id) { + if (entity.trashed) { + item.url = localizationService.dictionary.general_recycleBin; + } else { + item.url = data; + } } }); }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index 8a7b20498d..28e034bdaf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -1,7 +1,7 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerController", - function ($rootScope, $scope, dialogService, entityResource, mediaResource, mediaHelper, $timeout, userService, $location) { + function ($rootScope, $scope, dialogService, entityResource, mediaResource, mediaHelper, $timeout, userService, $location, localizationService) { //check the pre-values for multi-picker var multiPicker = $scope.model.config.multiPicker && $scope.model.config.multiPicker !== '0' ? true : false; @@ -26,17 +26,44 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl // the mediaResource has server side auth configured for which the user must have // access to the media section, if they don't they'll get auth errors. The entityResource // acts differently in that it allows access if the user has access to any of the apps that - // might require it's use. Therefore we need to use the metatData property to get at the thumbnail + // might require it's use. Therefore we need to use the metaData property to get at the thumbnail // value. - entityResource.getByIds(ids, "Media").then(function (medias) { + entityResource.getByIds(ids, "Media").then(function(medias) { - _.each(medias, function (media, i) { + // The service only returns item results for ids that exist (deleted items are silently ignored). + // This results in the picked items value to be set to contain only ids of picked items that could actually be found. + // Since a referenced item could potentially be restored later on, instead of changing the selected values here based + // on whether the items exist during a save event - we should keep "placeholder" items for picked items that currently + // could not be fetched. This will preserve references and ensure that the state of an item does not differ depending + // on whether it is simply resaved or not. + // This is done by remapping the int/guid ids into a new array of items, where we create "Deleted item" placeholders + // when there is no match for a selected id. This will ensure that the values being set on save, are the same as before. + + medias = _.map(ids, + function(id) { + var found = _.find(medias, + function(m) { + return m.udi === id || m.id === id; + }); + if (found) { + return found; + } else { + return { + name: localizationService.dictionary.mediaPicker_deletedItem, + id: $scope.model.config.idType !== "udi" ? id : null, + udi: $scope.model.config.idType === "udi" ? id : null, + icon: "icon-picture", + thumbnail: null, + trashed: true + }; + } + }); - //only show non-trashed items - if (media.parentId >= -1) { - - if (!media.thumbnail) { + _.each(medias, + function(media, i) { + // if there is no thumbnail, try getting one if the media is not a placeholder item + if (!media.thumbnail && media.id) { media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); } @@ -44,12 +71,10 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl if ($scope.model.config.idType === "udi") { $scope.ids.push(media.udi); - } - else { + } else { $scope.ids.push(media.id); } - } - }); + }); $scope.sync(); }); @@ -81,9 +106,9 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl show: true, submit: function(model) { - _.each(model.selectedImages, function(media, i) { - - if (!media.thumbnail) { + _.each(model.selectedImages, function(media, i) { + // if there is no thumbnail, try getting one if the media is not a placeholder item + if (!media.thumbnail && media.id) { media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); } @@ -101,10 +126,8 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl $scope.mediaPickerOverlay.show = false; $scope.mediaPickerOverlay = null; - } }; - }; $scope.sortableOptions = { @@ -142,5 +165,4 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl //update the display val again if it has changed from the server setupViewModel(); }; - }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html index f3aab992dd..70dd9ed2e5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html @@ -1,47 +1,47 @@
    -
      -
    • +

      +

      - - +
      +
        +
      • - - - + + - - - - {{image.name}} - + + + - + + + + {{image.name}} + -
      • -
      + +
    • - +
    + + + +
    - - + + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 23895db95b..297b760f19 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -90,7 +90,7 @@ Nedarv Sprog - eller nedarv sprog fra forældre noder. Gælder også
    + eller nedarv sprog fra forældre noder. Gælder også
    for den aktuelle node, medmindre et domæne nedenfor også indstiller et sprog.]]>
    Domæner @@ -383,15 +383,15 @@ - Du tilføjer flere sprog under 'sprog' i menuen til venstre + Du tilføjer flere sprog under 'sprog' i menuen til venstre ]]> Kulturnavn Rediger navnet på ordbogselementet. - @@ -669,14 +669,14 @@ Din database er blevet fundet og identificeret som Database konfiguration - installér knappen for at installere Umbraco %0% databasen + installér knappen for at installere Umbraco %0% databasen ]]> installér knappen for at installere Umbraco %0% databasen]]> Næste for at fortsætte.]]> - Databasen er ikke fundet. Kontrollér venligst at informationen i database forbindelsesstrengen i "web.config" filen er korrekt.

    + Databasen er ikke fundet. Kontrollér venligst at informationen i database forbindelsesstrengen i "web.config" filen er korrekt.

    For at fortsætte bedes du venligst rette "web.config" filen (ved at bruge Visual Studio eller dit favoritprogram), scroll til bunden, tilføj forbindelsesstrengen til din database i feltet som hedder "umbracoDbDSN" og gem filen.

    Klik på Forsøg igen knappen når du er færdig.
    Mere information om at redigere web.config her.

    ]]>
    Kontakt venligst din ISP hvis det er nødvendigt. Hvis du installerer på en lokal maskine eller server kan du muligvis få informationerne fra din systemadministrator.]]> @@ -724,7 +724,7 @@ Umbraco %0% er installeret og klar til brug /web.config filen og opdatére 'AppSetting' feltet UmbracoConfigurationStatus i bunden til '%0%'.]]> - komme igang med det samme ved at klikke på "Start Umbraco" knappen nedenfor.
    Hvis du er ny med Umbraco, kan du finde masser af ressourcer på vores 'getting started' sider. + komme igang med det samme ved at klikke på "Start Umbraco" knappen nedenfor.
    Hvis du er ny med Umbraco, kan du finde masser af ressourcer på vores 'getting started' sider. ]]>
    Start UmbracoFor at administrere dit website skal du blot åbne Umbraco administrationen og begynde at tilføje indhold, opdatere skabelonerne og stylesheets'ene eller tilføje ny funktionalitet.]]> @@ -789,25 +789,25 @@ Rediger dine notificeringer for %0% - - Hej %0%

    -

    Dette er en automatisk mail for at informere dig om at opgaven '%1%' - er blevet udførtpå siden '%2%' af brugeren '%3%'

    -

    + Hej %0%

    +

    Dette er en automatisk mail for at informere dig om at opgaven '%1%' + er blevet udførtpå siden '%2%' af brugeren '%3%'

    +

    Opdateringssammendrag:

    %6%

    Hav en fortsat god dag!

    De bedste hilsner fra umbraco robotten

    ]]> [%0%] Notificering om %1% udført på %2% @@ -864,7 +864,7 @@ Mange hilsner fra Umbraco robotten Pakken er på succesfuld vis blevet fjernet Afinstallér pakke - + Bemærk: at dokumenter og medier som afhænger af denne pakke vil muligvis holde op med at virke, så vær forsigtig. Hvis i tvivl, kontakt personen som har udviklet pakken.]]> Download opdatering fra opbevaringsbasen @@ -912,13 +912,13 @@ Mange hilsner fra Umbraco robotten Udgivelsen kunne ikke udgives da publiceringsdato er sat - - Udgivelsen fejlede fordi en overordnet side ikke er publiceret @@ -935,6 +935,11 @@ Mange hilsner fra Umbraco robotten Du har ikke konfigureret nogen godkendte farver + + Du har valgt et medie som er slettet eller lagt i papirkurven + Du har valgt medier som er slettede eller lagt i papirkurven + Slettet medie + indtast eksternt link vælg en intern side diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 30ebc137b9..48fd5a99c7 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -693,35 +693,35 @@ Your database has been found and is identified as Database configuration - install button to install the Umbraco %0% database + install button to install the Umbraco %0% database ]]> Next to proceed.]]> - Database not found! Please check that the information in the "connection string" of the "web.config" file is correct.

    -

    To proceed, please edit the "web.config" file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named "UmbracoDbDSN" and save the file.

    -

    - Click the retry button when - done.
    + Database not found! Please check that the information in the "connection string" of the "web.config" file is correct.

    +

    To proceed, please edit the "web.config" file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named "UmbracoDbDSN" and save the file.

    +

    + Click the retry button when + done.
    More information on editing web.config here.

    ]]>
    - - Please contact your ISP if necessary. + + Please contact your ISP if necessary. If you're installing on a local machine or server you might need information from your system administrator.]]> - - Press the upgrade button to upgrade your database to Umbraco %0%

    -

    - Don't worry - no content will be deleted and everything will continue working afterwards! -

    + + Press the upgrade button to upgrade your database to Umbraco %0%

    +

    + Don't worry - no content will be deleted and everything will continue working afterwards! +

    ]]>
    - Press Next to + Press Next to proceed. ]]> next to continue the configuration wizard]]> @@ -736,65 +736,65 @@ More information on setting up permissions for Umbraco here You need to grant ASP.NET modify permissions to the following files/folders - Your permission settings are almost perfect!

    + Your permission settings are almost perfect!

    You can run Umbraco without problems, but you will not be able to install packages which are recommended to take full advantage of Umbraco.]]>
    How to Resolve Click here to read the text version video tutorial on setting up folder permissions for Umbraco or read the text version.]]> - Your permission settings might be an issue! -

    + Your permission settings might be an issue! +

    You can run Umbraco without problems, but you will not be able to create folders or install packages which are recommended to take full advantage of Umbraco.]]>
    - Your permission settings are not ready for Umbraco! -

    + Your permission settings are not ready for Umbraco! +

    In order to run Umbraco, you'll need to update your permission settings.]]>
    - Your permission settings are perfect!

    + Your permission settings are perfect!

    You are ready to run Umbraco and install packages!]]>
    Resolving folder issue Follow this link for more information on problems with ASP.NET and creating folders Setting up folder permissions - I want to start from scratch - learn how) - You can still choose to install Runway later on. Please go to the Developer section and choose Packages. + learn how) + You can still choose to install Runway later on. Please go to the Developer section and choose Packages. ]]> You've just set up a clean Umbraco platform. What do you want to do next? Runway is installed - - This is our list of recommended modules, check off the ones you would like to install, or view the full list of modules + + This is our list of recommended modules, check off the ones you would like to install, or view the full list of modules ]]> Only recommended for experienced users I want to start with a simple website - - "Runway" is a simple website providing some basic document types and templates. The installer can set up Runway for you automatically, - but you can easily edit, extend or remove it. It's not necessary and you can perfectly use Umbraco without it. However, - Runway offers an easy foundation based on best practices to get you started faster than ever. - If you choose to install Runway, you can optionally select basic building blocks called Runway Modules to enhance your Runway pages. -

    - - Included with Runway: Home page, Getting Started page, Installing Modules page.
    - Optional Modules: Top Navigation, Sitemap, Contact, Gallery. -
    + + "Runway" is a simple website providing some basic document types and templates. The installer can set up Runway for you automatically, + but you can easily edit, extend or remove it. It's not necessary and you can perfectly use Umbraco without it. However, + Runway offers an easy foundation based on best practices to get you started faster than ever. + If you choose to install Runway, you can optionally select basic building blocks called Runway Modules to enhance your Runway pages. +

    + + Included with Runway: Home page, Getting Started page, Installing Modules page.
    + Optional Modules: Top Navigation, Sitemap, Contact, Gallery. +
    ]]>
    What is Runway @@ -805,24 +805,24 @@ Step 5/5: Umbraco is ready to get you started Thank you for choosing Umbraco - Browse your new site + Browse your new site You installed Runway, so why not see how your new website looks.]]> - Further help and information + Further help and information Get help from our award winning community, browse the documentation or watch some free videos on how to build a simple site, how to use packages and a quick guide to the Umbraco terminology]]> Umbraco %0% is installed and ready for use - /web.config file and update the AppSetting key UmbracoConfigurationStatus in the bottom to the value of '%0%'.]]> - started instantly by clicking the "Launch Umbraco" button below.
    If you are new to Umbraco, + started instantly by clicking the "Launch Umbraco" button below.
    If you are new to Umbraco, you can find plenty of resources on our getting started pages.]]>
    - Launch Umbraco + Launch Umbraco To manage your website, simply open the Umbraco back office and start adding content, updating the templates and stylesheets or add new functionality]]> Connection to database failed. @@ -830,8 +830,8 @@ To manage your website, simply open the Umbraco back office and start adding con Umbraco Version 4 Watch - Umbraco %0% for a fresh install or upgrading from version 3.0. -

    + Umbraco %0% for a fresh install or upgrading from version 3.0. +

    Press "next" to start the wizard.]]>
    @@ -889,47 +889,47 @@ To manage your website, simply open the Umbraco back office and start adding con Edit your notification for %0% - - Hi %0%

    - -

    This is an automated mail to inform you that the task '%1%' - has been performed on the page '%2%' - by the user '%3%' -

    - -

    -

    Update summary:

    - - %6% -
    -

    - - - -

    Have a nice day!

    - Cheers from the Umbraco robot + Hi %0%

    + +

    This is an automated mail to inform you that the task '%1%' + has been performed on the page '%2%' + by the user '%3%' +

    + +

    +

    Update summary:

    + + %6% +
    +

    + + + +

    Have a nice day!

    + Cheers from the Umbraco robot

    ]]>
    [%0%] Notification about %1% performed on %2% @@ -937,9 +937,9 @@ To manage your website, simply open the Umbraco back office and start adding con - - button and locating the package. Umbraco packages usually have a ".umb" or ".zip" extension. + + button and locating the package. Umbraco packages usually have a ".umb" or ".zip" extension. ]]> Drop to upload @@ -982,7 +982,7 @@ To manage your website, simply open the Umbraco back office and start adding con Package name Package doesn't contain any items -
    +
    You can safely remove this from the system by clicking "uninstall package" below.]]>
    No upgrades available @@ -994,8 +994,8 @@ To manage your website, simply open the Umbraco back office and start adding con The package was successfully uninstalled Uninstall package - - Notice: any documents, media etc depending on the items you remove, will stop working, and could lead to system instability, + + Notice: any documents, media etc depending on the items you remove, will stop working, and could lead to system instability, so uninstall with caution. If in doubt, contact the package author.]]> Download update from the repository @@ -1042,28 +1042,28 @@ To manage your website, simply open the Umbraco back office and start adding con - - - - - Include unpublished subpages @@ -1073,13 +1073,18 @@ To manage your website, simply open the Umbraco back office and start adding con %0% and subpages have been published Publish %0% and all its subpages - Publish to publish %0% and thereby making its content publicly available.

    - You can publish this page and all its subpages by checking Include unpublished subpages below. + Publish to publish %0% and thereby making its content publicly available.

    + You can publish this page and all its subpages by checking Include unpublished subpages below. ]]>
    You have not configured any approved colours + + + You have picked a media item currently deleted or in the recycle bin + You have picked media items currently deleted or in the recycle bin + Deleted item enter external link 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 93a0582c92..2ba7bf44f6 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -1079,6 +1079,11 @@ To manage your website, simply open the Umbraco back office and start adding con You have not configured any approved colors + + You have picked a media item currently deleted or in the recycle bin + You have picked media items currently deleted or in the recycle bin + Deleted item + enter external link choose internal page diff --git a/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs b/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs index 9f6e5b28da..12afceea05 100644 --- a/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs @@ -2,10 +2,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.Linq; using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Models.Validation; diff --git a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs b/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs index 273dbd34f3..ec72c9839d 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs @@ -6,7 +6,6 @@ using Examine; using Examine.LuceneEngine.Providers; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Mapping; using Umbraco.Core.Models.Membership; using Umbraco.Web.Models.ContentEditing; @@ -21,7 +20,7 @@ namespace Umbraco.Web.Models.Mapping config.CreateMap() .ForMember(x => x.Udi, expression => expression.MapFrom(x => Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(x.NodeObjectTypeId), x.Key))) .ForMember(basic => basic.Icon, expression => expression.MapFrom(entity => entity.ContentTypeIcon)) - .ForMember(dto => dto.Trashed, expression => expression.Ignore()) + .ForMember(dto => dto.Trashed, expression => expression.MapFrom(x => x.Trashed)) .ForMember(x => x.Alias, expression => expression.Ignore()) .AfterMap((entity, basic) => { @@ -98,8 +97,8 @@ namespace Umbraco.Web.Models.Mapping else if (entity.NodeObjectTypeId == Constants.ObjectTypes.TemplateTypeGuid) basic.Icon = "icon-newspaper-alt"; } - }); - + }); + config.CreateMap() //default to document icon .ForMember(x => x.Score, expression => expression.MapFrom(result => result.Score)) From 1ec07aa205051f167b8a5ea98df930230a30e835 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 12 Oct 2017 16:13:14 +0200 Subject: [PATCH 35/72] fixes breadcrumbs and search when start node is set --- .../mediaPicker/mediapicker.controller.js | 71 ++++++++++++------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js index a6a2ddcbab..ccb033a57c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js @@ -56,6 +56,46 @@ angular.module("umbraco") $scope.target = dialogOptions.currentTarget; } + function onInit() { + if ($scope.startNodeId !== -1) { + entityResource.getById($scope.startNodeId, "media") + .then(function (ent) { + $scope.startNodeId = ent.id; + run(); + }); + } else { + run(); + } + } + + function run() { + //default root item + if (!$scope.target) { + if ($scope.lastOpenedNode && $scope.lastOpenedNode !== -1) { + entityResource.getById($scope.lastOpenedNode, "media") + .then(ensureWithinStartNode, gotoStartNode); + } else { + gotoStartNode(); + } + } else { + //if a target is specified, go look it up - generally this target will just contain ids not the actual full + //media object so we need to look it up + var id = $scope.target.udi ? $scope.target.udi : $scope.target.id + var altText = $scope.target.altText; + mediaResource.getById(id) + .then(function (node) { + $scope.target = node; + if (ensureWithinStartNode(node)) { + selectImage(node); + $scope.target.url = mediaHelper.resolveFile(node); + $scope.target.altText = altText; + $scope.openDetailsDialog(); + } + }, + gotoStartNode); + } + } + $scope.upload = function(v) { angular.element(".umb-file-dropzone-directive .file-select").click(); }; @@ -107,7 +147,7 @@ angular.module("umbraco") if (folder.id > 0) { entityResource.getAncestors(folder.id, "media") - .then(function(anc) { + .then(function(anc) { $scope.path = _.filter(anc, function(f) { return f.path.indexOf($scope.startNodeId) !== -1; @@ -218,32 +258,6 @@ angular.module("umbraco") $scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" }); } - //default root item - if (!$scope.target) { - if ($scope.lastOpenedNode && $scope.lastOpenedNode !== -1) { - entityResource.getById($scope.lastOpenedNode, "media") - .then(ensureWithinStartNode, gotoStartNode); - } else { - gotoStartNode(); - } - } else { - //if a target is specified, go look it up - generally this target will just contain ids not the actual full - //media object so we need to look it up - var id = $scope.target.udi ? $scope.target.udi : $scope.target.id - var altText = $scope.target.altText; - mediaResource.getById(id) - .then(function(node) { - $scope.target = node; - if (ensureWithinStartNode(node)) { - selectImage(node); - $scope.target.url = mediaHelper.resolveFile(node); - $scope.target.altText = altText; - $scope.openDetailsDialog(); - } - }, - gotoStartNode); - } - $scope.openDetailsDialog = function() { $scope.mediaPickerDetailsOverlay = {}; @@ -368,4 +382,7 @@ angular.module("umbraco") } } } + + onInit(); + }); \ No newline at end of file From b1f2c5d93ac7f6fee2fc7f5bfafc6460b9d543a2 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Thu, 12 Oct 2017 23:46:02 +0200 Subject: [PATCH 36/72] Add better function to get initials from name --- .../directives/components/umbavatar.directive.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbavatar.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbavatar.directive.js index 7fad3e8a74..7dd2f0d7a3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbavatar.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbavatar.directive.js @@ -66,9 +66,13 @@ Use this directive to render an avatar. function getNameInitials(name) { if(name) { - var initials = name.match(/\b\w/g) || []; - initials = ((initials.shift() || '') + (initials.pop() || '')).toUpperCase(); - return initials; + var names = name.split(' '), + initials = names[0].substring(0, 1); + + if (names.length > 1) { + initials += names[names.length - 1].substring(0, 1); + } + return initials.toUpperCase(); } return null; } From 62ce7f61329e9d7489df406de16ef124512223e4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 13 Oct 2017 16:33:49 +1100 Subject: [PATCH 37/72] U4-10519 Legacy create is broken --- src/Umbraco.Web/UI/LegacyDialogHandler.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web/UI/LegacyDialogHandler.cs b/src/Umbraco.Web/UI/LegacyDialogHandler.cs index c51a8bb08b..0c3d09f1a2 100644 --- a/src/Umbraco.Web/UI/LegacyDialogHandler.cs +++ b/src/Umbraco.Web/UI/LegacyDialogHandler.cs @@ -126,8 +126,10 @@ namespace Umbraco.Web.UI { var task = GetTaskForOperation(httpContext, umbracoUser, Operation.Create, nodeType); if (task == null) - throw new InvalidOperationException( - string.Format("Could not task for operation {0} for node type {1}", Operation.Create, nodeType)); + { + //if no task was found it will use the default task and we cannot validate the application assigned so return true + return true; + } var dialogTask = task as LegacyDialogTask; if (dialogTask != null) @@ -154,8 +156,10 @@ namespace Umbraco.Web.UI { var task = GetTaskForOperation(httpContext, umbracoUser, Operation.Delete, nodeType); if (task == null) - throw new InvalidOperationException( - string.Format("Could not task for operation {0} for node type {1}", Operation.Delete, nodeType)); + { + //if no task was found it will use the default task and we cannot validate the application assigned so return true + return true; + } var dialogTask = task as LegacyDialogTask; if (dialogTask != null) From ca57b9c5e69e24cc61929bfe572a51d14a893ce9 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 13 Oct 2017 16:50:39 +1100 Subject: [PATCH 38/72] fixes issue with js exceptions --- .../propertyeditors/mediapicker/mediapicker.controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index 28e034bdaf..70ac49fb7a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -63,7 +63,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl _.each(medias, function(media, i) { // if there is no thumbnail, try getting one if the media is not a placeholder item - if (!media.thumbnail && media.id) { + if (!media.thumbnail && media.id && media.metaData) { media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); } @@ -108,7 +108,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl _.each(model.selectedImages, function(media, i) { // if there is no thumbnail, try getting one if the media is not a placeholder item - if (!media.thumbnail && media.id) { + if (!media.thumbnail && media.id && media.metaData) { media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); } From a2e6da24914bbb9f0d9ff8737a291b478f3ab448 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Fri, 13 Oct 2017 11:19:39 +0100 Subject: [PATCH 39/72] Fix in PowerShell build - The 7Zip tool download was giving two paths & thus break the build script --- build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 b/build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 index d1aaf7582f..2df42e5fd8 100644 --- a/build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 +++ b/build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 @@ -40,6 +40,7 @@ function Get-UmbracoBuildEnv &$nuget install 7-Zip.CommandLine -OutputDirectory $path -Verbosity quiet $dir = ls "$path\7-Zip.CommandLine.*" | sort -property Name -descending | select -first 1 $file = ls -path "$dir" -name 7za.exe -recurse + $file = ls -path "$dir" -name 7za.exe -recurse | select -first 1 #A select is because there is tools\7za.exe & tools\x64\7za.exe mv "$dir\$file" $sevenZip Remove-Directory $dir } From fd8daa398f4faa4d6516632fd381578386179b55 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Oct 2017 16:22:10 +1100 Subject: [PATCH 40/72] U4-10538 Disable Ping on front-end nodes when load balancing --- src/Umbraco.Web/Scheduling/KeepAlive.cs | 15 ++++++++++++++- src/Umbraco.Web/Scheduling/ScheduledPublishing.cs | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Scheduling/KeepAlive.cs b/src/Umbraco.Web/Scheduling/KeepAlive.cs index 763e28b608..f4beb9d5b8 100644 --- a/src/Umbraco.Web/Scheduling/KeepAlive.cs +++ b/src/Umbraco.Web/Scheduling/KeepAlive.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Umbraco.Core; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; +using Umbraco.Core.Sync; namespace Umbraco.Web.Scheduling { @@ -20,9 +21,21 @@ namespace Umbraco.Web.Scheduling _appContext = appContext; } + private ILogger Logger { get { return _appContext.ProfilingLogger.Logger; } } + public override async Task PerformRunAsync(CancellationToken token) { if (_appContext == null) return true; // repeat... + + switch (_appContext.GetCurrentServerRole()) + { + case ServerRole.Slave: + Logger.Debug("Does not run on slave servers."); + return true; // DO repeat, server role can change + case ServerRole.Unknown: + Logger.Debug("Does not run on servers with unknown role."); + return true; // DO repeat, server role can change + } // ensure we do not run if not main domain, but do NOT lock it if (_appContext.MainDom.IsMainDom == false) @@ -31,7 +44,7 @@ namespace Umbraco.Web.Scheduling return false; // do NOT repeat, going down } - using (DisposableTimer.DebugDuration(() => "Keep alive executing", () => "Keep alive complete")) + using (_appContext.ProfilingLogger.DebugDuration("Keep alive executing", "Keep alive complete")) { string umbracoAppUrl = null; diff --git a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs index 46a756e0ad..919a531549 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs @@ -31,7 +31,7 @@ namespace Umbraco.Web.Scheduling private ILogger Logger { get { return _appContext.ProfilingLogger.Logger; } } - public override async Task PerformRunAsync(CancellationToken token) + public override bool PerformRun() { if (_appContext == null) return true; // repeat... @@ -94,7 +94,7 @@ namespace Umbraco.Web.Scheduling public override bool IsAsync { - get { return true; } + get { return false; } } } } \ No newline at end of file From 2a0cc71d0666a496b9e6441de8ecbab73cfcde83 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Oct 2017 17:05:33 +1100 Subject: [PATCH 41/72] U4-10539 - reverts merge issue that reverted the fix for U4-9262 --- .../Views/Partials/Grid/Bootstrap2.cshtml | 28 ++++++++++++------- .../Views/Partials/Grid/Bootstrap3.cshtml | 28 ++++++++++++------- .../Partials/Grid/Editors/TextString.cshtml | 4 +-- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml index 8b189ae1a0..1437d4f14b 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml @@ -60,21 +60,29 @@ JObject cfg = contentItem.config; if(cfg != null) - foreach (JProperty property in cfg.Properties()) { - attrs.Add(property.Name + "='" + property.Value.ToString() + "'"); + foreach (JProperty property in cfg.Properties()) + { + var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString()); + attrs.Add(property.Name + "=\"" + propertyValue + "\""); } - + JObject style = contentItem.styles; - if (style != null) { - var cssVals = new List(); - foreach (JProperty property in style.Properties()) - cssVals.Add(property.Name + ":" + property.Value.ToString() + ";"); + if (style != null) { + var cssVals = new List(); + foreach (JProperty property in style.Properties()) + { + var propertyValue = property.Value.ToString(); + if (string.IsNullOrWhiteSpace(propertyValue) == false) + { + cssVals.Add(property.Name + ":" + propertyValue + ";"); + } + } - if (cssVals.Any()) - attrs.Add("style='" + string.Join(" ", cssVals) + "'"); + if (cssVals.Any()) + attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\""); } - + return new MvcHtmlString(string.Join(" ", attrs)); } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml index e672aa2a11..7b4f602b26 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml @@ -60,21 +60,29 @@ JObject cfg = contentItem.config; if(cfg != null) - foreach (JProperty property in cfg.Properties()) { - attrs.Add(property.Name + "='" + property.Value.ToString() + "'"); + foreach (JProperty property in cfg.Properties()) + { + var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString()); + attrs.Add(property.Name + "=\"" + propertyValue + "\""); } - + JObject style = contentItem.styles; - if (style != null) { - var cssVals = new List(); - foreach (JProperty property in style.Properties()) - cssVals.Add(property.Name + ":" + property.Value.ToString() + ";"); + if (style != null) { + var cssVals = new List(); + foreach (JProperty property in style.Properties()) + { + var propertyValue = property.Value.ToString(); + if (string.IsNullOrWhiteSpace(propertyValue) == false) + { + cssVals.Add(property.Name + ":" + propertyValue + ";"); + } + } - if (cssVals.Any()) - attrs.Add("style='" + string.Join(" ", cssVals) + "'"); + if (cssVals.Any()) + attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\""); } - + return new MvcHtmlString(string.Join(" ", attrs)); } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml index 0cac4eb1ff..4408cc4322 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml @@ -4,8 +4,8 @@ @if (Model.editor.config.markup != null) { string markup = Model.editor.config.markup.ToString(); - - markup = markup.Replace("#value#", Model.value.ToString()); + var umbracoHelper = new UmbracoHelper(UmbracoContext.Current); + markup = markup.Replace("#value#", umbracoHelper.ReplaceLineBreaksForHtml(HttpUtility.HtmlEncode(Model.value.ToString()))); markup = markup.Replace("#style#", Model.editor.config.style.ToString()); From 916bc6badbdfc1668944dd79c41298cdb052befa Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Oct 2017 17:05:33 +1100 Subject: [PATCH 42/72] U4-10539 - reverts merge issue that reverted the fix for U4-9262 (cherry picked from commit 2a0cc71d0666a496b9e6441de8ecbab73cfcde83) --- .../Views/Partials/Grid/Bootstrap2.cshtml | 28 ++++++++++++------- .../Views/Partials/Grid/Bootstrap3.cshtml | 28 ++++++++++++------- .../Partials/Grid/Editors/TextString.cshtml | 4 +-- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml index 8b189ae1a0..1437d4f14b 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml @@ -60,21 +60,29 @@ JObject cfg = contentItem.config; if(cfg != null) - foreach (JProperty property in cfg.Properties()) { - attrs.Add(property.Name + "='" + property.Value.ToString() + "'"); + foreach (JProperty property in cfg.Properties()) + { + var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString()); + attrs.Add(property.Name + "=\"" + propertyValue + "\""); } - + JObject style = contentItem.styles; - if (style != null) { - var cssVals = new List(); - foreach (JProperty property in style.Properties()) - cssVals.Add(property.Name + ":" + property.Value.ToString() + ";"); + if (style != null) { + var cssVals = new List(); + foreach (JProperty property in style.Properties()) + { + var propertyValue = property.Value.ToString(); + if (string.IsNullOrWhiteSpace(propertyValue) == false) + { + cssVals.Add(property.Name + ":" + propertyValue + ";"); + } + } - if (cssVals.Any()) - attrs.Add("style='" + string.Join(" ", cssVals) + "'"); + if (cssVals.Any()) + attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\""); } - + return new MvcHtmlString(string.Join(" ", attrs)); } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml index e672aa2a11..7b4f602b26 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml @@ -60,21 +60,29 @@ JObject cfg = contentItem.config; if(cfg != null) - foreach (JProperty property in cfg.Properties()) { - attrs.Add(property.Name + "='" + property.Value.ToString() + "'"); + foreach (JProperty property in cfg.Properties()) + { + var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString()); + attrs.Add(property.Name + "=\"" + propertyValue + "\""); } - + JObject style = contentItem.styles; - if (style != null) { - var cssVals = new List(); - foreach (JProperty property in style.Properties()) - cssVals.Add(property.Name + ":" + property.Value.ToString() + ";"); + if (style != null) { + var cssVals = new List(); + foreach (JProperty property in style.Properties()) + { + var propertyValue = property.Value.ToString(); + if (string.IsNullOrWhiteSpace(propertyValue) == false) + { + cssVals.Add(property.Name + ":" + propertyValue + ";"); + } + } - if (cssVals.Any()) - attrs.Add("style='" + string.Join(" ", cssVals) + "'"); + if (cssVals.Any()) + attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\""); } - + return new MvcHtmlString(string.Join(" ", attrs)); } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml index 0cac4eb1ff..4408cc4322 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml @@ -4,8 +4,8 @@ @if (Model.editor.config.markup != null) { string markup = Model.editor.config.markup.ToString(); - - markup = markup.Replace("#value#", Model.value.ToString()); + var umbracoHelper = new UmbracoHelper(UmbracoContext.Current); + markup = markup.Replace("#value#", umbracoHelper.ReplaceLineBreaksForHtml(HttpUtility.HtmlEncode(Model.value.ToString()))); markup = markup.Replace("#style#", Model.editor.config.style.ToString()); From b094bfad29561849cf1aae8e1286baf2dfec92f6 Mon Sep 17 00:00:00 2001 From: Claus Date: Mon, 16 Oct 2017 13:05:20 +0200 Subject: [PATCH 43/72] U4-10533 Show warning in UI for the link picker that is referencing a trashed or deleted item --- .../contentpicker/contentpicker.controller.js | 3 +- .../contentpicker/contentpicker.html | 3 + src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 64 ++++++++++--------- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 4 ++ .../umbraco/config/lang/en_us.xml | 4 ++ 5 files changed, 47 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index ec83988719..229ec614de 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -54,7 +54,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper setSortingState($scope.renderModel); }); - } + } $scope.renderModel = []; @@ -333,6 +333,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper "icon": item.icon, "path": item.path, "url": item.url, + "trashed": item.trashed, "published": (item.metaData && item.metaData.IsPublished === false && entityType === "Document") ? false : true // only content supports published/unpublished content so we set everything else to published so the UI looks correct }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index 75cc74f02d..1755840cc8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -1,5 +1,8 @@
    +

    +

    +
    diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 297b760f19..4a189642b6 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -90,7 +90,7 @@ Nedarv Sprog - eller nedarv sprog fra forældre noder. Gælder også
    + eller nedarv sprog fra forældre noder. Gælder også
    for den aktuelle node, medmindre et domæne nedenfor også indstiller et sprog.]]>
    Domæner @@ -383,15 +383,15 @@ - Du tilføjer flere sprog under 'sprog' i menuen til venstre + Du tilføjer flere sprog under 'sprog' i menuen til venstre ]]> Kulturnavn Rediger navnet på ordbogselementet. - @@ -669,14 +669,14 @@ Din database er blevet fundet og identificeret som Database konfiguration - installér knappen for at installere Umbraco %0% databasen + installér knappen for at installere Umbraco %0% databasen ]]> installér knappen for at installere Umbraco %0% databasen]]> Næste for at fortsætte.]]> - Databasen er ikke fundet. Kontrollér venligst at informationen i database forbindelsesstrengen i "web.config" filen er korrekt.

    + Databasen er ikke fundet. Kontrollér venligst at informationen i database forbindelsesstrengen i "web.config" filen er korrekt.

    For at fortsætte bedes du venligst rette "web.config" filen (ved at bruge Visual Studio eller dit favoritprogram), scroll til bunden, tilføj forbindelsesstrengen til din database i feltet som hedder "umbracoDbDSN" og gem filen.

    Klik på Forsøg igen knappen når du er færdig.
    Mere information om at redigere web.config her.

    ]]>
    Kontakt venligst din ISP hvis det er nødvendigt. Hvis du installerer på en lokal maskine eller server kan du muligvis få informationerne fra din systemadministrator.]]> @@ -724,7 +724,7 @@ Umbraco %0% er installeret og klar til brug /web.config filen og opdatére 'AppSetting' feltet UmbracoConfigurationStatus i bunden til '%0%'.]]> - komme igang med det samme ved at klikke på "Start Umbraco" knappen nedenfor.
    Hvis du er ny med Umbraco, kan du finde masser af ressourcer på vores 'getting started' sider. + komme igang med det samme ved at klikke på "Start Umbraco" knappen nedenfor.
    Hvis du er ny med Umbraco, kan du finde masser af ressourcer på vores 'getting started' sider. ]]>
    Start UmbracoFor at administrere dit website skal du blot åbne Umbraco administrationen og begynde at tilføje indhold, opdatere skabelonerne og stylesheets'ene eller tilføje ny funktionalitet.]]> @@ -789,25 +789,25 @@ Rediger dine notificeringer for %0% - - Hej %0%

    -

    Dette er en automatisk mail for at informere dig om at opgaven '%1%' - er blevet udførtpå siden '%2%' af brugeren '%3%'

    -

    + Hej %0%

    +

    Dette er en automatisk mail for at informere dig om at opgaven '%1%' + er blevet udførtpå siden '%2%' af brugeren '%3%'

    +

    Opdateringssammendrag:

    %6%

    Hav en fortsat god dag!

    De bedste hilsner fra umbraco robotten

    ]]> [%0%] Notificering om %1% udført på %2% @@ -864,7 +864,7 @@ Mange hilsner fra Umbraco robotten Pakken er på succesfuld vis blevet fjernet Afinstallér pakke - + Bemærk: at dokumenter og medier som afhænger af denne pakke vil muligvis holde op med at virke, så vær forsigtig. Hvis i tvivl, kontakt personen som har udviklet pakken.]]> Download opdatering fra opbevaringsbasen @@ -912,13 +912,13 @@ Mange hilsner fra Umbraco robotten Udgivelsen kunne ikke udgives da publiceringsdato er sat - - Udgivelsen fejlede fordi en overordnet side ikke er publiceret @@ -935,6 +935,10 @@ Mange hilsner fra Umbraco robotten Du har ikke konfigureret nogen godkendte farver + + Du har valgt et dokument som er slettet eller lagt i papirkurven + Du har valgt dokumenter som er slettede eller lagt i papirkurven + Du har valgt et medie som er slettet eller lagt i papirkurven Du har valgt medier som er slettede eller lagt i papirkurven diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 48fd5a99c7..9134d96d59 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -1081,6 +1081,10 @@ To manage your website, simply open the Umbraco back office and start adding con You have not configured any approved colours + + You have picked a content item currently deleted or in the recycle bin + You have picked content items currently deleted or in the recycle bin + You have picked a media item currently deleted or in the recycle bin You have picked media items currently deleted or in the recycle bin 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 2ba7bf44f6..58c2d2eefc 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -1079,6 +1079,10 @@ To manage your website, simply open the Umbraco back office and start adding con You have not configured any approved colors + + You have picked a content item currently deleted or in the recycle bin + You have picked content items currently deleted or in the recycle bin + You have picked a media item currently deleted or in the recycle bin You have picked media items currently deleted or in the recycle bin From 4bd263e7bcc5fb09678e2a552a06cae2913bce45 Mon Sep 17 00:00:00 2001 From: Claus Date: Mon, 16 Oct 2017 13:52:17 +0200 Subject: [PATCH 44/72] U4-10524 Show warning in UI for the media/content picker that is referencing a trashed or deleted item U4-10533 Show warning in UI for the link picker that is referencing a trashed or deleted item cherrypicked from dev-v7. --- .../src/less/property-editors.less | 149 +++++++++--------- .../contentpicker/contentpicker.controller.js | 12 +- .../contentpicker/contentpicker.html | 3 + .../mediapicker/mediapicker.controller.js | 56 +++++-- .../mediapicker/mediapicker.html | 70 ++++---- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 9 ++ src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 9 ++ .../umbraco/config/lang/en_us.xml | 9 ++ .../Models/ContentEditing/EntityBasic.cs | 3 - .../Models/Mapping/EntityModelMapper.cs | 2 +- 10 files changed, 189 insertions(+), 133 deletions(-) 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 2388aae2b2..723a365eb0 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -126,21 +126,21 @@ ul.color-picker li a { // Media picker // -------------------------------------------------- .umb-mediapicker .add-link { - display: inline-block; - height: 120px; - width: 120px; - text-align: center; - color: @gray-8; - border: 2px @gray-8 dashed; - line-height: 120px; - text-decoration: none; + display: flex; + justify-content:center; + align-items:center; + width: 120px; + text-align: center; + color: @gray-8; + border: 2px @gray-8 dashed; + text-decoration: none; - transition: all 150ms ease-in-out; + transition: all 150ms ease-in-out; - &:hover { - color: @turquoise-d1; - border-color: @turquoise; - } + &:hover { + color: @turquoise-d1; + border-color: @turquoise; + } } .umb-mediapicker .picked-image { @@ -207,11 +207,10 @@ ul.color-picker li a { .umb-mediapicker .umb-sortable-thumbnails li { flex-direction: column; - margin: 0; + margin: 0 5px 0 0; padding: 5px; } - .umb-sortable-thumbnails li:hover a { display: flex; justify-content: center; @@ -219,16 +218,20 @@ ul.color-picker li a { } .umb-sortable-thumbnails li img { - max-width:100%; - max-height:100%; - margin:auto; - display:block; - background-image: url(../img/checkered-background.png); + max-width:100%; + max-height:100%; + margin:auto; + display:block; + background-image: url(../img/checkered-background.png); } -.umb-sortable-thumbnails li img.noScale{ - max-width: none !important; - max-height: none !important; +.umb-sortable-thumbnails li img.trashed { + opacity:0.3; +} + +.umb-sortable-thumbnails li img.noScale { + max-width: none !important; + max-height: none !important; } .umb-sortable-thumbnails .umb-icon-holder { @@ -254,8 +257,8 @@ ul.color-picker li a { } .umb-sortable-thumbnails li:hover .umb-sortable-thumbnails__actions { - opacity: 1; - visibility: visible; + opacity: 1; + visibility: visible; } .umb-sortable-thumbnails .umb-sortable-thumbnails__action { @@ -285,27 +288,27 @@ ul.color-picker li a { // ------------------------------------------------- .umb-cropper{ - position: relative; + position: relative; } .umb-cropper img, .umb-cropper-gravity img{ - position: relative; - max-width: 100%; - height: auto; - top: 0; - left: 0; + position: relative; + max-width: 100%; + height: auto; + top: 0; + left: 0; } .umb-cropper img { - max-width: none; + max-width: none; } .umb-cropper .overlay, .umb-cropper-gravity .overlay { - top: 0; - left: 0; - cursor: move; - z-index: 6001; - position: absolute; + top: 0; + left: 0; + cursor: move; + z-index: 6001; + position: absolute; } .umb-cropper .viewport{ @@ -317,43 +320,43 @@ ul.color-picker li a { } .umb-cropper-gravity .viewport{ - overflow: hidden; - position: relative; - width: 100%; - height: 100%; + overflow: hidden; + position: relative; + width: 100%; + height: 100%; } .umb-cropper .viewport:after { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 5999; - -moz-opacity: .75; - opacity: .75; - filter: alpha(opacity=7); - -webkit-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); - -moz-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); - box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 5999; + -moz-opacity: .75; + opacity: .75; + filter: alpha(opacity=7); + -webkit-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); + -moz-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); + box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); } .umb-cropper-gravity .overlay{ - width: 14px; - height: 14px; - text-align: center; - border-radius: 20px; - background: @turquoise; - border: 3px solid @white; - opacity: 0.8; + width: 14px; + height: 14px; + text-align: center; + border-radius: 20px; + background: @turquoise; + border: 3px solid @white; + opacity: 0.8; } .umb-cropper-gravity .overlay i { - font-size: 26px; - line-height: 26px; - opacity: 0.8 !important; + font-size: 26px; + line-height: 26px; + opacity: 0.8 !important; } .umb-cropper .crop-container { @@ -361,16 +364,16 @@ ul.color-picker li a { } .umb-cropper .crop-slider { - padding: 10px; - border-top: 1px solid @gray-10; - margin-top: 10px; - display: flex; - align-items: center; - justify-content: center; - flex-wrap: wrap; - @media (min-width: 769px) { + padding: 10px; + border-top: 1px solid @gray-10; + margin-top: 10px; + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + @media (min-width: 769px) { padding: 10px 50px 10px 50px; - } + } } .umb-cropper .crop-slider i { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 9b3316ec1a..229ec614de 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -1,7 +1,7 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it -function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, miniEditorHelper) { +function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, miniEditorHelper, localizationService) { var unsubscribe; @@ -154,7 +154,6 @@ function contentPickerController($scope, entityResource, editorState, iconHelper } } - if ($routeParams.section === "settings" && $routeParams.tree === "documentTypes") { //if the content-picker is being rendered inside the document-type editor, we don't need to process the startnode query dialogOptions.startNodeId = -1; @@ -287,8 +286,12 @@ function contentPickerController($scope, entityResource, editorState, iconHelper entityResource.getUrl(entity.id, entityType).then(function(data){ // update url angular.forEach($scope.renderModel, function(item){ - if(item.id === entity.id) { - item.url = data; + if (item.id === entity.id) { + if (entity.trashed) { + item.url = localizationService.dictionary.general_recycleBin; + } else { + item.url = data; + } } }); }); @@ -330,6 +333,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper "icon": item.icon, "path": item.path, "url": item.url, + "trashed": item.trashed, "published": (item.metaData && item.metaData.IsPublished === false && entityType === "Document") ? false : true // only content supports published/unpublished content so we set everything else to published so the UI looks correct }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index 75cc74f02d..1755840cc8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -1,5 +1,8 @@
    +

    +

    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index 6652c52cc0..21a0717779 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -1,7 +1,7 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerController", - function ($rootScope, $scope, dialogService, entityResource, mediaResource, mediaHelper, $timeout, userService, $location) { + function ($rootScope, $scope, dialogService, entityResource, mediaResource, mediaHelper, $timeout, userService, $location, localizationService) { //check the pre-values for multi-picker var multiPicker = $scope.model.config.multiPicker && $scope.model.config.multiPicker !== '0' ? true : false; @@ -25,17 +25,44 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl // the mediaResource has server side auth configured for which the user must have // access to the media section, if they don't they'll get auth errors. The entityResource // acts differently in that it allows access if the user has access to any of the apps that - // might require it's use. Therefore we need to use the metatData property to get at the thumbnail + // might require it's use. Therefore we need to use the metaData property to get at the thumbnail // value. - entityResource.getByIds(ids, "Media").then(function (medias) { + entityResource.getByIds(ids, "Media").then(function(medias) { - _.each(medias, function (media, i) { + // The service only returns item results for ids that exist (deleted items are silently ignored). + // This results in the picked items value to be set to contain only ids of picked items that could actually be found. + // Since a referenced item could potentially be restored later on, instead of changing the selected values here based + // on whether the items exist during a save event - we should keep "placeholder" items for picked items that currently + // could not be fetched. This will preserve references and ensure that the state of an item does not differ depending + // on whether it is simply resaved or not. + // This is done by remapping the int/guid ids into a new array of items, where we create "Deleted item" placeholders + // when there is no match for a selected id. This will ensure that the values being set on save, are the same as before. + + medias = _.map(ids, + function(id) { + var found = _.find(medias, + function(m) { + return m.udi === id || m.id === id; + }); + if (found) { + return found; + } else { + return { + name: localizationService.dictionary.mediaPicker_deletedItem, + id: $scope.model.config.idType !== "udi" ? id : null, + udi: $scope.model.config.idType === "udi" ? id : null, + icon: "icon-picture", + thumbnail: null, + trashed: true + }; + } + }); - //only show non-trashed items - if (media.parentId >= -1) { - - if (!media.thumbnail) { + _.each(medias, + function(media, i) { + // if there is no thumbnail, try getting one if the media is not a placeholder item + if (!media.thumbnail && media.id && media.metaData) { media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); } @@ -43,12 +70,10 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl if ($scope.model.config.idType === "udi") { $scope.ids.push(media.udi); - } - else { + } else { $scope.ids.push(media.id); } - } - }); + }); $scope.sync(); }); @@ -80,8 +105,8 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl submit: function(model) { _.each(model.selectedImages, function(media, i) { - - if (!media.thumbnail) { + // if there is no thumbnail, try getting one if the media is not a placeholder item + if (!media.thumbnail && media.id && media.metaData) { media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); } @@ -99,10 +124,8 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl $scope.mediaPickerOverlay.show = false; $scope.mediaPickerOverlay = null; - } }; - }; $scope.sortableOptions = { @@ -140,5 +163,4 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl //update the display val again if it has changed from the server setupViewModel(); }; - }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html index f3aab992dd..70dd9ed2e5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html @@ -1,47 +1,47 @@
    -
      -
    • +

      +

      - - +
      +
        +
      • - - - + + - - - - {{image.name}} - + + + - + + + + {{image.name}} + -
      • -
      + +
    • - +
    + + + +
    - - + +
    diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index d01bea3d36..d64e8b82ed 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -784,6 +784,15 @@ Mange hilsner fra Umbraco robotten Du har ikke konfigureret nogen godkendte farver + + Du har valgt et dokument som er slettet eller lagt i papirkurven + Du har valgt dokumenter som er slettede eller lagt i papirkurven + + + Du har valgt et medie som er slettet eller lagt i papirkurven + Du har valgt medier som er slettede eller lagt i papirkurven + Slettet medie + indtast eksternt link vælg en intern side diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 1b71237ea4..4169c88335 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -952,6 +952,15 @@ To manage your website, simply open the Umbraco back office and start adding con You have not configured any approved colours + + You have picked a content item currently deleted or in the recycle bin + You have picked content items currently deleted or in the recycle bin + + + You have picked a media item currently deleted or in the recycle bin + You have picked media items currently deleted or in the recycle bin + Deleted item + enter external link choose internal page 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 c271ce8f65..13e0ebdad5 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -1011,6 +1011,15 @@ To manage your website, simply open the Umbraco back office and start adding con You have not configured any approved colors + + You have picked a content item currently deleted or in the recycle bin + You have picked content items currently deleted or in the recycle bin + + + You have picked a media item currently deleted or in the recycle bin + You have picked media items currently deleted or in the recycle bin + Deleted item + enter external link choose internal page diff --git a/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs b/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs index 9f6e5b28da..12afceea05 100644 --- a/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs @@ -2,10 +2,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.Linq; using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Models.Validation; diff --git a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs b/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs index 5be9d550e5..3d825ab055 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs @@ -20,7 +20,7 @@ namespace Umbraco.Web.Models.Mapping config.CreateMap() .ForMember(x => x.Udi, expression => expression.MapFrom(x => Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(x.NodeObjectTypeId), x.Key))) .ForMember(basic => basic.Icon, expression => expression.MapFrom(entity => entity.ContentTypeIcon)) - .ForMember(dto => dto.Trashed, expression => expression.Ignore()) + .ForMember(dto => dto.Trashed, expression => expression.MapFrom(x => x.Trashed)) .ForMember(x => x.Alias, expression => expression.Ignore()) .AfterMap((entity, basic) => { From 5df4d3509d569d48ab43d8e2b988cf34558a9585 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Fri, 13 Oct 2017 11:19:39 +0100 Subject: [PATCH 45/72] Fix in PowerShell build - The 7Zip tool download was giving two paths & thus break the build script (cherry picked from commit a2e6da24914bbb9f0d9ff8737a291b478f3ab448) --- build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 b/build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 index d1aaf7582f..2df42e5fd8 100644 --- a/build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 +++ b/build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 @@ -40,6 +40,7 @@ function Get-UmbracoBuildEnv &$nuget install 7-Zip.CommandLine -OutputDirectory $path -Verbosity quiet $dir = ls "$path\7-Zip.CommandLine.*" | sort -property Name -descending | select -first 1 $file = ls -path "$dir" -name 7za.exe -recurse + $file = ls -path "$dir" -name 7za.exe -recurse | select -first 1 #A select is because there is tools\7za.exe & tools\x64\7za.exe mv "$dir\$file" $sevenZip Remove-Directory $dir } From 80f172e3fbeff02e2d9cccb902a3a913d427d716 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 16 Oct 2017 14:56:43 +0200 Subject: [PATCH 46/72] Bump version to 7.7.3 --- src/SolutionInfo.cs | 4 ++-- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 662f0b666a..433a1e9a39 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -11,5 +11,5 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyFileVersion("7.7.2")] -[assembly: AssemblyInformationalVersion("7.7.2")] \ No newline at end of file +[assembly: AssemblyFileVersion("7.7.3")] +[assembly: AssemblyInformationalVersion("7.7.3")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 3bd45dabf2..4cd24ed56c 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration { public class UmbracoVersion { - private static readonly Version Version = new Version("7.7.2"); + private static readonly Version Version = new Version("7.7.3"); /// /// Gets the current version of Umbraco. diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index da10edda09..1a1b6bf7d3 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -1027,9 +1027,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" True True - 7720 + 7730 / - http://localhost:7720 + http://localhost:7730 False False From fb8c519f71c6672e7d256f4310b6e74b8d3ec244 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 11 Oct 2017 10:18:40 +0200 Subject: [PATCH 47/72] fixes: U4-10521 Creating new folders from the media picker no longer works (cherry picked from commit 1cc3b3960188a77580e49d493b55cdffe1d4c869) # Conflicts: # src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html --- .../src/less/components/umb-breadcrumbs.less | 7 +++++++ .../src/views/common/overlays/mediaPicker/mediapicker.html | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less index 2cad03a2ab..14a707a5bf 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less @@ -35,3 +35,10 @@ margin-right: 5px; color: @gray-7; } + +input.umb-breadcrumbs__add-ancestor { + height: 25px; + margin-top: -2px; + margin-left: 3px; + width: 100px; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html index ab745f0f75..2af3b1d834 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html @@ -55,7 +55,7 @@ Date: Thu, 12 Oct 2017 16:13:14 +0200 Subject: [PATCH 48/72] fixes breadcrumbs and search when start node is set (cherry picked from commit 1ec07aa205051f167b8a5ea98df930230a30e835) --- .../mediaPicker/mediapicker.controller.js | 71 ++++++++++++------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js index fb38581eac..37872ebd68 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js @@ -55,6 +55,46 @@ angular.module("umbraco") $scope.target = dialogOptions.currentTarget; } + function onInit() { + if ($scope.startNodeId !== -1) { + entityResource.getById($scope.startNodeId, "media") + .then(function (ent) { + $scope.startNodeId = ent.id; + run(); + }); + } else { + run(); + } + } + + function run() { + //default root item + if (!$scope.target) { + if ($scope.lastOpenedNode && $scope.lastOpenedNode !== -1) { + entityResource.getById($scope.lastOpenedNode, "media") + .then(ensureWithinStartNode, gotoStartNode); + } else { + gotoStartNode(); + } + } else { + //if a target is specified, go look it up - generally this target will just contain ids not the actual full + //media object so we need to look it up + var id = $scope.target.udi ? $scope.target.udi : $scope.target.id + var altText = $scope.target.altText; + mediaResource.getById(id) + .then(function (node) { + $scope.target = node; + if (ensureWithinStartNode(node)) { + selectImage(node); + $scope.target.url = mediaHelper.resolveFile(node); + $scope.target.altText = altText; + $scope.openDetailsDialog(); + } + }, + gotoStartNode); + } + } + $scope.upload = function(v) { angular.element(".umb-file-dropzone-directive .file-select").click(); }; @@ -106,7 +146,7 @@ angular.module("umbraco") if (folder.id > 0) { entityResource.getAncestors(folder.id, "media") - .then(function(anc) { + .then(function(anc) { $scope.path = _.filter(anc, function(f) { return f.path.indexOf($scope.startNodeId) !== -1; @@ -215,32 +255,6 @@ angular.module("umbraco") $scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" }); } - //default root item - if (!$scope.target) { - if ($scope.lastOpenedNode && $scope.lastOpenedNode !== -1) { - entityResource.getById($scope.lastOpenedNode, "media") - .then(ensureWithinStartNode, gotoStartNode); - } else { - gotoStartNode(); - } - } else { - //if a target is specified, go look it up - generally this target will just contain ids not the actual full - //media object so we need to look it up - var id = $scope.target.udi ? $scope.target.udi : $scope.target.id - var altText = $scope.target.altText; - mediaResource.getById(id) - .then(function(node) { - $scope.target = node; - if (ensureWithinStartNode(node)) { - selectImage(node); - $scope.target.url = mediaHelper.resolveFile(node); - $scope.target.altText = altText; - $scope.openDetailsDialog(); - } - }, - gotoStartNode); - } - $scope.openDetailsDialog = function() { $scope.mediaPickerDetailsOverlay = {}; @@ -365,4 +379,7 @@ angular.module("umbraco") } } } + + onInit(); + }); \ No newline at end of file From cefd9e7323ce4ebd84fa8fd7a868869004264dd7 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 12 Oct 2017 17:11:45 +1100 Subject: [PATCH 49/72] Fixes PostAddFolder (cherry picked from commit c5d874464a9e2ae9d3b423ed172b764d37a46576) # Conflicts: # src/Umbraco.Web/Editors/MediaController.cs --- src/Umbraco.Web/Editors/MediaController.cs | 93 ++++++++++++++----- .../Models/ContentEditing/PostedFolder.cs | 17 ++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 3 files changed, 89 insertions(+), 22 deletions(-) create mode 100644 src/Umbraco.Web/Models/ContentEditing/PostedFolder.cs diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 3ca4195a26..ee6b175355 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -413,7 +413,69 @@ namespace Umbraco.Web.Editors } return Request.CreateResponse(HttpStatusCode.OK); - } + } + + /// + /// Given a parent id which could be a GUID, UDI or an INT, this will resolve the INT + /// + /// + /// + /// If true, this will check if the current user has access to the resolved integer parent id + /// and if that check fails an unauthorized exception will occur + /// + /// + private int GetParentIdAsInt(string parentId, bool validatePermissions) + { + int intParentId; + GuidUdi parentUdi; + + // test for udi + if (GuidUdi.TryParse(parentId, out parentUdi)) + { + parentId = parentUdi.Guid.ToString(); + } + + //if it's not an INT then we'll check for GUID + if (int.TryParse(parentId, out intParentId) == false) + { + // if a guid then try to look up the entity + Guid idGuid; + if (Guid.TryParse(parentId, out idGuid)) + { + var entity = Services.EntityService.GetByKey(idGuid); + if (entity != null) + { + intParentId = entity.Id; + } + else + { + throw new EntityNotFoundException(parentId, "The passed id doesn't exist"); + } + } + else + { + throw new HttpResponseException( + Request.CreateValidationErrorResponse("The request was not formatted correctly, the parentId is not an integer, Guid or UDI")); + } + } + + //ensure the user has access to this folder by parent id! + if (CheckPermissions( + new Dictionary(), + Security.CurrentUser, + Services.MediaService, + intParentId) == false) + { + throw new HttpResponseException(Request.CreateResponse( + HttpStatusCode.Forbidden, + new SimpleNotificationModel(new Notification( + Services.TextService.Localize("speechBubbles/operationFailedHeader"), + Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"), + SpeechBubbleIcon.Warning)))); + } + + return intParentId; + } /// /// Change the sort order for media @@ -574,11 +636,13 @@ namespace Umbraco.Web.Editors } } - [EnsureUserPermissionForMedia("folder.ParentId")] - public MediaItemDisplay PostAddFolder(EntityBasic folder) + public MediaItemDisplay PostAddFolder(PostedFolder folder) { - var mediaService = ApplicationContext.Services.MediaService; - var f = mediaService.CreateMedia(folder.Name, folder.ParentId, Constants.Conventions.MediaTypes.Folder); + var intParentId = GetParentIdAsInt(folder.ParentId, validatePermissions: true); + + var mediaService = ApplicationContext.Services.MediaService; + + var f = mediaService.CreateMedia(folder.Name, intParentId, Constants.Conventions.MediaTypes.Folder); mediaService.Save(f, Security.CurrentUser.Id); return Mapper.Map(f); @@ -649,21 +713,6 @@ namespace Umbraco.Web.Editors } } - - //ensure the user has access to this folder by parent id! - if (CheckPermissions( - new Dictionary(), - Security.CurrentUser, - Services.MediaService, parentId) == false) - { - return Request.CreateResponse( - HttpStatusCode.Forbidden, - new SimpleNotificationModel(new Notification( - Services.TextService.Localize("speechBubbles/operationFailedHeader"), - Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"), - SpeechBubbleIcon.Warning))); - } - var tempFiles = new PostedFiles(); var mediaService = ApplicationContext.Services.MediaService; @@ -793,8 +842,8 @@ namespace Umbraco.Web.Editors if (origin.Value == "blueimp") { return Request.CreateResponse(HttpStatusCode.OK, - tempFiles, - //Don't output the angular xsrf stuff, blue imp doesn't like that + tempFiles, + //Don't output the angular xsrf stuff, blue imp doesn't like that new JsonMediaTypeFormatter()); } } diff --git a/src/Umbraco.Web/Models/ContentEditing/PostedFolder.cs b/src/Umbraco.Web/Models/ContentEditing/PostedFolder.cs new file mode 100644 index 0000000000..35cd908787 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/PostedFolder.cs @@ -0,0 +1,17 @@ +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + /// + /// Used to create a folder with the MediaController + /// + [DataContract] + public class PostedFolder + { + [DataMember(Name = "parentId")] + public string ParentId { get; set; } + + [DataMember(Name = "name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 16059f4e12..6b38bf6901 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -361,6 +361,7 @@ + From dc937ec9425840e516ae0d4ee194e8f3916406c9 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 16 Oct 2017 16:13:47 +0200 Subject: [PATCH 50/72] Fix the "Add media" box no longer being square --- src/Umbraco.Web.UI.Client/src/less/property-editors.less | 4 ++++ .../src/views/propertyeditors/mediapicker/mediapicker.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) 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 723a365eb0..9f536a7f0d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -165,6 +165,10 @@ ul.color-picker li a { text-decoration: none; } +.umb-mediapicker .add-link-square { + height: 120px; +} + .umb-thumbnails{ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html index 70dd9ed2e5..3ef1430cd3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html @@ -32,7 +32,7 @@
  • - + From 529ae961ffa31b4d768f0792901cd10db729d69c Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 16 Oct 2017 16:16:43 +0200 Subject: [PATCH 51/72] U4-5322 - add labels to color picker --- .../PropertyEditors/PreValueEditor.cs | 30 ++-- .../ColorPickerValueConverter.cs | 62 +++++++- .../src/less/property-editors.less | 38 ++++- .../colorpicker/colorpicker.controller.js | 138 ++++++++++++++++-- .../colorpicker/colorpicker.html | 8 +- .../colorpicker/colorpicker.prevalues.html | 5 +- .../multicolorpicker.controller.js | 52 +++++-- .../ColorListPreValueEditor.cs | 97 +++++++++++- 8 files changed, 362 insertions(+), 68 deletions(-) diff --git a/src/Umbraco.Core/PropertyEditors/PreValueEditor.cs b/src/Umbraco.Core/PropertyEditors/PreValueEditor.cs index d773eed2e9..0c8bec8f2e 100644 --- a/src/Umbraco.Core/PropertyEditors/PreValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PreValueEditor.cs @@ -178,29 +178,25 @@ namespace Umbraco.Core.PropertyEditors return result; } - private void ConvertItemsToJsonIfDetected(IDictionary result) + protected void ConvertItemsToJsonIfDetected(IDictionary result) { - //now we're going to try to see if any of the values are JSON, if they are we'll convert them to real JSON objects - // so they can be consumed as real json in angular! + // convert values that are Json to true Json objects that can be consumed by Angular var keys = result.Keys.ToArray(); for (var i = 0; i < keys.Length; i++) { - if (result[keys[i]] is string) + if ((result[keys[i]] is string) == false) continue; + + var asString = result[keys[i]].ToString(); + if (asString.DetectIsJson() == false) continue; + + try { - var asString = result[keys[i]].ToString(); - if (asString.DetectIsJson()) - { - try - { - var json = JsonConvert.DeserializeObject(asString); - result[keys[i]] = json; - } - catch - { - //swallow this exception, we thought it was json but it really isn't so continue returning a string - } - } + result[keys[i]] = JsonConvert.DeserializeObject(asString); + } + catch + { + // swallow this exception, we thought it was Json but it really isn't so continue returning a string } } } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/ColorPickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/ColorPickerValueConverter.cs index 987640716b..b0d2e0809d 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/ColorPickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/ColorPickerValueConverter.cs @@ -1,13 +1,14 @@ using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Umbraco.Core.Configuration; +using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Core.PropertyEditors.ValueConverters { [DefaultPropertyValueConverter] - [PropertyValueType(typeof(string))] - [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] - public class ColorPickerValueConverter : PropertyValueConverterBase + public class ColorPickerValueConverter : PropertyValueConverterBase, IPropertyValueConverterMeta { public override bool IsConverter(PublishedPropertyType propertyType) { @@ -18,11 +19,60 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters return false; } + public Type GetPropertyValueType(PublishedPropertyType propertyType) + { + return UseLabel(propertyType) ? typeof(PickedColor) : typeof(string); + } + + public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType, PropertyCacheValue cacheValue) + { + return PropertyCacheLevel.Content; + } + + private bool UseLabel(PublishedPropertyType propertyType) + { + var preValues = ApplicationContext.Current.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeId); + PreValue preValue; + return preValues.PreValuesAsDictionary.TryGetValue("useLabel", out preValue) && preValue.Value == "1"; + } + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) { - // make sure it's a string - return source == null ? string.Empty : source.ToString(); + var useLabel = UseLabel(propertyType); + + if (source == null) return useLabel ? null : string.Empty; + + var ssource = source.ToString(); + if (ssource.DetectIsJson()) + { + try + { + var jo = JsonConvert.DeserializeObject(ssource); + if (useLabel) return new PickedColor(jo["value"].ToString(), jo["label"].ToString()); + return jo["value"].ToString(); + } + catch { /* not json finally */ } + } + + if (useLabel) return new PickedColor(ssource, ssource); + return ssource; + } + + public class PickedColor + { + public PickedColor(string color, string label) + { + Color = color; + Label = label; + } + + public string Color { get; private set; } + public string Label { get; private set; } + + public override string ToString() + { + return Color; + } } - } } 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 8b10b2f91b..c2072a1d50 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -109,16 +109,48 @@ ul.color-picker li a { } /* pre-value editor */ - +/*.control-group.color-picker-preval:before { + content: ""; + display: inline-block; + vertical-align: middle; + height: 100%; +}*/ + +/*.control-group.color-picker-preval div.thumbnail { + display: inline-block; + vertical-align: middle; +}*/ +.control-group.color-picker-preval div.color-picker-prediv { + display: inline-block; + width: 60%; +} + .control-group.color-picker-preval pre { display: inline; margin-right: 20px; margin-left: 10px; + width: 50%; + white-space: nowrap; + overflow: hidden; + margin-bottom: 0; + vertical-align: middle; } +.control-group.color-picker-preval btn { + //vertical-align: middle; +} + +.control-group.color-picker-preval input[type="text"] { + min-width: 40%; + width: 40%; + display: inline-block; + margin-right: 20px; + margin-top: 1px; +} + .control-group.color-picker-preval label { - border:solid @white 1px; - padding:6px; + border: solid @white 1px; + padding: 6px; } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js index f14492e88a..7d847dbc83 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js @@ -1,25 +1,131 @@ -function ColorPickerController($scope) { - $scope.toggleItem = function (color) { - if ($scope.model.value == color) { - $scope.model.value = ""; - //this is required to re-validate - $scope.propertyForm.modelValue.$setViewValue($scope.model.value); - } - else { - $scope.model.value = color; - //this is required to re-validate - $scope.propertyForm.modelValue.$setViewValue($scope.model.value); - } - }; +function ColorPickerController($scope) { + + $scope.isConfigured = $scope.model.config && $scope.model.config.items && _.keys($scope.model.config.items).length > 0; + + if ($scope.isConfigured) { + + for (var key in $scope.model.config.items) { + if (!$scope.model.config.items[key].hasOwnProperty("value")) + $scope.model.config.items[key] = { value: $scope.model.config.items[key], label: $scope.model.config.items[key] }; + } + + $scope.model.useLabel = isTrue($scope.model.config.useLabel); + initActiveColor(); + } + + $scope.toggleItem = function (color) { + + var currentColor = $scope.model.value.hasOwnProperty("value") + ? $scope.model.value.value + : $scope.model.value; + + var newColor; + if (currentColor === color.value) { + // deselect + $scope.model.value = $scope.model.useLabel ? { value: "", label: "" } : ""; + newColor = ""; + } + else { + // select + $scope.model.value = $scope.model.useLabel ? { value: color.value, label: color.label } : color.value; + newColor = color.value; + } + + // this is required to re-validate + $scope.propertyForm.modelValue.$setViewValue(newColor); + }; + // Method required by the valPropertyValidator directive (returns true if the property editor has at least one color selected) - $scope.validateMandatory = function () { + $scope.validateMandatory = function () { + var isValid = !$scope.model.validation.mandatory || ( + $scope.model.value != null + && $scope.model.value != "" + && (!$scope.model.value.hasOwnProperty("value") || $scope.model.value.value !== "") + ); return { - isValid: !$scope.model.validation.mandatory || ($scope.model.value != null && $scope.model.value != ""), + isValid: isValid, errorMsg: "Value cannot be empty", errorKey: "required" }; } - $scope.isConfigured = $scope.model.config && $scope.model.config.items && _.keys($scope.model.config.items).length > 0; + $scope.isConfigured = $scope.model.config && $scope.model.config.items && _.keys($scope.model.config.items).length > 0; + + // A color is active if it matches the value and label of the model. + // If the model doesn't store the label, ignore the label during the comparison. + $scope.isActiveColor = function (color) { + + // no value + if (!$scope.model.value) + return false; + + // Complex color (value and label)? + if (!$scope.model.value.hasOwnProperty("value")) + return $scope.model.value === color.value; + + return $scope.model.value.value === color.value && $scope.model.value.label === color.label; + }; + + // Finds the color best matching the model's color, + // and sets the model color to that one. This is useful when + // either the value or label was changed on the data type. + function initActiveColor() { + + // no value + if (!$scope.model.value) + return; + + // Complex color (value and label)? + if (!$scope.model.value.hasOwnProperty("value")) + return; + + var modelColor = $scope.model.value.value; + var modelLabel = $scope.model.value.label; + + // Check for a full match or partial match. + var foundItem = null; + + // Look for a fully matching color. + for (var key in $scope.model.config.items) { + var item = $scope.model.config.items[key]; + if (item.value == modelColor && item.label == modelLabel) { + foundItem = item; + break; + } + } + + // Look for a color with a matching value. + if (!foundItem) { + for (var key in $scope.model.config.items) { + var item = $scope.model.config.items[key]; + if (item.value == modelColor) { + foundItem = item; + break; + } + } + } + + // Look for a color with a matching label. + if (!foundItem) { + for (var key in $scope.model.config.items) { + var item = $scope.model.config.items[key]; + if (item.label == modelLabel) { + foundItem = item; + break; + } + } + } + + // If a match was found, set it as the active color. + if (foundItem) { + $scope.model.value.value = foundItem.value; + $scope.model.value.label = foundItem.label; + } + } + + // figures out if a value is trueish enough + function isTrue(bool) { + return !!bool && bool !== "0" && angular.lowercase(bool) !== "false"; + } } angular.module("umbraco").controller("Umbraco.PropertyEditors.ColorPickerController", ColorPickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html index a493fffdd8..46b624adcc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html @@ -5,10 +5,10 @@ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.prevalues.html index a772f830c5..2b917e69f8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.prevalues.html @@ -1,12 +1,13 @@
    + + -
    -
    {{item.value}}
    +
    #{{item.value}} - {{item.label}}
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/multicolorpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/multicolorpicker.controller.js index d31ac911a9..dd25741aeb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/multicolorpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/multicolorpicker.controller.js @@ -2,15 +2,17 @@ function ($scope, $timeout, assetsService, angularHelper, $element) { //NOTE: We need to make each color an object, not just a string because you cannot 2-way bind to a primitive. var defaultColor = "000000"; - + var defaultLabel = null; + $scope.newColor = defaultColor; + $scope.newLavel = defaultLabel; $scope.hasError = false; assetsService.load([ //"lib/spectrum/tinycolor.js", - "lib/spectrum/spectrum.js" + "lib/spectrum/spectrum.js" ], $scope).then(function () { - var elem = $element.find("input"); + var elem = $element.find("input[name='newColor']"); elem.spectrum({ color: null, showInitial: false, @@ -21,7 +23,7 @@ clickoutFiresChange: true, hide: function (color) { //show the add butotn - $element.find(".btn.add").show(); + $element.find(".btn.add").show(); }, change: function (color) { angularHelper.safeApply($scope, function () { @@ -39,21 +41,41 @@ //make an array from the dictionary var items = []; for (var i in $scope.model.value) { - items.push({ - value: $scope.model.value[i], - id: i - }); + var oldValue = $scope.model.value[i]; + if (oldValue.hasOwnProperty("value")) { + items.push({ + value: oldValue.value, + label: oldValue.label, + id: i + }); + } else { + items.push({ + value: oldValue, + label: oldValue, + id: i + }); + } } //now make the editor model the array $scope.model.value = items; } + // ensure labels + for (var i = 0; i < $scope.model.value.length; i++) { + var item = $scope.model.value[i]; + item.label = item.hasOwnProperty("label") ? item.label : item.value; + } + + function validLabel(label) { + return label !== null && typeof label !== "undefined" && label !== "" && label.length && label.length > 0; + } + $scope.remove = function (item, evt) { evt.preventDefault(); $scope.model.value = _.reject($scope.model.value, function (x) { - return x.value === item.value; + return x.value === item.value && x.label === item.label; }); }; @@ -63,15 +85,15 @@ evt.preventDefault(); if ($scope.newColor) { + var newLabel = validLabel($scope.newLabel) ? $scope.newLabel : $scope.newColor; var exists = _.find($scope.model.value, function(item) { - return item.value.toUpperCase() == $scope.newColor.toUpperCase(); + return item.value.toUpperCase() === $scope.newColor.toUpperCase() || item.label.toUpperCase() === newLabel.toUpperCase(); }); if (!exists) { - $scope.model.value.push({ value: $scope.newColor }); - //$scope.newColor = defaultColor; - // set colorpicker to default color - //var elem = $element.find("input"); - //elem.spectrum("set", $scope.newColor); + $scope.model.value.push({ + value: $scope.newColor, + label: newLabel + }); $scope.hasError = false; return; } diff --git a/src/Umbraco.Web/PropertyEditors/ColorListPreValueEditor.cs b/src/Umbraco.Web/PropertyEditors/ColorListPreValueEditor.cs index e7750c4c65..a504ed0431 100644 --- a/src/Umbraco.Web/PropertyEditors/ColorListPreValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ColorListPreValueEditor.cs @@ -1,9 +1,12 @@ +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text.RegularExpressions; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Umbraco.Core; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; @@ -11,7 +14,7 @@ namespace Umbraco.Web.PropertyEditors { internal class ColorListPreValueEditor : ValueListPreValueEditor { - + public ColorListPreValueEditor() { var field = Fields.First(); @@ -23,14 +26,98 @@ namespace Umbraco.Web.PropertyEditors //change the label field.Name = "Add color"; //need to have some custom validation happening here - field.Validators.Add(new ColorListValidator()); + field.Validators.Add(new ColorListValidator()); + + Fields.Insert(0, new PreValueField + { + Name = "Include labels?", + View = "boolean", + Key = "useLabel", + Description = "Stores colors as a Json object containing both the color hex string and label, rather than just the hex string." + }); } public override IDictionary ConvertDbToEditor(IDictionary defaultPreVals, PreValueCollection persistedPreVals) { var dictionary = persistedPreVals.FormatAsDictionary(); - var arrayOfVals = dictionary.Select(item => item.Value).ToList(); - return new Dictionary { { "items", arrayOfVals.ToDictionary(x => x.Id, x => x.Value) } }; + var items = dictionary + .Where(x => x.Key != "useLabel") + .ToDictionary(x => x.Value.Id, x => x.Value.Value); + + var items2 = new Dictionary(); + foreach (var item in items) + { + if (item.Value.DetectIsJson() == false) + { + items2[item.Key] = item.Value; + continue; + } + + try + { + items2[item.Key] = JsonConvert.DeserializeObject(item.Value); + } + catch + { + // let's say parsing Json failed, so what we have is the string - build json + items2[item.Key] = new JObject { { "color", item.Value }, { "label", item.Value } }; + } + } + + var result = new Dictionary { { "items", items2 } }; + var useLabel = dictionary.ContainsKey("useLabel") && dictionary["useLabel"].Value == "1"; + if (useLabel) + result["useLabel"] = dictionary["useLabel"].Value; + + return result; + } + + public override IDictionary ConvertEditorToDb(IDictionary editorValue, PreValueCollection currentValue) + { + var val = editorValue["items"] as JArray; + var result = new Dictionary(); + if (val == null) return result; + + try + { + object useLabelObj; + var useLabel = false; + if (editorValue.TryGetValue("useLabel", out useLabelObj)) + { + useLabel = useLabelObj is string && (string) useLabelObj == "1"; + result["useLabel"] = new PreValue(useLabel ? "1" : "0"); + } + + // get all non-empty values + var index = 0; + foreach (var preValue in val.OfType() + .Where(x => x["value"] != null) + .Select(x => + { + var idString = x["id"] == null ? "0" : x["id"].ToString(); + int id; + if (int.TryParse(idString, out id) == false) id = 0; + + var color = x["value"].ToString(); + if (string.IsNullOrWhiteSpace(color)) return null; + + var label = x["label"].ToString(); + return new PreValue(id, useLabel + ? JsonConvert.SerializeObject(new { value = color, label = label }) + : color); + }) + .WhereNotNull()) + { + result.Add(index.ToInvariantString(), preValue); + index++; + } + } + catch (Exception ex) + { + LogHelper.Error("Could not deserialize the posted value: " + val, ex); + } + + return result; } internal class ColorListValidator : IPropertyValidator @@ -39,7 +126,7 @@ namespace Umbraco.Web.PropertyEditors { var json = value as JArray; if (json == null) yield break; - + //validate each item which is a json object for (var index = 0; index < json.Count; index++) { From 09a92ef6ceb4daf6b57bbb3eb97227ace1330a26 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 16 Oct 2017 16:58:15 +0200 Subject: [PATCH 52/72] U4-7360 - toggle password visibility --- .../components/umbpasswordtoggle.directive.js | 23 ++++++++++ .../src/less/pages/login.less | 46 +++++++++++++++++-- .../src/views/common/dialogs/login.html | 16 +++---- 3 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/umbpasswordtoggle.directive.js diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpasswordtoggle.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpasswordtoggle.directive.js new file mode 100644 index 0000000000..30cfaf88b2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpasswordtoggle.directive.js @@ -0,0 +1,23 @@ +(function () { + 'use strict'; + + function PasswordToggleDirective($compile) { + + var directive = { + restrict: 'A', + scope: {}, + link: function(scope, elem, attrs) { + scope.tgl = function () { elem.attr("type", (elem.attr("type") === "text" ? "password" : "text")); } + var lnk = angular.element("Toggle"); + $compile(lnk)(scope); + elem.wrap("
    ").after(lnk); + } + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbPasswordToggle', PasswordToggleDirective); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/less/pages/login.less b/src/Umbraco.Web.UI.Client/src/less/pages/login.less index 924f9b20bb..da91c07623 100644 --- a/src/Umbraco.Web.UI.Client/src/less/pages/login.less +++ b/src/Umbraco.Web.UI.Client/src/less/pages/login.less @@ -66,7 +66,7 @@ margin-bottom: auto; } -.login-overlay .form input[type="text"], +.login-overlay .form input[type="text"], .login-overlay .form input[type="password"], .login-overlay .form input[type="email"] { height: 36px; @@ -114,8 +114,46 @@ line-height: 36px; } -.login-overlay .text-error, -.login-overlay .text-info +.login-overlay .text-error, +.login-overlay .text-info { font-weight:bold; -} +} + +.password-toggle { + + position: relative; + display: block; + user-select: none; + + input::-ms-clear, input::-ms-reveal { + display: none; + } + + a { + opacity: .5; + cursor: pointer; + display: inline-block; + position: absolute; + height: 1px; + width: 45px; + height: 75%; + font-size: 0; + background-repeat: no-repeat; + background-size: 50%; + background-position: center; + top: 0; + margin-left: -45px; + z-index: 1; + -webkit-tap-highlight-color: transparent; + } + + [type="text"] + a { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill='%23444' d='M29.6.4C29 0 28 0 27.4.4L21 6.8c-1.4-.5-3-.8-5-.8C9 6 3 10 0 16c1.3 2.6 3 4.8 5.4 6.5l-5 5c-.5.5-.5 1.5 0 2 .3.4.7.5 1 .5s1 0 1.2-.4l27-27C30 2 30 1 29.6.4zM13 10c1.3 0 2.4 1 2.8 2L12 15.8c-1-.4-2-1.5-2-2.8 0-1.7 1.3-3 3-3zm-9.6 6c1.2-2 2.8-3.5 4.7-4.7l.7-.2c-.4 1-.6 2-.6 3 0 1.8.6 3.4 1.6 4.7l-2 2c-1.6-1.2-3-2.7-4-4.4zM24 13.8c0-.8 0-1.7-.4-2.4l-10 10c.7.3 1.6.4 2.4.4 4.4 0 8-3.6 8-8z'/%3E%3Cpath fill='%23444' d='M26 9l-2.2 2.2c2 1.3 3.6 3 4.8 4.8-1.2 2-2.8 3.5-4.7 4.7-2.7 1.5-5.4 2.3-8 2.3-1.4 0-2.6 0-3.8-.4L10 25c2 .6 4 1 6 1 7 0 13-4 16-10-1.4-2.8-3.5-5.2-6-7z'/%3E%3C/svg%3E"); + } + + [type="password"] + a { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath fill='%23444' d='M16 6C9 6 3 10 0 16c3 6 9 10 16 10s13-4 16-10c-3-6-9-10-16-10zm8 5.3c1.8 1.2 3.4 2.8 4.6 4.7-1.2 2-2.8 3.5-4.7 4.7-3 1.5-6 2.3-8 2.3s-6-.8-8-2.3C6 19.5 4 18 3 16c1.5-2 3-3.5 5-4.7l.6-.2C8 12 8 13 8 14c0 4.5 3.5 8 8 8s8-3.5 8-8c0-1-.3-2-.6-2.6l.4.3zM16 13c0 1.7-1.3 3-3 3s-3-1.3-3-3 1.3-3 3-3 3 1.3 3 3z'/%3E%3C/svg%3E"); + } + +} diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html index 61d04d30fb..a1913d76e7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html @@ -24,8 +24,8 @@ Your new password cannot be blank! - Minimum {{invitedUserPasswordModel.passwordPolicies.minPasswordLength}} characters - + Minimum {{invitedUserPasswordModel.passwordPolicies.minPasswordLength}} characters +
    @@ -49,15 +49,15 @@
    - + - + - +
    {{ avatarFile.serverErrorMessage }}
    @@ -69,7 +69,7 @@ ngf-multiple="false" ngf-pattern="{{avatarFile.acceptedFileTypes}}" ngf-max-size="{{ avatarFile.maxFileSize }}"> - + - +
    @@ -149,7 +149,7 @@
    - +
    From f46ef0b006139e16be551595ab79e87e04435a9e Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 6 Oct 2017 15:14:41 +0200 Subject: [PATCH 53/72] U4-10506 Importing a specially crafted document type file can cause XXE attack (cherry picked from commit 5dde2efe0d2b3a47d17439e03acabb7ea2befb64) --- .../umbraco/dialogs/importDocumenttype.aspx.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs index 27c1724bff..147e7604c1 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs @@ -70,10 +70,11 @@ namespace umbraco.presentation.umbraco.dialogs private void import_Click(object sender, EventArgs e) { var xd = new XmlDocument(); + xd.XmlResolver = null; xd.Load(tempFile.Value); var userId = base.getUser().Id; - + var element = XElement.Parse(xd.InnerXml); var importContentTypes = ApplicationContext.Current.Services.PackagingService.ImportContentTypes(element, userId); var contentType = importContentTypes.FirstOrDefault(); @@ -104,7 +105,8 @@ namespace umbraco.presentation.umbraco.dialogs documentTypeFile.PostedFile.SaveAs(fileName); var xd = new XmlDocument(); - xd.Load(fileName); + xd.XmlResolver = null; + xd.Load(fileName); dtName.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Name").FirstChild.Value; dtAlias.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Alias").FirstChild.Value; From fd577afe2c05a95226594ebe26f49e414ef31a70 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 6 Oct 2017 14:38:07 +0200 Subject: [PATCH 54/72] Html encode nodenames to prevent XSS attacks. Fixes U4-10497 XSS Vulnerability in page name. (cherry picked from commit fe2b86b681455ac975b294652064b2718d4e2ba2) --- src/Umbraco.Web.UI/umbraco/dialogs/Publish.aspx.cs | 2 +- .../umbraco.presentation/umbraco/dialogs/notifications.aspx.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/dialogs/Publish.aspx.cs b/src/Umbraco.Web.UI/umbraco/dialogs/Publish.aspx.cs index 7e09d0b425..ababea628a 100644 --- a/src/Umbraco.Web.UI/umbraco/dialogs/Publish.aspx.cs +++ b/src/Umbraco.Web.UI/umbraco/dialogs/Publish.aspx.cs @@ -30,7 +30,7 @@ namespace Umbraco.Web.UI.Umbraco.Dialogs } DocumentId = doc.Id; - PageName = doc.Name; + PageName = Server.HtmlEncode(doc.Name); DocumentPath = doc.Path; } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs index 97dc8d84d3..9010a70164 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs @@ -26,7 +26,7 @@ namespace umbraco.dialogs protected void Page_Load(object sender, EventArgs e) { Button1.Text = ui.Text("update"); - pane_form.Text = ui.Text("notifications", "editNotifications", node.Text, base.getUser()); + pane_form.Text = ui.Text("notifications", "editNotifications", Server.HtmlEncode(node.Text), base.getUser()); } #region Web Form Designer generated code From c5a55b17e6b1a10b92d922c6a21ea5c1d80e9a7a Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 17 Oct 2017 10:08:37 +0200 Subject: [PATCH 55/72] Manually applying U4-10369: Split out ConfigureUmbracoAuthentication method --- src/Umbraco.Web/UmbracoDefaultOwinStartup.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs index 5773d88f73..61568e3cb8 100644 --- a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs +++ b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs @@ -46,12 +46,8 @@ namespace Umbraco.Web /// protected virtual void ConfigureMiddleware(IAppBuilder app) { - //Ensure owin is configured for Umbraco back office authentication. If you have any front-end OWIN - // cookie configuration, this must be declared after it. + // Configure OWIN for authentication. app - .UseUmbracoBackOfficeCookieAuthentication(ApplicationContext, PipelineStage.Authenticate) - .UseUmbracoBackOfficeExternalCookieAuthentication(ApplicationContext, PipelineStage.Authenticate) - .UseUmbracoPreviewAuthentication(ApplicationContext, PipelineStage.Authorize) .UseSignalR() .FinalizeMiddlewareConfiguration(); } @@ -68,6 +64,20 @@ namespace Umbraco.Web Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider()); } + /// + /// Configure external/OAuth login providers + /// + /// + protected virtual void ConfigureUmbracoAuthentication(IAppBuilder app) + { + // Ensure owin is configured for Umbraco back office authentication. + // Front-end OWIN cookie configuration must be declared after this code. + app + .UseUmbracoBackOfficeCookieAuthentication(ApplicationContext, PipelineStage.Authenticate) + .UseUmbracoBackOfficeExternalCookieAuthentication(ApplicationContext, PipelineStage.Authenticate) + .UseUmbracoPreviewAuthentication(ApplicationContext, PipelineStage.Authorize); + } + /// /// Raised when the middleware has been configured /// From 461ce64feb031298b78d91e73572391b58a5835a Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 17 Oct 2017 10:57:18 +0200 Subject: [PATCH 56/72] Bump version to 7.6.10 --- src/SolutionInfo.cs | 4 ++-- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index a333fa499d..5de3f4ec9a 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -11,5 +11,5 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyFileVersion("7.6.9")] -[assembly: AssemblyInformationalVersion("7.6.9")] \ No newline at end of file +[assembly: AssemblyFileVersion("7.6.10")] +[assembly: AssemblyInformationalVersion("7.6.10")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 1c1724ae84..26b70ac6b6 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration { public class UmbracoVersion { - private static readonly Version Version = new Version("7.6.9"); + private static readonly Version Version = new Version("7.6.10"); /// /// Gets the current version of Umbraco. diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 15d48f8f1e..9979a69cfb 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -2379,9 +2379,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" True True - 7690 + 7610 / - http://localhost:7690 + http://localhost:7610 False False From 9411a22a0d90d10ccc9b76a150342cb8cec632b5 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 17 Oct 2017 11:04:01 +0200 Subject: [PATCH 57/72] Don't forget to call the ConfigureUmbracoAuthentication method --- src/Umbraco.Web/UmbracoDefaultOwinStartup.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs index 61568e3cb8..a3b9dac101 100644 --- a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs +++ b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs @@ -46,7 +46,9 @@ namespace Umbraco.Web /// protected virtual void ConfigureMiddleware(IAppBuilder app) { - // Configure OWIN for authentication. + // Configure OWIN for authentication. + ConfigureUmbracoAuthentication(app); + app .UseSignalR() .FinalizeMiddlewareConfiguration(); From 7de8f5a59a37343dbbc5b7171762ca681d9daa88 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 17 Oct 2017 12:09:55 +0200 Subject: [PATCH 58/72] Added missing width and height to the "show password" icon Not having the height and width on the icon, caused IE 11 to shrink the icon which looked a bit off. --- src/Umbraco.Web.UI.Client/src/less/pages/login.less | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/pages/login.less b/src/Umbraco.Web.UI.Client/src/less/pages/login.less index da91c07623..2fe214bf07 100644 --- a/src/Umbraco.Web.UI.Client/src/less/pages/login.less +++ b/src/Umbraco.Web.UI.Client/src/less/pages/login.less @@ -121,7 +121,6 @@ } .password-toggle { - position: relative; display: block; user-select: none; @@ -149,11 +148,10 @@ } [type="text"] + a { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill='%23444' d='M29.6.4C29 0 28 0 27.4.4L21 6.8c-1.4-.5-3-.8-5-.8C9 6 3 10 0 16c1.3 2.6 3 4.8 5.4 6.5l-5 5c-.5.5-.5 1.5 0 2 .3.4.7.5 1 .5s1 0 1.2-.4l27-27C30 2 30 1 29.6.4zM13 10c1.3 0 2.4 1 2.8 2L12 15.8c-1-.4-2-1.5-2-2.8 0-1.7 1.3-3 3-3zm-9.6 6c1.2-2 2.8-3.5 4.7-4.7l.7-.2c-.4 1-.6 2-.6 3 0 1.8.6 3.4 1.6 4.7l-2 2c-1.6-1.2-3-2.7-4-4.4zM24 13.8c0-.8 0-1.7-.4-2.4l-10 10c.7.3 1.6.4 2.4.4 4.4 0 8-3.6 8-8z'/%3E%3Cpath fill='%23444' d='M26 9l-2.2 2.2c2 1.3 3.6 3 4.8 4.8-1.2 2-2.8 3.5-4.7 4.7-2.7 1.5-5.4 2.3-8 2.3-1.4 0-2.6 0-3.8-.4L10 25c2 .6 4 1 6 1 7 0 13-4 16-10-1.4-2.8-3.5-5.2-6-7z'/%3E%3C/svg%3E"); + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath fill='%23444' d='M29.6.4C29 0 28 0 27.4.4L21 6.8c-1.4-.5-3-.8-5-.8C9 6 3 10 0 16c1.3 2.6 3 4.8 5.4 6.5l-5 5c-.5.5-.5 1.5 0 2 .3.4.7.5 1 .5s1 0 1.2-.4l27-27C30 2 30 1 29.6.4zM13 10c1.3 0 2.4 1 2.8 2L12 15.8c-1-.4-2-1.5-2-2.8 0-1.7 1.3-3 3-3zm-9.6 6c1.2-2 2.8-3.5 4.7-4.7l.7-.2c-.4 1-.6 2-.6 3 0 1.8.6 3.4 1.6 4.7l-2 2c-1.6-1.2-3-2.7-4-4.4zM24 13.8c0-.8 0-1.7-.4-2.4l-10 10c.7.3 1.6.4 2.4.4 4.4 0 8-3.6 8-8z'/%3E%3Cpath fill='%23444' d='M26 9l-2.2 2.2c2 1.3 3.6 3 4.8 4.8-1.2 2-2.8 3.5-4.7 4.7-2.7 1.5-5.4 2.3-8 2.3-1.4 0-2.6 0-3.8-.4L10 25c2 .6 4 1 6 1 7 0 13-4 16-10-1.4-2.8-3.5-5.2-6-7z'/%3E%3C/svg%3E"); } [type="password"] + a { background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath fill='%23444' d='M16 6C9 6 3 10 0 16c3 6 9 10 16 10s13-4 16-10c-3-6-9-10-16-10zm8 5.3c1.8 1.2 3.4 2.8 4.6 4.7-1.2 2-2.8 3.5-4.7 4.7-3 1.5-6 2.3-8 2.3s-6-.8-8-2.3C6 19.5 4 18 3 16c1.5-2 3-3.5 5-4.7l.6-.2C8 12 8 13 8 14c0 4.5 3.5 8 8 8s8-3.5 8-8c0-1-.3-2-.6-2.6l.4.3zM16 13c0 1.7-1.3 3-3 3s-3-1.3-3-3 1.3-3 3-3 3 1.3 3 3z'/%3E%3C/svg%3E"); } - } From 11342ff1dfd3da2a82426291281849e4785bcb36 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 17 Oct 2017 14:04:24 +0200 Subject: [PATCH 59/72] U4-8652 - accept exceptions when loading some assemblies --- src/Umbraco.Core/TypeFinder.cs | 50 ++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/TypeFinder.cs b/src/Umbraco.Core/TypeFinder.cs index ab2b24392e..6d85ea5a8a 100644 --- a/src/Umbraco.Core/TypeFinder.cs +++ b/src/Umbraco.Core/TypeFinder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.IO; using System.Linq; using System.Reflection; @@ -21,6 +22,38 @@ namespace Umbraco.Core { private static volatile HashSet _localFilteredAssemblyCache; private static readonly object LocalFilteredAssemblyCacheLocker = new object(); + private static readonly List NotifiedLoadExceptionAssemblies = new List(); + private static string[] _assembliesAcceptingLoadExceptions; + + private static string[] AssembliesAcceptingLoadExceptions + { + get + { + if (_assembliesAcceptingLoadExceptions != null) + return _assembliesAcceptingLoadExceptions; + + var s = ConfigurationManager.AppSettings["Umbraco.AssembliesAcceptingLoadExceptions"]; + return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s) + ? new string[0] + : s.Split(',').Select(x => x.Trim()).ToArray(); + } + } + + private static bool AcceptsLoadExceptions(Assembly a) + { + if (AssembliesAcceptingLoadExceptions.Length == 0) + return false; + if (AssembliesAcceptingLoadExceptions.Length == 1 && AssembliesAcceptingLoadExceptions[0] == "*") + return true; + var name = a.GetName().Name; // simple name of the assembly + return AssembliesAcceptingLoadExceptions.Any(pattern => + { + if (pattern.Length > name.Length) return false; // pattern longer than name + if (pattern.Length == name.Length) return pattern.InvariantEquals(name); // same length, must be identical + if (pattern[pattern.Length] != '.') return false; // pattern is shorter than name, must end with dot + return name.StartsWith(pattern); // and name must start with pattern + }); + } /// /// lazily load a reference to all assemblies and only local assemblies. @@ -528,9 +561,22 @@ namespace Umbraco.Core AppendCouldNotLoad(sb, a, getAll); foreach (var loaderException in rex.LoaderExceptions.WhereNotNull()) AppendLoaderException(sb, loaderException); + + var ex = new ReflectionTypeLoadException(rex.Types, rex.LoaderExceptions, sb.ToString()); - // rethrow with new message - throw new ReflectionTypeLoadException(rex.Types, rex.LoaderExceptions, sb.ToString()); + // rethrow with new message, unless accepted + if (AcceptsLoadExceptions(a) == false) throw ex; + + // log a warning, and return what we can + lock (NotifiedLoadExceptionAssemblies) + { + if (NotifiedLoadExceptionAssemblies.Contains(a.FullName) == false) + { + NotifiedLoadExceptionAssemblies.Add(a.FullName); + LogHelper.WarnWithException(typeof(TypeFinder), "Could not load all types from " + a.GetName().Name + ".", ex); + } + } + return rex.Types.WhereNotNull().ToArray(); } } From 24b147e2a6ac92a5c06148a12e4fc4d6aaea3848 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 17 Oct 2017 14:39:47 +0200 Subject: [PATCH 60/72] U4-10554 - warn if closing page with changes --- .../validation/valformmanager.directive.js | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js index 9a00d5718c..ab22894a21 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js @@ -3,7 +3,7 @@ * @name umbraco.directives.directive:valFormManager * @restrict A * @require formController -* @description Used to broadcast an event to all elements inside this one to notify that form validation has +* @description Used to broadcast an event to all elements inside this one to notify that form validation has * changed. If we don't use this that means you have to put a watch for each directive on a form's validation * changing which would result in much higher processing. We need to actually watch the whole $error collection of a form * because just watching $valid or $invalid doesn't acurrately trigger form validation changing. @@ -12,7 +12,7 @@ * Another thing this directive does is to ensure that any .control-group that contains form elements that are invalid will * be marked with the 'error' css class. This ensures that labels included in that control group are styled correctly. **/ -function valFormManager(serverValidationManager, $rootScope, $log, $timeout, notificationsService, eventsService, $routeParams) { +function valFormManager(serverValidationManager, $rootScope, $log, $timeout, notificationsService, eventsService, $routeParams, $window) { return { require: "form", restrict: "A", @@ -43,7 +43,7 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not return formCtrl.$error; }, function (e) { scope.$broadcast("valStatusChanged", { form: formCtrl }); - + //find all invalid elements' .control-group's and apply the error class var inError = element.find(".control-group .ng-invalid").closest(".control-group"); inError.addClass("error"); @@ -53,12 +53,12 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not noInError.removeClass("error"); }, true); - + var className = attr.valShowValidation ? attr.valShowValidation : "show-validation"; var savingEventName = attr.savingEvent ? attr.savingEvent : "formSubmitting"; var savedEvent = attr.savedEvent ? attr.savingEvent : "formSubmitted"; - //This tracks if the user is currently saving a new item, we use this to determine + //This tracks if the user is currently saving a new item, we use this to determine // if we should display the warning dialog that they are leaving the page - if a new item // is being saved we never want to display that dialog, this will also cause problems when there // are server side validation issues. @@ -85,7 +85,7 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not element.removeClass(className); //clear form state as at this point we retrieve new data from the server - //and all validation will have cleared at this point + //and all validation will have cleared at this point formCtrl.$setPristine(); })); @@ -117,11 +117,19 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not }); unsubscribe.push(locationEvent); + var savedUnloadHandler = $window.onbeforeunload; + $window.onbeforeunload = function () { + if (formCtrl.$dirty) { + return "You have unsaved changes."; // most browsers won't display it anyways + } + } + //Ensure to remove the event handler when this instance is destroyted scope.$on('$destroy', function() { for (var u in unsubscribe) { unsubscribe[u](); } + $window.onbeforeunload = savedUnloadHandler; }); $timeout(function(){ From 353b8f21cd86585a364eba31b5355b4531ef7bc7 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 17 Oct 2017 16:04:31 +0200 Subject: [PATCH 61/72] U4-7317 - select default template --- src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index 1809bffd3d..724ba26b9e 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -107,7 +107,7 @@ namespace Umbraco.Web.Models.Mapping } //fill in the template config to be passed to the template drop down. - var templateItemConfig = new Dictionary {{"", localizedText.Localize("general/choose")}}; + var templateItemConfig = new Dictionary(); foreach (var t in content.ContentType.AllowedTemplates .Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false)) { @@ -158,7 +158,9 @@ namespace Umbraco.Web.Models.Mapping { Alias = string.Format("{0}template", Constants.PropertyEditors.InternalGenericPropertiesPrefix), Label = localizedText.Localize("template/template"), - Value = display.TemplateAlias, + Value = string.IsNullOrEmpty(display.TemplateAlias) + ? (content.ContentType.DefaultTemplate == null ? "" : content.ContentType.DefaultTemplate.Alias) + : display.TemplateAlias, View = "dropdown", //TODO: Hard coding until we make a real dropdown property editor to lookup Config = new Dictionary { From 0688835ac0a35e3ae76fc0db2566fdd2266e7c23 Mon Sep 17 00:00:00 2001 From: Claus Date: Tue, 17 Oct 2017 23:44:03 +0200 Subject: [PATCH 62/72] fixing bug in U4-10524 Show warning in UI for the media/content picker that is referencing a trashed or deleted item --- .../propertyeditors/mediapicker/mediapicker.controller.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index f550d30c4c..91777bfc3e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -44,7 +44,10 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl function(id) { var found = _.find(medias, function(m) { - return m.udi === id || m.id === id; + // We could use coercion (two ='s) here .. but not sure if this works equally well in all browsers and + // it's prone to someone "fixing" it at some point without knowing the effects. Rather use toString() + // compares and be completely sure it works. + return m.udi.toString() === id.toString() || m.id.toString() === id.toString(); }); if (found) { return found; From 9c50e1c48dab639d7d97b0df2dfaa4a017db6e12 Mon Sep 17 00:00:00 2001 From: Claus Date: Tue, 17 Oct 2017 23:52:34 +0200 Subject: [PATCH 63/72] fixing bug in U4-10524 Show warning in UI for the media/content picker that is referencing a trashed or deleted item --- .../propertyeditors/mediapicker/mediapicker.controller.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index 21a0717779..27a1e85823 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -43,7 +43,10 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl function(id) { var found = _.find(medias, function(m) { - return m.udi === id || m.id === id; + // We could use coercion (two ='s) here .. but not sure if this works equally well in all browsers and + // it's prone to someone "fixing" it at some point without knowing the effects. Rather use toString() + // compares and be completely sure it works. + return m.udi.toString() === id.toString() || m.id.toString() === id.toString(); }); if (found) { return found; From b06d30c0f92af5514b4b259fec00226e6e5670ab Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 18 Oct 2017 09:29:50 +0200 Subject: [PATCH 64/72] U4-10554 - cross-browser --- .../validation/valformmanager.directive.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js index ab22894a21..a8c3261924 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js @@ -117,19 +117,27 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not }); unsubscribe.push(locationEvent); - var savedUnloadHandler = $window.onbeforeunload; - $window.onbeforeunload = function () { + // try to do it in the most cross-browser way + // some browsers will display their own custom message + // and some will just ignore this completely + function onBeforeUnload(e) { if (formCtrl.$dirty) { - return "You have unsaved changes."; // most browsers won't display it anyways + var message = "You have unsaved changes."; + e.returnValue = message; + return message; + } else { + return null; } } + $window.addEventListener("beforeunload", onBeforeUnload); + //Ensure to remove the event handler when this instance is destroyted scope.$on('$destroy', function() { for (var u in unsubscribe) { unsubscribe[u](); } - $window.onbeforeunload = savedUnloadHandler; + $window.removeEventListener("beforeunload", onBeforeUnload); }); $timeout(function(){ From dd5560f00b5d0deb292bc8c5c179687d8b3d534b Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 18 Oct 2017 10:11:23 +0200 Subject: [PATCH 65/72] U4-8616 - minor cleanup --- .../src/common/services/assets.service.js | 80 ++++++++----------- 1 file changed, 33 insertions(+), 47 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js index 0d1c2b30f9..16330f5493 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js @@ -2,12 +2,12 @@ * @ngdoc service * @name umbraco.services.assetsService * - * @requires $q + * @requires $q * @requires angularHelper - * + * * @description * Promise-based utillity service to lazy-load client-side dependencies inside angular controllers. - * + * * ##usage * To use, simply inject the assetsService into any controller that needs it, and make * sure the umbraco.services module is accesible - which it should be by default. @@ -18,7 +18,7 @@ * //this code executes when the dependencies are done loading * }); * }); - * + * * * You can also load individual files, which gives you greater control over what attibutes are passed to the file, as well as timeout * @@ -38,7 +38,7 @@ * //loadcss cannot determine when the css is done loading, so this will trigger instantly * }); * }); - * + * */ angular.module('umbraco.services') .factory('assetsService', function ($q, $log, angularHelper, umbRequestHelper, $rootScope, $http) { @@ -79,7 +79,7 @@ angular.module('umbraco.services') } }, - /** + /** Internal method. This is called when the application is loading and the user is already authenticated, or once the user is authenticated. There's a few assets the need to be loaded for the application to function but these assets require authentication to load. */ @@ -110,10 +110,10 @@ angular.module('umbraco.services') * * @description * Injects a file as a stylesheet into the document head - * + * * @param {String} path path to the css file to load * @param {Scope} scope optional scope to pass into the loader - * @param {Object} keyvalue collection of attributes to pass to the stylesheet element + * @param {Object} keyvalue collection of attributes to pass to the stylesheet element * @param {Number} timeout in milliseconds * @returns {Promise} Promise object which resolves when the file has loaded */ @@ -151,10 +151,10 @@ angular.module('umbraco.services') * * @description * Injects a file as a javascript into the document - * + * * @param {String} path path to the js file to load * @param {Scope} scope optional scope to pass into the loader - * @param {Object} keyvalue collection of attributes to pass to the script element + * @param {Object} keyvalue collection of attributes to pass to the script element * @param {Number} timeout in milliseconds * @returns {Promise} Promise object which resolves when the file has loaded */ @@ -195,7 +195,7 @@ angular.module('umbraco.services') * * @description * Injects a collection of css and js files - * + * * * @param {Array} pathArray string array of paths to the files to load * @param {Scope} scope optional scope to pass into the loader @@ -211,7 +211,8 @@ angular.module('umbraco.services') // Check to see if there's anything to load, resolve promise if not var nonEmpty = _.reject(pathArray, function (item) { return item === undefined || item === ""; - }); + }); + if (nonEmpty.length === 0) { var deferred = $q.defer(); promise = deferred.promise; @@ -233,8 +234,8 @@ angular.module('umbraco.services') assets.push(asset); } - //we need to always push to the promises collection to monitor correct - //execution + //we need to always push to the promises collection to monitor correct + //execution promises.push(asset.deferred.promise); } }); @@ -250,45 +251,30 @@ angular.module('umbraco.services') var jsAssets = _.filter(assets, function (asset) { return asset.path.match(/(\.js$|\.js\?)/ig); - }); - + }); + + function assetLoaded(asset) { + asset.state = "loaded"; + if (!scope) { + asset.deferred.resolve(true); + return; + } + angularHelper.safeApply(scope, + function () { + asset.deferred.resolve(true); + }); + } + if (cssAssets.length > 0) { var cssPaths = _.map(cssAssets, function (asset) { return appendRnd(asset.path) }); - LazyLoad.css(cssPaths, - function () { - _.each(cssAssets, - function (asset) { - asset.state = "loaded"; - if (!scope) { - asset.deferred.resolve(true); - return; - } - angularHelper.safeApply(scope, - function () { - asset.deferred.resolve(true); - }); - }); - }); + LazyLoad.css(cssPaths, function() { _.each(cssAssets, assetLoaded); }); } if (jsAssets.length > 0) { var jsPaths = _.map(jsAssets, function (asset) { return appendRnd(asset.path) }); - LazyLoad.js(jsPaths, - function () { - _.each(jsAssets, - function (asset) { - asset.state = "loaded"; - if (!scope) { - asset.deferred.resolve(true); - return; - } - angularHelper.safeApply(scope, - function () { - asset.deferred.resolve(true); - }); - }); - }); - } + LazyLoad.js(jsPaths, function () { _.each(jsAssets, assetLoaded); }); + } + return promise; } }; From 4c7358d63b4b169cadb0e8c6f417fa7c69483b4b Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 18 Oct 2017 12:17:58 +0200 Subject: [PATCH 66/72] U4-8960 - minor fixes --- src/Umbraco.Core/IO/IOHelper.cs | 29 ++++++++++++++++++++++------- src/Umbraco.Core/TypeFinder.cs | 6 +----- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Core/IO/IOHelper.cs b/src/Umbraco.Core/IO/IOHelper.cs index 04d390a3cc..a58d983667 100644 --- a/src/Umbraco.Core/IO/IOHelper.cs +++ b/src/Umbraco.Core/IO/IOHelper.cs @@ -13,13 +13,28 @@ using Umbraco.Core.Configuration; namespace Umbraco.Core.IO { public static class IOHelper - { - public static bool IAmUnitTestingSoNeverUseHttpContextEver = false; + { + /// + /// Gets or sets a value forcing Umbraco to consider it is non-hosted. + /// + /// This should always be false, unless unit testing. + public static bool ForceNotHosted { get; set; } private static string _rootDir = ""; // static compiled regex for faster performance private readonly static Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + + /// + /// Gets a value indicating whether Umbraco is hosted. + /// + public static bool IsHosted + { + get + { + return ForceNotHosted == false && (HttpContext.Current != null || HostingEnvironment.IsHosted); + } + } public static char DirSepChar { @@ -74,14 +89,14 @@ namespace Umbraco.Core.IO internal static string ResolveUrlsFromTextString(string text) { if (UmbracoConfig.For.UmbracoSettings().Content.ResolveUrlsFromTextString) - { + { using (DisposableTimer.DebugDuration(typeof(IOHelper), "ResolveUrlsFromTextString starting", "ResolveUrlsFromTextString complete")) { // find all relative urls (ie. urls that contain ~) var tags = ResolveUrlPattern.Matches(text); - + foreach (Match tag in tags) - { + { string url = ""; if (tag.Groups[1].Success) url = tag.Groups[1].Value; @@ -100,7 +115,7 @@ namespace Umbraco.Core.IO public static string MapPath(string path, bool useHttpContext) { if (path == null) throw new ArgumentNullException("path"); - useHttpContext = useHttpContext && IAmUnitTestingSoNeverUseHttpContextEver == false; + useHttpContext = useHttpContext && IsHosted; // Check if the path is already mapped if ((path.Length >= 2 && path[1] == Path.VolumeSeparatorChar) @@ -306,7 +321,7 @@ namespace Umbraco.Core.IO var debugFolder = Path.Combine(binFolder, "debug"); if (Directory.Exists(debugFolder)) return debugFolder; -#endif +#endif var releaseFolder = Path.Combine(binFolder, "release"); if (Directory.Exists(releaseFolder)) return releaseFolder; diff --git a/src/Umbraco.Core/TypeFinder.cs b/src/Umbraco.Core/TypeFinder.cs index 0f69ee5ba5..043c4aa361 100644 --- a/src/Umbraco.Core/TypeFinder.cs +++ b/src/Umbraco.Core/TypeFinder.cs @@ -5,9 +5,7 @@ using System.Linq; using System.Reflection; using System.Security; using System.Text; -using System.Web; using System.Web.Compilation; -using System.Web.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -19,8 +17,6 @@ namespace Umbraco.Core /// public static class TypeFinder { - public static bool IAmUnitTestingSoNeverUseHttpContextEver = false; - private static volatile HashSet _localFilteredAssemblyCache; private static readonly object LocalFilteredAssemblyCacheLocker = new object(); @@ -47,7 +43,7 @@ namespace Umbraco.Core HashSet assemblies = null; try { - var isHosted = (HttpContext.Current != null || HostingEnvironment.IsHosted) && IAmUnitTestingSoNeverUseHttpContextEver == false; + var isHosted = IOHelper.IsHosted; try { From 507e3ece98386eb293058bac047a8a2a190d275b Mon Sep 17 00:00:00 2001 From: Richard Thompson Date: Wed, 18 Oct 2017 15:36:17 +0100 Subject: [PATCH 67/72] Removed duplicate translations Removed duplicate definition of English translations --- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 241 +++++++++--------- 1 file changed, 116 insertions(+), 125 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index a3b12c2e78..3cb67797de 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -87,14 +87,14 @@ Domain '%0%' has been updated Edit Current Domains - Inherit Culture - or inherit culture from parent nodes. Will also apply
    + or inherit culture from parent nodes. Will also apply
    to the current node, unless a domain below applies too.]]>
    Domains @@ -351,11 +351,11 @@ Number of columns Number of rows - Set a placeholder id by setting an ID on your placeholder you can inject content into this template from child templates, + Set a placeholder id by setting an ID on your placeholder you can inject content into this template from child templates, by referring this ID using a <asp:content /> element.]]> - Select a placeholder id from the list below. You can only + Select a placeholder id from the list below. You can only choose Id's from the current template's master.]]> Click on the image to see full size @@ -397,15 +397,15 @@ - %0%' below
    You can add additional languages under the 'languages' in the menu on the left + %0%' below
    You can add additional languages under the 'languages' in the menu on the left ]]>
    Culture Name Edit the key of the dictionary item. - @@ -693,35 +693,35 @@ Your database has been found and is identified as Database configuration - install button to install the Umbraco %0% database + install button to install the Umbraco %0% database ]]> Next to proceed.]]> - Database not found! Please check that the information in the "connection string" of the "web.config" file is correct.

    -

    To proceed, please edit the "web.config" file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named "UmbracoDbDSN" and save the file.

    -

    - Click the retry button when - done.
    + Database not found! Please check that the information in the "connection string" of the "web.config" file is correct.

    +

    To proceed, please edit the "web.config" file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named "UmbracoDbDSN" and save the file.

    +

    + Click the retry button when + done.
    More information on editing web.config here.

    ]]>
    - - Please contact your ISP if necessary. + + Please contact your ISP if necessary. If you're installing on a local machine or server you might need information from your system administrator.]]> - - Press the upgrade button to upgrade your database to Umbraco %0%

    -

    - Don't worry - no content will be deleted and everything will continue working afterwards! -

    + + Press the upgrade button to upgrade your database to Umbraco %0%

    +

    + Don't worry - no content will be deleted and everything will continue working afterwards! +

    ]]>
    - Press Next to + Press Next to proceed. ]]> next to continue the configuration wizard]]> @@ -736,65 +736,65 @@ More information on setting up permissions for Umbraco here You need to grant ASP.NET modify permissions to the following files/folders - Your permission settings are almost perfect!

    + Your permission settings are almost perfect!

    You can run Umbraco without problems, but you will not be able to install packages which are recommended to take full advantage of Umbraco.]]>
    How to Resolve Click here to read the text version video tutorial on setting up folder permissions for Umbraco or read the text version.]]> - Your permission settings might be an issue! -

    + Your permission settings might be an issue! +

    You can run Umbraco without problems, but you will not be able to create folders or install packages which are recommended to take full advantage of Umbraco.]]>
    - Your permission settings are not ready for Umbraco! -

    + Your permission settings are not ready for Umbraco! +

    In order to run Umbraco, you'll need to update your permission settings.]]>
    - Your permission settings are perfect!

    + Your permission settings are perfect!

    You are ready to run Umbraco and install packages!]]>
    Resolving folder issue Follow this link for more information on problems with ASP.NET and creating folders Setting up folder permissions - I want to start from scratch - learn how) - You can still choose to install Runway later on. Please go to the Developer section and choose Packages. + learn how) + You can still choose to install Runway later on. Please go to the Developer section and choose Packages. ]]> You've just set up a clean Umbraco platform. What do you want to do next? Runway is installed - - This is our list of recommended modules, check off the ones you would like to install, or view the full list of modules + + This is our list of recommended modules, check off the ones you would like to install, or view the full list of modules ]]> Only recommended for experienced users I want to start with a simple website - - "Runway" is a simple website providing some basic document types and templates. The installer can set up Runway for you automatically, - but you can easily edit, extend or remove it. It's not necessary and you can perfectly use Umbraco without it. However, - Runway offers an easy foundation based on best practices to get you started faster than ever. - If you choose to install Runway, you can optionally select basic building blocks called Runway Modules to enhance your Runway pages. -

    - - Included with Runway: Home page, Getting Started page, Installing Modules page.
    - Optional Modules: Top Navigation, Sitemap, Contact, Gallery. -
    + + "Runway" is a simple website providing some basic document types and templates. The installer can set up Runway for you automatically, + but you can easily edit, extend or remove it. It's not necessary and you can perfectly use Umbraco without it. However, + Runway offers an easy foundation based on best practices to get you started faster than ever. + If you choose to install Runway, you can optionally select basic building blocks called Runway Modules to enhance your Runway pages. +

    + + Included with Runway: Home page, Getting Started page, Installing Modules page.
    + Optional Modules: Top Navigation, Sitemap, Contact, Gallery. +
    ]]>
    What is Runway @@ -805,24 +805,24 @@ Step 5/5: Umbraco is ready to get you started Thank you for choosing Umbraco - Browse your new site + Browse your new site You installed Runway, so why not see how your new website looks.]]> - Further help and information + Further help and information Get help from our award winning community, browse the documentation or watch some free videos on how to build a simple site, how to use packages and a quick guide to the Umbraco terminology]]> Umbraco %0% is installed and ready for use - /web.config file and update the AppSetting key UmbracoConfigurationStatus in the bottom to the value of '%0%'.]]> - started instantly by clicking the "Launch Umbraco" button below.
    If you are new to Umbraco, + started instantly by clicking the "Launch Umbraco" button below.
    If you are new to Umbraco, you can find plenty of resources on our getting started pages.]]>
    - Launch Umbraco + Launch Umbraco To manage your website, simply open the Umbraco back office and start adding content, updating the templates and stylesheets or add new functionality]]> Connection to database failed. @@ -830,8 +830,8 @@ To manage your website, simply open the Umbraco back office and start adding con Umbraco Version 4 Watch - Umbraco %0% for a fresh install or upgrading from version 3.0. -

    + Umbraco %0% for a fresh install or upgrading from version 3.0. +

    Press "next" to start the wizard.]]>
    @@ -889,47 +889,47 @@ To manage your website, simply open the Umbraco back office and start adding con Edit your notification for %0% - - Hi %0%

    - -

    This is an automated mail to inform you that the task '%1%' - has been performed on the page '%2%' - by the user '%3%' -

    - -

    -

    Update summary:

    - - %6% -
    -

    - - - -

    Have a nice day!

    - Cheers from the Umbraco robot + Hi %0%

    + +

    This is an automated mail to inform you that the task '%1%' + has been performed on the page '%2%' + by the user '%3%' +

    + +

    +

    Update summary:

    + + %6% +
    +

    + + + +

    Have a nice day!

    + Cheers from the Umbraco robot

    ]]>
    [%0%] Notification about %1% performed on %2% @@ -937,9 +937,9 @@ To manage your website, simply open the Umbraco back office and start adding con - - button and locating the package. Umbraco packages usually have a ".umb" or ".zip" extension. + + button and locating the package. Umbraco packages usually have a ".umb" or ".zip" extension. ]]> Drop to upload @@ -982,7 +982,7 @@ To manage your website, simply open the Umbraco back office and start adding con Package name Package doesn't contain any items -
    +
    You can safely remove this from the system by clicking "uninstall package" below.]]>
    No upgrades available @@ -994,8 +994,8 @@ To manage your website, simply open the Umbraco back office and start adding con The package was successfully uninstalled Uninstall package - - Notice: any documents, media etc depending on the items you remove, will stop working, and could lead to system instability, + + Notice: any documents, media etc depending on the items you remove, will stop working, and could lead to system instability, so uninstall with caution. If in doubt, contact the package author.]]> Download update from the repository @@ -1042,28 +1042,28 @@ To manage your website, simply open the Umbraco back office and start adding con - - - - - Include unpublished subpages @@ -1073,22 +1073,13 @@ To manage your website, simply open the Umbraco back office and start adding con %0% and subpages have been published Publish %0% and all its subpages - Publish to publish %0% and thereby making its content publicly available.

    - You can publish this page and all its subpages by checking Include unpublished subpages below. + Publish to publish %0% and thereby making its content publicly available.

    + You can publish this page and all its subpages by checking Include unpublished subpages below. ]]>
    You have not configured any approved colours - - - You have picked a content item currently deleted or in the recycle bin - You have picked content items currently deleted or in the recycle bin - - - You have picked a media item currently deleted or in the recycle bin - You have picked media items currently deleted or in the recycle bin - Deleted item You have picked a content item currently deleted or in the recycle bin @@ -1889,4 +1880,4 @@ To manage your website, simply open the Umbraco back office and start adding con characters left - \ No newline at end of file + From f85532f15eaf55938ac22519c1dcde38852d9797 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 20 Oct 2017 00:56:34 +1100 Subject: [PATCH 68/72] U4-10566 The legacy API Member.PopulateGroups throws an exception --- src/umbraco.cms/businesslogic/member/Member.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/umbraco.cms/businesslogic/member/Member.cs b/src/umbraco.cms/businesslogic/member/Member.cs index 71c8eda701..c46e4d914c 100644 --- a/src/umbraco.cms/businesslogic/member/Member.cs +++ b/src/umbraco.cms/businesslogic/member/Member.cs @@ -773,14 +773,20 @@ namespace umbraco.cms.businesslogic.member { var temp = new Hashtable(); + var groupIds = new List(); + using (var sqlHelper = Application.SqlHelper) using (var dr = sqlHelper.ExecuteReader( "select memberGroup from cmsMember2MemberGroup where member = @id", sqlHelper.CreateParameter("@id", Id))) { while (dr.Read()) - temp.Add(dr.GetInt("memberGroup"), - new MemberGroup(dr.GetInt("memberGroup"))); + groupIds.Add(dr.GetInt("memberGroup")); + } + + foreach (var groupId in groupIds) + { + temp.Add(groupId, new MemberGroup(groupId)); } _groups = temp; } From edfe2122276f58f0c1bf16e27eb2c12b84b7b7e7 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 20 Oct 2017 15:35:30 +0200 Subject: [PATCH 69/72] Fix login issue --- src/Umbraco.Tests/App.config | 5 + .../components/umbpasswordtoggle.directive.js | 3 + .../views/common/dialogs/login.controller.js | 22 ++- .../src/views/common/dialogs/login.html | 5 +- .../config/splashes/noNodes.aspx | 159 ++++++++++++++---- .../config/umbracoSettings.config | 58 +++---- 6 files changed, 181 insertions(+), 71 deletions(-) diff --git a/src/Umbraco.Tests/App.config b/src/Umbraco.Tests/App.config index 09fd6b24f3..a5295ad028 100644 --- a/src/Umbraco.Tests/App.config +++ b/src/Umbraco.Tests/App.config @@ -63,6 +63,11 @@ + + + + + diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpasswordtoggle.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpasswordtoggle.directive.js index 30cfaf88b2..d75b9e2de0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpasswordtoggle.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpasswordtoggle.directive.js @@ -1,6 +1,9 @@ (function () { 'use strict'; + // comes from https://codepen.io/jakob-e/pen/eNBQaP + // works fine with Angular 1.6.5 - alas not with 1.1.5 - binding issue + function PasswordToggleDirective($compile) { var directive = { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js index 18158a5ff2..3712324132 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js @@ -16,7 +16,13 @@ maxFileSize: Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB", acceptedFileTypes: mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes), uploaded: false - } + } + $scope.togglePassword = function () { + console.log('toggle'); + var elem = $("form[name='loginForm'] input[name='password']"); + console.log(elem); + elem.attr("type", (elem.attr("type") === "text" ? "password" : "text")); + } function init() { // Check if it is a new user @@ -48,7 +54,7 @@ ]).then(function () { $scope.inviteStep = Number(inviteVal); - + }); } } @@ -82,7 +88,7 @@ var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10); // set percentage property on file - $scope.avatarFile.uploadProgress = progressPercentage; + $scope.avatarFile.uploadProgress = progressPercentage; } }).success(function (data, status, headers, config) { @@ -149,11 +155,11 @@ //error formHelper.handleError(err); - + $scope.invitedUserPasswordModel.buttonState = "error"; }); - } + } }; var setFieldFocus = function (form, field) { @@ -180,7 +186,7 @@ } function resetInputValidation() { - $scope.confirmPassword = ""; + $scope.confirmPassword = ""; $scope.password = ""; $scope.login = ""; if ($scope.loginForm) { @@ -255,7 +261,7 @@ //TODO: Do validation properly like in the invite password update - //if the login and password are not empty we need to automatically + //if the login and password are not empty we need to automatically // validate them - this is because if there are validation errors on the server // then the user has to change both username & password to resubmit which isn't ideal, // so if they're not empty, we'll just make sure to set them to valid. @@ -289,7 +295,7 @@ }); //setup a watch for both of the model values changing, if they change - // while the form is invalid, then revalidate them so that the form can + // while the form is invalid, then revalidate them so that the form can // be submitted again. $scope.loginForm.username.$viewChangeListeners.push(function () { if ($scope.loginForm.username.$invalid) { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html index a1913d76e7..6505af4de9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html @@ -149,8 +149,9 @@
    - -
    +
    + Toggle +
    diff --git a/src/Umbraco.Web.UI/config/splashes/noNodes.aspx b/src/Umbraco.Web.UI/config/splashes/noNodes.aspx index d46ecd6113..ac8504ce45 100644 --- a/src/Umbraco.Web.UI/config/splashes/noNodes.aspx +++ b/src/Umbraco.Web.UI/config/splashes/noNodes.aspx @@ -1,7 +1,7 @@ -<%@ Page Language="C#" AutoEventWireup="True" Inherits="Umbraco.Web.UI.Config.Splashes.NoNodes" CodeBehind="NoNodes.aspx.cs" %> -<%@ Import Namespace="Umbraco.Core.Configuration" %> +<%@ Page Language="C#" AutoEventWireup="true"%> <%@ Import Namespace="Umbraco.Core.IO" %> - +<%@ Import Namespace="Umbraco.Deploy.UI" %> +<%@ Import Namespace="Umbraco.Web" %> @@ -11,51 +11,146 @@ - + - + + + + + + + +<% if(HttpContext.Current.Request.IsLocal == false){ %>
    -
    -
    - +
    +
    + -

    Welcome to your Umbraco installation

    -

    You're seeing this wonderful page because your website doesn't contain any published content yet.

    +

    Welcome to your Umbraco installation

    +

    You're seeing the wonderful page because your website doesn't contain any published content yet.

    - + -
    -
    -

    Easy start with Umbraco.tv

    -

    We have created a bunch of 'how-to' videos, to get you easily started with Umbraco. Learn how to build projects in just a couple of minutes. Easiest CMS in the world.

    - - Umbraco.tv → -
    +
    +
    +

    Easy start with Umbraco.tv

    +

    We have created a bunch of 'how-to' videos, to get you easily started with Umbraco. Learn how to build projects in just a couple of minutes. Easiest CMS in the world.

    -
    -

    Be a part of the community

    -

    The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we're sure that you can get your answers from the community.

    - - our.Umbraco → -
    -
    + Umbraco.tv → +
    -
    -
    +
    +

    Be a part of the community

    +

    The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we’re sure that you can get your answers from the community.

    + + our.Umbraco → +
    +
    + +
    + + + +<% }else{ %> + +
    +
    + +
    + + +
    + +
    + +

    Initializing your website...

    + +
    + Press the button below to get started +
    + +
    +
    +
    + +
    +

    Restore from Umbraco Cloud

    + +
    + +
    +

    {{ vm.restore.restoreMessage }}

    +

    {{ vm.restore.restoreProgress }}% restored

    + {{ vm.restore.currentActivity }} +
    {{ vm.restore.timestamp }}
    +
    + +
    +

    Ready to rock n' roll!

    +

    Everything has been restored and is ready for use, click below to open Umbraco

    + +
    + + + + +
    +
    +
    + + <%--
    +

    An error occurred:

    +

    {{ vm.restore.error.exceptionMessage }}

    + Show details +
    {{ vm.restore.error.log }}
    +
    --%> + +
    + +
    +
    - - +<%= NoNodesHelper.ServerVariables(HttpContext.Current.Request.RequestContext, UmbracoContext.Current) %> + + + + + + + + + + +<% } %> - + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 01e5fdcc37..d322439fba 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -10,12 +10,12 @@ umbracoWidth - umbracoHeight + umbracoHeight umbracoBytes umbracoExtension - + @@ -98,9 +98,9 @@ error handler is defined then you'll see the Yellow Screen Of Death (YSOD) error page. Note the error can also be handled by the umbraco.macro.Error event, where you can log/alarm with your own code and change the behaviour per event. --> throw - + - ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,xhtml,html,htm,svg,php,htaccess + ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,xhtml,html,htm,php,htaccess @@ -109,13 +109,13 @@ Textstring false - + - true - + true + assets/img/installer.jpg - + @@ -124,7 +124,7 @@ false - + true @@ -209,7 +209,7 @@ - @@ -237,40 +237,40 @@ - - - + + @@ -288,7 +288,7 @@ - @@ -11,146 +11,51 @@ - + - - - - - - - + -<% if(HttpContext.Current.Request.IsLocal == false){ %>
    -
    -
    - +
    +
    + -

    Welcome to your Umbraco installation

    -

    You're seeing the wonderful page because your website doesn't contain any published content yet.

    +

    Welcome to your Umbraco installation

    +

    You're seeing this wonderful page because your website doesn't contain any published content yet.

    - + -
    -
    -

    Easy start with Umbraco.tv

    -

    We have created a bunch of 'how-to' videos, to get you easily started with Umbraco. Learn how to build projects in just a couple of minutes. Easiest CMS in the world.

    +
    +
    +

    Easy start with Umbraco.tv

    +

    We have created a bunch of 'how-to' videos, to get you easily started with Umbraco. Learn how to build projects in just a couple of minutes. Easiest CMS in the world.

    + + Umbraco.tv → +
    - Umbraco.tv → -
    +
    +

    Be a part of the community

    +

    The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we're sure that you can get your answers from the community.

    + + our.Umbraco → +
    +
    -
    -

    Be a part of the community

    -

    The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we’re sure that you can get your answers from the community.

    - - our.Umbraco → -
    -
    - -
    -
    -
    - -<% }else{ %> - -
    -
    - -
    - - -
    - -
    - -

    Initializing your website...

    - -
    - Press the button below to get started -
    - -
    -
    -
    - -
    -

    Restore from Umbraco Cloud

    - -
    - -
    -

    {{ vm.restore.restoreMessage }}

    -

    {{ vm.restore.restoreProgress }}% restored

    - {{ vm.restore.currentActivity }} -
    {{ vm.restore.timestamp }}
    -
    - -
    -

    Ready to rock n' roll!

    -

    Everything has been restored and is ready for use, click below to open Umbraco

    - -
    - - - - -
    -
    -
    - - <%--
    -

    An error occurred:

    -

    {{ vm.restore.error.exceptionMessage }}

    - Show details -
    {{ vm.restore.error.log }}
    -
    --%> - -
    - -
    -
    +
    + -<%= NoNodesHelper.ServerVariables(HttpContext.Current.Request.RequestContext, UmbracoContext.Current) %> - - - - - - - - - - -<% } %> + + - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index d322439fba..01e5fdcc37 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -10,12 +10,12 @@ umbracoWidth - umbracoHeight + umbracoHeight umbracoBytes umbracoExtension - + @@ -98,9 +98,9 @@ error handler is defined then you'll see the Yellow Screen Of Death (YSOD) error page. Note the error can also be handled by the umbraco.macro.Error event, where you can log/alarm with your own code and change the behaviour per event. --> throw - + - ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,xhtml,html,htm,php,htaccess + ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,xhtml,html,htm,svg,php,htaccess @@ -109,13 +109,13 @@ Textstring false - + - true - + true + assets/img/installer.jpg - + @@ -124,7 +124,7 @@ false - + true @@ -209,7 +209,7 @@ - @@ -237,40 +237,40 @@ - - - + + @@ -288,7 +288,7 @@ -