Extensions are not enabled any more if they can't actually be loaded, fixes #2437

This commit is contained in:
Lombiq
2016-12-10 01:43:36 +01:00
committed by Zoltán Lehóczky
parent 80856c2d13
commit f89dfbf715
13 changed files with 133 additions and 23 deletions

View File

@@ -194,6 +194,10 @@ Features:
throw new NotImplementedException(); throw new NotImplementedException();
} }
public bool LoaderIsSuitable(ExtensionDescriptor descriptor) {
throw new NotImplementedException();
}
#endregion #endregion
} }

View File

@@ -114,6 +114,10 @@ namespace Orchard.Tests.Environment.Extensions {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public bool LoaderIsSuitable(ExtensionDescriptor descriptor) {
throw new NotImplementedException();
}
#endregion #endregion
} }

View File

@@ -119,6 +119,10 @@ namespace Orchard.Tests.Environment.Extensions {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public bool LoaderIsSuitable(ExtensionDescriptor descriptor) {
throw new NotImplementedException();
}
#endregion #endregion
} }

View File

@@ -46,8 +46,7 @@ namespace Orchard.Modules.Controllers {
IRecipeManager recipeManager, IRecipeManager recipeManager,
ShellDescriptor shellDescriptor, ShellDescriptor shellDescriptor,
ShellSettings shellSettings, ShellSettings shellSettings,
IShapeFactory shapeFactory) IShapeFactory shapeFactory) {
{
Services = services; Services = services;
_extensionDisplayEventHandler = extensionDisplayEventHandlers.FirstOrDefault(); _extensionDisplayEventHandler = extensionDisplayEventHandlers.FirstOrDefault();
_moduleService = moduleService; _moduleService = moduleService;
@@ -77,7 +76,7 @@ namespace Orchard.Modules.Controllers {
IEnumerable<ModuleEntry> modules = _extensionManager.AvailableExtensions() IEnumerable<ModuleEntry> modules = _extensionManager.AvailableExtensions()
.Where(extensionDescriptor => DefaultExtensionTypes.IsModule(extensionDescriptor.ExtensionType) && .Where(extensionDescriptor => DefaultExtensionTypes.IsModule(extensionDescriptor.ExtensionType) &&
(string.IsNullOrEmpty(options.SearchText) || extensionDescriptor.Name.ToLowerInvariant().Contains(options.SearchText.ToLowerInvariant()))) (string.IsNullOrEmpty(options.SearchText) || extensionDescriptor.Name.ToLowerInvariant().Contains(options.SearchText.ToLowerInvariant())))
.OrderBy(extensionDescriptor => extensionDescriptor.Name) .OrderBy(extensionDescriptor => extensionDescriptor.Name)
.Select(extensionDescriptor => new ModuleEntry { Descriptor = extensionDescriptor }); .Select(extensionDescriptor => new ModuleEntry { Descriptor = extensionDescriptor });
@@ -158,13 +157,13 @@ namespace Orchard.Modules.Controllers {
try { try {
_recipeManager.Execute(recipe); _recipeManager.Execute(recipe);
} }
catch(Exception e) { catch (Exception e) {
Logger.Error(e, "Error while executing recipe {0} in {1}", moduleId, name); 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.Error(T("Recipes {0} contains unsupported module installation steps.", recipe.Name));
} }
Services.Notifier.Success(T("The recipe {0} was executed successfully.", recipe.Name)); Services.Notifier.Success(T("The recipe {0} was executed successfully.", recipe.Name));
return RedirectToAction("Recipes"); return RedirectToAction("Recipes");
} }
@@ -177,15 +176,15 @@ namespace Orchard.Modules.Controllers {
IEnumerable<ModuleFeature> features = _featureManager.GetAvailableFeatures() IEnumerable<ModuleFeature> features = _featureManager.GetAvailableFeatures()
.Where(f => !DefaultExtensionTypes.IsTheme(f.Extension.ExtensionType)) .Where(f => !DefaultExtensionTypes.IsTheme(f.Extension.ExtensionType))
.Select(f => new ModuleFeature { .Select(f => new ModuleFeature {
Descriptor = f, Descriptor = f,
IsEnabled = _shellDescriptor.Features.Any(sf => sf.Name == f.Id), IsEnabled = _shellDescriptor.Features.Any(sf => sf.Name == f.Id),
IsRecentlyInstalled = _moduleService.IsRecentlyInstalled(f.Extension), IsRecentlyInstalled = _moduleService.IsRecentlyInstalled(f.Extension),
NeedsUpdate = featuresThatNeedUpdate.Contains(f.Id), NeedsUpdate = featuresThatNeedUpdate.Contains(f.Id),
DependentFeatures = _moduleService.GetDependentFeatures(f.Id).Where(x => x.Id != f.Id).ToList() DependentFeatures = _moduleService.GetDependentFeatures(f.Id).Where(x => x.Id != f.Id).ToList()
}) })
.ToList(); .ToList();
return View(new FeaturesViewModel { return View(new FeaturesViewModel {
Features = features, Features = features,
IsAllowed = ExtensionIsAllowed IsAllowed = ExtensionIsAllowed
}); });
@@ -212,13 +211,13 @@ namespace Orchard.Modules.Controllers {
case FeaturesBulkAction.None: case FeaturesBulkAction.None:
break; break;
case FeaturesBulkAction.Enable: case FeaturesBulkAction.Enable:
_moduleService.EnableFeatures(disabledFeatures, force == true); EnableFeatures(disabledFeatures, force == true);
break; break;
case FeaturesBulkAction.Disable: case FeaturesBulkAction.Disable:
_moduleService.DisableFeatures(enabledFeatures, force == true); _moduleService.DisableFeatures(enabledFeatures, force == true);
break; break;
case FeaturesBulkAction.Toggle: case FeaturesBulkAction.Toggle:
_moduleService.EnableFeatures(disabledFeatures, force == true); EnableFeatures(disabledFeatures, force == true);
_moduleService.DisableFeatures(enabledFeatures, force == true); _moduleService.DisableFeatures(enabledFeatures, force == true);
break; break;
case FeaturesBulkAction.Update: case FeaturesBulkAction.Update:
@@ -250,5 +249,16 @@ namespace Orchard.Modules.Controllers {
private bool ExtensionIsAllowed(ExtensionDescriptor extensionDescriptor) { private bool ExtensionIsAllowed(ExtensionDescriptor extensionDescriptor) {
return _shellSettings.Modules.Length == 0 || _shellSettings.Modules.Contains(extensionDescriptor.Id); return _shellSettings.Modules.Length == 0 || _shellSettings.Modules.Contains(extensionDescriptor.Id);
} }
private void EnableFeatures(List<string> 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));
}
}
}
} }
} }

