Make "setup" command available for "orchard.exe"

Extracted the setup code into a "SetupService" used by setup controller
and command.

--HG--
branch : dev
This commit is contained in:
Renaud Paquay
2010-04-28 18:21:32 -07:00
parent ef31c709e5
commit 8a1c9cc46f
11 changed files with 286 additions and 122 deletions

View File

@@ -39,6 +39,7 @@ namespace Orchard.MultiTenancy.Commands {
[CommandHelp("tenant add <tenantName> /Host:<hostname> /UrlPrefix:<url prefix>\r\n\t" +
"Create new tenant named <tenantName> on the site")]
[CommandName("tenant add")]
[OrchardSwitches("Host,UrlPrefix")]
public void Create(string tenantName) {
Context.Output.WriteLine(T("Creating tenant"));
_tenantService.CreateTenant(

View File

@@ -0,0 +1,62 @@
using System.Linq;
using Orchard.Commands;
using Orchard.Setup.Services;
namespace Orchard.Setup.Commands {
public class SetupCommand : DefaultOrchardCommandHandler {
private readonly ISetupService _setupService;
public SetupCommand(ISetupService setupService) {
_setupService = setupService;
}
[OrchardSwitch]
public string SiteName { get; set; }
[OrchardSwitch]
public string AdminUsername { get; set; }
[OrchardSwitch]
public string AdminPassword { get; set; }
[OrchardSwitch]
public string DatabaseProvider { get; set; }
[OrchardSwitch]
public string DatabaseConnectionString { get; set; }
[OrchardSwitch]
public string DatabaseTablePrefix { get; set; }
[OrchardSwitch]
public string EnabledFeatures { get; set; }
[CommandHelp("setup /SiteName:<siteName> /AdminUserName:<username> /AdminPassword:<password> /DatabaseProvider:<SQLite|SQLServer> " +
"/DatabaseConnectionString:<connection_string> /DatabaseTablePrefix:<table_prefix> /EnabledFeatures:<feature1,feature2,...>" +
"\r\n\tRun first time setup for the site or for a given tenant")]
[CommandName("setup")]
[OrchardSwitches("SiteName,AdminUsername,AdminPassword,DatabaseProvider,DatabaseConnectionString,DatabaseTablePrefix,EnabledFeatures")]
public void Setup() {
var setupContext = new SetupContext {
SiteName = this.SiteName,
AdminUsername = this.AdminUsername,
AdminPassword = this.AdminPassword,
DatabaseProvider = this.DatabaseProvider,
DatabaseConnectionString = this.DatabaseConnectionString,
DatabaseTablePrefix = this.DatabaseTablePrefix,
EnabledFeatures = this.EnabledFeatures.Split(',').Select(s => s.Trim())
};
_setupService.Setup(setupContext);
Context.Output.WriteLine("Site \"{0}\" setup to run data provider \"{1}\" (with table prefix \"{2}\") with the following features enabled:",
setupContext.SiteName,
setupContext.DatabaseProvider,
setupContext.DatabaseTablePrefix);
foreach (var feature in setupContext.EnabledFeatures) {
this.Context.Output.WriteLine("{0}", feature);
}
}
}
}

View File

@@ -15,6 +15,7 @@ using Orchard.Environment.Topology;
using Orchard.Environment.Topology.Models;
using Orchard.Security;
using Orchard.Settings;
using Orchard.Setup.Services;
using Orchard.Setup.ViewModels;
using Orchard.Localization;
using Orchard.Themes;
@@ -23,29 +24,14 @@ using Orchard.UI.Notify;
namespace Orchard.Setup.Controllers {
[ValidateInput(false)]
public class SetupController : Controller {
private readonly ShellSettings _shellSettings;
private readonly INotifier _notifier;
private readonly IOrchardHost _orchardHost;
private readonly IShellSettingsManager _shellSettingsManager;
private readonly IShellContainerFactory _shellContainerFactory;
private readonly ICompositionStrategy _compositionStrategy;
private readonly IAppDataFolder _appDataFolder;
private readonly INotifier _notifier;
private readonly ISetupService _setupService;
public SetupController(
ShellSettings shellSettings,
INotifier notifier,
IOrchardHost orchardHost,
IShellSettingsManager shellSettingsManager,
IShellContainerFactory shellContainerFactory,
ICompositionStrategy compositionStrategy,
IAppDataFolder appDataFolder) {
_shellSettings = shellSettings;
_notifier = notifier;
_orchardHost = orchardHost;
_shellSettingsManager = shellSettingsManager;
_shellContainerFactory = shellContainerFactory;
_compositionStrategy = compositionStrategy;
public SetupController(INotifier notifier, ISetupService setupService, IAppDataFolder appDataFolder) {
_appDataFolder = appDataFolder;
_notifier = notifier;
_setupService = setupService;
T = NullLocalizer.Instance;
}
@@ -78,103 +64,37 @@ namespace Orchard.Setup.Controllers {
}
try {
var shellSettings = new ShellSettings(_shellSettings) {
DataProvider = model.DatabaseOptions ? "SQLite" : "SqlServer",
DataConnectionString = model.DatabaseConnectionString,
DataTablePrefix = model.DatabaseTablePrefix,
};
// The vanilla Orchard distibution has the following features enabled.
const string hardcoded =
@"Orchard.Framework,
Common,Dashboard,Feeds,HomePage,Navigation,Scheduling,Settings,XmlRpc,
Orchard.Users,Orchard.Roles,TinyMce,
Orchard.Modules,Orchard.Themes,
Orchard.Pages,Orchard.Comments";
string[] hardcoded = {
"Orchard.Framework",
"Common",
"Dashboard",
"Feeds",
"HomePage",
"Navigation",
"Scheduling",
"Settings",
"XmlRpc",
"Orchard.Users",
"Orchard.Roles",
"TinyMce",
"Orchard.Modules",
"Orchard.Themes",
"Orchard.MultiTenancy",
"Orchard.Pages",
"Orchard.Comments" };
var shellDescriptor = new ShellDescriptor {
EnabledFeatures = hardcoded.Split(',').Select(name => new ShellFeature { Name = name.Trim() })
var setupContext = new SetupContext {
SiteName = model.SiteName,
AdminUsername = model.AdminUsername,
AdminPassword = model.AdminPassword,
DatabaseProvider = model.DatabaseOptions ? "SQLite" : "SqlServer",
DatabaseConnectionString = model.DatabaseConnectionString,
DatabaseTablePrefix = model.DatabaseTablePrefix,
EnabledFeatures = hardcoded
};
var shellToplogy = _compositionStrategy.Compose(shellSettings, shellDescriptor);
// initialize database explicitly, and store shell descriptor
var bootstrapLifetimeScope = _shellContainerFactory.CreateContainer(shellSettings, shellToplogy);
using (var environment = new StandaloneEnvironment(bootstrapLifetimeScope)) {
environment.Resolve<ISessionFactoryHolder>().CreateDatabase();
environment.Resolve<IShellDescriptorManager>().UpdateShellDescriptor(
0,
shellDescriptor.EnabledFeatures,
shellDescriptor.Parameters);
}
// 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
// must mark state as Running - otherwise standalone enviro is created "for setup"
shellSettings.State = new TenantState("Running");
using (var environment = _orchardHost.CreateStandaloneEnvironment(shellSettings)) {
try {
// create superuser
var membershipService = environment.Resolve<IMembershipService>();
var user =
membershipService.CreateUser(new CreateUserParams(model.AdminUsername, model.AdminPassword,
String.Empty, String.Empty, String.Empty,
true));
// set site name and settings
var siteService = environment.Resolve<ISiteService>();
var siteSettings = siteService.GetSiteSettings().As<SiteSettings>();
siteSettings.Record.SiteSalt = Guid.NewGuid().ToString("N");
siteSettings.Record.SiteName = model.SiteName;
siteSettings.Record.SuperUser = model.AdminUsername;
siteSettings.Record.PageTitleSeparator = " - ";
// set site theme
var themeService = environment.Resolve<IThemeService>();
themeService.SetSiteTheme("Classic");
var contentManager = environment.Resolve<IContentManager>();
// simulate installation-time module activation events
var hackInstallationGenerator = environment.Resolve<IHackInstallationGenerator>();
hackInstallationGenerator.GenerateInstallEvents();
// create home page as a CMS page
var page = contentManager.Create("page", VersionOptions.Draft);
page.As<BodyAspect>().Text = "<p>Welcome to Orchard!</p><p>Congratulations, you've successfully set-up your Orchard site.</p><p>This is the home page of your new site. We've taken the liberty to write here about a few things you could look at next in order to get familiar with the application. Once you feel confident you don't need this anymore, just click <a href=\"Admin/Pages/Edit/3\">Edit</a> to go into edit mode and replace this with whatever you want on your home page to make it your own.</p><p>One thing you could do (but you don't have to) is go into <a href=\"Admin/Settings\">Manage Settings</a> (follow the <a href=\"Admin\">Admin</a> link and then look for it under \"Settings\" in the menu on the left) and check that everything is configured the way you want.</p><p>You probably want to make the site your own. One of the ways you can do that is by clicking <a href=\"Admin/Themes\">Manage Themes</a> in the admin menu. A theme is a packaged look and feel that affects the whole site.</p><p>Next, you can start playing with the content types that we installed. For example, go ahead and click <a href=\"Admin/Pages/Create\">Add New Page</a> in the admin menu and create an \"about\" page. Then, add it to the navigation menu by going to <a href=\"Admin/Navigation\">Manage Menu</a>. You can also click <a href=\"Admin/Blogs/Create\">Add New Blog</a> and start posting by clicking \"Add New Post\".</p><p>Finally, Orchard has been designed to be extended. It comes with a few built-in modules such as pages and blogs or themes. You can install new themes by going to <a href=\"Admin/Themes\">Manage Themes</a> and clicking <a href=\"Admin/Themes/Install\">Install a new Theme</a>. Like for themes, modules are created by other users of Orchard just like you so if you feel up to it, please <a href=\"http://www.orchardproject.net/\">consider participating</a>.</p><p>--The Orchard Crew</p>";
page.As<RoutableAspect>().Slug = "home";
page.As<RoutableAspect>().Title = T("Home").ToString();
page.As<HasComments>().CommentsShown = false;
page.As<CommonAspect>().Owner = user;
contentManager.Publish(page);
siteSettings.Record.HomePage = "PageHomePageProvider;" + page.Id;
// add a menu item for the shiny new home page
var menuItem = contentManager.Create("menuitem");
menuItem.As<MenuPart>().MenuPosition = "1";
menuItem.As<MenuPart>().MenuText = T("Home").ToString();
menuItem.As<MenuPart>().OnMainMenu = true;
menuItem.As<MenuItem>().Url = "";
var authenticationService = environment.Resolve<IAuthenticationService>();
authenticationService.SignIn(user, true);
}
catch {
environment.Resolve<ITransactionManager>().Cancel();
throw;
}
}
_shellSettingsManager.SaveSettings(shellSettings);
// MultiTenancy: This will not be needed when host listens to event bus
_orchardHost.Reinitialize_Obsolete();
_setupService.Setup(setupContext);
// redirect to the welcome page.
return Redirect("~/");

View File

@@ -67,9 +67,13 @@
<ItemGroup>
<Compile Include="Annotations\SqlDatabaseConnectionStringAttribute.cs" />
<Compile Include="Annotations\StringLengthMin.cs" />
<Compile Include="Commands\SetupCommand.cs" />
<Compile Include="Controllers\SetupController.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Routes.cs" />
<Compile Include="Services\ISetupService.cs" />
<Compile Include="Services\SetupContext.cs" />
<Compile Include="Services\SetupService.cs" />
<Compile Include="SetupMode.cs" />
<Compile Include="ViewModels\SetupViewModel.cs" />
</ItemGroup>

View File

@@ -0,0 +1,5 @@
namespace Orchard.Setup.Services {
public interface ISetupService : IDependency {
void Setup(SetupContext context);
}
}

View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace Orchard.Setup.Services {
public class SetupContext {
public string SiteName { get; set; }
public string AdminUsername { get; set; }
public string AdminPassword { get; set; }
public string DatabaseProvider { get; set; }
public string DatabaseConnectionString { get; set; }
public string DatabaseTablePrefix { get; set; }
public IEnumerable<string> EnabledFeatures { get; set; }
}
}

View File

@@ -0,0 +1,140 @@
using System;
using System.Linq;
using System.Web;
using Orchard.Comments.Models;
using Orchard.ContentManagement;
using Orchard.Core.Common.Models;
using Orchard.Core.Navigation.Models;
using Orchard.Core.Settings.Models;
using Orchard.Data;
using Orchard.Environment;
using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions;
using Orchard.Environment.ShellBuilders;
using Orchard.Environment.Topology;
using Orchard.Environment.Topology.Models;
using Orchard.Localization;
using Orchard.Security;
using Orchard.Settings;
using Orchard.Themes;
using Orchard.UI.Notify;
namespace Orchard.Setup.Services {
public class SetupService : ISetupService {
private readonly ShellSettings _shellSettings;
private readonly IOrchardHost _orchardHost;
private readonly IShellSettingsManager _shellSettingsManager;
private readonly IShellContainerFactory _shellContainerFactory;
private readonly ICompositionStrategy _compositionStrategy;
public SetupService(
ShellSettings shellSettings,
INotifier notifier,
IOrchardHost orchardHost,
IShellSettingsManager shellSettingsManager,
IShellContainerFactory shellContainerFactory,
ICompositionStrategy compositionStrategy) {
_shellSettings = shellSettings;
_orchardHost = orchardHost;
_shellSettingsManager = shellSettingsManager;
_shellContainerFactory = shellContainerFactory;
_compositionStrategy = compositionStrategy;
T = NullLocalizer.Instance;
}
private Localizer T { get; set; }
public void Setup(SetupContext context) {
var shellSettings = new ShellSettings(_shellSettings) {
DataProvider = context.DatabaseProvider,
DataConnectionString = context.DatabaseConnectionString,
DataTablePrefix = context.DatabaseTablePrefix,
};
var shellDescriptor = new ShellDescriptor {
EnabledFeatures = context.EnabledFeatures.Select(name => new ShellFeature { Name = name })
};
var shellToplogy = _compositionStrategy.Compose(shellSettings, shellDescriptor);
// initialize database explicitly, and store shell descriptor
var bootstrapLifetimeScope = _shellContainerFactory.CreateContainer(shellSettings, shellToplogy);
using (var environment = new StandaloneEnvironment(bootstrapLifetimeScope)) {
environment.Resolve<ISessionFactoryHolder>().CreateDatabase();
environment.Resolve<IShellDescriptorManager>().UpdateShellDescriptor(
0,
shellDescriptor.EnabledFeatures,
shellDescriptor.Parameters);
}
// 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
// must mark state as Running - otherwise standalone enviro is created "for setup"
shellSettings.State = new TenantState("Running");
using (var environment = _orchardHost.CreateStandaloneEnvironment(shellSettings)) {
try {
// create superuser
var membershipService = environment.Resolve<IMembershipService>();
var user =
membershipService.CreateUser(new CreateUserParams(context.AdminUsername, context.AdminPassword,
String.Empty, String.Empty, String.Empty,
true));
// set site name and settings
var siteService = environment.Resolve<ISiteService>();
var siteSettings = siteService.GetSiteSettings().As<SiteSettings>();
siteSettings.Record.SiteSalt = Guid.NewGuid().ToString("N");
siteSettings.Record.SiteName = context.SiteName;
siteSettings.Record.SuperUser = context.AdminUsername;
siteSettings.Record.PageTitleSeparator = " - ";
// set site theme
var themeService = environment.Resolve<IThemeService>();
themeService.SetSiteTheme("Classic");
var contentManager = environment.Resolve<IContentManager>();
// simulate installation-time module activation events
var hackInstallationGenerator = environment.Resolve<IHackInstallationGenerator>();
hackInstallationGenerator.GenerateInstallEvents();
// create home page as a CMS page
var page = contentManager.Create("page", VersionOptions.Draft);
page.As<BodyAspect>().Text = "<p>Welcome to Orchard!</p><p>Congratulations, you've successfully set-up your Orchard site.</p><p>This is the home page of your new site. We've taken the liberty to write here about a few things you could look at next in order to get familiar with the application. Once you feel confident you don't need this anymore, just click <a href=\"Admin/Pages/Edit/3\">Edit</a> to go into edit mode and replace this with whatever you want on your home page to make it your own.</p><p>One thing you could do (but you don't have to) is go into <a href=\"Admin/Settings\">Manage Settings</a> (follow the <a href=\"Admin\">Admin</a> link and then look for it under \"Settings\" in the menu on the left) and check that everything is configured the way you want.</p><p>You probably want to make the site your own. One of the ways you can do that is by clicking <a href=\"Admin/Themes\">Manage Themes</a> in the admin menu. A theme is a packaged look and feel that affects the whole site.</p><p>Next, you can start playing with the content types that we installed. For example, go ahead and click <a href=\"Admin/Pages/Create\">Add New Page</a> in the admin menu and create an \"about\" page. Then, add it to the navigation menu by going to <a href=\"Admin/Navigation\">Manage Menu</a>. You can also click <a href=\"Admin/Blogs/Create\">Add New Blog</a> and start posting by clicking \"Add New Post\".</p><p>Finally, Orchard has been designed to be extended. It comes with a few built-in modules such as pages and blogs or themes. You can install new themes by going to <a href=\"Admin/Themes\">Manage Themes</a> and clicking <a href=\"Admin/Themes/Install\">Install a new Theme</a>. Like for themes, modules are created by other users of Orchard just like you so if you feel up to it, please <a href=\"http://www.orchardproject.net/\">consider participating</a>.</p><p>--The Orchard Crew</p>";
page.As<RoutableAspect>().Slug = "home";
page.As<RoutableAspect>().Title = T("Home").ToString();
page.As<HasComments>().CommentsShown = false;
page.As<CommonAspect>().Owner = user;
contentManager.Publish(page);
siteSettings.Record.HomePage = "PageHomePageProvider;" + page.Id;
// add a menu item for the shiny new home page
var menuItem = contentManager.Create("menuitem");
menuItem.As<MenuPart>().MenuPosition = "1";
menuItem.As<MenuPart>().MenuText = T("Home").ToString();
menuItem.As<MenuPart>().OnMainMenu = true;
menuItem.As<MenuItem>().Url = "";
//Temporary fix for running setup on command line
if (HttpContext.Current != null) {
var authenticationService = environment.Resolve<IAuthenticationService>();
authenticationService.SignIn(user, true);
}
}
catch {
environment.Resolve<ITransactionManager>().Cancel();
throw;
}
}
_shellSettingsManager.SaveSettings(shellSettings);
// MultiTenancy: This will not be needed when host listens to event bus
_orchardHost.Reinitialize_Obsolete();
}
}
}

View File

@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Web;
using System.Web.Routing;
using Autofac;
using Orchard.Commands;
using Orchard.Commands.Builtin;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.Data.Builders;
@@ -14,6 +16,7 @@ using Orchard.Mvc.ModelBinders;
using Orchard.Mvc.Routes;
using Orchard.Mvc.ViewEngines;
using Orchard.Settings;
using Orchard.Setup.Commands;
using Orchard.Themes;
using Orchard.UI.Notify;
using Orchard.UI.PageClass;
@@ -26,6 +29,7 @@ namespace Orchard.Setup {
// standard services needed in setup mode
builder.RegisterModule(new MvcModule());
builder.RegisterModule(new CommandModule());
builder.RegisterType<RoutePublisher>().As<IRoutePublisher>().InstancePerLifetimeScope();
builder.RegisterType<ModelBinderPublisher>().As<IModelBinderPublisher>().InstancePerLifetimeScope();
builder.RegisterType<WebFormsViewEngineProvider>().As<IViewEngineProvider>().InstancePerLifetimeScope();
@@ -37,6 +41,8 @@ namespace Orchard.Setup {
builder.RegisterType<Notifier>().As<INotifier>().InstancePerLifetimeScope();
builder.RegisterType<NotifyFilter>().As<IFilterProvider>().InstancePerLifetimeScope();
builder.RegisterType<SessionFactoryBuilder>().As<ISessionFactoryBuilder>().InstancePerLifetimeScope();
builder.RegisterType<DefaultCommandManager>().As<ICommandManager>().InstancePerLifetimeScope();
builder.RegisterType<HelpCommand>().As<ICommandHandler>().InstancePerLifetimeScope();
// setup mode specific implementations of needed service interfaces
builder.RegisterType<NullHackInstallationGenerator>().As<IHackInstallationGenerator>().InstancePerLifetimeScope();

View File

@@ -5,9 +5,9 @@ using Orchard.Mvc.ViewModels;
namespace Orchard.Setup.ViewModels {
public class SetupViewModel : BaseViewModel {
public SetupViewModel() {
DatabaseOptions = true;
}
public SetupViewModel() {
DatabaseOptions = true;
}
[Required(ErrorMessage = "Site name is required."), StringLength(70, ErrorMessage = "Site name can be no longer than 70 characters.")]
public string SiteName { get; set; }