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