View File

@@ -11,10 +11,12 @@ namespace Orchard.Environment.Extensions.Loaders {
public class CoreExtensionLoader : ExtensionLoaderBase { public class CoreExtensionLoader : ExtensionLoaderBase {
private const string CoreAssemblyName = "Orchard.Core"; private const string CoreAssemblyName = "Orchard.Core";
private readonly IAssemblyLoader _assemblyLoader; private readonly IAssemblyLoader _assemblyLoader;
private readonly IDependenciesFolder _dependenciesFolder;
public CoreExtensionLoader(IDependenciesFolder dependenciesFolder, IAssemblyLoader assemblyLoader) public CoreExtensionLoader(IAssemblyLoader assemblyLoader, IDependenciesFolder dependenciesFolder)
: base(dependenciesFolder) { : base(dependenciesFolder) {
_assemblyLoader = assemblyLoader; _assemblyLoader = assemblyLoader;
_dependenciesFolder = dependenciesFolder;
Logger = NullLogger.Instance; 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) { private static bool IsTypeFromModule(Type type, ExtensionDescriptor descriptor) {
return (type.Namespace + ".").StartsWith(CoreAssemblyName + "." + descriptor.Id + "."); return (type.Namespace + ".").StartsWith(CoreAssemblyName + "." + descriptor.Id + ".");
} }

View File

@@ -212,6 +212,20 @@ namespace Orchard.Environment.Extensions.Loaders {
return dependencies; 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<string> currentSet) { private void AddDependencies(string projectPath, HashSet<string> currentSet) {
// Skip files from locations other than "~/Modules" and "~/Themes" etc. // Skip files from locations other than "~/Modules" and "~/Themes" etc.
if (string.IsNullOrEmpty(PrefixMatch(projectPath, _extensionsVirtualPathPrefixes))) { if (string.IsNullOrEmpty(PrefixMatch(projectPath, _extensionsVirtualPathPrefixes))) {

View File

@@ -57,5 +57,7 @@ namespace Orchard.Environment.Extensions.Loaders {
public virtual IEnumerable<string> GetVirtualPathDependencies(DependencyDescriptor dependency) { public virtual IEnumerable<string> GetVirtualPathDependencies(DependencyDescriptor dependency) {
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();
} }
public abstract bool LoaderIsSuitable(ExtensionDescriptor descriptor);
} }
} }

View File

@@ -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. /// For example, Razor or WebForms views needs to be recompiled when a dependency of a module changes.
/// </summary> /// </summary>
IEnumerable<string> GetVirtualPathDependencies(DependencyDescriptor dependency); IEnumerable<string> GetVirtualPathDependencies(DependencyDescriptor dependency);
bool LoaderIsSuitable(ExtensionDescriptor descriptor);
} }
} }

View File

@@ -19,6 +19,7 @@ namespace Orchard.Environment.Extensions.Loaders {
private readonly IAssemblyProbingFolder _assemblyProbingFolder; private readonly IAssemblyProbingFolder _assemblyProbingFolder;
private readonly IVirtualPathProvider _virtualPathProvider; private readonly IVirtualPathProvider _virtualPathProvider;
private readonly IVirtualPathMonitor _virtualPathMonitor; private readonly IVirtualPathMonitor _virtualPathMonitor;
private readonly IDependenciesFolder _dependenciesFolder;
public PrecompiledExtensionLoader( public PrecompiledExtensionLoader(
IHostEnvironment hostEnvironment, IHostEnvironment hostEnvironment,
@@ -31,6 +32,7 @@ namespace Orchard.Environment.Extensions.Loaders {
_assemblyProbingFolder = assemblyProbingFolder; _assemblyProbingFolder = assemblyProbingFolder;
_virtualPathProvider = virtualPathProvider; _virtualPathProvider = virtualPathProvider;
_virtualPathMonitor = virtualPathMonitor; _virtualPathMonitor = virtualPathMonitor;
_dependenciesFolder = dependenciesFolder;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
} }
@@ -166,7 +168,7 @@ namespace Orchard.Environment.Extensions.Loaders {
Loader = this, Loader = this,
Name = Path.GetFileNameWithoutExtension(path), Name = Path.GetFileNameWithoutExtension(path),
VirtualPath = path VirtualPath = path
} ) })
.ToList(); .ToList();
Logger.Information("Done probing references for module '{0}'", descriptor.Id); Logger.Information("Done probing references for module '{0}'", descriptor.Id);
@@ -244,5 +246,14 @@ namespace Orchard.Environment.Extensions.Loaders {
return assemblyPath; 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;
}
} }
} }

