From f89dfbf715359b4cee2c75a57a4c23c16618dab2 Mon Sep 17 00:00:00 2001 From: Lombiq Date: Sat, 10 Dec 2016 01:43:36 +0100 Subject: [PATCH] Extensions are not enabled any more if they can't actually be loaded, fixes #2437 --- .../Migrations/SchemaCommandGeneratorTests.cs | 4 ++ .../ExtensionLoaderCoordinatorTests.cs | 4 ++ .../Extensions/ExtensionManagerTests.cs | 4 ++ .../Controllers/AdminController.cs | 38 ++++++++++++------- .../Extensions/Loaders/CoreExtensionLoader.cs | 13 ++++++- .../Loaders/DynamicExtensionLoader.cs | 14 +++++++ .../Extensions/Loaders/ExtensionLoaderBase.cs | 2 + .../Extensions/Loaders/IExtensionLoader.cs | 2 + .../Loaders/PrecompiledExtensionLoader.cs | 13 ++++++- .../Loaders/RawThemeExtensionLoader.cs | 17 +++++++-- .../Loaders/ReferencedExtensionLoader.cs | 11 ++++++ .../Environment/Features/FeatureManager.cs | 26 +++++++++++-- .../Environment/Features/IFeatureManager.cs | 8 ++++ 13 files changed, 133 insertions(+), 23 deletions(-) diff --git a/src/Orchard.Tests.Modules/Migrations/SchemaCommandGeneratorTests.cs b/src/Orchard.Tests.Modules/Migrations/SchemaCommandGeneratorTests.cs index ef1bcfbe5..16e52356a 100644 --- a/src/Orchard.Tests.Modules/Migrations/SchemaCommandGeneratorTests.cs +++ b/src/Orchard.Tests.Modules/Migrations/SchemaCommandGeneratorTests.cs @@ -194,6 +194,10 @@ Features: throw new NotImplementedException(); } + public bool LoaderIsSuitable(ExtensionDescriptor descriptor) { + throw new NotImplementedException(); + } + #endregion } diff --git a/src/Orchard.Tests/Environment/Extensions/ExtensionLoaderCoordinatorTests.cs b/src/Orchard.Tests/Environment/Extensions/ExtensionLoaderCoordinatorTests.cs index b2b18afce..e1fe8cb94 100644 --- a/src/Orchard.Tests/Environment/Extensions/ExtensionLoaderCoordinatorTests.cs +++ b/src/Orchard.Tests/Environment/Extensions/ExtensionLoaderCoordinatorTests.cs @@ -114,6 +114,10 @@ namespace Orchard.Tests.Environment.Extensions { throw new NotImplementedException(); } + public bool LoaderIsSuitable(ExtensionDescriptor descriptor) { + throw new NotImplementedException(); + } + #endregion } diff --git a/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs b/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs index 88cd472a1..29b96b8d4 100644 --- a/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs +++ b/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs @@ -119,6 +119,10 @@ namespace Orchard.Tests.Environment.Extensions { throw new NotImplementedException(); } + public bool LoaderIsSuitable(ExtensionDescriptor descriptor) { + throw new NotImplementedException(); + } + #endregion } diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs index 20e6061db..3f7247cdf 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs @@ -46,8 +46,7 @@ namespace Orchard.Modules.Controllers { IRecipeManager recipeManager, ShellDescriptor shellDescriptor, ShellSettings shellSettings, - IShapeFactory shapeFactory) - { + IShapeFactory shapeFactory) { Services = services; _extensionDisplayEventHandler = extensionDisplayEventHandlers.FirstOrDefault(); _moduleService = moduleService; @@ -77,7 +76,7 @@ namespace Orchard.Modules.Controllers { IEnumerable modules = _extensionManager.AvailableExtensions() .Where(extensionDescriptor => DefaultExtensionTypes.IsModule(extensionDescriptor.ExtensionType) && - + (string.IsNullOrEmpty(options.SearchText) || extensionDescriptor.Name.ToLowerInvariant().Contains(options.SearchText.ToLowerInvariant()))) .OrderBy(extensionDescriptor => extensionDescriptor.Name) .Select(extensionDescriptor => new ModuleEntry { Descriptor = extensionDescriptor }); @@ -158,13 +157,13 @@ namespace Orchard.Modules.Controllers { try { _recipeManager.Execute(recipe); } - catch(Exception e) { + catch (Exception e) { Logger.Error(e, "Error while executing recipe {0} in {1}", moduleId, name); Services.Notifier.Error(T("Recipes {0} contains unsupported module installation steps.", recipe.Name)); } Services.Notifier.Success(T("The recipe {0} was executed successfully.", recipe.Name)); - + return RedirectToAction("Recipes"); } @@ -177,15 +176,15 @@ namespace Orchard.Modules.Controllers { IEnumerable features = _featureManager.GetAvailableFeatures() .Where(f => !DefaultExtensionTypes.IsTheme(f.Extension.ExtensionType)) .Select(f => new ModuleFeature { - Descriptor = f, - IsEnabled = _shellDescriptor.Features.Any(sf => sf.Name == f.Id), - IsRecentlyInstalled = _moduleService.IsRecentlyInstalled(f.Extension), - NeedsUpdate = featuresThatNeedUpdate.Contains(f.Id), - DependentFeatures = _moduleService.GetDependentFeatures(f.Id).Where(x => x.Id != f.Id).ToList() - }) + Descriptor = f, + IsEnabled = _shellDescriptor.Features.Any(sf => sf.Name == f.Id), + IsRecentlyInstalled = _moduleService.IsRecentlyInstalled(f.Extension), + NeedsUpdate = featuresThatNeedUpdate.Contains(f.Id), + DependentFeatures = _moduleService.GetDependentFeatures(f.Id).Where(x => x.Id != f.Id).ToList() + }) .ToList(); - return View(new FeaturesViewModel { + return View(new FeaturesViewModel { Features = features, IsAllowed = ExtensionIsAllowed }); @@ -212,13 +211,13 @@ namespace Orchard.Modules.Controllers { case FeaturesBulkAction.None: break; case FeaturesBulkAction.Enable: - _moduleService.EnableFeatures(disabledFeatures, force == true); + EnableFeatures(disabledFeatures, force == true); break; case FeaturesBulkAction.Disable: _moduleService.DisableFeatures(enabledFeatures, force == true); break; case FeaturesBulkAction.Toggle: - _moduleService.EnableFeatures(disabledFeatures, force == true); + EnableFeatures(disabledFeatures, force == true); _moduleService.DisableFeatures(enabledFeatures, force == true); break; case FeaturesBulkAction.Update: @@ -250,5 +249,16 @@ namespace Orchard.Modules.Controllers { private bool ExtensionIsAllowed(ExtensionDescriptor extensionDescriptor) { return _shellSettings.Modules.Length == 0 || _shellSettings.Modules.Contains(extensionDescriptor.Id); } + + private void EnableFeatures(List disabledFeatures, bool force) { + foreach (var feature in disabledFeatures) { + if (_featureManager.HasLoader(feature)) { + _moduleService.EnableFeatures(disabledFeatures, force); + } + else { + Services.Notifier.Error(T("No loader found for feature's (\"{0}\") exension!", feature)); + } + } + } } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Loaders/CoreExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/CoreExtensionLoader.cs index 66ff0f36b..85523e9e3 100644 --- a/src/Orchard/Environment/Extensions/Loaders/CoreExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/CoreExtensionLoader.cs @@ -11,10 +11,12 @@ namespace Orchard.Environment.Extensions.Loaders { public class CoreExtensionLoader : ExtensionLoaderBase { private const string CoreAssemblyName = "Orchard.Core"; private readonly IAssemblyLoader _assemblyLoader; + private readonly IDependenciesFolder _dependenciesFolder; - public CoreExtensionLoader(IDependenciesFolder dependenciesFolder, IAssemblyLoader assemblyLoader) + public CoreExtensionLoader(IAssemblyLoader assemblyLoader, IDependenciesFolder dependenciesFolder) : base(dependenciesFolder) { _assemblyLoader = assemblyLoader; + _dependenciesFolder = dependenciesFolder; Logger = NullLogger.Instance; } @@ -59,6 +61,15 @@ namespace Orchard.Environment.Extensions.Loaders { }; } + public override bool LoaderIsSuitable(ExtensionDescriptor descriptor) { + var dependency = _dependenciesFolder.GetDescriptor(descriptor.Id); + if (dependency != null && dependency.LoaderName == this.Name) { + return _assemblyLoader.Load(CoreAssemblyName) != null; + } + + return false; + } + private static bool IsTypeFromModule(Type type, ExtensionDescriptor descriptor) { return (type.Namespace + ".").StartsWith(CoreAssemblyName + "." + descriptor.Id + "."); } diff --git a/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs index 9e4a7706f..3fda23a3d 100644 --- a/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs @@ -212,6 +212,20 @@ namespace Orchard.Environment.Extensions.Loaders { return dependencies; } + public override bool LoaderIsSuitable(ExtensionDescriptor descriptor) { + var dependency = _dependenciesFolder.GetDescriptor(descriptor.Id); + if (dependency != null && dependency.LoaderName == this.Name) { + var projectPath = GetProjectPath(descriptor); + if (projectPath == null) { + return false; + } + + return _buildManager.GetCompiledAssembly(projectPath) != null; + } + + return false; + } + private void AddDependencies(string projectPath, HashSet currentSet) { // Skip files from locations other than "~/Modules" and "~/Themes" etc. if (string.IsNullOrEmpty(PrefixMatch(projectPath, _extensionsVirtualPathPrefixes))) { diff --git a/src/Orchard/Environment/Extensions/Loaders/ExtensionLoaderBase.cs b/src/Orchard/Environment/Extensions/Loaders/ExtensionLoaderBase.cs index 5ee4ac69e..1a547784a 100644 --- a/src/Orchard/Environment/Extensions/Loaders/ExtensionLoaderBase.cs +++ b/src/Orchard/Environment/Extensions/Loaders/ExtensionLoaderBase.cs @@ -57,5 +57,7 @@ namespace Orchard.Environment.Extensions.Loaders { public virtual IEnumerable GetVirtualPathDependencies(DependencyDescriptor dependency) { return Enumerable.Empty(); } + + public abstract bool LoaderIsSuitable(ExtensionDescriptor descriptor); } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Loaders/IExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/IExtensionLoader.cs index ec201dfe3..591a1f6b3 100644 --- a/src/Orchard/Environment/Extensions/Loaders/IExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/IExtensionLoader.cs @@ -59,5 +59,7 @@ namespace Orchard.Environment.Extensions.Loaders { /// For example, Razor or WebForms views needs to be recompiled when a dependency of a module changes. /// IEnumerable GetVirtualPathDependencies(DependencyDescriptor dependency); + + bool LoaderIsSuitable(ExtensionDescriptor descriptor); } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs index d86f4e1bb..ce8dc04c0 100644 --- a/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs @@ -19,6 +19,7 @@ namespace Orchard.Environment.Extensions.Loaders { private readonly IAssemblyProbingFolder _assemblyProbingFolder; private readonly IVirtualPathProvider _virtualPathProvider; private readonly IVirtualPathMonitor _virtualPathMonitor; + private readonly IDependenciesFolder _dependenciesFolder; public PrecompiledExtensionLoader( IHostEnvironment hostEnvironment, @@ -31,6 +32,7 @@ namespace Orchard.Environment.Extensions.Loaders { _assemblyProbingFolder = assemblyProbingFolder; _virtualPathProvider = virtualPathProvider; _virtualPathMonitor = virtualPathMonitor; + _dependenciesFolder = dependenciesFolder; Logger = NullLogger.Instance; } @@ -166,7 +168,7 @@ namespace Orchard.Environment.Extensions.Loaders { Loader = this, Name = Path.GetFileNameWithoutExtension(path), VirtualPath = path - } ) + }) .ToList(); Logger.Information("Done probing references for module '{0}'", descriptor.Id); @@ -244,5 +246,14 @@ namespace Orchard.Environment.Extensions.Loaders { return assemblyPath; } + + public override bool LoaderIsSuitable(ExtensionDescriptor descriptor) { + var dependency = _dependenciesFolder.GetDescriptor(descriptor.Id); + if (dependency != null && dependency.LoaderName == this.Name) { + return _assemblyProbingFolder.AssemblyExists(descriptor.Id); + } + + return false; + } } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Loaders/RawThemeExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/RawThemeExtensionLoader.cs index c173274dc..47eb75ddc 100644 --- a/src/Orchard/Environment/Extensions/Loaders/RawThemeExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/RawThemeExtensionLoader.cs @@ -8,10 +8,12 @@ using Orchard.Logging; namespace Orchard.Environment.Extensions.Loaders { public class RawThemeExtensionLoader : ExtensionLoaderBase { private readonly IVirtualPathProvider _virtualPathProvider; + private readonly IDependenciesFolder _dependenciesFolder; public RawThemeExtensionLoader(IDependenciesFolder dependenciesFolder, IVirtualPathProvider virtualPathProvider) : base(dependenciesFolder) { _virtualPathProvider = virtualPathProvider; + _dependenciesFolder = dependenciesFolder; Logger = NullLogger.Instance; } @@ -26,12 +28,12 @@ namespace Orchard.Environment.Extensions.Loaders { return null; // Temporary - theme without own project should be under ~/themes - if (descriptor.Location.StartsWith("~/Themes",StringComparison.InvariantCultureIgnoreCase)) { + if (descriptor.Location.StartsWith("~/Themes", StringComparison.InvariantCultureIgnoreCase)) { string projectPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Id, descriptor.Id + ".csproj"); // ignore themes including a .csproj in this loader - if ( _virtualPathProvider.FileExists(projectPath) ) { + if (_virtualPathProvider.FileExists(projectPath)) { return null; } @@ -39,7 +41,7 @@ namespace Orchard.Environment.Extensions.Loaders { descriptor.Id + ".dll"); // ignore themes with /bin in this loader - if ( _virtualPathProvider.FileExists(assemblyPath) ) + if (_virtualPathProvider.FileExists(assemblyPath)) return null; return new ExtensionProbeEntry { @@ -64,5 +66,14 @@ namespace Orchard.Environment.Extensions.Loaders { ExportedTypes = new Type[0] }; } + + public override bool LoaderIsSuitable(ExtensionDescriptor descriptor) { + var dependency = _dependenciesFolder.GetDescriptor(descriptor.Id); + if (dependency != null && dependency.LoaderName == this.Name) { + return true; + } + + return false; + } } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Loaders/ReferencedExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/ReferencedExtensionLoader.cs index 7db73b223..86a236faa 100644 --- a/src/Orchard/Environment/Extensions/Loaders/ReferencedExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/ReferencedExtensionLoader.cs @@ -13,12 +13,14 @@ namespace Orchard.Environment.Extensions.Loaders { public class ReferencedExtensionLoader : ExtensionLoaderBase { private readonly IVirtualPathProvider _virtualPathProvider; private readonly IBuildManager _buildManager; + private readonly IDependenciesFolder _dependenciesFolder; public ReferencedExtensionLoader(IDependenciesFolder dependenciesFolder, IVirtualPathProvider virtualPathProvider, IBuildManager buildManager) : base(dependenciesFolder) { _virtualPathProvider = virtualPathProvider; _buildManager = buildManager; + _dependenciesFolder = dependenciesFolder; Logger = NullLogger.Instance; } @@ -82,5 +84,14 @@ namespace Orchard.Environment.Extensions.Loaders { ExportedTypes = assembly.GetExportedTypes() }; } + + public override bool LoaderIsSuitable(ExtensionDescriptor descriptor) { + var dependency = _dependenciesFolder.GetDescriptor(descriptor.Id); + if (dependency != null && dependency.LoaderName == this.Name) { + return _buildManager.GetReferencedAssembly(descriptor.Id) != null; + } + + return false; + } } } diff --git a/src/Orchard/Environment/Features/FeatureManager.cs b/src/Orchard/Environment/Features/FeatureManager.cs index 2cdc3fffb..417186578 100644 --- a/src/Orchard/Environment/Features/FeatureManager.cs +++ b/src/Orchard/Environment/Features/FeatureManager.cs @@ -4,7 +4,7 @@ using System.Linq; using Orchard.Environment.Descriptor; using Orchard.Environment.Descriptor.Models; using Orchard.Environment.Extensions; -using Orchard.Environment.Extensions.Helpers; +using Orchard.Environment.Extensions.Loaders; using Orchard.Environment.Extensions.Models; using Orchard.Localization; using Orchard.Logging; @@ -13,6 +13,7 @@ namespace Orchard.Environment.Features { public class FeatureManager : IFeatureManager { private readonly IExtensionManager _extensionManager; private readonly IShellDescriptorManager _shellDescriptorManager; + private readonly IEnumerable _loaders; /// /// Delegate to notify about feature dependencies. @@ -21,10 +22,11 @@ namespace Orchard.Environment.Features { public FeatureManager( IExtensionManager extensionManager, - IShellDescriptorManager shellDescriptorManager) { + IShellDescriptorManager shellDescriptorManager, + IEnumerable loaders) { _extensionManager = extensionManager; _shellDescriptorManager = shellDescriptorManager; - + _loaders = loaders; T = NullLocalizer.Instance; Logger = NullLogger.Instance; } @@ -161,6 +163,22 @@ namespace Orchard.Environment.Features { return GetAffectedFeatures(featureId, availableFeatures, getEnabledDependants); } + public bool HasLoader(string featureId) { + var descriptor = _extensionManager + .AvailableExtensions() + .Where(d => DefaultExtensionTypes.IsModule(d.ExtensionType) || DefaultExtensionTypes.IsTheme(d.ExtensionType)) + .OrderBy(d => d.Id) + .FirstOrDefault(e => e.Id == featureId || e.Features.Select(f => f.Id).Contains(featureId)); + + foreach (var loader in _loaders) { + if (loader.LoaderIsSuitable(descriptor)) { + return true; + } + } + + return false; + } + /// /// Enables a feature. /// @@ -228,7 +246,7 @@ namespace Orchard.Environment.Features { } private static IEnumerable GetAffectedFeatures( - string featureId, IDictionary features, + string featureId, IDictionary features, Func, IDictionary> getAffectedDependencies) { var dependencies = new HashSet(StringComparer.OrdinalIgnoreCase) { featureId }; diff --git a/src/Orchard/Environment/Features/IFeatureManager.cs b/src/Orchard/Environment/Features/IFeatureManager.cs index abb255c54..8063d3aca 100644 --- a/src/Orchard/Environment/Features/IFeatureManager.cs +++ b/src/Orchard/Environment/Features/IFeatureManager.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Orchard.Environment.Extensions.Models; +using Orchard.Environment.Extensions; namespace Orchard.Environment.Features { public delegate void FeatureDependencyNotificationHandler(string messageFormat, string featureId, IEnumerable featureIds); @@ -61,5 +62,12 @@ namespace Orchard.Environment.Features { /// ID of the feature to check. /// An enumeration with dependent feature IDs. IEnumerable GetDependentFeatures(string featureId); + + /// + /// Checks whether the feature's extension has a loadee, i.e. a suitable can be found. + /// + /// ID of the feature to check. + /// True if a suitable can be found. + bool HasLoader(string featureId); } } \ No newline at end of file