Some work on the feature managment UI

- start of dependency aware feature disable/enable
- bit of a perf improvement when working with the available features list (thanks, ToList, as usual)

--HG--
branch : dev
This commit is contained in:
Nathan Heskew
2010-05-14 15:52:22 -07:00
parent d5cdc0d37e
commit 7db897c475
4 changed files with 66 additions and 106 deletions

View File

@@ -44,44 +44,12 @@ namespace Orchard.Modules.Controllers {
}); });
} }
public ActionResult Features(FeaturesOptions options) { public ActionResult Features() {
if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features"))) if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features")))
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
var features = _moduleService.GetAvailableFeatures(); var features = _moduleService.GetAvailableFeatures();
return View(new FeaturesViewModel {Features = features, Options = options}); return View(new FeaturesViewModel {Features = features});
}
[HttpPost, ActionName("Features")]
[FormValueRequired("submit.BulkEdit")]
public ActionResult FeaturesPOST(FeaturesOptions options, IList<string> selection) {
if (selection != null && selection.Count > 0)
{
switch (options.BulkAction)
{
case FeaturesBulkAction.None:
break;
case FeaturesBulkAction.Enable:
if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to enable features")))
return new HttpUnauthorizedResult();
_moduleService.EnableFeatures(selection);
//todo: (heskew) need better messages
//todo: (heskew) hmmm...need a helper to comma-separate all but last, which would get the " and " treatment...all localized, of course
Services.Notifier.Information(T("{0} were enabled", string.Join(", ", selection.ToArray())));
break;
case FeaturesBulkAction.Disable:
if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to disable features")))
return new HttpUnauthorizedResult();
_moduleService.DisableFeatures(selection);
//todo: (heskew) need better messages
Services.Notifier.Information(T("{0} were disabled", string.Join(", ", selection.ToArray())));
break;
default:
throw new ArgumentOutOfRangeException();
}
}
return RedirectToAction("Features");
} }
[ValidateAntiForgeryTokenOrchard] [ValidateAntiForgeryTokenOrchard]
@@ -107,7 +75,7 @@ namespace Orchard.Modules.Controllers {
return new NotFoundResult(); return new NotFoundResult();
_moduleService.DisableFeatures(new[] { featureName }); _moduleService.DisableFeatures(new[] { featureName });
Services.Notifier.Information(T("{0} was disabled", featureName)); //Services.Notifier.Information(T("{0} was disabled", featureName));
return RedirectToAction("Features"); return RedirectToAction("Features");
} }

View File