View File

@@ -8,10 +8,12 @@ using Orchard.Logging;
namespace Orchard.Environment.Extensions.Loaders { namespace Orchard.Environment.Extensions.Loaders {
public class RawThemeExtensionLoader : ExtensionLoaderBase { public class RawThemeExtensionLoader : ExtensionLoaderBase {
private readonly IVirtualPathProvider _virtualPathProvider; private readonly IVirtualPathProvider _virtualPathProvider;
private readonly IDependenciesFolder _dependenciesFolder;
public RawThemeExtensionLoader(IDependenciesFolder dependenciesFolder, IVirtualPathProvider virtualPathProvider) public RawThemeExtensionLoader(IDependenciesFolder dependenciesFolder, IVirtualPathProvider virtualPathProvider)
: base(dependenciesFolder) { : base(dependenciesFolder) {
_virtualPathProvider = virtualPathProvider; _virtualPathProvider = virtualPathProvider;
_dependenciesFolder = dependenciesFolder;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
} }
@@ -26,12 +28,12 @@ namespace Orchard.Environment.Extensions.Loaders {
return null; return null;
// Temporary - theme without own project should be under ~/themes // 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, string projectPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Id,
descriptor.Id + ".csproj"); descriptor.Id + ".csproj");
// ignore themes including a .csproj in this loader // ignore themes including a .csproj in this loader
if ( _virtualPathProvider.FileExists(projectPath) ) { if (_virtualPathProvider.FileExists(projectPath)) {
return null; return null;
} }
@@ -39,7 +41,7 @@ namespace Orchard.Environment.Extensions.Loaders {
descriptor.Id + ".dll"); descriptor.Id + ".dll");
// ignore themes with /bin in this loader // ignore themes with /bin in this loader
if ( _virtualPathProvider.FileExists(assemblyPath) ) if (_virtualPathProvider.FileExists(assemblyPath))
return null; return null;
return new ExtensionProbeEntry { return new ExtensionProbeEntry {
@@ -64,5 +66,14 @@ namespace Orchard.Environment.Extensions.Loaders {
ExportedTypes = new Type[0] 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;
}
} }
} }

View File

