mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
#19014: Modules can now be filtered for tenants. Also adding "Select all" checkbox for tenant editor and fixing that tenant editing didn't preserve custom ShellSettings fields.
Work Item 19014
This commit is contained in:
@@ -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<IExtensionDisplayEventHandler> 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<ModuleEntry> 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<ModuleEntry> 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<ModuleFeature> 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");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the module is allowed for the current tenant
|
||||
/// </summary>
|
||||
private bool ModuleIsAllowed(ExtensionDescriptor extensionDescriptor) {
|
||||
return _shellSettings.Modules.Length == 0 || _shellSettings.Modules.Contains(extensionDescriptor.Id);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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");
|
||||
|
@@ -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
|
@@ -75,6 +75,7 @@
|
||||
<Compile Include="Routes.cs" />
|
||||
<Compile Include="Services\ITenantService.cs" />
|
||||
<Compile Include="Services\TenantService.cs" />
|
||||
<Compile Include="ViewModels\ModuleEntry.cs" />
|
||||
<Compile Include="ViewModels\TenantEditViewModel.cs" />
|
||||
<Compile Include="ViewModels\TenantAddViewModel.cs" />
|
||||
<Compile Include="ViewModels\TenantsIndexViewModel.cs" />
|
||||
@@ -85,6 +86,7 @@
|
||||
<Content Include="Content\Admin\images\disabled.gif" />
|
||||
<Content Include="Content\Admin\images\enabled.gif" />
|
||||
<Content Include="Module.txt" />
|
||||
<Content Include="Scripts\multi-tenancy.admin.js" />
|
||||
<Content Include="Styles\orchard-multitenancy-admin.css" />
|
||||
<Content Include="Views\Admin\Add.cshtml" />
|
||||
<Content Include="Views\Admin\Edit.cshtml" />
|
||||
@@ -108,12 +110,19 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Styles\Web.config">
|
||||
<SubType>Designer</SubType>
|
||||
<SubType>
|
||||
</SubType>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="web.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Scripts\Web.config">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
|
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<staticContent>
|
||||
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
|
||||
</staticContent>
|
||||
|
||||
<handlers accessPolicy="Script,Read">
|
||||
<!--
|
||||
iis7 - for any request to a file exists on disk, return it via native http module.
|
||||
accessPolicy 'Script' is to allow for a managed 404 page.
|
||||
-->
|
||||
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
</configuration>
|
@@ -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);
|
@@ -12,5 +12,10 @@ namespace Orchard.MultiTenancy.Services {
|
||||
/// Returns a list of all installed themes
|
||||
/// </summary>
|
||||
IEnumerable<ExtensionDescriptor> GetInstalledThemes();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all installed modules
|
||||
/// </summary>
|
||||
IEnumerable<ExtensionDescriptor> GetInstalledModules();
|
||||
}
|
||||
}
|
@@ -35,6 +35,13 @@ namespace Orchard.MultiTenancy.Services {
|
||||
return GetThemes(_extensionManager.AvailableExtensions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads only installed modules
|
||||
/// </summary>
|
||||
public IEnumerable<ExtensionDescriptor> GetInstalledModules() {
|
||||
return _extensionManager.AvailableExtensions().Where(descriptor => DefaultExtensionTypes.IsModule(descriptor.ExtensionType));
|
||||
}
|
||||
|
||||
private IEnumerable<ExtensionDescriptor> GetThemes(IEnumerable<ExtensionDescriptor> extensions) {
|
||||
var themes = new List<ExtensionDescriptor>();
|
||||
foreach (var descriptor in extensions) {
|
||||
|
@@ -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; }
|
||||
}
|
||||
}
|
@@ -8,6 +8,7 @@ namespace Orchard.MultiTenancy.ViewModels {
|
||||
// define "Allow the tenant to set up the database" as default value
|
||||
DataProvider = "";
|
||||
Themes = new List<ThemeEntry>();
|
||||
Modules = new List<ModuleEntry>();
|
||||
}
|
||||
|
||||
[Required]
|
||||
@@ -20,6 +21,7 @@ namespace Orchard.MultiTenancy.ViewModels {
|
||||
public string DatabaseTablePrefix { get; set; }
|
||||
|
||||
public List<ThemeEntry> Themes { get; set; }
|
||||
public List<ModuleEntry> Modules { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -7,6 +7,7 @@ namespace Orchard.MultiTenancy.ViewModels {
|
||||
public class TenantEditViewModel {
|
||||
public TenantEditViewModel() {
|
||||
Themes = new List<ThemeEntry>();
|
||||
Modules = new List<ModuleEntry>();
|
||||
}
|
||||
|
||||
[Required]
|
||||
@@ -20,6 +21,8 @@ namespace Orchard.MultiTenancy.ViewModels {
|
||||
public TenantState State { get; set; }
|
||||
|
||||
public List<ThemeEntry> Themes { get; set; }
|
||||
public List<ModuleEntry> Modules { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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 @@
|
||||
<div>
|
||||
<label for="@Html.FieldIdFor(m => m.Themes)">@T("Available Themes")</label>
|
||||
|
||||
<input type="checkbox" class="select-all" id="select-all-themes" />
|
||||
<label class="forcheckbox" for="select-all-themes">@("Select all")</label>
|
||||
<br />
|
||||
|
||||
@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]) })
|
||||
@@ -63,7 +72,27 @@
|
||||
|
||||
<span class="hint">@T("Select the Themes which should be available for this tenant. If none is selected, they will all be available.")</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<div>
|
||||
<label for="@Html.FieldIdFor(m => m.Modules)">@T("Available Modules")</label>
|
||||
|
||||
<input type="checkbox" class="select-all" id="select-all-modules" />
|
||||
<label class="forcheckbox" for="select-all-modules">@("Select all")</label>
|
||||
<br />
|
||||
|
||||
@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]) })
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.Modules[i])">@Model.Modules[i].ModuleName</label>
|
||||
@Html.HiddenFor(m => m.Modules[i].ModuleId)
|
||||
@Html.HiddenFor(m => m.Modules[i].ModuleName)
|
||||
}
|
||||
|
||||
<span class="hint">@T("Select the Modules which should be available for this tenant. If none is selected, they will all be available.")</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<button class="primaryAction" type="submit">@T("Save")</button>
|
||||
|
@@ -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 @@
|
||||
<div>
|
||||
<label for="@Html.FieldIdFor(m => m.Themes)">@T("Available Themes")</label>
|
||||
|
||||
<input type="checkbox" class="select-all" id="select-all-themes" />
|
||||
<label class="forcheckbox" for="select-all-themes">@("Select all")</label>
|
||||
<br />
|
||||
|
||||
@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 @@
|
||||
|
||||
<span class="hint">@T("Select the Themes which should be available for this tenant. If none is selected, they will all be available.")</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<div>
|
||||
<label for="@Html.FieldIdFor(m => m.Modules)">@T("Available Modules")</label>
|
||||
|
||||
<input type="checkbox" class="select-all" id="select-all-modules" />
|
||||
<label class="forcheckbox" for="select-all-modules">@("Select all")</label>
|
||||
<br />
|
||||
|
||||
@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]) })
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.Modules[i])">@Model.Modules[i].ModuleName</label>
|
||||
@Html.HiddenFor(m => m.Modules[i].ModuleId)
|
||||
@Html.HiddenFor(m => m.Modules[i].ModuleName)
|
||||
}
|
||||
|
||||
<span class="hint">@T("Select the Modules which should be available for this tenant. If none is selected, they will all be available.")</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<button class="primaryAction" type="submit">@T("Save")</button>
|
||||
</fieldset>
|
||||
}
|
||||
}
|
@@ -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<string, string> _values;
|
||||
|
||||
public ShellSettings() {
|
||||
_values = new Dictionary<string, string>(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 {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of available modules for this tenant
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The state is which the tenant is
|
||||
/// </summary>
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user