Adding some UI to manage module features

--HG--
branch : dev
This commit is contained in:
Nathan Heskew
2010-04-27 11:24:06 -07:00
parent 9819dfa8b1
commit 18bba7873b
15 changed files with 286 additions and 24 deletions

View File

@@ -3,9 +3,21 @@
As a root Orchard system operator As a root Orchard system operator
I want to install and enable modules and enable features I want to install and enable modules and enable features
Scenario: Default modules are listed Scenario: Installed modules are listed
Given I have installed Orchard Given I have installed Orchard
When I go to "admin/modules" When I go to "admin/modules"
Then I should see "Installed Modules" Then I should see "<h2>Installed Modules</h2>"
And I should see "<h3>Themes</h3>" And I should see "<h3>Themes</h3>"
And the status should be 200 OK And the status should be 200 OK
Scenario: Edit module shows its features
Given I have installed Orchard
When I go to "admin/modules/Edit/Orchard.Themes"
Then I should see "<h1>Edit Module: Themes</h1>"
And the status should be 200 OK
Scenario: Features of installed modules are listed
Given I have installed Orchard
When I go to "admin/modules/features"
Then I should see "<h2>Available Features</h2>"
And the status should be 200 OK

View File

@@ -51,10 +51,10 @@ namespace Orchard.Specs
} }
[NUnit.Framework.TestAttribute()] [NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Default modules are listed")] [NUnit.Framework.DescriptionAttribute("Installed modules are listed")]
public virtual void DefaultModulesAreListed() public virtual void InstalledModulesAreListed()
{ {
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Default modules are listed", ((string[])(null))); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Installed modules are listed", ((string[])(null)));
#line 6 #line 6
this.ScenarioSetup(scenarioInfo); this.ScenarioSetup(scenarioInfo);
#line 7 #line 7
@@ -62,11 +62,49 @@ this.ScenarioSetup(scenarioInfo);
#line 8 #line 8
testRunner.When("I go to \"admin/modules\""); testRunner.When("I go to \"admin/modules\"");
#line 9 #line 9
testRunner.Then("I should see \"Installed Modules\""); testRunner.Then("I should see \"<h2>Installed Modules</h2>\"");
#line 10 #line 10
testRunner.And("I should see \"<h3>Themes</h3>\""); testRunner.And("I should see \"<h3>Themes</h3>\"");
#line 11 #line 11
testRunner.And("the status should be 200 OK"); testRunner.And("the status should be 200 OK");
#line hidden
testRunner.CollectScenarioErrors();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Edit module shows its features")]
public virtual void EditModuleShowsItsFeatures()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Edit module shows its features", ((string[])(null)));
#line 13
this.ScenarioSetup(scenarioInfo);
#line 14
testRunner.Given("I have installed Orchard");
#line 15
testRunner.When("I go to \"admin/modules/Edit/Orchard.Themes\"");
#line 16
testRunner.Then("I should see \"<h1>Edit Module: Themes</h1>\"");
#line 17
testRunner.And("the status should be 200 OK");
#line hidden
testRunner.CollectScenarioErrors();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Features of installed modules are listed")]
public virtual void FeaturesOfInstalledModulesAreListed()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Features of installed modules are listed", ((string[])(null)));
#line 19
this.ScenarioSetup(scenarioInfo);
#line 20
testRunner.Given("I have installed Orchard");
#line 21
testRunner.When("I go to \"admin/modules/features\"");
#line 22
testRunner.Then("I should see \"<h2>Available Features</h2>\"");
#line 23
testRunner.And("the status should be 200 OK");
#line hidden #line hidden
testRunner.CollectScenarioErrors(); testRunner.CollectScenarioErrors();
} }

View File

@@ -8,7 +8,9 @@ namespace Orchard.Modules {
builder.Add("Modules", "10", builder.Add("Modules", "10",
menu => menu menu => menu
.Add("Manage Modules", "1.0", item => item.Action("Index", "Admin", new { area = "Orchard.Modules" }) .Add("Manage Modules", "1.0", item => item.Action("Index", "Admin", new { area = "Orchard.Modules" })
.Permission(Permissions.ManageModules))); .Permission(Permissions.ManageModules))
.Add("Manage Features", "2.0", item => item.Action("Features", "Admin", new { area = "Orchard.Modules" })
.Permission(Permissions.ManageFeatures)));
} }
} }
} }

View File

