diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs index df9b8141a..51771c4c3 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Web.Mvc; using Orchard.Data.Migration; using Orchard.DisplayManagement; +using Orchard.Environment.Configuration; using Orchard.Environment.Descriptor.Models; using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Models; @@ -34,6 +35,7 @@ namespace Orchard.Modules.Controllers { private readonly IRecipeHarvester _recipeHarvester; private readonly IRecipeManager _recipeManager; private readonly ShellDescriptor _shellDescriptor; + private readonly ShellSettings _shellSettings; public AdminController( IEnumerable extensionDisplayEventHandlers, @@ -46,6 +48,7 @@ namespace Orchard.Modules.Controllers { IRecipeHarvester recipeHarvester, IRecipeManager recipeManager, ShellDescriptor shellDescriptor, + ShellSettings shellSettings, IShapeFactory shapeFactory) { Services = services; @@ -58,6 +61,7 @@ namespace Orchard.Modules.Controllers { _recipeHarvester = recipeHarvester; _recipeManager = recipeManager; _shellDescriptor = shellDescriptor; + _shellSettings = shellSettings; Shape = shapeFactory; T = NullLocalizer.Instance; @@ -77,6 +81,7 @@ namespace Orchard.Modules.Controllers { IEnumerable modules = _extensionManager.AvailableExtensions() .Where(extensionDescriptor => DefaultExtensionTypes.IsModule(extensionDescriptor.ExtensionType) && + ModuleIsAllowed(extensionDescriptor) && (string.IsNullOrEmpty(options.SearchText) || extensionDescriptor.Name.ToLowerInvariant().Contains(options.SearchText.ToLowerInvariant()))) .OrderBy(extensionDescriptor => extensionDescriptor.Name) .Select(extensionDescriptor => new ModuleEntry { Descriptor = extensionDescriptor }); @@ -112,7 +117,7 @@ namespace Orchard.Modules.Controllers { IEnumerable modules = _extensionManager.AvailableExtensions() .Where(extensionDescriptor => DefaultExtensionTypes.IsModule(extensionDescriptor.ExtensionType)) - .Where(extensionDescriptor => extensionDescriptor.Id != "Orchard.Setup") + .Where(extensionDescriptor => extensionDescriptor.Id != "Orchard.Setup" && ModuleIsAllowed(extensionDescriptor)) .OrderBy(extensionDescriptor => extensionDescriptor.Name) .Select(extensionDescriptor => new ModuleEntry { Descriptor = extensionDescriptor }); @@ -136,7 +141,7 @@ namespace Orchard.Modules.Controllers { return new HttpUnauthorizedResult(); ModuleEntry module = _extensionManager.AvailableExtensions() - .Where(extensionDescriptor => extensionDescriptor.Id == moduleId) + .Where(extensionDescriptor => extensionDescriptor.Id == moduleId && ModuleIsAllowed(extensionDescriptor)) .Select(extensionDescriptor => new ModuleEntry { Descriptor = extensionDescriptor }).FirstOrDefault(); if (module == null) { @@ -169,7 +174,7 @@ namespace Orchard.Modules.Controllers { var featuresThatNeedUpdate = _dataMigrationManager.GetFeaturesThatNeedUpdate(); IEnumerable features = _featureManager.GetAvailableFeatures() - .Where(f => !DefaultExtensionTypes.IsTheme(f.Extension.ExtensionType)) + .Where(f => !DefaultExtensionTypes.IsTheme(f.Extension.ExtensionType) && ModuleIsAllowed(f.Extension)) .Select(f => new ModuleFeature { Descriptor = f, IsEnabled = _shellDescriptor.Features.Any(sf => sf.Name == f.Id), @@ -192,7 +197,7 @@ namespace Orchard.Modules.Controllers { } if (ModelState.IsValid) { - var availableFeatures = _moduleService.GetAvailableFeatures().ToList(); + var availableFeatures = _moduleService.GetAvailableFeatures().Where(feature => ModuleIsAllowed(feature.Descriptor.Extension)).ToList(); var selectedFeatures = availableFeatures.Where(x => featureIds.Contains(x.Descriptor.Id)).ToList(); var enabledFeatures = availableFeatures.Where(x => x.IsEnabled && featureIds.Contains(x.Descriptor.Id)).Select(x => x.Descriptor.Id).ToList(); var disabledFeatures = availableFeatures.Where(x => !x.IsEnabled && featureIds.Contains(x.Descriptor.Id)).Select(x => x.Descriptor.Id).ToList(); @@ -230,5 +235,12 @@ namespace Orchard.Modules.Controllers { return RedirectToAction("Features"); } + + /// + /// Checks whether the module is allowed for the current tenant + /// + private bool ModuleIsAllowed(ExtensionDescriptor extensionDescriptor) { + return _shellSettings.Modules.Length == 0 || _shellSettings.Modules.Contains(extensionDescriptor.Id); + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Controllers/AdminController.cs index 3e3c7c235..132dfca2d 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Controllers/AdminController.cs @@ -43,8 +43,9 @@ namespace Orchard.MultiTenancy.Controllers { var model = new TenantAddViewModel(); - // fetches all available themes + // fetches all available themes and modules model.Themes = _tenantService.GetInstalledThemes().Select(x => new ThemeEntry { ThemeId = x.Id, ThemeName = x.Name }).ToList(); + model.Modules = _tenantService.GetInstalledModules().Select(x => new ModuleEntry { ModuleId = x.Id, ModuleName = x.Name }).ToList(); return View(model); } @@ -76,7 +77,8 @@ namespace Orchard.MultiTenancy.Controllers { DataConnectionString = viewModel.DatabaseConnectionString, DataTablePrefix = viewModel.DatabaseTablePrefix, State = TenantState.Uninitialized, - Themes = viewModel.Themes.Where(x => x.Checked).Select(x => x.ThemeId).ToArray() + Themes = viewModel.Themes.Where(x => x.Checked).Select(x => x.ThemeId).ToArray(), + Modules = viewModel.Modules.Where(x => x.Checked).Select(x => x.ModuleId).ToArray() }); return RedirectToAction("Index"); @@ -111,6 +113,11 @@ namespace Orchard.MultiTenancy.Controllers { ThemeId = x.Id, ThemeName = x.Name, Checked = tenant.Themes.Contains(x.Id) + }).ToList(), + Modules = _tenantService.GetInstalledModules().Select(x => new ModuleEntry { + ModuleId = x.Id, + ModuleName = x.Name, + Checked = tenant.Modules.Contains(x.Id) }).ToList() }); } @@ -133,7 +140,7 @@ namespace Orchard.MultiTenancy.Controllers { try { _tenantService.UpdateTenant( - new ShellSettings { + new ShellSettings(tenant) { Name = tenant.Name, RequestUrlHost = viewModel.RequestUrlHost, RequestUrlPrefix = viewModel.RequestUrlPrefix, @@ -145,7 +152,8 @@ namespace Orchard.MultiTenancy.Controllers { EncryptionKey = tenant.EncryptionKey, HashAlgorithm = tenant.HashAlgorithm, HashKey = tenant.HashKey, - Themes = viewModel.Themes.Where(x => x.Checked).Select(x => x.ThemeId).ToArray() + Themes = viewModel.Themes.Where(x => x.Checked).Select(x => x.ThemeId).ToArray(), + Modules = viewModel.Modules.Where(x => x.Checked).Select(x => x.ModuleId).ToArray() }); return RedirectToAction("Index"); diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Module.txt b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Module.txt index 20f966640..8dc56a112 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Module.txt @@ -8,3 +8,4 @@ OrchardVersion: 1.7.2 Description: The multi-tenancy module enables multiple Orchard sites to run in isolation inside of a single web application, improving site density on a single server or hosted account. FeatureDescription: Configure multiple site tenants. Category: Hosting +Dependencies: Orchard.jQuery \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Orchard.MultiTenancy.csproj b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Orchard.MultiTenancy.csproj index e64d22092..044b0a168 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Orchard.MultiTenancy.csproj +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Orchard.MultiTenancy.csproj @@ -75,6 +75,7 @@ + @@ -85,6 +86,7 @@ + @@ -108,12 +110,19 @@ - Designer + + + + + + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Scripts/Web.config b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Scripts/Web.config new file mode 100644 index 000000000..817198995 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Scripts/Web.config @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Scripts/multi-tenancy.admin.js b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Scripts/multi-tenancy.admin.js new file mode 100644 index 000000000..64486562e --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Scripts/multi-tenancy.admin.js @@ -0,0 +1,14 @@ +(function ($) { + $(function () { + $(".select-all").click(function () { + var $checkbox = $(this); + var $allCheckboxes = $checkbox.parent().find(":checkbox"); + if ($checkbox.is(':checked')) { + $allCheckboxes.prop("checked", true); + } + else { + $allCheckboxes.prop("checked", false); + } + }); + }); +})(jQuery); \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/ITenantService.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/ITenantService.cs index 4cf4844f0..8832f0c76 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/ITenantService.cs +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/ITenantService.cs @@ -12,5 +12,10 @@ namespace Orchard.MultiTenancy.Services { /// Returns a list of all installed themes /// IEnumerable GetInstalledThemes(); + + /// + /// Returns a list of all installed modules + /// + IEnumerable GetInstalledModules(); } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/TenantService.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/TenantService.cs index 44dc17356..e907b7104 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/TenantService.cs +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/TenantService.cs @@ -35,6 +35,13 @@ namespace Orchard.MultiTenancy.Services { return GetThemes(_extensionManager.AvailableExtensions()); } + /// + /// Loads only installed modules + /// + public IEnumerable GetInstalledModules() { + return _extensionManager.AvailableExtensions().Where(descriptor => DefaultExtensionTypes.IsModule(descriptor.ExtensionType)); + } + private IEnumerable GetThemes(IEnumerable extensions) { var themes = new List(); foreach (var descriptor in extensions) { diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/ModuleEntry.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/ModuleEntry.cs new file mode 100644 index 000000000..24ab76657 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/ModuleEntry.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; + +namespace Orchard.MultiTenancy.ViewModels { + public class ModuleEntry { + public bool Checked { get; set; } + public string ModuleName { get; set; } + public string ModuleId { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantAddViewModel.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantAddViewModel.cs index 642dce7b3..21554f8aa 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantAddViewModel.cs +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantAddViewModel.cs @@ -8,6 +8,7 @@ namespace Orchard.MultiTenancy.ViewModels { // define "Allow the tenant to set up the database" as default value DataProvider = ""; Themes = new List(); + Modules = new List(); } [Required] @@ -20,6 +21,7 @@ namespace Orchard.MultiTenancy.ViewModels { public string DatabaseTablePrefix { get; set; } public List Themes { get; set; } + public List Modules { get; set; } } } diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantEditViewModel.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantEditViewModel.cs index 9bede7332..a4722e942 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantEditViewModel.cs +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantEditViewModel.cs @@ -7,6 +7,7 @@ namespace Orchard.MultiTenancy.ViewModels { public class TenantEditViewModel { public TenantEditViewModel() { Themes = new List(); + Modules = new List(); } [Required] @@ -20,6 +21,8 @@ namespace Orchard.MultiTenancy.ViewModels { public TenantState State { get; set; } public List Themes { get; set; } + public List Modules { get; set; } + } } diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Add.cshtml b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Add.cshtml index ff09e7aaf..bc0ad74d7 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Add.cshtml +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Add.cshtml @@ -1,6 +1,11 @@ @model Orchard.MultiTenancy.ViewModels.TenantAddViewModel -@{ Layout.Title = T("Add New Tenant").ToString(); } +@{ + Layout.Title = T("Add New Tenant").ToString(); + + Script.Require("jQuery").AtFoot(); + Script.Include("multi-tenancy.admin.js").AtFoot(); +} @using (Html.BeginFormAntiForgeryPost()) { @Html.ValidationSummary() @@ -53,6 +58,10 @@
+ + +
+ @for (var i=0 ;i m.Themes[i].Checked), Model.Themes[i].Checked, new { id = Html.FieldIdFor(x => x.Themes[i]) }) @@ -63,7 +72,27 @@ @T("Select the Themes which should be available for this tenant. If none is selected, they will all be available.")
- + + +
+
+ + + + +
+ + @for (var i=0 ;i m.Modules[i].Checked), Model.Modules[i].Checked, new { id = Html.FieldIdFor(x => x.Modules[i]) }) + + @Html.HiddenFor(m => m.Modules[i].ModuleId) + @Html.HiddenFor(m => m.Modules[i].ModuleName) + } + + @T("Select the Modules which should be available for this tenant. If none is selected, they will all be available.") +
+
diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Edit.cshtml b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Edit.cshtml index 778248978..09462ea1a 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Edit.cshtml +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Edit.cshtml @@ -1,7 +1,12 @@ @model Orchard.MultiTenancy.ViewModels.TenantEditViewModel @using Orchard.Environment.Configuration; -@{ Layout.Title = T("Edit Tenant").ToString(); } +@{ + Layout.Title = T("Edit Tenant").ToString(); + + Script.Require("jQuery").AtFoot(); + Script.Include("multi-tenancy.admin.js").AtFoot(); +} @using (Html.BeginFormAntiForgeryPost()) { @Html.ValidationSummary() @@ -54,6 +59,10 @@
+ + +
+ @for (var i = 0; i < Model.Themes.Count; i++) { var theme = Model.Themes[i]; @Html.CheckBox(Html.FieldNameFor(m => m.Themes[i].Checked), Model.Themes[i].Checked, new { id = Html.FieldIdFor(x => x.Themes[i]) }) @@ -64,9 +73,29 @@ @T("Select the Themes which should be available for this tenant. If none is selected, they will all be available.")
+
+ +
+
+ + + + +
+ + @for (var i = 0; i < Model.Modules.Count; i++) { + var theme = Model.Modules[i]; + @Html.CheckBox(Html.FieldNameFor(m => m.Modules[i].Checked), Model.Modules[i].Checked, new { id = Html.FieldIdFor(x => x.Modules[i]) }) + + @Html.HiddenFor(m => m.Modules[i].ModuleId) + @Html.HiddenFor(m => m.Modules[i].ModuleName) + } + + @T("Select the Modules which should be available for this tenant. If none is selected, they will all be available.") +
-} +} \ No newline at end of file diff --git a/src/Orchard/Environment/Configuration/ShellSettings.cs b/src/Orchard/Environment/Configuration/ShellSettings.cs index 01581998d..b7c17119d 100644 --- a/src/Orchard/Environment/Configuration/ShellSettings.cs +++ b/src/Orchard/Environment/Configuration/ShellSettings.cs @@ -12,12 +12,14 @@ namespace Orchard.Environment.Configuration { public const string DefaultName = "Default"; private TenantState _tenantState; private string[] _themes; + private string[] _modules; private readonly IDictionary _values; public ShellSettings() { _values = new Dictionary(StringComparer.OrdinalIgnoreCase); State = TenantState.Invalid; Themes = new string[0]; + Modules = new string[0]; } public ShellSettings(ShellSettings settings) { @@ -35,6 +37,7 @@ namespace Orchard.Environment.Configuration { HashKey = settings.HashKey; State = settings.State; Themes = settings.Themes; + Modules = settings.Modules; } public string this[string key] { @@ -145,6 +148,21 @@ namespace Orchard.Environment.Configuration { } } + /// + /// List of available modules for this tenant + /// + public string[] Modules { + get { + return _modules ?? (Modules = (_values["Modules"] ?? "").Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + .Select(t => t.Trim()) + .ToArray(); + } + set { + _modules = value; + this["Modules"] = string.Join(";", value); + } + } + /// /// The state is which the tenant is /// diff --git a/src/Orchard/Environment/Configuration/ShellSettingsSerializer.cs b/src/Orchard/Environment/Configuration/ShellSettingsSerializer.cs index 7b50051e6..8e4879bd8 100644 --- a/src/Orchard/Environment/Configuration/ShellSettingsSerializer.cs +++ b/src/Orchard/Environment/Configuration/ShellSettingsSerializer.cs @@ -63,6 +63,9 @@ namespace Orchard.Environment.Configuration { case "Themes": shellSettings.Themes = value.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); break; + case "Modules": + shellSettings.Modules = value.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + break; default: shellSettings[key] = value; break;