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 @@
-