Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 72e0e72d16 | |||
| 5fdec15a22 | |||
| 9ac72b7232 | |||
| a13eba0ac5 | |||
| d03aa54dfc | |||
| 31d910432a | |||
| 0e67976cc6 | |||
| 85399a97a6 | |||
| cb7e12215e |
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user