@@ -1,17 +1,50 @@
using System.Web.Mvc; using System;
using System.Web.Mvc;
using Orchard.Localization;
using Orchard.Modules.ViewModels; using Orchard.Modules.ViewModels;
using Orchard.Mvc.Results;
namespace Orchard.Modules.Controllers { namespace Orchard.Modules.Controllers {
public class AdminController : Controller { public class AdminController : Controller {
private readonly IModuleService _moduleService; private readonly IModuleService _moduleService;
public AdminController(IModuleService moduleService) { public AdminController(IOrchardServices services, IModuleService moduleService) {
Services = services;
_moduleService = moduleService; _moduleService = moduleService;
T = NullLocalizer.Instance;
} }
private Localizer T { get; set; }
public IOrchardServices Services { get; set; }
public ActionResult Index() { public ActionResult Index() {
if (!Services.Authorizer.Authorize(Permissions.ManageModules, T("Not allowed to manage modules")))
return new HttpUnauthorizedResult();
var modules = _moduleService.GetInstalledModules(); var modules = _moduleService.GetInstalledModules();
return View(new ModulesIndexViewModel {Modules = modules}); return View(new ModulesIndexViewModel {Modules = modules});
} }
public ActionResult Edit(string moduleName) {
if (!Services.Authorizer.Authorize(Permissions.ManageModules, T("Not allowed to edit module")))
return new HttpUnauthorizedResult();
var module = _moduleService.GetModuleByName(moduleName);
if (module == null)
return new NotFoundResult();
return View(new ModuleEditViewModel {
Name = module.DisplayName
});
}
public ActionResult Features() {
if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features")))
return new HttpUnauthorizedResult();
var features = _moduleService.GetAvailableFeatures();
return View(new FeatureListViewModel {Features = features});
}
} }
} }

View File

