diff --git a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj index 45f9dd353..1f4405293 100644 --- a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj +++ b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj @@ -89,6 +89,7 @@ + @@ -113,6 +114,10 @@ {D10AD48F-407D-4DB5-A328-173EC7CB010F} Orchard.Roles + + {8C7FCBC2-E6E1-405E-BFB5-D8D9E67A09C4} + Orchard.Setup + {79AED36E-ABD0-4747-93D3-8722B042454B} Orchard.Users diff --git a/src/Orchard.Tests.Modules/Setup/SetupControllerTests.cs b/src/Orchard.Tests.Modules/Setup/SetupControllerTests.cs new file mode 100644 index 000000000..87a86d320 --- /dev/null +++ b/src/Orchard.Tests.Modules/Setup/SetupControllerTests.cs @@ -0,0 +1,110 @@ +using System.IO; +using System.Linq; +using System.Web.Mvc; +using System.Web.Routing; +using Autofac; +using Autofac.Builder; +using JetBrains.Annotations; +using Moq; +using NUnit.Framework; +using Orchard.Data.Migrations; +using Orchard.Environment; +using Orchard.Environment.Configuration; +using Orchard.Setup.Controllers; +using Orchard.Setup.ViewModels; +using Orchard.UI.Notify; + +namespace Orchard.Tests.Modules.Setup { + [TestFixture, Ignore("this can't be made to work")] + public class SetupControllerTests { + private string _tempFolder; + private IContainer _container; + + [SetUp] + public void Init() { + _tempFolder = Path.GetTempFileName(); + File.Delete(_tempFolder); + Directory.CreateDirectory(_tempFolder); + + var hostContainer = OrchardStarter.CreateHostContainer(builder => { + builder.Register(new ControllerBuilder()); + builder.Register(new ViewEngineCollection { new WebFormViewEngine() }); + builder.Register(new RouteCollection()); + builder.Register(new ModelBinderDictionary()); + }); + + hostContainer.Resolve().SetBasePath(_tempFolder); + + var host = (DefaultOrchardHost)hostContainer.Resolve(); + _container = host.CreateShellContainer(); + _container.Build(builder => { + builder.Register(); + }); + + + //var builder = new ContainerBuilder(); + //builder.Register(); + //builder.Register().As(); + //builder.Register().As(); + //builder.Register().As(); + //builder.Register().As(); + //builder.Register().As(); + //_container = builder.Build(); + } + + private string GetMessages() { + var notifier = _container.Resolve(); + return notifier.List().Aggregate("", (a, b) => a + b.Message.ToString()); + } + + private SetupViewModel GetTestSetupModel() { + return new SetupViewModel { + AdminUsername = "test1", + AdminPassword = "test2", + DatabaseOptions = true, + SiteName = "test3" + }; + } + + [Test] + public void IndexNormallyReturnsWithDefaultAdminUsername() { + var controller = _container.Resolve(); + var result = controller.Index(null); + + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.TypeOf()); + + var viewResult = (ViewResult)result; + Assert.That(viewResult.ViewData.Model, Is.TypeOf()); + + var model2 = (SetupViewModel)viewResult.ViewData.Model; + Assert.That(model2.AdminUsername, Is.EqualTo("admin")); + } + + [Test] + public void SetupShouldCreateShellSettingsFile() { + var model = GetTestSetupModel(); + var controller = _container.Resolve(); + var result = controller.IndexPOST(model); + + Assert.That(GetMessages(), Is.StringContaining("Setup succeeded")); + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.TypeOf()); + Assert.That(File.Exists(Path.Combine(_tempFolder, "Sites\\default.txt"))); + } + + [Test] + public void BuiltinDatabaseShouldCreateSQLiteFile() { + var model = GetTestSetupModel(); + var controller = _container.Resolve(); + var result = controller.IndexPOST(model); + + Assert.That(GetMessages(), Is.StringContaining("Setup succeeded")); + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.TypeOf()); + Assert.That(File.Exists(Path.Combine(_tempFolder, "Sites\\default\\orchard.db"))); + } + + + } +} diff --git a/src/Orchard.Tests/Environment/Configuration/AppDataFolderTests.cs b/src/Orchard.Tests/Environment/Configuration/AppDataFolderTests.cs new file mode 100644 index 000000000..c6797050b --- /dev/null +++ b/src/Orchard.Tests/Environment/Configuration/AppDataFolderTests.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Orchard.Environment.Configuration; + +namespace Orchard.Tests.Environment.Configuration { + [TestFixture] + public class AppDataFolderTests { + private string _tempFolder; + private IAppDataFolder _appDataFolder; + + [SetUp] + public void Init() { + _tempFolder = Path.GetTempFileName(); + File.Delete(_tempFolder); + Directory.CreateDirectory(Path.Combine(_tempFolder, "alpha")); + File.WriteAllText(Path.Combine(_tempFolder, "alpha\\beta.txt"), "beta-content"); + File.WriteAllText(Path.Combine(_tempFolder, "alpha\\gamma.txt"), "gamma-content"); + + _appDataFolder = new AppDataFolder(); + _appDataFolder.SetBasePath(_tempFolder); + } + + [TearDown] + public void Term() { + Directory.Delete(_tempFolder, true); + } + + [Test] + public void ListFilesShouldContainSubPathAndFileName() { + var files = _appDataFolder.ListFiles("alpha"); + Assert.That(files.Count(), Is.EqualTo(2)); + Assert.That(files, Has.Some.EqualTo("alpha\\beta.txt")); + Assert.That(files, Has.Some.EqualTo("alpha\\gamma.txt")); + } + + [Test] + public void NonExistantFolderShouldListAsEmptyCollection() { + var files = _appDataFolder.ListFiles("delta"); + Assert.That(files.Count(), Is.EqualTo(0)); + } + + [Test] + public void PhysicalPathAddsToBasePathAndDoesNotNeedToExist() { + var physicalPath = _appDataFolder.MapPath("delta\\epsilon.txt"); + Assert.That(physicalPath, Is.EqualTo(Path.Combine(_tempFolder, "delta\\epsilon.txt"))); + } + + } +} diff --git a/src/Orchard.Tests/Orchard.Tests.csproj b/src/Orchard.Tests/Orchard.Tests.csproj index 60ca7b993..7f1120125 100644 --- a/src/Orchard.Tests/Orchard.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Tests.csproj @@ -138,6 +138,7 @@ + diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Controllers/SetupController.cs b/src/Orchard.Web/Modules/Orchard.Setup/Controllers/SetupController.cs index 38c6a08dc..481f031a3 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Controllers/SetupController.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Controllers/SetupController.cs @@ -18,19 +18,19 @@ using Orchard.UI.Notify; namespace Orchard.Setup.Controllers { public class SetupController : Controller { private readonly INotifier _notifier; - private readonly IDatabaseMigrationManager _databaseMigrationManager; private readonly IOrchardHost _orchardHost; private readonly IShellSettingsLoader _shellSettingsLoader; + private readonly IAppDataFolder _appDataFolder; public SetupController( INotifier notifier, - IDatabaseMigrationManager databaseMigrationManager, - IOrchardHost orchardHost, - IShellSettingsLoader shellSettingsLoader) { + IOrchardHost orchardHost, + IShellSettingsLoader shellSettingsLoader, + IAppDataFolder appDataFolder) { _notifier = notifier; - _databaseMigrationManager = databaseMigrationManager; _orchardHost = orchardHost; _shellSettingsLoader = shellSettingsLoader; + _appDataFolder = appDataFolder; T = NullLocalizer.Instance; } @@ -38,7 +38,7 @@ namespace Orchard.Setup.Controllers { public ActionResult Index(SetupViewModel model) { string message = ""; - if(!CanWriteTo(Server.MapPath("~/App_Data"), out message)) { + if (!CanWriteTo(out message)) { _notifier.Error( T( "Hey, it looks like I can't write to the App_Data folder in the root of this application and that's where I need to save some of the information you're about to enter.\r\n\r\nPlease give me (the machine account this application is running under) write access to App_Data so I can get this app all set up for you.\r\n\r\nThanks!\r\n\r\n----\r\n{0}", @@ -60,11 +60,11 @@ namespace Orchard.Setup.Controllers { try { var shellSettings = new ShellSettings { - Name = "default", - DataProvider = model.DatabaseOptions ? "SQLite" : "SqlServer", - DataConnectionString = model.DatabaseConnectionString - }; - + Name = "default", + DataProvider = model.DatabaseOptions ? "SQLite" : "SqlServer", + DataConnectionString = model.DatabaseConnectionString + }; + // creating a standalone environment. // in theory this environment can be used to resolve any normal components by interface, and those // components will exist entirely in isolation - no crossover between the safemode container currently in effect @@ -74,7 +74,6 @@ namespace Orchard.Setup.Controllers { var sessionFactoryHolder = finiteEnvironment.Resolve(); sessionFactoryHolder.UpdateSchema(); - var contentManager = finiteEnvironment.Resolve(); // create superuser var membershipService = finiteEnvironment.Resolve(); @@ -83,6 +82,7 @@ namespace Orchard.Setup.Controllers { String.Empty, String.Empty, String.Empty, true)); + /* // set site name and settings var siteService = finiteEnvironment.Resolve(); var siteSettings = siteService.GetSiteSettings().As(); @@ -91,6 +91,9 @@ namespace Orchard.Setup.Controllers { siteSettings.Record.SuperUser = model.AdminUsername; siteSettings.Record.PageTitleSeparator = " - "; + + var contentManager = finiteEnvironment.Resolve(); + // create home page as a CMS page var page = contentManager.Create("page"); page.As().Text = "Welcome to Orchard"; @@ -102,6 +105,7 @@ namespace Orchard.Setup.Controllers { var authenticationService = finiteEnvironment.Resolve(); authenticationService.SignIn(user, true); + */ } catch { finiteEnvironment.Resolve().Cancel(); @@ -124,17 +128,15 @@ namespace Orchard.Setup.Controllers { } } - static bool CanWriteTo(string path, out string message) { + bool CanWriteTo(out string message) { try { - var systemCheckPath = Path.Combine(path, "_systemcheck.txt"); - - System.IO.File.WriteAllText(systemCheckPath, "Communicator check one two one two"); - System.IO.File.AppendAllText(systemCheckPath, "\r\nThis is Bones McCoy on a line to Sulu"); - System.IO.File.Delete(systemCheckPath); + _appDataFolder.CreateFile("_systemcheck.txt", "Communicator check one two one two"); + _appDataFolder.DeleteFile("_systemcheck.txt"); message = ""; return true; - } catch (Exception ex) { + } + catch (Exception ex) { message = ex.Message.Replace("_systemcheck.txt", ""); return false; } diff --git a/src/Orchard/Data/SessionFactoryHolder.cs b/src/Orchard/Data/SessionFactoryHolder.cs index 15ade6961..a18c4e0f9 100644 --- a/src/Orchard/Data/SessionFactoryHolder.cs +++ b/src/Orchard/Data/SessionFactoryHolder.cs @@ -15,16 +15,19 @@ namespace Orchard.Data { private readonly IShellSettings _shellSettings; private readonly ICompositionStrategy _compositionStrategy; private readonly IDatabaseMigrationManager _databaseMigrationManager; + private readonly IAppDataFolder _appDataFolder; private ISessionFactory _sessionFactory; public SessionFactoryHolder( IShellSettings shellSettings, ICompositionStrategy compositionStrategy, - IDatabaseMigrationManager databaseMigrationManager) { + IDatabaseMigrationManager databaseMigrationManager, + IAppDataFolder appDataFolder) { _shellSettings = shellSettings; _compositionStrategy = compositionStrategy; _databaseMigrationManager = databaseMigrationManager; + _appDataFolder = appDataFolder; } @@ -34,7 +37,7 @@ namespace Orchard.Data { } public ISessionFactory GetSessionFactory() { - lock(this) { + lock (this) { if (_sessionFactory == null) { _sessionFactory = BuildSessionFactory(); } @@ -49,8 +52,8 @@ namespace Orchard.Data { private IDatabaseCoordinator GetDatabaseCoordinator() { - var sitesPath = HostingEnvironment.MapPath("~/App_Data/Sites"); - var shellPath = Path.Combine(sitesPath, _shellSettings.Name); + var shellPath = _appDataFolder.CreateDirectory(Path.Combine("Sites", _shellSettings.Name)); + return _databaseMigrationManager.CreateCoordinator(_shellSettings.DataProvider, shellPath, _shellSettings.DataConnectionString); } } diff --git a/src/Orchard/Environment/Configuration/AppDataFolder.cs b/src/Orchard/Environment/Configuration/AppDataFolder.cs new file mode 100644 index 000000000..13fdea458 --- /dev/null +++ b/src/Orchard/Environment/Configuration/AppDataFolder.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Web.Hosting; + +namespace Orchard.Environment.Configuration { + /// + /// Abstraction of App_Data folder + /// Expected to work on physical filesystem, but decouples core + /// system from web hosting apis + /// + public interface IAppDataFolder { + IEnumerable ListFiles(string path); + + void CreateFile(string path, string content); + void DeleteFile(string path); + + string CreateDirectory(string path); + + + /// + /// May be called to initialize component when not in a hosting environment + /// app domain + /// + void SetBasePath(string basePath); + string MapPath(string path); + + } + + public class AppDataFolder : IAppDataFolder { + protected string _basePath; + + public AppDataFolder() { + _basePath = HostingEnvironment.MapPath("~/App_Data"); + } + + public void CreateFile(string path, string content) { + File.WriteAllText(Path.Combine(_basePath, path), content); + } + + public void DeleteFile(string path) { + File.Delete(Path.Combine(_basePath, path)); + } + + public IEnumerable ListFiles(string path) { + var directoryPath = Path.Combine(_basePath, path); + if (!Directory.Exists(directoryPath)) + return Enumerable.Empty(); + + var files = Directory.GetFiles(directoryPath); + + return files.Select(file => { + var fileName = Path.GetFileName(file); + return Path.Combine(path, fileName); + }); + } + + public string CreateDirectory(string path) { + var directory = Path.Combine(_basePath, path); + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + return directory; + } + + public void SetBasePath(string basePath) { + _basePath = basePath; + } + + public string MapPath(string path) { + return Path.Combine(_basePath, path); + } + } +} diff --git a/src/Orchard/Environment/Configuration/ShellSettingsLoader.cs b/src/Orchard/Environment/Configuration/ShellSettingsLoader.cs index 3dfaa9e50..88fc36830 100644 --- a/src/Orchard/Environment/Configuration/ShellSettingsLoader.cs +++ b/src/Orchard/Environment/Configuration/ShellSettingsLoader.cs @@ -14,9 +14,11 @@ namespace Orchard.Environment.Configuration { } public class ShellSettingsLoader : IShellSettingsLoader { + private readonly IAppDataFolder _appDataFolder; Localizer T { get; set; } - public ShellSettingsLoader() { + public ShellSettingsLoader(IAppDataFolder appDataFolder) { + _appDataFolder = appDataFolder; T = NullLocalizer.Instance; } @@ -30,31 +32,24 @@ namespace Orchard.Environment.Configuration { if (string.IsNullOrEmpty(settings.Name)) throw new ArgumentException(T("Settings \"Name\" is not set.").ToString()); - var sitesPath = HostingEnvironment.MapPath("~/App_Data/Sites"); - if (string.IsNullOrEmpty(sitesPath)) - throw new ArgumentException(T("Can't determine the path on the server to save settings. Looking for something like \"~/App_Data/Sites\".").ToString()); - - if (!Directory.Exists(sitesPath)) - Directory.CreateDirectory(sitesPath); - - var filePath = Path.Combine(sitesPath, string.Format("{0}.txt", settings.Name)); - File.WriteAllText(filePath, ComposeSettings(settings)); + var filePath = Path.Combine("Sites", settings.Name + ".txt"); + _appDataFolder.CreateFile(filePath, ComposeSettings(settings)); } - static IEnumerable LoadSettings() { + IEnumerable LoadSettings() { foreach (var yamlDocument in LoadFiles()) { yield return ParseSettings(yamlDocument); } } - static IEnumerable LoadFiles() { - var sitesPath = HostingEnvironment.MapPath("~/App_Data/Sites"); - if (sitesPath != null && Directory.Exists(sitesPath)) { - foreach (var settingsFilePath in Directory.GetFiles(sitesPath, "*.txt")) { - var yamlStream = YamlParser.Load(settingsFilePath); - yield return yamlStream.Documents.Single(); - } + IEnumerable LoadFiles() { + var filePaths = _appDataFolder.ListFiles("Sites") + .Where(path => path.EndsWith(".txt", StringComparison.InvariantCultureIgnoreCase)); + + foreach (var filePath in filePaths) { + var yamlStream = YamlParser.Load(_appDataFolder.MapPath(filePath)); + yield return yamlStream.Documents.Single(); } } @@ -65,10 +60,10 @@ namespace Orchard.Environment.Configuration { .ToDictionary(x => ((Scalar)x.Key).Text, x => x.Value); return new ShellSettings { - Name = GetValue(fields, "Name"), - DataProvider = GetValue(fields, "DataProvider"), - DataConnectionString = GetValue(fields, "DataConnectionString") - }; + Name = GetValue(fields, "Name"), + DataProvider = GetValue(fields, "DataProvider"), + DataConnectionString = GetValue(fields, "DataConnectionString") + }; } static string GetValue( diff --git a/src/Orchard/Environment/DefaultOrchardHost.cs b/src/Orchard/Environment/DefaultOrchardHost.cs index 9175d1219..877db4513 100644 --- a/src/Orchard/Environment/DefaultOrchardHost.cs +++ b/src/Orchard/Environment/DefaultOrchardHost.cs @@ -36,6 +36,7 @@ namespace Orchard.Environment { get { return _current; } } + void IOrchardHost.Initialize() { ViewEngines.Engines.Insert(0, LayoutViewEngine.CreateShim()); _controllerBuilder.SetControllerFactory(new OrchardControllerFactory()); diff --git a/src/Orchard/Environment/OrchardStarter.cs b/src/Orchard/Environment/OrchardStarter.cs index d3ea08a00..60712d41d 100644 --- a/src/Orchard/Environment/OrchardStarter.cs +++ b/src/Orchard/Environment/OrchardStarter.cs @@ -22,6 +22,7 @@ namespace Orchard.Environment { builder.Register().As().SingletonScoped(); builder.Register().As().SingletonScoped(); builder.Register().As().SingletonScoped(); + builder.Register().As().SingletonScoped(); builder.Register().As().SingletonScoped(); builder.Register().As().SingletonScoped(); diff --git a/src/Orchard/Orchard.csproj b/src/Orchard/Orchard.csproj index e105568a5..c16b5de53 100644 --- a/src/Orchard/Orchard.csproj +++ b/src/Orchard/Orchard.csproj @@ -140,6 +140,7 @@ +