using System; using System.Collections.Generic; using System.Linq; using System.Web; using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Models; using Orchard.Environment.Descriptor; using Orchard.Environment.Descriptor.Models; using Orchard.Localization; using Orchard.Modules.Models; using Orchard.UI.Notify; namespace Orchard.Modules.Services { public class ModuleService : IModuleService { private const string ModuleExtensionType = "module"; private readonly IExtensionManager _extensionManager; private readonly IShellDescriptorManager _shellDescriptorManager; private readonly IWorkContextAccessor _workContextAccessor; public ModuleService( IOrchardServices orchardServices, IExtensionManager extensionManager, IShellDescriptorManager shellDescriptorManager, IWorkContextAccessor workContextAccessor) { Services = orchardServices; _extensionManager = extensionManager; _shellDescriptorManager = shellDescriptorManager; _workContextAccessor = workContextAccessor; T = NullLocalizer.Instance; } public Localizer T { get; set; } public IOrchardServices Services { get; set; } public IModule GetModuleByName(string moduleName) { return _extensionManager .AvailableExtensions() .Where(e => string.Equals(e.Name, moduleName, StringComparison.OrdinalIgnoreCase)) .Where(e => 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)); } public void InstallModule(HttpPostedFileBase file) { _extensionManager.InstallExtension(ModuleExtensionType, file); } public void UninstallModule(string moduleName) { _extensionManager.UninstallExtension(ModuleExtensionType, moduleName); } public IEnumerable GetAvailableFeatures() { var enabledFeatures = _shellDescriptorManager.GetShellDescriptor().Features; return _extensionManager.AvailableExtensions() .SelectMany(m => _extensionManager.LoadFeatures(m.Features)) .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.Features.ToList(); var featuresToEnable = features.Select(s => EnableFeature(s, GetAvailableFeatures(), force)). SelectMany(ies => ies.Select(s => s)); 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); } public void DisableFeatures(IEnumerable featureNames) { DisableFeatures(featureNames, false); } public void DisableFeatures(IEnumerable features, bool force) { var shellDescriptor = _shellDescriptorManager.GetShellDescriptor(); var enabledFeatures = shellDescriptor.Features.ToList(); var availableFeatures = GetAvailableFeatures().ToList(); var featuresToDisable = features.Select(s => DisableFeature(s, availableFeatures, force)).SelectMany( ies => ies.Select(s => s)); 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); } 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 string TryLocalize(string key, string original, Localizer localizer) { var localized = localizer(key).Text; if(key == localized) { // no specific localization available return original; } return localized; } private IModule AssembleModuleFromDescriptor(ExtensionDescriptor extensionDescriptor) { var localizer = LocalizationUtilities.Resolve(_workContextAccessor.GetContext(), String.Concat(extensionDescriptor.Location, "/", extensionDescriptor.Name, "/Module.txt")); return new Module { ModuleName = extensionDescriptor.Name, DisplayName = TryLocalize("Name", extensionDescriptor.DisplayName, localizer), Description = TryLocalize("Description", extensionDescriptor.Description, localizer), Version = extensionDescriptor.Version, Author = TryLocalize("Author", extensionDescriptor.Author, localizer), HomePage = TryLocalize("Website", extensionDescriptor.WebSite, localizer), Tags = TryLocalize("Tags", extensionDescriptor.Tags, localizer), Features = extensionDescriptor.Features.Select(f => new FeatureDescriptor { Category = TryLocalize(f.Name + " Category", f.Category, localizer), Dependencies = f.Dependencies, Description = TryLocalize(f.Description + " Description", f.Description, localizer), Extension = f.Extension, Name = f.Name }) }; } private static IModuleFeature AssembleModuleFromDescriptor(Feature feature, bool isEnabled) { return new ModuleFeature { Descriptor = feature.Descriptor, IsEnabled = isEnabled }; } } }