diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs index 84060a88f..490bb121f 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs @@ -1,11 +1,8 @@ using System.Linq; -using System.Reflection; using System.Web.Mvc; using Orchard.Localization; using Orchard.Modules.ViewModels; -using Orchard.Mvc.AntiForgery; using Orchard.Mvc.Results; -using Orchard.UI.Notify; namespace Orchard.Modules.Controllers { public class AdminController : Controller { @@ -37,9 +34,7 @@ namespace Orchard.Modules.Controllers { if (module == null) return new NotFoundResult(); - return View(new ModuleEditViewModel { - Name = module.DisplayName - }); + return View(new ModuleEditViewModel {Name = module.DisplayName}); } public ActionResult Features() { @@ -50,43 +45,30 @@ namespace Orchard.Modules.Controllers { return View(new FeaturesViewModel {Features = features}); } - [ValidateAntiForgeryTokenOrchard] - public ActionResult Enable(string id) { + [HttpPost] + public ActionResult Enable(string id, bool? force) { if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features"))) return new HttpUnauthorizedResult(); if (string.IsNullOrEmpty(id)) return new NotFoundResult(); - _moduleService.EnableFeatures(new [] {id}); + _moduleService.EnableFeatures(new[] {id}, force != null && (bool) force); return RedirectToAction("Features"); } - [ValidateAntiForgeryTokenOrchard] - public ActionResult Disable(string id) { + [HttpPost] + public ActionResult Disable(string id, bool? force) { if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features"))) return new HttpUnauthorizedResult(); if (string.IsNullOrEmpty(id)) return new NotFoundResult(); - _moduleService.DisableFeatures(new[] { id }); + _moduleService.DisableFeatures(new[] {id}, force != null && (bool) force); return RedirectToAction("Features"); } - - private class FormValueRequiredAttribute : ActionMethodSelectorAttribute { - private readonly string _submitButtonName; - - public FormValueRequiredAttribute(string submitButtonName) { - _submitButtonName = submitButtonName; - } - - public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { - var value = controllerContext.HttpContext.Request.Form[_submitButtonName]; - return !string.IsNullOrEmpty(value); - } - } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs b/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs index cfd21bee2..917d318ae 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs @@ -16,7 +16,8 @@ namespace Orchard.Modules.Services { private readonly IExtensionManager _extensionManager; private readonly IShellDescriptorManager _shellDescriptorManager; - public ModuleService(IOrchardServices orchardServices,IExtensionManager extensionManager, IShellDescriptorManager shellDescriptorManager) { + public ModuleService(IOrchardServices orchardServices, IExtensionManager extensionManager, + IShellDescriptorManager shellDescriptorManager) { Services = orchardServices; _extensionManager = extensionManager; _shellDescriptorManager = shellDescriptorManager; @@ -27,15 +28,20 @@ namespace Orchard.Modules.Services { public IOrchardServices Services { get; set; } public IModule GetModuleByName(string moduleName) { - return _extensionManager.AvailableExtensions().Where(e => string.Equals(e.Name, moduleName, StringComparison.OrdinalIgnoreCase) && string.Equals(e.ExtensionType, ModuleExtensionType, StringComparison.OrdinalIgnoreCase)).Select( - descriptor => AssembleModuleFromDescriptor(descriptor)).FirstOrDefault(); + return + _extensionManager.AvailableExtensions().Where( + e => + string.Equals(e.Name, moduleName, StringComparison.OrdinalIgnoreCase) && + string.Equals(e.ExtensionType, ModuleExtensionType, StringComparison.OrdinalIgnoreCase)).Select( + descriptor => AssembleModuleFromDescriptor(descriptor)).FirstOrDefault(); } public IEnumerable GetInstalledModules() { return _extensionManager.AvailableExtensions().Where( - e => String.Equals(e.ExtensionType, ModuleExtensionType, StringComparison.OrdinalIgnoreCase)).Select( - descriptor => AssembleModuleFromDescriptor(descriptor)); + e => String.Equals(e.ExtensionType, ModuleExtensionType, StringComparison.OrdinalIgnoreCase)).Select + ( + descriptor => AssembleModuleFromDescriptor(descriptor)); } public void InstallModule(HttpPostedFileBase file) { @@ -46,86 +52,145 @@ namespace Orchard.Modules.Services { _extensionManager.UninstallExtension(ModuleExtensionType, moduleName); } - public IModule GetModuleByFeatureName(string featureName) { - return GetInstalledModules() - .Where( - m => - m.Features.FirstOrDefault(f => string.Equals(f.Name, featureName, StringComparison.OrdinalIgnoreCase)) != - null).FirstOrDefault(); - } - public IEnumerable GetAvailableFeatures() { var enabledFeatures = _shellDescriptorManager.GetShellDescriptor().EnabledFeatures; return GetInstalledModules() .SelectMany(m => _extensionManager.LoadFeatures(m.Features)) - .Select(f => AssembleModuleFromDescriptor(f, enabledFeatures.FirstOrDefault(sf => string.Equals(sf.Name, f.Descriptor.Name, StringComparison.OrdinalIgnoreCase)) != null)); - } - - public IEnumerable GetAvailableFeaturesByModule(string moduleName) { - throw new NotImplementedException(); + .Select( + f => + AssembleModuleFromDescriptor(f, + enabledFeatures.FirstOrDefault( + sf => + string.Equals(sf.Name, f.Descriptor.Name, + StringComparison.OrdinalIgnoreCase)) != null)); } public void EnableFeatures(IEnumerable featureNames) { + EnableFeatures(featureNames, false); + } + + public void EnableFeatures(IEnumerable features, bool force) { var shellDescriptor = _shellDescriptorManager.GetShellDescriptor(); var enabledFeatures = shellDescriptor.EnabledFeatures.ToList(); - var features = GetAvailableFeatures().ToList(); - foreach (var name in featureNames) { - var featureName = name; - var feature = features.Single(f => f.Descriptor.Name == featureName); - var sleepingDependencies = - feature.Descriptor.Dependencies.Where(s => enabledFeatures.FirstOrDefault(sf => sf.Name == s) == null); + var featuresToEnable = + features.Select(s => EnableFeature(s, GetAvailableFeatures(), force)). + SelectMany(ies => ies.Select(s => s)); - if (sleepingDependencies.Count() != 0) { - Services.Notifier.Warning(T( - "If you want to enable {0}, then you'll also need {1} (and I won't let you flip everything on in one go yet).", - featureName, - sleepingDependencies.Count() > 1 - ? string.Join("", - sleepingDependencies.Select( - (s, i) => - i == sleepingDependencies.Count() - 2 - ? T("{0} and ", s).ToString() - : T("{0}, ", s).ToString()).ToArray()) - : sleepingDependencies.First())); - } else if (enabledFeatures.FirstOrDefault(f => f.Name == featureName) == null) { - enabledFeatures.Add(new ShellFeature {Name = featureName}); - Services.Notifier.Information(T("{0} was enabled", featureName)); - } + if (featuresToEnable.Count() == 0) + return; + + foreach (var featureToEnable in featuresToEnable) { + enabledFeatures.Add(new ShellFeature {Name = featureToEnable}); + Services.Notifier.Information(T("{0} was enabled", featureToEnable)); } - _shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures, shellDescriptor.Parameters); + _shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures, + shellDescriptor.Parameters); } public void DisableFeatures(IEnumerable featureNames) { + DisableFeatures(featureNames, false); + } + + public void DisableFeatures(IEnumerable features, bool force) { var shellDescriptor = _shellDescriptorManager.GetShellDescriptor(); var enabledFeatures = shellDescriptor.EnabledFeatures.ToList(); - var features = GetAvailableFeatures().ToList(); - foreach (var name in featureNames) { - var featureName = name; - var dependants = features.Where(f => f.IsEnabled && f.Descriptor.Dependencies != null && f.Descriptor.Dependencies.Contains(featureName)); + var featuresToDisable = + features.Select(s => DisableFeature(s, GetAvailableFeatures(), force)).SelectMany( + ies => ies.Select(s => s)); - if (dependants.Count() != 0) { - Services.Notifier.Warning(T( - "If you want to disable {0}, then you'll also lose {1} (and I won't let you do that yet).", - featureName, - dependants.Count() > 0 - ? string.Join("", - dependants.Select( - (f, i) => - i == dependants.Count() - 2 - ? T("{0} and ", f.Descriptor.Name).ToString() - : T("{0}, ", f.Descriptor.Name).ToString()).ToArray()) - : dependants.First().Descriptor.Name)); - } - else { - enabledFeatures.RemoveAll(f => f.Name == featureName); - Services.Notifier.Information(T("{0} was disabled", featureName)); - } + if (featuresToDisable.Count() == 0) + return; + + foreach (var featureToDisable in featuresToDisable) { + var feature = featureToDisable; + enabledFeatures.RemoveAll(f => f.Name == feature); + Services.Notifier.Information(T("{0} was disabled", feature)); } - _shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures, shellDescriptor.Parameters); + _shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures, + shellDescriptor.Parameters); + } + + public IModule GetModuleByFeatureName(string featureName) { + return GetInstalledModules() + .Where( + m => + m.Features.FirstOrDefault( + f => string.Equals(f.Name, featureName, StringComparison.OrdinalIgnoreCase)) != + null).FirstOrDefault(); + } + + private IEnumerable EnableFeature(string featureName, IEnumerable features, bool force) { + var featuresList = features.ToList(); + var getDisabledDependencies = + new Func, IEnumerable>( + (n, fs) => { + var feature = fs.Single(f => f.Descriptor.Name == n); + return feature.Descriptor.Dependencies != null + ? feature.Descriptor.Dependencies.Select( + fn => fs.Single(f => f.Descriptor.Name == fn)).Where(f => !f.IsEnabled) + : Enumerable.Empty(); + }); + + var featuresToEnable = GetAffectedFeatures(featureName, featuresList, getDisabledDependencies); + + if (featuresToEnable.Count() > 1 && !force) { + GenerateWarning("If you want {0} enabled, then you'll also need to enable {1}.", + featureName, + featuresToEnable.Where(fn => fn != featureName)); + return Enumerable.Empty(); + } + + return featuresToEnable; + } + + private IEnumerable DisableFeature(string featureName, IEnumerable features, bool force) { + var featuresList = features.ToList(); + var getEnabledDependants = + new Func, IEnumerable>( + (n, fs) => fs.Where(f => f.IsEnabled && f.Descriptor.Dependencies != null && f.Descriptor.Dependencies.Contains(n))); + + var featuresToDisable = GetAffectedFeatures(featureName, featuresList, getEnabledDependants); + + if (featuresToDisable.Count() > 1 && !force) { + GenerateWarning("If {0} is disabled, then you'll also lose {1}.", + featureName, + featuresToDisable.Where(fn => fn != featureName)); + return Enumerable.Empty(); + } + + return featuresToDisable; + } + + private static IEnumerable GetAffectedFeatures(string featureName, IEnumerable features, Func, IEnumerable> getAffectedDependencies) { + var dependencies = new List {featureName}; + + foreach (var dependency in getAffectedDependencies(featureName, features)) + dependencies.AddRange(GetAffectedFeatures(dependency.Descriptor.Name, features, getAffectedDependencies)); + + return dependencies; + } + + private void GenerateWarning(string messageFormat, string featureName, IEnumerable featuresInQuestion) { + if (featuresInQuestion.Count() < 1) + return; + + Services.Notifier.Warning(T( + messageFormat, + featureName, + featuresInQuestion.Count() > 1 + ? string.Join("", + featuresInQuestion.Select( + (fn, i) => + T(i == featuresInQuestion.Count() - 1 + ? "{0}" + : (i == featuresInQuestion.Count() - 2 + ? "{0} and " + : "{0}, "), fn).ToString()).ToArray()) + : featuresInQuestion.First())); } private static IModule AssembleModuleFromDescriptor(ExtensionDescriptor extensionDescriptor) { diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.ascx b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.ascx index 0aa3f2a9d..c8e348d1b 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.ascx +++ b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.ascx @@ -56,6 +56,22 @@ } } %> +
<% + //temporary forceful feature actions + if (feature.IsEnabled) { + using (Html.BeginFormAntiForgeryPost(string.Format("{0}", Url.Action("Disable", new { area = "Orchard.Modules" })), FormMethod.Post, new {@class = "inline link"})) { %> + <%=Html.Hidden("id", feature.Descriptor.Name, new { id = "" })%> + <%=Html.Hidden("force", true)%> + <% + } + } else { + using (Html.BeginFormAntiForgeryPost(string.Format("{0}", Url.Action("Enable", new { area = "Orchard.Modules" })), FormMethod.Post, new {@class = "inline link"})) { %> + <%=Html.Hidden("id", feature.Descriptor.Name, new { id = "" })%> + <%=Html.Hidden("force", true)%> + <% + } + } %> +
<% } %> diff --git a/src/Orchard.Web/Modules/Orchard.Modules/styles/admin.css b/src/Orchard.Web/Modules/Orchard.Modules/styles/admin.css index bed7b7169..f6afd126a 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/styles/admin.css +++ b/src/Orchard.Web/Modules/Orchard.Modules/styles/admin.css @@ -1,4 +1,9 @@ -.features .category { +.orchard-modules #main h2 { + border:0; + margin-bottom:.2em; +} + +.features .category { overflow:hidden; } .features .feature { @@ -28,8 +33,8 @@ } .features .enabled.feature { - background:#D1F2A5; - border-color:#BCD994; + background:#FFF; + border-color:#CFE493;cfe493 } .features .disabled.feature { background:#EAEAEA; @@ -40,7 +45,7 @@ padding:.4em .5em; } .features .dependencies li, -.features .actions a { +.features .actions { font-size:1.4em; } .features .dependencies { @@ -65,3 +70,16 @@ right:.8em; top:.6em; } + +.cathedral { + bottom:0; + font-size:.8em; + position:absolute; + right:3px; +} +.cathedral a, +.cathedral a:link, +.cathedral a:visited, +.cathedral form.inline.link button { + color:#aeaeae; +} \ No newline at end of file diff --git a/src/Orchard.Web/Themes/TheAdmin/Styles/site.css b/src/Orchard.Web/Themes/TheAdmin/Styles/site.css index 7cdee089c..96a7c5011 100644 --- a/src/Orchard.Web/Themes/TheAdmin/Styles/site.css +++ b/src/Orchard.Web/Themes/TheAdmin/Styles/site.css @@ -337,8 +337,8 @@ span.message { color:#fff; } .info.message { - background:#D1F2A5; /* green*/ - border:1px solid #BCD994; + background:#e6f1c9; /* green */ + border:1px solid #cfe493; color:#062232; } .debug.message { diff --git a/src/Orchard/Modules/IModuleService.cs b/src/Orchard/Modules/IModuleService.cs index d2792315e..a4033bb84 100644 --- a/src/Orchard/Modules/IModuleService.cs +++ b/src/Orchard/Modules/IModuleService.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Web; -using Orchard.Environment.Extensions.Models; namespace Orchard.Modules { public interface IModuleService : IDependency { @@ -8,9 +7,10 @@ namespace Orchard.Modules { IEnumerable GetInstalledModules(); void InstallModule(HttpPostedFileBase file); void UninstallModule(string moduleName); - IModule GetModuleByFeatureName(string featureName); IEnumerable GetAvailableFeatures(); void EnableFeatures(IEnumerable featureNames); + void EnableFeatures(IEnumerable featureNames, bool force); void DisableFeatures(IEnumerable featureNames); + void DisableFeatures(IEnumerable featureNames, bool force); } } \ No newline at end of file