diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec
index f3f8f47b57..56462fcc40 100644
--- a/build/NuSpecs/UmbracoCms.Core.nuspec
+++ b/build/NuSpecs/UmbracoCms.Core.nuspec
@@ -52,7 +52,7 @@
-
+
diff --git a/build/NuSpecs/UmbracoCms.Web.nuspec b/build/NuSpecs/UmbracoCms.Web.nuspec
index 3c8fed78f5..614a816f3f 100644
--- a/build/NuSpecs/UmbracoCms.Web.nuspec
+++ b/build/NuSpecs/UmbracoCms.Web.nuspec
@@ -53,9 +53,9 @@
-
-
-
+
+
+
diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs b/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs
index 2295745637..d86c682bd5 100644
--- a/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs
+++ b/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs
@@ -389,7 +389,7 @@ namespace Umbraco.Core.Migrations.Install
private DatabaseSchemaResult ValidateSchema(IScope scope)
{
- if (_databaseFactory.Configured == false)
+ if (_databaseFactory.Initialized == false)
return new DatabaseSchemaResult(_databaseFactory.SqlContext.SqlSyntax);
if (_databaseSchemaValidationResult != null)
@@ -513,7 +513,7 @@ namespace Umbraco.Core.Migrations.Install
private Attempt CheckReadyForInstall()
{
- if (_databaseFactory.Configured == false)
+ if (_databaseFactory.CanConnect == false)
{
return Attempt.Fail(new Result
{
diff --git a/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs
index 0236fc4bd5..c2d65b824f 100644
--- a/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs
+++ b/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs
@@ -10,22 +10,35 @@ namespace Umbraco.Core.Persistence
///
/// Creates a new database.
///
- /// The new database must be disposed after being used.
+ ///
+ /// The new database must be disposed after being used.
+ /// Creating a database causes the factory to initialize if it is not already initialized.
+ ///
IUmbracoDatabase CreateDatabase();
///
- /// Gets a value indicating whether the database factory is configured.
+ /// Gets a value indicating whether the database factory is configured, i.e. whether
+ /// its connection string and provider name have been set. The factory may however not
+ /// be initialized (see ).
///
bool Configured { get; }
+ ///
+ /// Gets a value indicating whether the database factory is initialized, i.e. whether
+ /// its internal state is ready and it has been possible to connect to the database.
+ ///
+ bool Initialized { get; }
+
///
/// Gets the connection string.
///
- /// Throws if the factory is not configured.
+ /// May return null if the database factory is not configured.
string ConnectionString { get; }
///
- /// Gets a value indicating whether the database can connect.
+ /// 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
+ /// ).
///
bool CanConnect { get; }
@@ -37,6 +50,9 @@ namespace Umbraco.Core.Persistence
///
/// Gets the Sql context.
///
+ ///
+ /// Getting the Sql context causes the factory to initialize if it is not already initialized.
+ ///
ISqlContext SqlContext { get; }
///
diff --git a/src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs b/src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs
index c9d85feb25..0574e37c4c 100644
--- a/src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs
+++ b/src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs
@@ -232,6 +232,14 @@ namespace Umbraco.Core.Persistence
using (var copy = new SqlBulkCopy(tConnection, SqlBulkCopyOptions.Default, tTransaction) { BulkCopyTimeout = 10000, DestinationTableName = tableName })
using (var bulkReader = new PocoDataDataReader(records, pocoData, syntax))
{
+ //we need to add column mappings here because otherwise columns will be matched by their order and if the order of them are different in the DB compared
+ //to the order in which they are declared in the model then this will not work, so instead we will add column mappings by name so that this explicitly uses
+ //the names instead of their ordering.
+ foreach(var col in bulkReader.ColumnMappings)
+ {
+ copy.ColumnMappings.Add(col.DestinationColumn, col.DestinationColumn);
+ }
+
copy.WriteToServer(bulkReader);
return bulkReader.RecordsAffected;
}
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs
index 645ab9f924..ccafb9f771 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs
@@ -140,10 +140,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
while (templateDtoIx < templateDtos.Count && templateDtos[templateDtoIx].ContentTypeNodeId == contentType.Id)
{
var allowedDto = templateDtos[templateDtoIx];
+ templateDtoIx++;
if (!templates.TryGetValue(allowedDto.TemplateNodeId, out var template)) continue;
allowedTemplates.Add(template);
- templateDtoIx++;
-
+
if (allowedDto.IsDefault)
defaultTemplateId = template.Id;
}
diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs
index dc86ff060c..13422f43b1 100644
--- a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs
+++ b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs
@@ -28,7 +28,8 @@ namespace Umbraco.Core.Persistence
{
private readonly Lazy _mappers;
private readonly ILogger _logger;
- private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
+
+ private object _lock = new object();
private DatabaseFactory _npocoDatabaseFactory;
private IPocoDataFactory _pocoDataFactory;
@@ -36,12 +37,13 @@ namespace Umbraco.Core.Persistence
private string _providerName;
private DbProviderFactory _dbProviderFactory;
private DatabaseType _databaseType;
- private bool _serverVersionDetected;
private ISqlSyntaxProvider _sqlSyntax;
private RetryPolicy _connectionRetryPolicy;
private RetryPolicy _commandRetryPolicy;
private NPoco.MapperCollection _pocoMappers;
+ private SqlContext _sqlContext;
private bool _upgrading;
+ private bool _initialized;
#region Constructors
@@ -106,36 +108,30 @@ namespace Umbraco.Core.Persistence
#endregion
///
- public bool Configured { get; private set; }
-
- ///
- public string ConnectionString
+ public bool Configured
{
get
{
- EnsureConfigured();
- return _connectionString;
+ lock (_lock)
+ {
+ return !_connectionString.IsNullOrWhiteSpace() && !_providerName.IsNullOrWhiteSpace();
+ }
}
}
///
- public bool CanConnect
- {
- get
- {
- if (!Configured || !DbConnectionExtensions.IsConnectionAvailable(_connectionString, _providerName)) return false;
+ public bool Initialized => Volatile.Read(ref _initialized);
- if (_serverVersionDetected) return true;
+ ///
+ public string ConnectionString => _connectionString;
- if (_databaseType.IsSqlServer())
- DetectSqlServerVersion();
- _serverVersionDetected = true;
+ ///
+ public bool CanConnect =>
+ // actually tries to connect to the database (regardless of configured/initialized)
+ !_connectionString.IsNullOrWhiteSpace() && !_providerName.IsNullOrWhiteSpace() &&
+ DbConnectionExtensions.IsConnectionAvailable(_connectionString, _providerName);
- return true;
- }
- }
-
- private void DetectSqlServerVersion()
+ private void UpdateSqlServerDatabaseType()
{
// replace NPoco database type by a more efficient one
@@ -171,7 +167,15 @@ namespace Umbraco.Core.Persistence
}
///
- public ISqlContext SqlContext { get; private set; }
+ public ISqlContext SqlContext
+ {
+ get
+ {
+ // must be initialized to have a context
+ EnsureInitialized();
+ return _sqlContext;
+ }
+ }
///
public void ConfigureForUpgrade()
@@ -182,63 +186,79 @@ namespace Umbraco.Core.Persistence
///
public void Configure(string connectionString, string providerName)
{
- try
+ if (connectionString.IsNullOrWhiteSpace()) throw new ArgumentNullException(nameof(connectionString));
+ if (providerName.IsNullOrWhiteSpace()) throw new ArgumentNullException(nameof(providerName));
+
+ lock (_lock)
{
- _lock.EnterWriteLock();
-
- _logger.Debug("Configuring.");
-
- if (Configured) throw new InvalidOperationException("Already configured.");
-
- if (connectionString.IsNullOrWhiteSpace()) throw new ArgumentNullException(nameof(connectionString));
- if (providerName.IsNullOrWhiteSpace()) throw new ArgumentNullException(nameof(providerName));
+ if (Volatile.Read(ref _initialized))
+ throw new InvalidOperationException("Already initialized.");
_connectionString = connectionString;
_providerName = providerName;
-
- _connectionRetryPolicy = RetryPolicyFactory.GetDefaultSqlConnectionRetryPolicyByConnectionString(_connectionString);
- _commandRetryPolicy = RetryPolicyFactory.GetDefaultSqlCommandRetryPolicyByConnectionString(_connectionString);
-
- _dbProviderFactory = DbProviderFactories.GetFactory(_providerName);
- if (_dbProviderFactory == null)
- throw new Exception($"Can't find a provider factory for provider name \"{_providerName}\".");
- _databaseType = DatabaseType.Resolve(_dbProviderFactory.GetType().Name, _providerName);
- if (_databaseType == null)
- throw new Exception($"Can't find an NPoco database type for provider name \"{_providerName}\".");
-
- _sqlSyntax = GetSqlSyntaxProvider(_providerName);
- if (_sqlSyntax == null)
- throw new Exception($"Can't find a sql syntax provider for provider name \"{_providerName}\".");
-
- // ensure we have only 1 set of mappers, and 1 PocoDataFactory, for all database
- // so that everything NPoco is properly cached for the lifetime of the application
- _pocoMappers = new NPoco.MapperCollection { new PocoMapper() };
- var factory = new FluentPocoDataFactory(GetPocoDataFactoryResolver);
- _pocoDataFactory = factory;
- var config = new FluentConfig(xmappers => factory);
-
- // create the database factory
- _npocoDatabaseFactory = DatabaseFactory.Config(x => x
- .UsingDatabase(CreateDatabaseInstance) // creating UmbracoDatabase instances
- .WithFluentConfig(config)); // with proper configuration
-
- if (_npocoDatabaseFactory == null) throw new NullReferenceException("The call to UmbracoDatabaseFactory.Config yielded a null UmbracoDatabaseFactory instance.");
-
- SqlContext = new SqlContext(_sqlSyntax, _databaseType, _pocoDataFactory, _mappers);
-
- _logger.Debug("Configured.");
- Configured = true;
- }
- finally
- {
- if (_lock.IsWriteLockHeld)
- _lock.ExitWriteLock();
}
+
+ // rest to be lazy-initialized
+ }
+
+ private void EnsureInitialized()
+ {
+ LazyInitializer.EnsureInitialized(ref _sqlContext, ref _initialized, ref _lock, Initialize);
+ }
+
+ private SqlContext Initialize()
+ {
+ _logger.Debug("Initializing.");
+
+ if (_connectionString.IsNullOrWhiteSpace()) throw new InvalidOperationException("The factory has not been configured with a proper connection string.");
+ if (_providerName.IsNullOrWhiteSpace()) throw new InvalidOperationException("The factory has not been configured with a proper provider name.");
+
+ // cannot initialize without being able to talk to the database
+ if (!DbConnectionExtensions.IsConnectionAvailable(_connectionString, _providerName))
+ throw new Exception("Cannot connect to the database.");
+
+ _connectionRetryPolicy = RetryPolicyFactory.GetDefaultSqlConnectionRetryPolicyByConnectionString(_connectionString);
+ _commandRetryPolicy = RetryPolicyFactory.GetDefaultSqlCommandRetryPolicyByConnectionString(_connectionString);
+
+ _dbProviderFactory = DbProviderFactories.GetFactory(_providerName);
+ if (_dbProviderFactory == null)
+ throw new Exception($"Can't find a provider factory for provider name \"{_providerName}\".");
+ _databaseType = DatabaseType.Resolve(_dbProviderFactory.GetType().Name, _providerName);
+ if (_databaseType == null)
+ throw new Exception($"Can't find an NPoco database type for provider name \"{_providerName}\".");
+
+ _sqlSyntax = GetSqlSyntaxProvider(_providerName);
+ if (_sqlSyntax == null)
+ throw new Exception($"Can't find a sql syntax provider for provider name \"{_providerName}\".");
+
+ if (_databaseType.IsSqlServer())
+ UpdateSqlServerDatabaseType();
+
+ // ensure we have only 1 set of mappers, and 1 PocoDataFactory, for all database
+ // so that everything NPoco is properly cached for the lifetime of the application
+ _pocoMappers = new NPoco.MapperCollection { new PocoMapper() };
+ var factory = new FluentPocoDataFactory(GetPocoDataFactoryResolver);
+ _pocoDataFactory = factory;
+ var config = new FluentConfig(xmappers => factory);
+
+ // create the database factory
+ _npocoDatabaseFactory = DatabaseFactory.Config(x => x
+ .UsingDatabase(CreateDatabaseInstance) // creating UmbracoDatabase instances
+ .WithFluentConfig(config)); // with proper configuration
+
+ if (_npocoDatabaseFactory == null)
+ throw new NullReferenceException("The call to UmbracoDatabaseFactory.Config yielded a null UmbracoDatabaseFactory instance.");
+
+ _logger.Debug("Initialized.");
+
+ return new SqlContext(_sqlSyntax, _databaseType, _pocoDataFactory, _mappers);
}
///
public IUmbracoDatabase CreateDatabase()
{
+ // must be initialized to create a database
+ EnsureInitialized();
return (IUmbracoDatabase) _npocoDatabaseFactory.GetDatabase();
}
@@ -260,22 +280,6 @@ namespace Umbraco.Core.Persistence
}
}
- // ensures that the database is configured, else throws
- private void EnsureConfigured()
- {
- _lock.EnterReadLock();
- try
- {
- if (Configured == false)
- throw new InvalidOperationException("Not configured.");
- }
- finally
- {
- if (_lock.IsReadLockHeld)
- _lock.ExitReadLock();
- }
- }
-
// method used by NPoco's UmbracoDatabaseFactory to actually create the database instance
private UmbracoDatabase CreateDatabaseInstance()
{
@@ -292,7 +296,7 @@ namespace Umbraco.Core.Persistence
//var db = _umbracoDatabaseAccessor.UmbracoDatabase;
//_umbracoDatabaseAccessor.UmbracoDatabase = null;
//db?.Dispose();
- Configured = false;
+ Volatile.Write(ref _initialized, false);
}
// during tests, the thread static var can leak between tests
diff --git a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs
index c276dc35ca..fb451b1d5c 100644
--- a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs
+++ b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs
@@ -43,16 +43,6 @@ namespace Umbraco.Tests.Persistence
_databaseFactory = null;
}
- [Test]
- public void GetDatabaseType()
- {
- using (var database = _databaseFactory.CreateDatabase())
- {
- var databaseType = database.DatabaseType;
- Assert.AreEqual(DatabaseType.SQLCe, databaseType);
- }
- }
-
[Test]
public void CreateDatabase() // FIXME: move to DatabaseBuilderTest!
{
@@ -79,6 +69,13 @@ namespace Umbraco.Tests.Persistence
// re-create the database factory and database context with proper connection string
_databaseFactory = new UmbracoDatabaseFactory(connString, Constants.DbProviderNames.SqlCe, _logger, new Lazy(() => Mock.Of()));
+ // test get database type (requires an actual database)
+ using (var database = _databaseFactory.CreateDatabase())
+ {
+ var databaseType = database.DatabaseType;
+ Assert.AreEqual(DatabaseType.SQLCe, databaseType);
+ }
+
// create application context
//var appCtx = new ApplicationContext(
// _databaseFactory,
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 bbbdd392b2..396699866c 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
@@ -505,6 +505,7 @@
property.showOnMemberProfile = propertyModel.showOnMemberProfile;
property.memberCanEdit = propertyModel.memberCanEdit;
property.isSensitiveValue = propertyModel.isSensitiveValue;
+ property.allowCultureVariant = propertyModel.allowCultureVariant;
// update existing data types
if(model.updateSameDataTypes) {
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js
index 3c34890fa9..3ed5f49b92 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js
@@ -20,6 +20,13 @@ angular.module("umbraco")
editorConfig.maxImageSize = tinyMceService.defaultPrevalues().maxImageSize;
}
+ var width = editorConfig.dimensions ? parseInt(editorConfig.dimensions.width, 10) || null : null;
+ var height = editorConfig.dimensions ? parseInt(editorConfig.dimensions.height, 10) || null : null;
+
+ $scope.containerWidth = editorConfig.mode === "distraction-free" ? (width ? width : "auto") : "auto";
+ $scope.containerHeight = editorConfig.mode === "distraction-free" ? (height ? height : "auto") : "auto";
+ $scope.containerOverflow = editorConfig.mode === "distraction-free" ? (height ? "auto" : "inherit") : "inherit";
+
var promises = [];
//queue file loading
@@ -41,10 +48,16 @@ angular.module("umbraco")
$q.all(promises).then(function (result) {
var standardConfig = result[promises.length - 1];
-
+
+ if (height !== null) {
+ standardConfig.plugins.splice(standardConfig.plugins.indexOf("autoresize"), 1);
+ }
+
//create a baseline Config to extend upon
var baseLineConfigObj = {
- maxImageSize: editorConfig.maxImageSize
+ maxImageSize: editorConfig.maxImageSize,
+ width: width,
+ height: height
};
angular.extend(baseLineConfigObj, standardConfig);
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html
index a68bfe978e..7dde17bb06 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html
@@ -1,6 +1,6 @@