diff --git a/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs
index c2d65b824f..be2f0b075e 100644
--- a/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs
+++ b/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs
@@ -35,6 +35,12 @@ namespace Umbraco.Core.Persistence
/// May return null if the database factory is not configured.
string ConnectionString { get; }
+ ///
+ /// Gets the provider name.
+ ///
+ /// May return null if the database factory is not configured.
+ string ProviderName { get; }
+
///
/// Gets a value indicating whether the database factory is configured (see ),
/// and it is possible to connect to the database. The factory may however not be initialized (see
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/UserRepository.cs
index a4b19ea2f3..bafc3348e9 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/UserRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/UserRepository.cs
@@ -28,6 +28,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
{
private readonly IMapperCollection _mapperCollection;
private readonly IGlobalSettings _globalSettings;
+ private readonly IRuntimeState _runtimeState;
private string _passwordConfigJson;
private bool _passwordConfigInitialized;
@@ -41,19 +42,22 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
/// A dictionary specifying the configuration for user passwords. If this is null then no password configuration will be persisted or read.
///
///
- public UserRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, IMapperCollection mapperCollection, IGlobalSettings globalSettings)
+ ///
+ public UserRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, IMapperCollection mapperCollection, IGlobalSettings globalSettings, IRuntimeState runtimeState)
: base(scopeAccessor, appCaches, logger)
{
_mapperCollection = mapperCollection;
_globalSettings = globalSettings;
+ _runtimeState = runtimeState;
}
// for tests
- internal UserRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, IMapperCollection mapperCollection, IDictionary passwordConfig, IGlobalSettings globalSettings)
+ internal UserRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, IMapperCollection mapperCollection, IDictionary passwordConfig, IGlobalSettings globalSettings, IRuntimeState runtimeState)
: base(scopeAccessor, appCaches, logger)
{
_mapperCollection = mapperCollection;
_globalSettings = globalSettings;
+ _runtimeState = runtimeState;
_passwordConfigJson = JsonConvert.SerializeObject(passwordConfig);
_passwordConfigInitialized = true;
}
@@ -86,9 +90,21 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// This will never resolve to a user, yet this is asked
// for all of the time (especially in cases of members).
// Don't issue a SQL call for this, we know it will not exist.
- if (id == default || id < -1)
+ if (_runtimeState.Level == RuntimeLevel.Upgrade)
{
- return null;
+ // when upgrading people might come from version 7 where user 0 was the default,
+ // only in upgrade mode do we want to fetch the user of Id 0
+ if (id < -1)
+ {
+ return null;
+ }
+ }
+ else
+ {
+ if (id == default || id < -1)
+ {
+ return null;
+ }
}
var sql = SqlContext.Sql()
diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs
index b113769a1a..61a43d636f 100644
--- a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs
+++ b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs
@@ -124,6 +124,9 @@ namespace Umbraco.Core.Persistence
///
public string ConnectionString => _connectionString;
+ ///
+ public string ProviderName => _providerName;
+
///
public bool CanConnect =>
// actually tries to connect to the database (regardless of configured/initialized)
diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs
index 9b9da2f926..6b74cab3ed 100644
--- a/src/Umbraco.Core/Runtime/CoreRuntime.cs
+++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs
@@ -1,9 +1,9 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
+using System.Data.SqlServerCe;
using System.Diagnostics;
using System.IO;
-using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Web;
@@ -20,9 +20,7 @@ using Umbraco.Core.Migrations.Install;
using Umbraco.Core.Migrations.Upgrade;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Mappers;
-using Umbraco.Core.Scoping;
using Umbraco.Core.Security;
-using Umbraco.Core.Services;
using Umbraco.Core.Sync;
namespace Umbraco.Core.Runtime
@@ -172,7 +170,7 @@ namespace Umbraco.Core.Runtime
// run handlers
RuntimeOptions.DoRuntimeEssentials(composition, appCaches, typeLoader, databaseFactory);
-
+
// register runtime-level services
// there should be none, really - this is here "just in case"
@@ -276,7 +274,7 @@ namespace Umbraco.Core.Runtime
|| unattendedEmail.IsNullOrWhiteSpace()
|| unattendedPassword.IsNullOrWhiteSpace())
{
-
+
fileExists = File.Exists(filePath);
if (fileExists == false)
{
@@ -342,7 +340,7 @@ namespace Umbraco.Core.Runtime
Current.Services.UserService.Save(admin);
- // Delete JSON file if it existed to tidy
+ // Delete JSON file if it existed to tidy
if (fileExists)
{
File.Delete(filePath);
@@ -363,6 +361,19 @@ namespace Umbraco.Core.Runtime
// no connection string set
if (databaseFactory.Configured == false) return;
+ // create SQL CE database if not existing and database provider is SQL CE
+ if (databaseFactory.ProviderName == Constants.DbProviderNames.SqlCe)
+ {
+ var dataSource = new SqlCeConnectionStringBuilder(databaseFactory.ConnectionString).DataSource;
+ var dbFilePath = dataSource.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString());
+
+ if(File.Exists(dbFilePath) == false)
+ {
+ var engine = new SqlCeEngine(databaseFactory.ConnectionString);
+ engine.CreateDatabase();
+ }
+ }
+
var tries = 5;
var connect = false;
for (var i = 0;;)
@@ -385,7 +396,7 @@ namespace Umbraco.Core.Runtime
// all conditions fulfilled, do the install
Logger.Info("Starting unattended install.");
-
+
try
{
database.BeginTransaction();
diff --git a/src/Umbraco.Core/Security/MachineKeyGenerator.cs b/src/Umbraco.Core/Security/MachineKeyGenerator.cs
index a20f04c919..848c7a0983 100644
--- a/src/Umbraco.Core/Security/MachineKeyGenerator.cs
+++ b/src/Umbraco.Core/Security/MachineKeyGenerator.cs
@@ -71,14 +71,16 @@ namespace Umbraco.Core.Security
private string GenerateKey(int len = 64)
{
var buff = new byte[len / 2];
- var rng = new RNGCryptoServiceProvider();
- rng.GetBytes(buff);
- var sb = new StringBuilder(len);
+ using (var rng = new RNGCryptoServiceProvider())
+ {
+ rng.GetBytes(buff);
+ var sb = new StringBuilder(len);
- for (int i = 0; i < buff.Length; i++)
- sb.Append(string.Format("{0:X2}", buff[i]));
+ for (int i = 0; i < buff.Length; i++)
+ sb.Append(string.Format("{0:X2}", buff[i]));
- return sb.ToString();
+ return sb.ToString();
+ }
}
}
}
diff --git a/src/Umbraco.Core/Security/MembershipProviderBase.cs b/src/Umbraco.Core/Security/MembershipProviderBase.cs
index 0bc8de492a..1747b5a939 100644
--- a/src/Umbraco.Core/Security/MembershipProviderBase.cs
+++ b/src/Umbraco.Core/Security/MembershipProviderBase.cs
@@ -703,44 +703,46 @@ namespace Umbraco.Core.Security
if (PasswordFormat == MembershipPasswordFormat.Hashed)
{
- var hashAlgorithm = GetHashAlgorithm(pass);
- var algorithm = hashAlgorithm as KeyedHashAlgorithm;
- if (algorithm != null)
+ using (var hashAlgorithm = GetHashAlgorithm(pass))
{
- var keyedHashAlgorithm = algorithm;
- if (keyedHashAlgorithm.Key.Length == saltBytes.Length)
+ var algorithm = hashAlgorithm as KeyedHashAlgorithm;
+ if (algorithm != null)
{
- //if the salt bytes is the required key length for the algorithm, use it as-is
- keyedHashAlgorithm.Key = saltBytes;
- }
- else if (keyedHashAlgorithm.Key.Length < saltBytes.Length)
- {
- //if the salt bytes is too long for the required key length for the algorithm, reduce it
- var numArray2 = new byte[keyedHashAlgorithm.Key.Length];
- Buffer.BlockCopy(saltBytes, 0, numArray2, 0, numArray2.Length);
- keyedHashAlgorithm.Key = numArray2;
+ var keyedHashAlgorithm = algorithm;
+ if (keyedHashAlgorithm.Key.Length == saltBytes.Length)
+ {
+ //if the salt bytes is the required key length for the algorithm, use it as-is
+ keyedHashAlgorithm.Key = saltBytes;
+ }
+ else if (keyedHashAlgorithm.Key.Length < saltBytes.Length)
+ {
+ //if the salt bytes is too long for the required key length for the algorithm, reduce it
+ var numArray2 = new byte[keyedHashAlgorithm.Key.Length];
+ Buffer.BlockCopy(saltBytes, 0, numArray2, 0, numArray2.Length);
+ keyedHashAlgorithm.Key = numArray2;
+ }
+ else
+ {
+ //if the salt bytes is too short for the required key length for the algorithm, extend it
+ var numArray2 = new byte[keyedHashAlgorithm.Key.Length];
+ var dstOffset = 0;
+ while (dstOffset < numArray2.Length)
+ {
+ var count = Math.Min(saltBytes.Length, numArray2.Length - dstOffset);
+ Buffer.BlockCopy(saltBytes, 0, numArray2, dstOffset, count);
+ dstOffset += count;
+ }
+ keyedHashAlgorithm.Key = numArray2;
+ }
+ inArray = keyedHashAlgorithm.ComputeHash(bytes);
}
else
{
- //if the salt bytes is too short for the required key length for the algorithm, extend it
- var numArray2 = new byte[keyedHashAlgorithm.Key.Length];
- var dstOffset = 0;
- while (dstOffset < numArray2.Length)
- {
- var count = Math.Min(saltBytes.Length, numArray2.Length - dstOffset);
- Buffer.BlockCopy(saltBytes, 0, numArray2, dstOffset, count);
- dstOffset += count;
- }
- keyedHashAlgorithm.Key = numArray2;
+ var buffer = new byte[saltBytes.Length + bytes.Length];
+ Buffer.BlockCopy(saltBytes, 0, buffer, 0, saltBytes.Length);
+ Buffer.BlockCopy(bytes, 0, buffer, saltBytes.Length, bytes.Length);
+ inArray = hashAlgorithm.ComputeHash(buffer);
}
- inArray = keyedHashAlgorithm.ComputeHash(bytes);
- }
- else
- {
- var buffer = new byte[saltBytes.Length + bytes.Length];
- Buffer.BlockCopy(saltBytes, 0, buffer, 0, saltBytes.Length);
- Buffer.BlockCopy(bytes, 0, buffer, saltBytes.Length, bytes.Length);
- inArray = hashAlgorithm.ComputeHash(buffer);
}
}
else
@@ -850,7 +852,9 @@ namespace Umbraco.Core.Security
protected internal static string GenerateSalt()
{
var numArray = new byte[16];
- new RNGCryptoServiceProvider().GetBytes(numArray);
+ using (var rng = new RNGCryptoServiceProvider()) {
+ rng.GetBytes(numArray);
+ }
return Convert.ToBase64String(numArray);
}
diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs
index b2efbd34b8..73430826d6 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs
@@ -17,6 +17,7 @@ using Umbraco.Core.Persistence;
using Umbraco.Core.PropertyEditors;
using System;
using Umbraco.Core.Persistence.Dtos;
+using Umbraco.Tests.Components;
namespace Umbraco.Tests.Persistence.Repositories
{
@@ -67,7 +68,7 @@ namespace Umbraco.Tests.Persistence.Repositories
private UserRepository CreateRepository(IScopeProvider provider)
{
var accessor = (IScopeAccessor) provider;
- var repository = new UserRepository(accessor, AppCaches.Disabled, Logger, Mappers, TestObjects.GetGlobalSettings());
+ var repository = new UserRepository(accessor, AppCaches.Disabled, Logger, Mappers, TestObjects.GetGlobalSettings(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
return repository;
}
@@ -85,8 +86,8 @@ namespace Umbraco.Tests.Persistence.Repositories
var user = MockedUser.CreateUser();
using (var scope = provider.CreateScope(autoComplete: true))
{
- var repository = CreateRepository(provider);
- repository.Save(user);
+ var repository = CreateRepository(provider);
+ repository.Save(user);
}
using (var scope = provider.CreateScope(autoComplete: true))
@@ -253,7 +254,7 @@ namespace Umbraco.Tests.Persistence.Repositories
var id = user.Id;
- var repository2 = new UserRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, Mock.Of(),TestObjects.GetGlobalSettings());
+ var repository2 = new UserRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, Mock.Of(), TestObjects.GetGlobalSettings(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
repository2.Delete(user);
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js
index b412a8309f..d4c8b3d300 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js
@@ -78,11 +78,13 @@
localizationService.localizeMany([
"validation_validation",
"contentTypeEditor_tabHasNoSortOrder",
- "general_generic"
+ "general_generic",
+ "contentTypeEditor_tabDirectPropertiesDropZone"
]).then(data => {
validationTranslated = data[0];
tabNoSortOrderTranslated = data[1];
scope.genericTab.name = data[2];
+ scope.tabDirectPropertiesDropZoneLabel = data[3];
});
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
index d44b10bb80..b433ab5d13 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
@@ -1543,6 +1543,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
dataTypeKey: args.model.dataTypeKey,
ignoreUserStartNodes: args.model.config.ignoreUserStartNodes,
anchors: anchorValues,
+ size: args.model.config.editor.overlayWidthSize,
submit: function (model) {
self.insertLinkInEditor(args.editor, model.target, anchorElement);
editorService.close();
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/check-circle.less b/src/Umbraco.Web.UI.Client/src/less/components/check-circle.less
index fadf9c7940..a167644ea8 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/check-circle.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/check-circle.less
@@ -1,19 +1,17 @@
.check_circle {
display: flex;
+ justify-content: center;
+ align-items: center;
width: 20px;
height: 20px;
- margin: 0 auto;
.icon {
background-color: rgba(0,0,0,.15);
border-radius: 50%;
+ padding: 3px;
color: @white;
- font-size: 1em;
display: flex;
- width: 100%;
- height: 100%;
align-items: center;
justify-content: center;
- float: left;
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less
index 148b46ead7..1122558b05 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less
@@ -323,6 +323,7 @@
.umb-group-builder__ungrouped-properties {
margin-top: 20px;
+ position: relative;
}
/* ---------- GROUPS ---------- */
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html
index 15b15a6e4e..c61110adb2 100644
--- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html
+++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html
@@ -94,13 +94,6 @@
-
-
-
-
+
+
+ {{ tabDirectPropertiesDropZoneLabel }}
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js
index a5ccf7a9aa..d398c943c4 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js
@@ -30,6 +30,7 @@ function MarkdownEditorController($scope, $element, assetsService, editorService
function openLinkPicker(callback) {
var linkPicker = {
hideTarget: true,
+ size: $scope.model.config.overlayWidthSize,
submit: function(model) {
callback(model.target.url, model.target.name);
editorService.close();
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.prevalues.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.prevalues.controller.js
new file mode 100644
index 0000000000..29a3b876ea
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.prevalues.controller.js
@@ -0,0 +1,6 @@
+angular.module("umbraco").controller("Umbraco.PrevalueEditors.MarkdownEditorController",
+ function ($scope) {
+ if (!$scope.model.value) {
+ $scope.model.value = "small";
+ }
+ });
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.prevalues.html
new file mode 100644
index 0000000000..8564a9f490
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.prevalues.html
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js
index 1b8d60601f..fc8fa8b809 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js
@@ -83,6 +83,7 @@ function multiUrlPickerController($scope, localizationService, entityResource, i
dataTypeKey: $scope.model.dataTypeKey,
ignoreUserStartNodes : ($scope.model.config && $scope.model.config.ignoreUserStartNodes) ? $scope.model.config.ignoreUserStartNodes : "0",
hideAnchor: $scope.model.config && $scope.model.config.hideAnchor ? true : false,
+ size: $scope.model.config.overlayWidthSize,
submit: function (model) {
if (model.target.url || model.target.anchor) {
// if an anchor exists, check that it is appropriately prefixed
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.prevalues.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.prevalues.controller.js
new file mode 100644
index 0000000000..3a1e6368e9
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.prevalues.controller.js
@@ -0,0 +1,6 @@
+angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiUrlPickerController",
+ function ($scope) {
+ if (!$scope.model.value) {
+ $scope.model.value = "small";
+ }
+ });
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.prevalues.html
new file mode 100644
index 0000000000..7abb3c3c01
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.prevalues.html
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
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 47d1f401c7..24eaba9886 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
@@ -23,6 +23,10 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController",
$scope.model.value.mode = "classic";
}
+ if(!$scope.model.value.overlayWidthSize) {
+ $scope.model.value.overlayWidthSize = "small";
+ }
+
tinyMceService.configuration().then(function(config){
$scope.tinyMceConfig = config;
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html
index 96de0cd040..68fb2dbf5f 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html
@@ -46,4 +46,14 @@
+
+
+
+
+
+
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 c2b16cae96..ec2fd1245c 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
@@ -1743,6 +1743,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
This will also delete all items below this group.Add tabConvert to tab
+ Drag properties here to place directly on the tabAdd language
diff --git a/src/Umbraco.Web/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs
index 30294b7581..3ecc6b64a4 100644
--- a/src/Umbraco.Web/Editors/AuthenticationController.cs
+++ b/src/Umbraco.Web/Editors/AuthenticationController.cs
@@ -17,7 +17,6 @@ using Umbraco.Core.Models.Identity;
using Umbraco.Core.Services;
using Umbraco.Web.Models;
using Umbraco.Web.Models.ContentEditing;
-using Umbraco.Web.Mvc;
using Umbraco.Web.Security;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
@@ -34,10 +33,11 @@ namespace Umbraco.Web.Editors
///
/// The API controller used for editing content
///
- [PluginController("UmbracoApi")]
+ [Mvc.PluginController("UmbracoApi")]
[ValidationFilter]
[AngularJsonOnlyConfiguration]
[IsBackOffice]
+ [DisableBrowserCache]
public class AuthenticationController : UmbracoApiController
{
private BackOfficeUserManager _userManager;
diff --git a/src/Umbraco.Web/PropertyEditors/MarkdownConfiguration.cs b/src/Umbraco.Web/PropertyEditors/MarkdownConfiguration.cs
index 4c2374ccd5..0a65246c7c 100644
--- a/src/Umbraco.Web/PropertyEditors/MarkdownConfiguration.cs
+++ b/src/Umbraco.Web/PropertyEditors/MarkdownConfiguration.cs
@@ -12,5 +12,9 @@ namespace Umbraco.Web.PropertyEditors
[ConfigurationField("defaultValue", "Default value", "textarea", Description = "If value is blank, the editor will show this")]
public string DefaultValue { get; set; }
+
+
+ [ConfigurationField("overlayWidthSize", "Overlay Width Size", "views/propertyeditors/multiurlpicker/multiurlpicker.prevalues.html")]
+ public string OverlayWidthSize { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerConfiguration.cs b/src/Umbraco.Web/PropertyEditors/MultiUrlPickerConfiguration.cs
index 239569478f..0c18f05dc1 100644
--- a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerConfiguration.cs
+++ b/src/Umbraco.Web/PropertyEditors/MultiUrlPickerConfiguration.cs
@@ -11,6 +11,9 @@ namespace Umbraco.Web.PropertyEditors
[ConfigurationField("maxNumber", "Maximum number of items", "number")]
public int MaxNumber { get; set; }
+ [ConfigurationField("overlayWidthSize", "Overlay Width Size", "views/propertyeditors/multiurlpicker/multiurlpicker.prevalues.html")]
+ public string OverlayWidthSize { get; set; }
+
[ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes,
"Ignore User Start Nodes", "boolean",
Description = "Selecting this option allows a user to choose nodes that they normally don't have access to.")]
diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs
index f043c8e66e..5c7fc6f14d 100644
--- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs
@@ -1,6 +1,4 @@
-using Newtonsoft.Json.Linq;
-using System;
-using System.Collections.Generic;
+using System;
using Umbraco.Core;
using Umbraco.Core.Models.Blocks;
using Umbraco.Core.Models.PublishedContent;
@@ -10,7 +8,7 @@ using Umbraco.Web.PublishedCache;
namespace Umbraco.Web.PropertyEditors.ValueConverters
{
///
- /// Converts json block objects into
+ /// Converts JSON block objects into .
///
public sealed class BlockEditorConverter
{
@@ -23,30 +21,52 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
_publishedModelFactory = publishedModelFactory;
}
- public IPublishedElement ConvertToElement(
- BlockItemData data,
- PropertyCacheLevel referenceCacheLevel, bool preview)
+ public IPublishedElement ConvertToElement(BlockItemData data, PropertyCacheLevel referenceCacheLevel, bool preview)
{
- // hack! we need to cast, we have no choice beacuse we cannot make breaking changes.
+ var publishedContentType = GetContentType(data.ContentTypeKey);
+
+ // Only convert element types
+ if (publishedContentType == null || publishedContentType.IsElement == false)
+ {
+ return null;
+ }
+
+ var propertyValues = data.RawPropertyValues;
+
+ // Get the UDI from the deserialized object. If this is empty, we can fallback to checking the 'key' if there is one
+ var key = (data.Udi is GuidUdi gudi) ? gudi.Guid : Guid.Empty;
+ if (key == Guid.Empty && propertyValues.TryGetValue("key", out var keyo))
+ {
+ Guid.TryParse(keyo.ToString(), out key);
+ }
+
+ IPublishedElement element = new PublishedElement(publishedContentType, key, propertyValues, preview, referenceCacheLevel, _publishedSnapshotAccessor);
+ element = _publishedModelFactory.CreateModel(element);
+
+ return element;
+ }
+
+ public Type GetModelType(Guid contentTypeKey)
+ {
+ var publishedContentType = GetContentType(contentTypeKey);
+ if (publishedContentType != null)
+ {
+ var modelType = ModelType.For(publishedContentType.Alias);
+
+ return _publishedModelFactory.MapModelType(modelType);
+ }
+
+ return typeof(IPublishedElement);
+ }
+
+ private IPublishedContentType GetContentType(Guid contentTypeKey)
+ {
+ // HACK! We need to cast, we have no choice because we can't make breaking changes (and we need the GUID overload)
var publishedContentCache = _publishedSnapshotAccessor.PublishedSnapshot.Content as IPublishedContentCache2;
if (publishedContentCache == null)
throw new InvalidOperationException("The published content cache is not " + typeof(IPublishedContentCache2));
- // only convert element types - content types will cause an exception when PublishedModelFactory creates the model
- var publishedContentType = publishedContentCache.GetContentType(data.ContentTypeKey);
- if (publishedContentType == null || publishedContentType.IsElement == false)
- return null;
-
- var propertyValues = data.RawPropertyValues;
-
- // Get the udi from the deserialized object. If this is empty we can fallback to checking the 'key' if there is one
- var key = (data.Udi is GuidUdi gudi) ? gudi.Guid : Guid.Empty;
- if (propertyValues.TryGetValue("key", out var keyo))
- Guid.TryParse(keyo.ToString(), out key);
-
- IPublishedElement element = new PublishedElement(publishedContentType, key, propertyValues, preview, referenceCacheLevel, _publishedSnapshotAccessor);
- element = _publishedModelFactory.CreateModel(element);
- return element;
+ return publishedContentCache.GetContentType(contentTypeKey);
}
}
}
diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs
index 5d216f2b4c..c1915809fa 100644
--- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs
@@ -1,6 +1,4 @@
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
@@ -51,16 +49,9 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
using (_proflog.DebugDuration($"ConvertPropertyToBlockList ({propertyType.DataType.Id})"))
{
- var configuration = propertyType.DataType.ConfigurationAs();
- var blockConfigMap = configuration.Blocks.ToDictionary(x => x.ContentElementTypeKey);
- var validSettingElementTypes = blockConfigMap.Values.Select(x => x.SettingsElementTypeKey).Where(x => x.HasValue).Distinct().ToList();
-
- var contentPublishedElements = new Dictionary();
- var settingsPublishedElements = new Dictionary();
-
- var layout = new List();
-
var value = (string)inter;
+
+ // Short-circuit on empty values
if (string.IsNullOrWhiteSpace(value)) return BlockListModel.Empty;
var converted = _blockListEditorDataConverter.Deserialize(value);
@@ -68,49 +59,60 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
var blockListLayout = converted.Layout.ToObject>();
- // convert the content data
+ // Get configuration
+ var configuration = propertyType.DataType.ConfigurationAs();
+ var blockConfigMap = configuration.Blocks.ToDictionary(x => x.ContentElementTypeKey);
+ var validSettingsElementTypes = blockConfigMap.Values.Select(x => x.SettingsElementTypeKey).Where(x => x.HasValue).Distinct().ToList();
+
+ // Convert the content data
+ var contentPublishedElements = new Dictionary();
foreach (var data in converted.BlockValue.ContentData)
{
if (!blockConfigMap.ContainsKey(data.ContentTypeKey)) continue;
var element = _blockConverter.ConvertToElement(data, referenceCacheLevel, preview);
if (element == null) continue;
+
contentPublishedElements[element.Key] = element;
}
- // convert the settings data
+
+ // If there are no content elements, it doesn't matter what is stored in layout
+ if (contentPublishedElements.Count == 0) return BlockListModel.Empty;
+
+ // Convert the settings data
+ var settingsPublishedElements = new Dictionary();
foreach (var data in converted.BlockValue.SettingsData)
{
- if (!validSettingElementTypes.Contains(data.ContentTypeKey)) continue;
+ if (!validSettingsElementTypes.Contains(data.ContentTypeKey)) continue;
var element = _blockConverter.ConvertToElement(data, referenceCacheLevel, preview);
if (element == null) continue;
+
settingsPublishedElements[element.Key] = element;
}
- // if there's no elements just return since if there's no data it doesn't matter what is stored in layout
- if (contentPublishedElements.Count == 0) return BlockListModel.Empty;
-
+ var layout = new List();
foreach (var layoutItem in blockListLayout)
{
- // get the content reference
- var contentGuidUdi = (GuidUdi)layoutItem.ContentUdi;
+ // Get the content reference
+ var contentGuidUdi = (GuidUdi)layoutItem.ContentUdi;
if (!contentPublishedElements.TryGetValue(contentGuidUdi.Guid, out var contentData))
continue;
- // get the setting reference
- IPublishedElement settingsData = null;
- var settingGuidUdi = layoutItem.SettingsUdi != null ? (GuidUdi)layoutItem.SettingsUdi : null;
- if (settingGuidUdi != null)
- settingsPublishedElements.TryGetValue(settingGuidUdi.Guid, out settingsData);
-
if (!contentData.ContentType.TryGetKey(out var contentTypeKey))
throw new InvalidOperationException("The content type was not of type " + typeof(IPublishedContentType2));
if (!blockConfigMap.TryGetValue(contentTypeKey, out var blockConfig))
continue;
- // this can happen if they have a settings type, save content, remove the settings type, and display the front-end page before saving the content again
- // we also ensure that the content type's match since maybe the settings type has been changed after this has been persisted.
+ // Get the setting reference
+ IPublishedElement settingsData = null;
+ var settingGuidUdi = layoutItem.SettingsUdi != null ? (GuidUdi)layoutItem.SettingsUdi : null;
+ if (settingGuidUdi != null)
+ settingsPublishedElements.TryGetValue(settingGuidUdi.Guid, out settingsData);
+
+ // This can happen if they have a settings type, save content, remove the settings type, and display the front-end page before saving the content again
+ // We also ensure that the content types match, since maybe the settings type has been changed after this has been persisted
if (settingsData != null)
{
if (!settingsData.ContentType.TryGetKey(out var settingsElementTypeKey))
@@ -120,8 +122,13 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
settingsData = null;
}
+ // Get settings type from configuration
+ var settingsType = blockConfig.SettingsElementTypeKey.HasValue
+ ? _blockConverter.GetModelType(blockConfig.SettingsElementTypeKey.Value)
+ : typeof(IPublishedElement);
+
// TODO: This should be optimized/cached, as calling Activator.CreateInstance is slow
- var layoutType = typeof(BlockListItem<,>).MakeGenericType(contentData.GetType(), settingsData?.GetType() ?? typeof(IPublishedElement));
+ var layoutType = typeof(BlockListItem<,>).MakeGenericType(contentData.GetType(), settingsType);
var layoutRef = (BlockListItem)Activator.CreateInstance(layoutType, contentGuidUdi, contentData, settingGuidUdi, settingsData);
layout.Add(layoutRef);
diff --git a/src/Umbraco.Web/Security/GetUserSecondsMiddleWare.cs b/src/Umbraco.Web/Security/GetUserSecondsMiddleWare.cs
index 8da8ad23f8..dfa80a337e 100644
--- a/src/Umbraco.Web/Security/GetUserSecondsMiddleWare.cs
+++ b/src/Umbraco.Web/Security/GetUserSecondsMiddleWare.cs
@@ -62,7 +62,7 @@ namespace Umbraco.Web.Security
response.ContentType = "application/json; charset=utf-8";
response.StatusCode = 200;
- response.Headers.Add("Cache-Control", new[] { "no-cache" });
+ response.Headers.Add("Cache-Control", new[] { "no-store", "must-revalidate", "no-cache", "max-age=0" });
response.Headers.Add("Pragma", new[] { "no-cache" });
response.Headers.Add("Expires", new[] { "-1" });
response.Headers.Add("Date", new[] { _authOptions.SystemClock.UtcNow.ToString("R") });