Compare commits

...

9 Commits

54 changed files with 830 additions and 128 deletions
+22 -8
View File
@@ -1,18 +1,22 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Profiling;
using Umbraco.Core.Services;
using Umbraco.Core.Sync;
namespace Umbraco.Core
{
/// <summary>
/// <summary>
/// the Umbraco Application context
/// </summary>
/// <remarks>
@@ -183,6 +187,7 @@ namespace Umbraco.Core
readonly ManualResetEventSlim _isReadyEvent = new ManualResetEventSlim(false);
private DatabaseContext _databaseContext;
private ServiceContext _services;
private PackageMigrationsContext _packageMigrationsContext;
public bool IsReady
{
@@ -212,17 +217,19 @@ namespace Umbraco.Core
public bool IsConfigured
{
get { return _configured.Value; }
get { return _umbracoVersionConfigured.Value; }
}
/// <summary>
/// If the db is configured, there is a database context and there is an umbraco schema, but we are not 'configured' , then it means we are upgrading
/// If the db is configured, there is a database context and there is an umbraco schema,
/// but we are: (not 'configured' OR there's pending migrations), then it means we are upgrading
/// </summary>
public bool IsUpgrading
{
get
{
if (IsConfigured == false
if ((IsConfigured == false || PackageMigrationsContext.HasPendingPackageMigrations)
&& DatabaseContext != null
&& DatabaseContext.IsDatabaseConfigured)
{
@@ -268,7 +275,8 @@ namespace Umbraco.Core
// ReSharper disable once InconsistentNaming
internal string _umbracoApplicationUrl;
private Lazy<bool> _configured;
private Lazy<bool> _umbracoVersionConfigured;
internal MainDom MainDom { get; private set; }
private void Init()
@@ -277,7 +285,7 @@ namespace Umbraco.Core
MainDom.Acquire();
//Create the lazy value to resolve whether or not the application is 'configured'
_configured = new Lazy<bool>(() =>
_umbracoVersionConfigured = new Lazy<bool>(() =>
{
try
{
@@ -318,9 +326,10 @@ namespace Umbraco.Core
LogHelper.Error<ApplicationContext>("Error determining if application is configured, returning false", ex);
return false;
}
});
});
}
_packageMigrationsContext = new PackageMigrationsContext(DatabaseContext, Services.MigrationEntryService, ProfilingLogger.Logger);
}
private string ConfigurationStatus
{
@@ -343,6 +352,11 @@ namespace Umbraco.Core
throw new Exception("ApplicationContext has already been initialized.");
}
internal PackageMigrationsContext PackageMigrationsContext
{
get { return _packageMigrationsContext; }
}
/// <summary>
/// Gets the current DatabaseContext
/// </summary>
@@ -32,12 +32,7 @@ namespace Umbraco.Core.Configuration
public static SemVersion GetSemanticVersion()
{
return new SemVersion(
Current.Major,
Current.Minor,
Current.Build,
CurrentComment.IsNullOrWhiteSpace() ? null : CurrentComment,
Current.Revision > 0 ? Current.Revision.ToInvariantString() : null);
return Current.GetSemanticVersion();
}
}
}
+6 -1
View File
@@ -500,7 +500,12 @@ namespace Umbraco.Core
//the database migration objects
MigrationResolver.Current = new MigrationResolver(
ProfilingLogger.Logger,
() => PluginManager.ResolveTypes<IMigration>());
() => PluginManager.ResolveTypes<IMigration>())
{
//This needs to be resolved before it's frozen because we need to find migrations to determine
// if Umbraco IsConfigured
CanResolveBeforeFrozen = true
};
// todo: remove once we drop IPropertyEditorValueConverter support.
PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
+1 -2
View File
@@ -1,5 +1,4 @@
using System;
using Semver;
using Semver;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
@@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Semver;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Services;
namespace Umbraco.Core.Packaging
{
internal class PackageMigrationsContext
{
private readonly DatabaseContext _dbContext;
private readonly IMigrationEntryService _migrationEntryService;
private readonly ILogger _logger;
public PackageMigrationsContext(DatabaseContext dbContext,
IMigrationEntryService migrationEntryService,
ILogger logger)
{
_dbContext = dbContext;
_migrationEntryService = migrationEntryService;
_logger = logger;
ResetPendingPackageMigrations();
}
//List of product names and their maximum sem version registered in migrations
private Lazy<ReadOnlyDictionary<string, SemVersion>> _packageVersionsConfigured;
/// <summary>
/// Called to initialize or when package migrations have executed - since an app restart is mandatory
/// </summary>
internal void ResetPendingPackageMigrations()
{
_packageVersionsConfigured = new Lazy<ReadOnlyDictionary<string, SemVersion>>(() =>
{
var result = new Dictionary<string, SemVersion>();
//The versions are the same in config, but are they the same in the database. We can only check this
// if we have a db context available, if we don't then we are not installed anyways
if (_dbContext.IsDatabaseConfigured && _dbContext.CanConnect)
{
//Now we can check if there are any package migrations that haven't been executed.
//Find and group the packages that have migrations.
//Then we need to determine if there are outstanding migrations that need to be run based on the
// migration versions stored in the db compared to the available migrations.
var packageMigrations = MigrationResolver.Current.MigrationMetaData
.Where(x => x.ProductName != GlobalSettings.UmbracoMigrationName)
.GroupBy(x => x.ProductName)
.ToArray();
var packageMigrationProductNames = packageMigrations.Select(x => x.Key).Distinct().ToArray();
var allPackageDbEntries = _migrationEntryService.FindEntries(packageMigrationProductNames)
.ToArray();
foreach (var packageMigration in packageMigrations)
{
var packageDbVersions = allPackageDbEntries
.Where(x => x.MigrationName.InvariantEquals(packageMigration.Key))
.Select(x => x.Version);
var notExecuted = packageMigration
.Select(x => x.TargetVersion.GetSemanticVersion())
.Except(packageDbVersions)
.ToArray();
if (notExecuted.Any())
{
var maxVersion = notExecuted.Max();
result.Add(packageMigration.Key, maxVersion);
_logger.Debug<ApplicationContext>(
string.Format("The migration {0} for version: '{1} has not been executed, there is no record in the database",
packageMigration.Key,
maxVersion.ToSemanticString()));
}
}
}
return new ReadOnlyDictionary<string, SemVersion>(result);
});
}
/// <summary>
/// Returns true if there are pending package migrations that need to be executed
/// </summary>
internal bool HasPendingPackageMigrations
{
get { return _packageVersionsConfigured.Value.Any(); }
}
/// <summary>
/// Returns the list of package migration names that need to be executed
/// </summary>
internal ReadOnlyDictionary<string, SemVersion> GetPendingPackageMigrations()
{
return _packageVersionsConfigured.Value;
}
/// <summary>
/// Returns the list of package migration names and versions that need to be executed
/// with the following format: PackageProductName (2.0.0)
/// </summary>
/// <returns></returns>
internal IEnumerable<string> GetPendingPackageMigrationFriendlyNames()
{
return _packageVersionsConfigured.Value.Select(x => string.Format("{0} ({1})", x.Key, x.Value));
}
}
}
@@ -33,6 +33,11 @@ namespace Umbraco.Core.Persistence.Migrations
get { return Values; }
}
public IEnumerable<MigrationAttribute> MigrationMetaData
{
get { return Values.SelectMany(x => x.GetType().GetCustomAttributes<MigrationAttribute>(false)).WhereNotNull(); }
}
/// <summary>
/// This will ctor the IMigration instances
@@ -94,7 +94,6 @@ namespace Umbraco.Core.Persistence.Migrations
var migrations = isUpgrade
? OrderedUpgradeMigrations(foundMigrations).ToList()
: OrderedDowngradeMigrations(foundMigrations).ToList();
if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, _productName, true), this))
{
@@ -298,7 +297,6 @@ namespace Umbraco.Core.Persistence.Migrations
{
_migrationEntryService.CreateEntry(_productName, _targetVersion);
}
}
}
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Semver;
using Umbraco.Core.Models;
@@ -7,5 +8,9 @@ namespace Umbraco.Core.Persistence.Repositories
public interface IMigrationEntryRepository : IRepositoryQueryable<int, IMigrationEntry>
{
IMigrationEntry FindEntry(string migrationName, SemVersion version);
IEnumerable<IMigrationEntry> FindEntries(SemVersion version, IEnumerable<string> migrationNames);
IEnumerable<IMigrationEntry> FindEntries(IEnumerable<string> migrationNames);
IEnumerable<IMigrationEntry> FindEntries(string migrationName);
}
}
@@ -128,5 +128,53 @@ namespace Umbraco.Core.Persistence.Repositories
return result == null ? null : factory.BuildEntity(result);
}
public IEnumerable<IMigrationEntry> FindEntries(SemVersion version, IEnumerable<string> migrationNames)
{
if (migrationNames.Any() == false) return Enumerable.Empty<IMigrationEntry>();
var versionString = version.ToString();
var sql = new Sql().Select("*")
.From<MigrationDto>(SqlSyntax)
.Where(
SqlSyntax.GetQuotedColumnName("version") + "=@versionString AND " +
SqlSyntax.GetQuotedColumnName("name") + " IN (@names)", new {names = migrationNames, versionString});
var result = Database.Fetch<MigrationDto>(sql);
var factory = new MigrationEntryFactory();
return result.Select(factory.BuildEntity);
}
public IEnumerable<IMigrationEntry> FindEntries(IEnumerable<string> migrationNames)
{
if (migrationNames.Any() == false) return Enumerable.Empty<IMigrationEntry>();
var sql = new Sql().Select("*")
.From<MigrationDto>(SqlSyntax)
.Where(
SqlSyntax.GetQuotedColumnName("name") + " IN (@names)", new { names = migrationNames });
var result = Database.Fetch<MigrationDto>(sql);
var factory = new MigrationEntryFactory();
return result.Select(factory.BuildEntity);
}
public IEnumerable<IMigrationEntry> FindEntries(string migrationName)
{
var sql = new Sql().Select("*")
.From<MigrationDto>(SqlSyntax)
.Where<MigrationDto>(x => x.Name.InvariantEquals(migrationName));
var result = Database.Fetch<MigrationDto>(sql);
var factory = new MigrationEntryFactory();
return result.Select(factory.BuildEntity);
}
}
}
-12
View File
@@ -1,12 +0,0 @@
using Semver;
namespace Umbraco.Core
{
public static class SemVersionExtensions
{
public static string ToSemanticString(this SemVersion semVersion)
{
return semVersion.ToString().Replace("--", "-").Replace("-+", "+");
}
}
}
@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using Semver;
using Umbraco.Core.Models;
@@ -23,6 +22,28 @@ namespace Umbraco.Core.Services
/// <returns></returns>
IMigrationEntry FindEntry(string migrationName, SemVersion version);
/// <summary>
/// Finds migration entries by name
/// </summary>
/// <param name="migrationName"></param>
/// <returns></returns>
IEnumerable<IMigrationEntry> FindEntries(string migrationName);
/// <summary>
/// Finds migration entries by list of product names
/// </summary>
/// <param name="migrationNames"></param>
/// <returns></returns>
IEnumerable<IMigrationEntry> FindEntries(IEnumerable<string> migrationNames);
/// <summary>
/// Finds migrations by version and list of product names
/// </summary>
/// <param name="version"></param>
/// <param name="migrationNames"></param>
/// <returns></returns>
IEnumerable<IMigrationEntry> FindEntries(SemVersion version, IEnumerable<string> migrationNames);
/// <summary>
/// Gets all entries for a given migration name
/// </summary>
@@ -60,6 +60,33 @@ namespace Umbraco.Core.Services
}
}
public IEnumerable<IMigrationEntry> FindEntries(string migrationName)
{
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateMigrationEntryRepository(uow))
{
return repo.FindEntries(migrationName);
}
}
public IEnumerable<IMigrationEntry> FindEntries(IEnumerable<string> migrationNames)
{
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateMigrationEntryRepository(uow))
{
return repo.FindEntries(migrationNames);
}
}
public IEnumerable<IMigrationEntry> FindEntries(SemVersion version, IEnumerable<string> migrationNames)
{
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateMigrationEntryRepository(uow))
{
return repo.FindEntries(version, migrationNames);
}
}
/// <summary>
/// Gets all entries for a given migration name
/// </summary>
+1 -1
View File
@@ -404,6 +404,7 @@
<Compile Include="Models\UmbracoDomain.cs" />
<Compile Include="Models\DoNotCloneAttribute.cs" />
<Compile Include="Models\IDomain.cs" />
<Compile Include="Packaging\PackageMigrationsContext.cs" />
<Compile Include="Persistence\DatabaseNodeLockExtensions.cs" />
<Compile Include="Persistence\Factories\ExternalLoginFactory.cs" />
<Compile Include="Persistence\Factories\MigrationEntryFactory.cs" />
@@ -494,7 +495,6 @@
<Compile Include="Security\IBackOfficeUserPasswordChecker.cs" />
<Compile Include="Security\MembershipPasswordHasher.cs" />
<Compile Include="Security\EmailService.cs" />
<Compile Include="SemVersionExtensions.cs" />
<Compile Include="Serialization\NoTypeConverterJsonConverter.cs" />
<Compile Include="ServiceProviderExtensions.cs" />
<Compile Include="IO\ResizedImage.cs" />
+19 -3
View File
@@ -5,9 +5,25 @@ using Semver;
namespace Umbraco.Core
{
internal static class VersionExtensions
public static class VersionExtensions
{
public static Version GetVersion(this SemVersion semVersion, int maxParts = 4)
public static string ToSemanticString(this SemVersion semVersion)
{
return semVersion.ToString().Replace("--", "-").Replace("-+", "+");
}
internal static SemVersion GetSemanticVersion(this Version version)
{
return new SemVersion(
version.Major,
version.Minor,
version.Build,
null,
version.Revision > 0 ? version.Revision.ToInvariantString() : null);
}
internal static Version GetVersion(this SemVersion semVersion, int maxParts = 4)
{
int build = 0;
int.TryParse(semVersion.Build, out build);
@@ -24,7 +40,7 @@ namespace Umbraco.Core
return new Version(semVersion.Major, semVersion.Minor);
}
public static Version SubtractRevision(this Version version)
internal static Version SubtractRevision(this Version version)
{
var parts = new List<int>(new[] {version.Major, version.Minor, version.Build, version.Revision});
@@ -26,7 +26,7 @@ namespace Umbraco.Tests.Migrations
() => new List<Type>
{
typeof (AlterUserTableMigrationStub),
typeof(Dummy),
typeof (Dummy),
typeof (SixZeroMigration1),
typeof (SixZeroMigration2),
typeof (FourElevenMigration),
@@ -51,7 +51,6 @@ namespace Umbraco.Tests.Migrations
};
Resolution.Freeze();
}
[Test]
@@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Moq;
using NUnit.Framework;
using Semver;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Profiling;
using Umbraco.Core.Services;
using Umbraco.Tests.Migrations.Stubs;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web.Install.MigrationSteps;
using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings;
namespace Umbraco.Tests.Migrations
{
[DatabaseTestBehavior(DatabaseBehavior.EmptyDbFilePerTest)]
[TestFixture]
public class PackageMigrationsTests : BaseDatabaseFactoryTest
{
private DatabaseContext _databaseContext;
private ILogger _logger;
private IMigrationEntryService _migrationEntryService;
/// <summary>
/// sets up resolvers before resolution is frozen
/// </summary>
protected override void FreezeResolution()
{
MigrationResolver.Current = new MigrationResolver(
Mock.Of<ILogger>(),
() => new List<Type>
{
typeof (PackageMigration1),
typeof (PackageMigration2)
});
//This is needed because the Migration resolver is creating the migration instances with their full ctors
_logger = Mock.Of<ILogger>();
LoggerResolver.Current = new LoggerResolver(_logger)
{
CanResolveBeforeFrozen = true
};
base.FreezeResolution();
}
protected override ApplicationContext CreateApplicationContext()
{
var sqlSyntax = new SqlCeSyntaxProvider();
var db = GetConfiguredDatabase();
var databaseContext = new Mock<DatabaseContext>(Mock.Of<IDatabaseFactory>(), Mock.Of<ILogger>(), sqlSyntax, "test");
databaseContext.SetupGet(x => x.CanConnect).Returns(true);
databaseContext.SetupGet(x => x.IsDatabaseConfigured).Returns(true);
databaseContext.Setup(x => x.Database).Returns(db);
_databaseContext = databaseContext.Object;
var umbracoMigrationEntry = new MigrationEntry(1, DateTime.UtcNow, GlobalSettings.UmbracoMigrationName,
UmbracoVersion.GetSemanticVersion());
var migrationEntryService = new Mock<IMigrationEntryService>();
migrationEntryService.Setup(x => x.FindEntry(It.IsAny<string>(), It.IsAny<SemVersion>()))
.Returns(umbracoMigrationEntry);
migrationEntryService.Setup(x => x.FindEntries(It.IsAny<string>())).Returns(Enumerable.Empty<IMigrationEntry>());
migrationEntryService.Setup(x => x.FindEntries(It.IsAny<string[]>())).Returns(Enumerable.Empty<IMigrationEntry>());
_migrationEntryService = migrationEntryService.Object;
//This is needed because the Migration resolver is creating migration instances with their full ctors
var applicationContext = new ApplicationContext(
//assign the db context
_databaseContext,
//assign the service context
new ServiceContext(migrationEntryService: _migrationEntryService),
CacheHelper.CreateDisabledCacheHelper(),
ProfilingLogger)
{
IsReady = true
};
return applicationContext;
}
[Test]
public void PackageMigrationsContext_HasPendingPackageMigrations()
{
var packageMigrationsContext = new PackageMigrationsContext(_databaseContext,
_migrationEntryService, _logger);
var hasPackageMigrations = packageMigrationsContext.HasPendingPackageMigrations;
Assert.True(hasPackageMigrations);
var result = packageMigrationsContext.GetPendingPackageMigrations();
Assert.AreEqual(1, result.Count);
Assert.True(result.ContainsKey("com.company.package"));
Assert.AreEqual(new SemVersion(2, 0, 0), result.First().Value);
var names = packageMigrationsContext.GetPendingPackageMigrationFriendlyNames();
var friendlyName = names.First();
Assert.AreEqual("com.company.package (2.0.0)", friendlyName);
}
[Test]
public void ExecutionMigrationStep_Can_Execute()
{
var packageMigrationsContext = new PackageMigrationsContext(_databaseContext,
_migrationEntryService, _logger);
var migrationStep = new ExecutionMigrationStep(_migrationEntryService, packageMigrationsContext,
_databaseContext.Database, _databaseContext.DatabaseProvider, _logger);
//Note: model isn't used here, so we just pass in an empty string
var executed = migrationStep.Execute(string.Empty);
Assert.Null(executed);
}
public UmbracoDatabase GetConfiguredDatabase()
{
return new UmbracoDatabase(GetDbConnectionString(), GetDbProviderName(), Mock.Of<ILogger>());
}
[TearDown]
public override void TearDown()
{
base.TearDown();
LoggerResolver.Reset();
MigrationResolver.Reset();
}
}
}
@@ -0,0 +1,29 @@
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Tests.Migrations.Stubs
{
[Migration("1.0.0", 0, "com.company.package")]
public class PackageMigration1 : MigrationBase
{
private const string TableName = "RandomTableTest";
public PackageMigration1(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
{
}
public override void Up()
{
Create.Table(TableName)
.WithColumn("Id").AsGuid().Unique().NotNullable().PrimaryKey("awesome_id")
.WithColumn("Content").AsString(500).NotNullable()
.WithColumn("TimeStamp").AsDateTime().NotNullable();
}
public override void Down()
{
Delete.Table(TableName);
}
}
}
@@ -0,0 +1,27 @@
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Tests.Migrations.Stubs
{
[Migration("2.0.0", 0, "com.company.package")]
public class PackageMigration2 : MigrationBase
{
private const string TableName = "RandomTableTest";
private const string ColumnName = "MoreContent";
public PackageMigration2(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
{
}
public override void Up()
{
Alter.Table(TableName).AddColumn(ColumnName).AsString(200).Nullable();
}
public override void Down()
{
Delete.Column(ColumnName).FromTable(TableName);
}
}
}
@@ -1,19 +1,14 @@
using System;
using System.Collections.Generic;
using System.Data.SqlServerCe;
using System.Linq;
using System.Text.RegularExpressions;
using Moq;
using NUnit.Framework;
using Semver;
using SQLCE4Umbraco;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers;
using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings;
@@ -193,9 +193,7 @@ namespace Umbraco.Tests.TestHelpers
engine.CreateDatabase();
}
}
}
}
/// <summary>
+3
View File
@@ -176,6 +176,9 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Migrations\MigrationIssuesTests.cs" />
<Compile Include="Migrations\PackageMigrationsTests.cs" />
<Compile Include="Migrations\Stubs\PackageMigration1.cs" />
<Compile Include="Migrations\Stubs\PackageMigration2.cs" />
<Compile Include="Persistence\Migrations\MigrationStartupHandlerTests.cs" />
<Compile Include="Web\AngularIntegration\AngularAntiForgeryTests.cs" />
<Compile Include="Web\AngularIntegration\ContentModelSerializationTests.cs" />
@@ -1,5 +1,6 @@
using System;
using NUnit.Framework;
using Semver;
using Umbraco.Core;
namespace Umbraco.Tests
@@ -26,5 +27,6 @@ namespace Umbraco.Tests
Assert.AreEqual(new Version(outcome), result);
}
}
}
@@ -12,7 +12,8 @@ angular.module("umbraco.install").factory('installerService', function($rootScop
var _installerModel = {
installId: undefined,
instructions: {
}
},
installType: Umbraco.Sys.ServerVariables.installType
};
//add to umbraco installer facts here
@@ -107,7 +108,7 @@ angular.module("umbraco.install").factory('installerService', function($rootScop
init : function(){
service.status.loading = true;
if(!_status.all){
service.getSteps().then(function(response){
service.getSteps(Umbraco.Sys.ServerVariables.installType).then(function (response) {
service.status.steps = response.data.steps;
service.status.index = 0;
_installerModel.installId = response.data.installId;
@@ -126,8 +127,8 @@ angular.module("umbraco.install").factory('installerService', function($rootScop
return $http.get(Umbraco.Sys.ServerVariables.installApiBaseUrl + "GetPackages");
},
getSteps : function(){
return $http.get(Umbraco.Sys.ServerVariables.installApiBaseUrl + "GetSetup");
getSteps : function(installType){
return $http.get(Umbraco.Sys.ServerVariables.installApiBaseUrl + "GetSetup?installType=" + installType);
},
gotoStep : function(index){
@@ -0,0 +1,12 @@
<div>
<h1>Package migration failed</h1>
<p>
The package migration failed for product <strong>{{installer.current.model}}</strong>.
Package migrations normally only fail due to a 3rd party library that has cancelled the migration event.
</p>
<p>
See the log for full details
</p>
</div>
@@ -0,0 +1,17 @@
<div>
<h1>Package migrations installation</h1>
<p>
The following packages have migrations that need to be executed:
</p>
<ul>
<li ng-repeat="item in installer.current.model">
{{item}}
</li>
</ul>
<p>
<button class="btn btn-success" ng-click="forward()">Continue</button>
</p>
</div>
@@ -64,7 +64,8 @@
Umbraco.Sys = {};
Umbraco.Sys.ServerVariables = {
"installApiBaseUrl": "@ViewBag.InstallApiBaseUrl",
"umbracoBaseUrl": "@ViewBag.UmbracoBaseFolder"
"umbracoBaseUrl": "@ViewBag.UmbracoBaseFolder",
"installType": "@ViewBag.InstallType"
};
</script>
<script src="lib/rgrove-lazyload/lazyload.js"></script>
@@ -63,7 +63,7 @@ namespace Umbraco.Web.Install.Controllers
/// Gets the install setup
/// </summary>
/// <returns></returns>
public InstallSetup GetSetup()
public InstallSetup GetSetup([FromUri]InstallerType installType)
{
var setup = new InstallSetup();
@@ -71,7 +71,7 @@ namespace Umbraco.Web.Install.Controllers
var steps = new List<InstallSetupStep>();
var installSteps = InstallHelper.GetStepsForCurrentInstallType().ToArray();
var installSteps = InstallHelper.GetStepsForCurrentInstallType(installType).ToArray();
//only get the steps that are targetting the current install type
steps.AddRange(installSteps);
@@ -110,7 +110,7 @@ namespace Umbraco.Web.Install.Controllers
{
var stepStatus = queue.Dequeue();
var step = InstallHelper.GetAllSteps().Single(x => x.Name == stepStatus.Name);
var step = InstallHelper.GetAllSteps(installModel.InstallType).Single(x => x.Name == stepStatus.Name);
JToken instruction = null;
//If this step has any instructions then extract them
@@ -193,7 +193,7 @@ namespace Umbraco.Web.Install.Controllers
if (queue.Count > 0)
{
var next = queue.Peek();
var step = InstallHelper.GetAllSteps().Single(x => x.Name == next.Name);
var step = InstallHelper.GetAllSteps(installModel.InstallType).Single(x => x.Name == next.Name);
//If the current step restarts the app pool then we must simply return the next one in the queue,
// we cannot peek ahead as the next step might rely on the app restart and therefore RequiresExecution
@@ -33,6 +33,18 @@ namespace Umbraco.Web.Install.Controllers
_umbracoContext = umbracoContext;
}
[HttpGet]
public ActionResult PackageMigrations()
{
if (ApplicationContext.Current.PackageMigrationsContext.HasPendingPackageMigrations == false)
{
return Redirect(SystemDirectories.Umbraco.EnsureEndsWith('/'));
}
ViewBag.InstallType = "Migrations";
return CheckUpgradingAndReturnInstallerView();
}
[HttpGet]
public ActionResult Index()
@@ -42,6 +54,13 @@ namespace Umbraco.Web.Install.Controllers
return Redirect(SystemDirectories.Umbraco.EnsureEndsWith('/'));
}
ViewBag.InstallType = "Core";
return CheckUpgradingAndReturnInstallerView();
}
private ActionResult CheckUpgradingAndReturnInstallerView()
{
if (ApplicationContext.Current.IsUpgrading)
{
var result = _umbracoContext.Security.ValidateCurrentUser(false);
@@ -53,15 +72,15 @@ namespace Umbraco.Web.Install.Controllers
return Redirect(SystemDirectories.Umbraco + "/AuthorizeUpgrade?redir=" + Server.UrlEncode(Request.RawUrl));
}
}
//gen the install base url
ViewBag.InstallApiBaseUrl = Url.GetUmbracoApiService("GetSetup", "InstallApi", "UmbracoInstall").TrimEnd("GetSetup");
//get the base umbraco folder
ViewBag.UmbracoBaseFolder = IOHelper.ResolveUrl(SystemDirectories.Umbraco);
InstallHelper ih = new InstallHelper(_umbracoContext);
var ih = new InstallHelper(_umbracoContext);
ih.InstallStatus(false, "");
//always ensure full path (see NOTE in the class remarks)
@@ -54,8 +54,9 @@ namespace Umbraco.Web.Install
try
{
var appContext = GetApplicationContext();
//if its not configured then we can continue
if (!GetApplicationContext().IsConfigured)
if (appContext.IsConfigured == false || appContext.PackageMigrationsContext.HasPendingPackageMigrations)
{
return true;
}
+64 -43
View File
@@ -15,6 +15,7 @@ using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Web.Install.InstallSteps;
using Umbraco.Web.Install.MigrationSteps;
using Umbraco.Web.Install.Models;
@@ -23,7 +24,7 @@ namespace Umbraco.Web.Install
internal class InstallHelper
{
private readonly UmbracoContext _umbContext;
private InstallationType? _installationType;
private CoreInstallationType? _coreInstallationType;
internal InstallHelper(UmbracoContext umbContext)
{
@@ -34,41 +35,64 @@ namespace Umbraco.Web.Install
/// <summary>
/// Get the installer steps
/// </summary>
/// <param name="installType"></param>
/// <returns></returns>
/// <remarks>
/// The step order returned here is how they will appear on the front-end if they have views assigned
/// </remarks>
public IEnumerable<InstallSetupStep> GetAllSteps()
public IEnumerable<InstallSetupStep> GetAllSteps(InstallerType installType)
{
return new List<InstallSetupStep>
switch (installType)
{
case InstallerType.Core:
return new List<InstallSetupStep>
{
new NewInstallStep(_umbContext.HttpContext, _umbContext.Application),
new UpgradeStep(),
new FilePermissionsStep(),
new MajorVersion7UpgradeReport(_umbContext.Application),
new Version73FileCleanup(_umbContext.HttpContext, _umbContext.Application.ProfilingLogger.Logger),
new DatabaseConfigureStep(_umbContext.Application),
new DatabaseInstallStep(_umbContext.Application),
new DatabaseUpgradeStep(_umbContext.Application),
new StarterKitDownloadStep(_umbContext.Application),
new StarterKitInstallStep(_umbContext.Application, _umbContext.HttpContext),
new StarterKitCleanupStep(_umbContext.Application),
new SetUmbracoVersionStep(_umbContext.Application, _umbContext.HttpContext),
};
new UpgradeStep(),
new FilePermissionsStep(),
new MajorVersion7UpgradeReport(_umbContext.Application),
new Version73FileCleanup(_umbContext.HttpContext, _umbContext.Application.ProfilingLogger.Logger),
new DatabaseConfigureStep(_umbContext.Application),
new DatabaseInstallStep(_umbContext.Application),
new DatabaseUpgradeStep(_umbContext.Application),
new StarterKitDownloadStep(_umbContext.Application),
new StarterKitInstallStep(_umbContext.Application, _umbContext.HttpContext),
new StarterKitCleanupStep(_umbContext.Application),
new SetUmbracoVersionStep(_umbContext.Application, _umbContext.HttpContext),
};
case InstallerType.Migrations:
return new List<InstallSetupStep>
{
new InitialMigrationStep(_umbContext.Application),
new ExecutionMigrationStep(_umbContext.Application),
};
default:
throw new ArgumentOutOfRangeException();
}
}
/// <summary>
/// Returns the steps that are used only for the current installation type
/// </summary>
/// <param name="installType"></param>
/// <returns></returns>
public IEnumerable<InstallSetupStep> GetStepsForCurrentInstallType()
public IEnumerable<InstallSetupStep> GetStepsForCurrentInstallType(InstallerType installType)
{
return GetAllSteps().Where(x => x.InstallTypeTarget.HasFlag(GetInstallationType()));
switch (installType)
{
case InstallerType.Core:
return GetAllSteps(installType).Where(x => x.InstallTypeTarget.HasFlag(GetCoreInstallationType()));
case InstallerType.Migrations:
return GetAllSteps(installType);
default:
throw new ArgumentOutOfRangeException("installType", installType, null);
}
}
public InstallationType GetInstallationType()
public CoreInstallationType GetCoreInstallationType()
{
return _installationType ?? (_installationType = IsBrandNewInstall ? InstallationType.NewInstall : InstallationType.Upgrade).Value;
return _coreInstallationType ?? (_coreInstallationType = IsBrandNewInstall ? CoreInstallationType.NewInstall : CoreInstallationType.Upgrade).Value;
}
internal void DeleteLegacyInstaller()
@@ -100,7 +124,7 @@ namespace Umbraco.Web.Install
{
try
{
string userAgent = _umbContext.HttpContext.Request.UserAgent;
var userAgent = _umbContext.HttpContext.Request.UserAgent;
// Check for current install Id
var installId = Guid.NewGuid();
@@ -115,23 +139,24 @@ namespace Umbraco.Web.Install
}
}
installCookie.SetValue(installId.ToString());
string dbProvider = string.Empty;
if (IsBrandNewInstall == false)
dbProvider = ApplicationContext.Current.DatabaseContext.DatabaseProvider.ToString();
bool brandNewInstall = IsBrandNewInstall;
string dbProvider = brandNewInstall
? ApplicationContext.Current.DatabaseContext.DatabaseProvider.ToString()
: string.Empty;
org.umbraco.update.CheckForUpgrade check = new org.umbraco.update.CheckForUpgrade();
check.Install(installId,
IsBrandNewInstall == false,
isCompleted,
DateTime.Now,
UmbracoVersion.Current.Major,
UmbracoVersion.Current.Minor,
UmbracoVersion.Current.Build,
UmbracoVersion.CurrentComment,
errorMsg,
userAgent,
dbProvider);
check.Install(installId,
brandNewInstall == false,
isCompleted,
DateTime.Now,
UmbracoVersion.Current.Major,
UmbracoVersion.Current.Minor,
UmbracoVersion.Current.Build,
UmbracoVersion.CurrentComment,
errorMsg,
userAgent,
dbProvider);
}
catch (Exception ex)
{
@@ -147,8 +172,7 @@ namespace Umbraco.Web.Install
get
{
var databaseSettings = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName];
if (GlobalSettings.ConfigurationStatus.IsNullOrWhiteSpace()
&& _umbContext.Application.DatabaseContext.IsConnectionStringConfigured(databaseSettings) == false)
if (GlobalSettings.ConfigurationStatus.IsNullOrWhiteSpace() && _umbContext.Application.DatabaseContext.IsConnectionStringConfigured(databaseSettings) == false)
{
//no version or conn string configured, must be a brand new install
return true;
@@ -156,15 +180,13 @@ namespace Umbraco.Web.Install
//now we have to check if this is really a new install, the db might be configured and might contain data
if (_umbContext.Application.DatabaseContext.IsConnectionStringConfigured(databaseSettings) == false
|| _umbContext.Application.DatabaseContext.IsDatabaseConfigured == false)
if (_umbContext.Application.DatabaseContext.IsConnectionStringConfigured(databaseSettings) == false || _umbContext.Application.DatabaseContext.IsDatabaseConfigured == false)
{
return true;
}
//check if we have the default user configured already
var result = _umbContext.Application.DatabaseContext.Database.ExecuteScalar<int>(
"SELECT COUNT(*) FROM umbracoUser WHERE id=0 AND userPassword='default'");
var result = _umbContext.Application.DatabaseContext.Database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoUser WHERE id=0 AND userPassword='default'");
if (result == 1)
{
//the user has not been configured
@@ -194,8 +216,7 @@ namespace Umbraco.Web.Install
try
{
var requestUri = string.Format("http://our.umbraco.org/webapi/StarterKit/Get/?umbracoVersion={0}",
UmbracoVersion.Current);
var requestUri = string.Format("http://our.umbraco.org/webapi/StarterKit/Get/?umbracoVersion={0}", UmbracoVersion.Current);
using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri))
using (var httpClient = new HttpClient())
@@ -13,7 +13,7 @@ using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.InstallSteps
{
[InstallSetupStep(InstallationType.NewInstall,
[CoreInstallSetupStep(CoreInstallationType.NewInstall,
"DatabaseConfigure", "database", 10, "Setting up a database, so Umbraco has a place to store your website",
PerformsAppRestart = true)]
internal class DatabaseConfigureStep : InstallSetupStep<DatabaseModel>
@@ -8,7 +8,7 @@ using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.InstallSteps
{
[InstallSetupStep(InstallationType.NewInstall | InstallationType.Upgrade,
[CoreInstallSetupStep(CoreInstallationType.NewInstall | CoreInstallationType.Upgrade,
"DatabaseInstall", 11, "")]
internal class DatabaseInstallStep : InstallSetupStep<object>
{
@@ -9,7 +9,7 @@ using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.InstallSteps
{
[InstallSetupStep(InstallationType.Upgrade | InstallationType.NewInstall,
[CoreInstallSetupStep(CoreInstallationType.Upgrade | CoreInstallationType.NewInstall,
"DatabaseUpgrade", 12, "")]
internal class DatabaseUpgradeStep : InstallSetupStep<object>
{
@@ -8,7 +8,7 @@ using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.InstallSteps
{
[InstallSetupStep(InstallationType.NewInstall | InstallationType.Upgrade,
[CoreInstallSetupStep(CoreInstallationType.NewInstall | CoreInstallationType.Upgrade,
"Permissions", 0, "",
PerformsAppRestart = true)]
internal class FilePermissionsStep : InstallSetupStep<object>
@@ -10,7 +10,7 @@ using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.InstallSteps
{
[InstallSetupStep(InstallationType.Upgrade,
[CoreInstallSetupStep(CoreInstallationType.Upgrade,
"MajorVersion7UpgradeReport", 1, "")]
internal class MajorVersion7UpgradeReport : InstallSetupStep<object>
{
@@ -19,7 +19,7 @@ namespace Umbraco.Web.Install.InstallSteps
/// error, etc... and the end-user refreshes the installer then we cannot show the user screen because they've already entered that information so instead we'll
/// display a simple continue installation view.
/// </remarks>
[InstallSetupStep(InstallationType.NewInstall,
[CoreInstallSetupStep(CoreInstallationType.NewInstall,
"User", 20, "")]
internal class NewInstallStep : InstallSetupStep<UserModel>
{
@@ -11,7 +11,7 @@ using GlobalSettings = umbraco.GlobalSettings;
namespace Umbraco.Web.Install.InstallSteps
{
[InstallSetupStep(InstallationType.NewInstall | InstallationType.Upgrade,
[CoreInstallSetupStep(CoreInstallationType.NewInstall | CoreInstallationType.Upgrade,
"UmbracoVersion", 50, "Installation is complete!, get ready to be redirected to your new CMS.",
PerformsAppRestart = true)]
internal class SetUmbracoVersionStep : InstallSetupStep<object>
@@ -9,7 +9,7 @@ using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.InstallSteps
{
[InstallSetupStep(InstallationType.NewInstall,
[CoreInstallSetupStep(CoreInstallationType.NewInstall,
"StarterKitCleanup", 32, "Almost done")]
internal class StarterKitCleanupStep : InstallSetupStep<object>
{
@@ -8,7 +8,7 @@ using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.InstallSteps
{
[InstallSetupStep(InstallationType.NewInstall,
[CoreInstallSetupStep(CoreInstallationType.NewInstall,
"StarterKitDownload", "starterKit", 30, "Adding a simple website to Umbraco, will make it easier for you to get started")]
internal class StarterKitDownloadStep : InstallSetupStep<Guid?>
{
@@ -8,7 +8,7 @@ using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.InstallSteps
{
[InstallSetupStep(InstallationType.NewInstall,
[CoreInstallSetupStep(CoreInstallationType.NewInstall,
"StarterKitInstall", 31, "",
PerformsAppRestart = true)]
internal class StarterKitInstallStep : InstallSetupStep<object>
@@ -5,7 +5,7 @@ namespace Umbraco.Web.Install.InstallSteps
/// <summary>
/// This step is purely here to show the button to commence the upgrade
/// </summary>
[InstallSetupStep(InstallationType.Upgrade,
[CoreInstallSetupStep(CoreInstallationType.Upgrade,
"Upgrade", "upgrade", 1, "Upgrading Umbraco to the latest and greatest version.")]
internal class UpgradeStep : InstallSetupStep<object>
{
@@ -10,7 +10,7 @@ using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.InstallSteps
{
[InstallSetupStep(InstallationType.Upgrade,
[CoreInstallSetupStep(CoreInstallationType.Upgrade,
"Version73FileCleanup", 2,
"Performing some housecleaning...",
PerformsAppRestart = true)]
@@ -0,0 +1,89 @@
using System.Linq;
using Semver;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Services;
using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.MigrationSteps
{
[InstallSetupStep("Execute Migrations", 2, "",
//This isn't always the case but packages can certainly restart during migrations
PerformsAppRestart = true)]
internal class ExecutionMigrationStep : InstallSetupStep<object>
{
private readonly IMigrationEntryService _migrationEntryService;
private readonly PackageMigrationsContext _packageMigrationsContext;
private readonly Database _database;
private readonly DatabaseProviders _databaseProvider;
private readonly ILogger _logger;
public ExecutionMigrationStep(ApplicationContext applicationContext)
{
_migrationEntryService = applicationContext.Services.MigrationEntryService;
_packageMigrationsContext = applicationContext.PackageMigrationsContext;
_database = applicationContext.DatabaseContext.Database;
_databaseProvider = applicationContext.DatabaseContext.DatabaseProvider;
_logger = applicationContext.ProfilingLogger.Logger;
}
public ExecutionMigrationStep(IMigrationEntryService migrationEntryService,
PackageMigrationsContext packageMigrationsContext, Database database,
DatabaseProviders databaseProvider, ILogger logger)
{
_migrationEntryService = migrationEntryService;
_packageMigrationsContext = packageMigrationsContext;
_database = database;
_databaseProvider = databaseProvider;
_logger = logger;
}
public override bool RequiresExecution(object model)
{
return _packageMigrationsContext.HasPendingPackageMigrations;
}
public override InstallSetupResult Execute(object model)
{
//NOTE: We could separate these out into separate tasks (dynamically) if we wanted to but I
// think it will be fine processing them all at once.
var pendingMigrations = _packageMigrationsContext.GetPendingPackageMigrations();
var packageDbMigrations = _migrationEntryService
.FindEntries(pendingMigrations.Select(x => x.Key))
.ToArray();
foreach (var pendingPackageMigration in _packageMigrationsContext.GetPendingPackageMigrations())
{
var latestDbMigration = packageDbMigrations
.Where(x => x.MigrationName.InvariantEquals(pendingPackageMigration.Key))
.OrderByDescending(x => x.Version)
.FirstOrDefault();
var runner = new MigrationRunner(_migrationEntryService, _logger,
//This is the version this package is coming from:
latestDbMigration == null ? new SemVersion(0) : latestDbMigration.Version,
//This is the latest migration specified in the packages c# migrations
pendingPackageMigration.Value, pendingPackageMigration.Key);
var result = runner.Execute(_database, _databaseProvider, true);
if (result == false)
{
//this will only happen normally if something cancels the migration
return new InstallSetupResult("failedmigration", new
{
productName = pendingPackageMigration
});
}
}
//very important to reset migrations since an app restart isn't actually a mandatory thing
_packageMigrationsContext.ResetPendingPackageMigrations();
//everything was ok
return null;
}
}
}
@@ -0,0 +1,39 @@
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Linq;
using System.Web.Security;
using Umbraco.Core;
using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.MigrationSteps
{
[InstallSetupStep("Migrations", "migrations", 1, "Running Package migrations.")]
internal class InitialMigrationStep : InstallSetupStep<object>
{
private readonly ApplicationContext _applicationContext;
public InitialMigrationStep(ApplicationContext applicationContext)
{
_applicationContext = applicationContext;
}
public override bool RequiresExecution(object model)
{
return true;
}
/// <summary>
/// The view model used to render the view
/// </summary>
public override object ViewModel
{
get { return _applicationContext.PackageMigrationsContext.GetPendingPackageMigrationFriendlyNames(); }
}
public override InstallSetupResult Execute(object model)
{
return null;
}
}
}
@@ -0,0 +1,21 @@
using System.Text.RegularExpressions;
namespace Umbraco.Web.Install.Models
{
public sealed class CoreInstallSetupStepAttribute : InstallSetupStepAttribute
{
public CoreInstallSetupStepAttribute(CoreInstallationType installTypeTarget, string name, string view, int serverOrder, string description)
: base(name, view, serverOrder, description)
{
InstallTypeTarget = installTypeTarget;
}
public CoreInstallSetupStepAttribute(CoreInstallationType installTypeTarget, string name, int serverOrder, string description)
: base(name, serverOrder, description)
{
InstallTypeTarget = installTypeTarget;
}
public CoreInstallationType InstallTypeTarget { get; private set; }
}
}
@@ -3,9 +3,9 @@ using System;
namespace Umbraco.Web.Install.Models
{
[Flags]
public enum InstallationType
public enum CoreInstallationType
{
NewInstall = 1 << 0, // 1
Upgrade = 1 << 1, // 2
Upgrade = 1 << 1, // 2
}
}
@@ -13,5 +13,8 @@ namespace Umbraco.Web.Install.Models
[DataMember(Name = "installId")]
public Guid InstallId { get; set; }
[DataMember(Name = "installType")]
public InstallerType InstallType { get; set; }
}
}
@@ -47,7 +47,11 @@ namespace Umbraco.Web.Install.Models
View = att.View;
ServerOrder = att.ServerOrder;
Description = att.Description;
InstallTypeTarget = att.InstallTypeTarget;
var coreSetupAtt = att as CoreInstallSetupStepAttribute;
if (coreSetupAtt != null)
{
InstallTypeTarget = coreSetupAtt.InstallTypeTarget;
}
PerformsAppRestart = att.PerformsAppRestart;
}
@@ -67,7 +71,7 @@ namespace Umbraco.Web.Install.Models
public string Description { get; private set; }
[IgnoreDataMember]
public InstallationType InstallTypeTarget { get; private set; }
public CoreInstallationType InstallTypeTarget { get; private set; }
[IgnoreDataMember]
public bool PerformsAppRestart { get; private set; }
@@ -1,13 +1,11 @@
using System;
using System.Text.RegularExpressions;
namespace Umbraco.Web.Install.Models
{
public sealed class InstallSetupStepAttribute : Attribute
public class InstallSetupStepAttribute : Attribute
{
public InstallSetupStepAttribute(InstallationType installTypeTarget, string name, string view, int serverOrder, string description)
public InstallSetupStepAttribute(string name, string view, int serverOrder, string description)
{
InstallTypeTarget = installTypeTarget;
Name = name;
View = view;
ServerOrder = serverOrder;
@@ -17,9 +15,8 @@ namespace Umbraco.Web.Install.Models
PerformsAppRestart = false;
}
public InstallSetupStepAttribute(InstallationType installTypeTarget, string name, int serverOrder, string description)
public InstallSetupStepAttribute(string name, int serverOrder, string description)
{
InstallTypeTarget = installTypeTarget;
Name = name;
View = string.Empty;
ServerOrder = serverOrder;
@@ -29,7 +26,6 @@ namespace Umbraco.Web.Install.Models
PerformsAppRestart = false;
}
public InstallationType InstallTypeTarget { get; private set; }
public string Name { get; private set; }
public string View { get; private set; }
public int ServerOrder { get; private set; }
@@ -0,0 +1,15 @@
namespace Umbraco.Web.Install.Models
{
public enum InstallerType
{
/// <summary>
/// Represents a normal Core Umbraco intallation procedure
/// </summary>
Core,
/// <summary>
/// Represents a package migrations installer procedure
/// </summary>
Migrations
}
}
@@ -26,11 +26,17 @@ namespace Umbraco.Web.Install
/// </remarks>
public override void RegisterArea(AreaRegistrationContext context)
{
//Can easily do this two routes with one route and some constraints but whatevs, we'll just leave as 2 for now
context.MapRoute(
"umbraco-install",
"Install",
new { controller = "Install", action = "Index", id = UrlParameter.Optional },
new[] { typeof(InstallController).Namespace });
context.MapRoute(
"umbraco-install-package-migrations",
"Install/PackageMigrations",
new { controller = "Install", action = "PackageMigrations", id = UrlParameter.Optional },
new[] { typeof(InstallController).Namespace });
//TODO: We can remove this when we re-build the back office package installer
//Create the install routes
@@ -39,6 +39,11 @@ namespace Umbraco.Web.Routing
/// <summary>
/// There was no content at all.
/// </summary>
NoContent = 13
NoContent = 13,
/// <summary>
/// Umbraco was not configured.
/// </summary>
PendingPackageMigrations = 14,
}
}
+6 -2
View File
@@ -326,6 +326,10 @@
<Compile Include="HtmlHelperBackOfficeExtensions.cs" />
<Compile Include="Install\InstallSteps\Version73FileCleanup.cs" />
<Compile Include="Models\ContentEditing\DocumentTypeDisplay.cs" />
<Compile Include="Install\MigrationSteps\ExecutionMigrationStep.cs" />
<Compile Include="Install\MigrationSteps\InitialMigrationStep.cs" />
<Compile Include="Install\Models\InstallerType.cs" />
<Compile Include="Install\Models\InstallSetupStepAttribute.cs" />
<Compile Include="Media\EmbedProviders\Flickr.cs" />
<Compile Include="Models\ContentEditing\ContentTypeCompositionDisplay.cs" />
<Compile Include="Models\ContentEditing\ContentTypeSave.cs" />
@@ -486,14 +490,14 @@
<Compile Include="Install\Controllers\InstallPackageController.cs" />
<Compile Include="Install\Models\DatabaseModel.cs" />
<Compile Include="Install\Models\DatabaseType.cs" />
<Compile Include="Install\Models\InstallationType.cs" />
<Compile Include="Install\Models\CoreInstallationType.cs" />
<Compile Include="Install\Models\InstallInstructions.cs" />
<Compile Include="Install\Models\InstallPackageModel.cs" />
<Compile Include="Install\Models\InstallProgressResultModel.cs" />
<Compile Include="Install\Models\InstallSetup.cs" />
<Compile Include="Install\Models\InstallSetupResult.cs" />
<Compile Include="Install\Models\InstallSetupStep.cs" />
<Compile Include="Install\Models\InstallSetupStepAttribute.cs" />
<Compile Include="Install\Models\CoreInstallSetupStepAttribute.cs" />
<Compile Include="Install\Models\InstallTrackingItem.cs" />
<Compile Include="Install\Models\Package.cs" />
<Compile Include="Install\Models\UserModel.cs" />
+27 -1
View File
@@ -97,7 +97,9 @@ namespace Umbraco.Web
//re-write for the default back office path
if (httpContext.Request.Url.IsDefaultBackOfficeRequest())
{
if (EnsureIsConfigured(httpContext, umbracoContext.OriginalRequestUrl))
//only continue to process the back office if the app is configured and there are no pending migrations
if (EnsureIsConfigured(httpContext, umbracoContext.OriginalRequestUrl)
&& EnsurePackageMigrationsHaveExecuted(httpContext, umbracoContext.OriginalRequestUrl))
{
RewriteToBackOfficeHandler(httpContext);
}
@@ -177,6 +179,11 @@ namespace Umbraco.Web
{
reason = EnsureRoutableOutcome.NotConfigured;
}
// ensure Umbraco is properly configured to serve documents
else if (!EnsurePackageMigrationsHaveExecuted(httpContext, uri))
{
reason = EnsureRoutableOutcome.PendingPackageMigrations;
}
// ensure Umbraco has documents to serve
else if (!EnsureHasContent(context, httpContext))
{
@@ -312,6 +319,25 @@ namespace Umbraco.Web
return false;
}
private bool EnsurePackageMigrationsHaveExecuted(HttpContextBase httpContext, Uri uri)
{
if (ApplicationContext.Current.PackageMigrationsContext.HasPendingPackageMigrations == false)
return true;
if (_notConfiguredReported)
{
// remember it's been reported so we don't flood the log
// no thread-safety so there may be a few log entries, doesn't matter
_notConfiguredReported = true;
LogHelper.Warn<UmbracoModule>("Umbraco is not configured - package migrations are pending");
}
var installPath = UriUtility.ToAbsolute(SystemDirectories.Install);
var installUrl = string.Format("{0}/PackageMigrations?redir=true&url={1}", installPath, HttpUtility.UrlEncode(uri.ToString()));
httpContext.Response.Redirect(installUrl, true);
return false;
}
// returns a value indicating whether redirection took place and the request has
// been completed - because we don't want to Response.End() here to terminate
// everything properly.