@@ -48,10 +48,11 @@ namespace Orchard.Modules.Services {
} }
public IEnumerable<IModuleFeature> GetAvailableFeatures() { public IEnumerable<IModuleFeature> GetAvailableFeatures() {
var enabledFeatures = _shellDescriptorManager.GetShellDescriptor().EnabledFeatures; var enabledFeatures = _shellDescriptorManager.GetShellDescriptor().EnabledFeatures.ToList();
return GetInstalledModules() return GetInstalledModules()
.SelectMany(m => _extensionManager.LoadFeatures(m.Features)) .SelectMany(m => _extensionManager.LoadFeatures(m.Features))
.Select(f => AssembleModuleFromDescriptor(f, enabledFeatures.FirstOrDefault(sf => string.Equals(sf.Name, f.Descriptor.Name, StringComparison.OrdinalIgnoreCase)) != null)); .Select(f => AssembleModuleFromDescriptor(f, enabledFeatures.FirstOrDefault(sf => string.Equals(sf.Name, f.Descriptor.Name, StringComparison.OrdinalIgnoreCase)) != null))
.ToList();
} }
public IEnumerable<Feature> GetAvailableFeaturesByModule(string moduleName) { public IEnumerable<Feature> GetAvailableFeaturesByModule(string moduleName) {
@@ -69,9 +70,20 @@ namespace Orchard.Modules.Services {
public void DisableFeatures(IEnumerable<string> featureNames) { public void DisableFeatures(IEnumerable<string> featureNames) {
var shellDescriptor = _shellDescriptorManager.GetShellDescriptor(); var shellDescriptor = _shellDescriptorManager.GetShellDescriptor();
var enabledFeatures = shellDescriptor.EnabledFeatures.ToList(); var enabledFeatures = shellDescriptor.EnabledFeatures.ToList();
enabledFeatures.RemoveAll(f => featureNames.Contains(f.Name)); var features = GetAvailableFeatures();
foreach (var featureName in featureNames) {
var feature = featureName;
var dependants = features.Where(f => f.IsEnabled && f.Descriptor.Dependencies != null && f.Descriptor.Dependencies.Contains(feature));
if (dependants.Count() == 0) {
enabledFeatures.RemoveAll(f => f.Name == feature);
}
else {
// list what else will be disabled with ok/cancel
}
}
_shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures, shellDescriptor.Parameters); _shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures, shellDescriptor.Parameters);
} }

View File

@@ -4,16 +4,5 @@ using Orchard.Mvc.ViewModels;
namespace Orchard.Modules.ViewModels { namespace Orchard.Modules.ViewModels {
public class FeaturesViewModel : BaseViewModel { public class FeaturesViewModel : BaseViewModel {
public IEnumerable<IModuleFeature> Features { get; set; } public IEnumerable<IModuleFeature> Features { get; set; }
public FeaturesOptions Options { get; set; }
}
public class FeaturesOptions {
public FeaturesBulkAction BulkAction { get; set; }
}
public enum FeaturesBulkAction {
None,
Enable,
Disable
} }
} }

View File

@@ -2,62 +2,53 @@
<%@ Import Namespace="Orchard.Mvc.Html"%> <%@ Import Namespace="Orchard.Mvc.Html"%>
<%@ Import Namespace="Orchard.Modules.ViewModels"%> <%@ Import Namespace="Orchard.Modules.ViewModels"%>
<h1><%=Html.TitleForPage(T("Manage Features").ToString()) %></h1> <h1><%=Html.TitleForPage(T("Manage Features").ToString()) %></h1>
<div class="manage" style="visibility:hidden"><%=Html.ActionLink(T("∞").ToString(), "Features", new { }, new { @class = "button primaryAction" })%></div> <% if (Model.Features.Count() > 0) { %>
<% if (Model.Features.Count() > 0) { <ul class="contentItems"><%
var featureGroups = Model.Features.OrderBy(f => f.Descriptor.Category).GroupBy(f => f.Descriptor.Category);
using (Html.BeginFormAntiForgeryPost()) { %> foreach (var featureGroup in featureGroups) { %>
<%=Html.ValidationSummary()%> <li<%=featureGroup == featureGroups.Last() ? " class=\"last\"" : "" %>>
<fieldset class="actions bulk"> <h2><%=Html.Encode(featureGroup.First().Descriptor.Category ?? T("Uncategorized")) %></h2>
<label for="publishActions"><%=_Encoded("Actions: ")%></label> <ul><%
<select id="publishActions" name="<%=Html.NameOf(m => m.Options.BulkAction) %>"> var features = featureGroup.OrderBy(f => f.Descriptor.Name);
<%=Html.SelectOption(Model.Options.BulkAction, FeaturesBulkAction.None, _Encoded("Choose action...").ToString())%> foreach (var feature in features) {%>
<%=Html.SelectOption(Model.Options.BulkAction, FeaturesBulkAction.Enable, _Encoded("Enable").ToString())%> <li<%=feature == features.Last() ? " class=\"last\"" : "" %> id="<%=Html.Encode(feature.Descriptor.Name) %>">
<%=Html.SelectOption(Model.Options.BulkAction, FeaturesBulkAction.Disable, _Encoded("Disable").ToString())%> <div class="summary">
</select> <div class="properties">
<input class="button" type="submit" name="submit.BulkEdit" value="<%=_Encoded("Apply") %>" /> <h3><%=Html.Encode(feature.Descriptor.Name) %></h3>
</fieldset> <ul class="pageStatus">
<fieldset class="pageList"> <li><%
<ul class="contentItems"><% //enabled or not
foreach (var featureGroup in Model.Features.OrderBy(f => f.Descriptor.Category).GroupBy(f => f.Descriptor.Category)) { %> if (feature.IsEnabled) { %>
<li<%=featureGroup == Model.Features.Last() ? " class=\"last\"" : "" %>> <img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Modules/Content/Admin/images/enabled.gif") %>" alt="<%=_Encoded("Enabled") %>" title="<%=_Encoded("This feature is currently enabled") %>" /><%=_Encoded("Enabled") %><%
<h2><%=Html.Encode(featureGroup.First().Descriptor.Category ?? T("Uncategorized")) %></h2> }
<ul><% else { %>
foreach (var feature in featureGroup.OrderBy(f => f.Descriptor.Name)) {%> <img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Modules/Content/Admin/images/disabled.gif") %>" alt="<%=_Encoded("Disabled") %>" title="<%=_Encoded("This feature is currently disabled") %>" /><%=_Encoded("Disabled")%><%
<li<%=feature == featureGroup.Last() ? " class=\"last\"" : "" %> id="<%=Html.Encode(feature.Descriptor.Name) %>"> } %>
<div class="summary"> </li><%
<div class="properties"> //dependencies
<input type="checkbox" name="selection" value="<%=Html.Encode(feature.Descriptor.Name) %>" /> if (feature.Descriptor.Dependencies != null && feature.Descriptor.Dependencies.Count() > 0) { %>
<h3><%=Html.Encode(feature.Descriptor.Name) %></h3> <li>&nbsp;&#124;&nbsp;<%=T("Depends on: {0}", string.Join(", ", feature.Descriptor.Dependencies.Select(s => Html.Link(Html.Encode(s), string.Format("{0}#{1}", Url.Action("features", new { area = "Orchard.Modules" }), Html.Encode(s)))).OrderBy(s => s).ToArray())) %></li><%
<ul class="pageStatus"> } %>
<li><% </ul>
//enabled or not </div>
if (feature.IsEnabled) { %> <div class="related"><%
<img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Modules/Content/Admin/images/enabled.gif") %>" alt="<%=_Encoded("Enabled") %>" title="<%=_Encoded("This feature is currently enabled") %>" /><%=_Encoded("Enabled") %><% if (feature.IsEnabled) {
} using (Html.BeginFormAntiForgeryPost(string.Format("{0}#{1}", Url.Action("Disable", new { area = "Orchard.Modules" }), Html.AttributeEncode(feature.Descriptor.Name)), FormMethod.Post, new {@class = "inline link"})) { %>
else { %> <%=Html.Hidden("featureName", feature.Descriptor.Name) %>
<img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Modules/Content/Admin/images/disabled.gif") %>" alt="<%=_Encoded("Disabled") %>" title="<%=_Encoded("This feature is currently disabled") %>" /><%=_Encoded("Disabled")%><% <button type="submit"><%=_Encoded("Disable") %></button><%
} %> }
</li><% } else {
//dependencies using (Html.BeginFormAntiForgeryPost(string.Format("{0}#{1}", Url.Action("Enable", new { area = "Orchard.Modules" }), Html.AttributeEncode(feature.Descriptor.Name)), FormMethod.Post, new {@class = "inline link"})) { %>
if (feature.Descriptor.Dependencies != null && feature.Descriptor.Dependencies.Count() > 0) { %> <%=Html.Hidden("featureName", feature.Descriptor.Name) %>
<li>&nbsp;&#124;&nbsp;<%=T("Depends on: {0}", string.Join(", ", feature.Descriptor.Dependencies.Select(s => Html.Link(Html.Encode(s), string.Format("{0}#{1}", Url.Action("features", new { area = "Orchard.Modules" }), Html.Encode(s)))).OrderBy(s => s).ToArray())) %></li><% <button type="submit"><%=_Encoded("Enable") %></button><%
} %> }
</ul> } %>
</div> </div>
<div class="related"><% </div>
if (feature.IsEnabled) { %>
<a href="<%=Html.AntiForgeryTokenGetUrl(Url.Action("Disable", new { featureName = feature.Descriptor.Name, area = "Orchard.Modules" })) %>"><%=_Encoded("Disable") %></a><%
} else { %>
<a href="<%=Html.AntiForgeryTokenGetUrl(Url.Action("Enable", new { featureName = feature.Descriptor.Name, area = "Orchard.Modules" })) %>"><%=_Encoded("Enable") %></a><%
} %>
</div>
</div>
</li><%
} %>
</ul>
</li><% </li><%
} %> } %>
</ul><% </ul>
} %> </li><%
</fieldset><% } %>
</ul><%
} %> } %>