@@ -13,12 +13,14 @@ namespace Orchard.Environment.Extensions.Loaders {
public class ReferencedExtensionLoader : ExtensionLoaderBase { public class ReferencedExtensionLoader : ExtensionLoaderBase {
private readonly IVirtualPathProvider _virtualPathProvider; private readonly IVirtualPathProvider _virtualPathProvider;
private readonly IBuildManager _buildManager; private readonly IBuildManager _buildManager;
private readonly IDependenciesFolder _dependenciesFolder;
public ReferencedExtensionLoader(IDependenciesFolder dependenciesFolder, IVirtualPathProvider virtualPathProvider, IBuildManager buildManager) public ReferencedExtensionLoader(IDependenciesFolder dependenciesFolder, IVirtualPathProvider virtualPathProvider, IBuildManager buildManager)
: base(dependenciesFolder) { : base(dependenciesFolder) {
_virtualPathProvider = virtualPathProvider; _virtualPathProvider = virtualPathProvider;
_buildManager = buildManager; _buildManager = buildManager;
_dependenciesFolder = dependenciesFolder;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
} }
@@ -82,5 +84,14 @@ namespace Orchard.Environment.Extensions.Loaders {
ExportedTypes = assembly.GetExportedTypes() 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;
}
} }
} }

View File

@@ -4,7 +4,7 @@ using System.Linq;
using Orchard.Environment.Descriptor; using Orchard.Environment.Descriptor;
using Orchard.Environment.Descriptor.Models; using Orchard.Environment.Descriptor.Models;
using Orchard.Environment.Extensions; using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Helpers; using Orchard.Environment.Extensions.Loaders;
using Orchard.Environment.Extensions.Models; using Orchard.Environment.Extensions.Models;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Logging; using Orchard.Logging;
@@ -13,6 +13,7 @@ namespace Orchard.Environment.Features {
public class FeatureManager : IFeatureManager { public class FeatureManager : IFeatureManager {
private readonly IExtensionManager _extensionManager; private readonly IExtensionManager _extensionManager;
private readonly IShellDescriptorManager _shellDescriptorManager; private readonly IShellDescriptorManager _shellDescriptorManager;
private readonly IEnumerable<IExtensionLoader> _loaders;
/// <summary> /// <summary>
/// Delegate to notify about feature dependencies. /// Delegate to notify about feature dependencies.
@@ -21,10 +22,11 @@ namespace Orchard.Environment.Features {
public FeatureManager( public FeatureManager(
IExtensionManager extensionManager, IExtensionManager extensionManager,
IShellDescriptorManager shellDescriptorManager) { IShellDescriptorManager shellDescriptorManager,
IEnumerable<IExtensionLoader> loaders) {
_extensionManager = extensionManager; _extensionManager = extensionManager;
_shellDescriptorManager = shellDescriptorManager; _shellDescriptorManager = shellDescriptorManager;
_loaders = loaders;
T = NullLocalizer.Instance; T = NullLocalizer.Instance;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
} }
@@ -161,6 +163,22 @@ namespace Orchard.Environment.Features {
return GetAffectedFeatures(featureId, availableFeatures, getEnabledDependants); 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;
}
/// <summary> /// <summary>
/// Enables a feature. /// Enables a feature.
/// </summary> /// </summary>
@@ -228,7 +246,7 @@ namespace Orchard.Environment.Features {
} }
private static IEnumerable<string> GetAffectedFeatures( private static IEnumerable<string> GetAffectedFeatures(
string featureId, IDictionary<FeatureDescriptor, bool> features, string featureId, IDictionary<FeatureDescriptor, bool> features,
Func<string, IDictionary<FeatureDescriptor, bool>, IDictionary<FeatureDescriptor, bool>> getAffectedDependencies) { Func<string, IDictionary<FeatureDescriptor, bool>, IDictionary<FeatureDescriptor, bool>> getAffectedDependencies) {
var dependencies = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { featureId }; var dependencies = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { featureId };

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Orchard.Environment.Extensions.Models; using Orchard.Environment.Extensions.Models;
using Orchard.Environment.Extensions;
namespace Orchard.Environment.Features { namespace Orchard.Environment.Features {
public delegate void FeatureDependencyNotificationHandler(string messageFormat, string featureId, IEnumerable<string> featureIds); public delegate void FeatureDependencyNotificationHandler(string messageFormat, string featureId, IEnumerable<string> featureIds);
@@ -61,5 +62,12 @@ namespace Orchard.Environment.Features {
/// <param name="featureId">ID of the feature to check.</param> /// <param name="featureId">ID of the feature to check.</param>
/// <returns>An enumeration with dependent feature IDs.</returns> /// <returns>An enumeration with dependent feature IDs.</returns>
IEnumerable<string> GetDependentFeatures(string featureId); IEnumerable<string> GetDependentFeatures(string featureId);
/// <summary>
/// Checks whether the feature's extension has a loadee, i.e. a suitable <see cref="IExtensionLoader"/> can be found.
/// </summary>
/// <param name="featureId">ID of the feature to check.</param>
/// <returns><c>True</c> if a suitable <see cref="IExtensionLoader"/> can be found.</returns>
bool HasLoader(string featureId);
} }
} }