@@ -63,10 +63,14 @@
<ItemGroup> <ItemGroup>
<Compile Include="AdminMenu.cs" /> <Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\AdminController.cs" /> <Compile Include="Controllers\AdminController.cs" />
<Compile Include="ViewModels\FeatureListViewModel.cs" />
<Compile Include="Models\Module.cs" /> <Compile Include="Models\Module.cs" />
<Compile Include="Permissions.cs" /> <Compile Include="Permissions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Routes.cs" />
<Compile Include="Routing\ModuleNameConstraint.cs" />
<Compile Include="Services\ModuleService.cs" /> <Compile Include="Services\ModuleService.cs" />
<Compile Include="ViewModels\ModuleEditViewModel.cs" />
<Compile Include="ViewModels\ModulesIndexViewModel.cs" /> <Compile Include="ViewModels\ModulesIndexViewModel.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -81,8 +85,14 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Views\Admin\Features.ascx" />
<Content Include="Views\Admin\Edit.ascx" />
<Content Include="Views\Web.config" /> <Content Include="Views\Web.config" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Views\DisplayTemplates\Items\" />
<Folder Include="Views\DisplayTemplates\Parts\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" /> <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@@ -4,13 +4,14 @@ using Orchard.Security.Permissions;
namespace Orchard.Modules { namespace Orchard.Modules {
public class Permissions : IPermissionProvider { public class Permissions : IPermissionProvider {
public static readonly Permission ManageModules = new Permission { Description = "Manage Modules", Name = "ManageModules" }; public static readonly Permission ManageModules = new Permission { Description = "Manage Modules", Name = "ManageModules" };
public static readonly Permission ManageFeatures = new Permission { Description = "Manage Features", Name = "ManageFeatures", ImpliedBy = new[] {ManageModules}};
public string ModuleName { public string ModuleName {
get { return "Modules"; } get { return "Modules"; }
} }
public IEnumerable<Permission> GetPermissions() { public IEnumerable<Permission> GetPermissions() {
return new[] {ManageModules}; return new[] {ManageModules, ManageFeatures};
} }
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() { public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {

View File

@@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.Modules.Routing;
using Orchard.Mvc.Routes;
namespace Orchard.Modules {
public class Routes : IRouteProvider {
private readonly IModuleNameConstraint _moduleNameConstraint;
public Routes(IModuleNameConstraint moduleNameConstraint) {
_moduleNameConstraint = moduleNameConstraint;
}
public IEnumerable<RouteDescriptor> GetRoutes() {
return new[] {
new RouteDescriptor {
Route = new Route(
"Admin/Modules/Edit/{moduleName}",
new RouteValueDictionary {
{"area", "Orchard.Modules"},
{"controller", "Admin"},
{"action", "Edit"}
},
new RouteValueDictionary {
{"moduleName", _moduleNameConstraint}
},
new RouteValueDictionary {
{"area", "Orchard.Modules"}
},
new MvcRouteHandler())
}
};
}
public void GetRoutes(ICollection<RouteDescriptor> routes) {
foreach (var routeDescriptor in GetRoutes())
routes.Add(routeDescriptor);
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Web;
using System.Web.Routing;
namespace Orchard.Modules.Routing {
public interface IModuleNameConstraint : IRouteConstraint, ISingletonDependency {
}
public class ModuleNameConstraint : IModuleNameConstraint {
private readonly IModuleService _moduleService;
public ModuleNameConstraint(IModuleService moduleService) {
_moduleService = moduleService;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
if (routeDirection == RouteDirection.UrlGeneration)
return true;
object value;
if (values.TryGetValue(parameterName, out value))
return _moduleService.GetModuleByName(Convert.ToString(value)) != null;
return false;
}
}
}

View File

@@ -3,10 +3,12 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Web; using System.Web;
using Orchard.Environment.Extensions; using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Models;
using Orchard.Modules.Models; using Orchard.Modules.Models;
namespace Orchard.Modules.Services { namespace Orchard.Modules.Services {
public class ModuleService : IModuleService { public class ModuleService : IModuleService {
private const string ModuleExtensionType = "module";
private readonly IExtensionManager _extensionManager; private readonly IExtensionManager _extensionManager;
public ModuleService(IExtensionManager extensionManager) { public ModuleService(IExtensionManager extensionManager) {
@@ -14,28 +16,56 @@ namespace Orchard.Modules.Services {
} }
public IModule GetModuleByName(string moduleName) { public IModule GetModuleByName(string moduleName) {
return null; return _extensionManager.AvailableExtensions().Where(e => string.Equals(e.Name, moduleName, StringComparison.OrdinalIgnoreCase) && string.Equals(e.ExtensionType, "Module", StringComparison.OrdinalIgnoreCase)).Select(
descriptor => AssembleModuleFromDescriptor(descriptor)).FirstOrDefault();
} }
public IEnumerable<IModule> GetInstalledModules() { public IEnumerable<IModule> GetInstalledModules() {
return return
_extensionManager.AvailableExtensions().Where( _extensionManager.AvailableExtensions().Where(
e => String.Equals(e.ExtensionType, "Module", StringComparison.OrdinalIgnoreCase)).Select( e => String.Equals(e.ExtensionType, ModuleExtensionType, StringComparison.OrdinalIgnoreCase)).Select(
descriptor => (new Module { descriptor => AssembleModuleFromDescriptor(descriptor));
ModuleName = descriptor.Name,
DisplayName = descriptor.DisplayName,
Description = descriptor.Description,
Version = descriptor.Version,
Author = descriptor.Author,
HomePage = descriptor.WebSite,
Tags = descriptor.Tags
}) as IModule);
} }
public void InstallModule(HttpPostedFileBase file) { public void InstallModule(HttpPostedFileBase file) {
_extensionManager.InstallExtension(ModuleExtensionType, file);
} }
public void UninstallModule(string moduleName) { public void UninstallModule(string moduleName) {
_extensionManager.UninstallExtension(ModuleExtensionType, moduleName);
}
public IEnumerable<Feature> GetAvailableFeatures() {
return GetInstalledModules()
.Where(m => m.Features != null)
.SelectMany(m => _extensionManager.LoadFeatures(m.Features));
}
public IEnumerable<Feature> GetAvailableFeaturesByModule(string moduleName) {
var module = GetModuleByName(moduleName);
if (module == null || module.Features == null)
return null;
return _extensionManager.LoadFeatures(module.Features);
}
public void EnableFeatures(IEnumerable<string> featureNames) {
}
public void DisableFeatures(IEnumerable<string> featureNames) {
}
private static IModule AssembleModuleFromDescriptor(ExtensionDescriptor extensionDescriptor) {
return new Module {
ModuleName = extensionDescriptor.Name,
DisplayName = extensionDescriptor.DisplayName,
Description = extensionDescriptor.Description,
Version = extensionDescriptor.Version,
Author = extensionDescriptor.Author,
HomePage = extensionDescriptor.WebSite,
Tags = extensionDescriptor.Tags,
Features = extensionDescriptor.Features
};
} }
} }
} }

View File

@@ -0,0 +1,9 @@
using System.Collections.Generic;
using Orchard.Environment.Extensions.Models;
using Orchard.Mvc.ViewModels;
namespace Orchard.Modules.ViewModels {
public class FeatureListViewModel : BaseViewModel {
public IEnumerable<Feature> Features { get; set; }
}
}

View File

@@ -0,0 +1,7 @@
using Orchard.Mvc.ViewModels;
namespace Orchard.Modules.ViewModels {
public class ModuleEditViewModel : BaseViewModel {
public string Name { get; set; }
}
}

View File

@@ -0,0 +1,5 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<ModuleEditViewModel>" %>
<%@ Import Namespace="Orchard.Mvc.Html"%>
<%@ Import Namespace="Orchard.Modules.ViewModels"%>
<h1><%=Html.TitleForPage(T("Edit Module: {0}", Model.Name).ToString()) %></h1>
<p><%=_Encoded("Edit the module. Maybe show module's features.") %></p>

View File

@@ -0,0 +1,33 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<FeatureListViewModel>" %>
<%@ Import Namespace="Orchard.Mvc.Html"%>
<%@ Import Namespace="Orchard.Modules.ViewModels"%>
<h1><%=Html.TitleForPage(T("Manage Features").ToString()) %></h1>
<h2><%=T("Available Features") %></h2>
<% if (Model.Features.Count() > 0) { %>
<ul class="contentItems blogs"><%
foreach (var featureGroup in Model.Features.OrderBy(f => f.Descriptor.Name).GroupBy(f => f.Descriptor.Category)) { %>
<li>
<h3><%=Html.Encode(featureGroup.First().Descriptor.Category ?? T("General")) %></h3>
<ul><%
foreach (var feature in featureGroup.Select(f => f.Descriptor)) {%>
<li>
<h4><%=Html.Encode(feature.Name) %></h4>
<p><%=T("From: {0}", Html.Encode(feature.Extension.DisplayName)) %></p><%
if (!string.IsNullOrEmpty(feature.Description)) { %>
<p><%=Html.Encode(feature.Description) %></p><%
}
if (feature.Dependencies.Count() > 0) {%>
<h5><%=_Encoded("Depends on:")%></h5>
<ul><%
foreach (var dependency in feature.Dependencies) { %>
<li><%=Html.Encode(dependency) %></li><%
} %>
</ul><%
}%>
</li><%
} %>
</ul>
</li><%
} %>
</ul><%
} %>

View File

@@ -4,11 +4,21 @@
<h1><%=Html.TitleForPage(T("Manage Modules").ToString()) %></h1> <h1><%=Html.TitleForPage(T("Manage Modules").ToString()) %></h1>
<h2><%=T("Installed Modules") %></h2> <h2><%=T("Installed Modules") %></h2>
<% if (Model.Modules.Count() > 0) { %> <% if (Model.Modules.Count() > 0) { %>
<ul><% <ul class="contentItems blogs"><%
foreach (var module in Model.Modules.OrderBy(m => m.DisplayName)) { %> foreach (var module in Model.Modules.OrderBy(m => m.DisplayName)) { %>
<li> <li>
<h3><%=Html.Encode(module.DisplayName) %></h3> <div class="summary">
<p><%=module.Description != null ? Html.Encode(module.Description) : T("<em>no description</em>") %></p> <div class="properties">
<h3><%=Html.Encode(module.DisplayName) %></h3>
<div class="related">
<%=Html.ActionLink(T("Edit").ToString(), "edit", new {moduleName = module.ModuleName, area = "Orchard.Modules"}) %><%=_Encoded(" | ")%>
<a href="#">Delete</a>
</div>
</div>
</div><%
if (!string.IsNullOrEmpty(module.Description)) { %>
<p><%=Html.Encode(module.Description) %></p><%
} %>
</li><% </li><%
} %> } %>
</ul><% </ul><%

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Web; using System.Web;
using Orchard.Environment.Extensions.Models;
namespace Orchard.Modules { namespace Orchard.Modules {
public interface IModuleService : IDependency { public interface IModuleService : IDependency {
@@ -7,5 +8,8 @@ namespace Orchard.Modules {
IEnumerable<IModule> GetInstalledModules(); IEnumerable<IModule> GetInstalledModules();
void InstallModule(HttpPostedFileBase file); void InstallModule(HttpPostedFileBase file);
void UninstallModule(string moduleName); void UninstallModule(string moduleName);
IEnumerable<Feature> GetAvailableFeatures();
void EnableFeatures(IEnumerable<string> featureNames);
void DisableFeatures(IEnumerable<string> featureNames);
} }
} }