mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-09-23 04:43:35 +08:00
Preventing modules from breaking the website if a referenced assembly is missing
--HG-- branch : 1.x
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Orchard.Environment.Extensions;
|
||||||
|
using Orchard.Localization;
|
||||||
|
using Orchard.UI.Admin.Notification;
|
||||||
|
using Orchard.UI.Notify;
|
||||||
|
|
||||||
|
namespace Orchard.Core.Dashboard.Services {
|
||||||
|
public class CompilationErrorBanner : INotificationProvider {
|
||||||
|
private readonly ICriticalErrorProvider _errorProvider;
|
||||||
|
|
||||||
|
public CompilationErrorBanner(ICriticalErrorProvider errorProvider) {
|
||||||
|
_errorProvider = errorProvider;
|
||||||
|
T = NullLocalizer.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Localizer T { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<NotifyEntry> GetNotifications() {
|
||||||
|
return _errorProvider.GetErrors()
|
||||||
|
.Select(message => new NotifyEntry { Message = message, Type = NotifyType.Error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -124,6 +124,7 @@
|
|||||||
<Compile Include="Contents\Settings\ContentPartSettings.cs" />
|
<Compile Include="Contents\Settings\ContentPartSettings.cs" />
|
||||||
<Compile Include="Contents\Shapes.cs" />
|
<Compile Include="Contents\Shapes.cs" />
|
||||||
<Compile Include="Contents\ViewModels\PublishContentViewModel.cs" />
|
<Compile Include="Contents\ViewModels\PublishContentViewModel.cs" />
|
||||||
|
<Compile Include="Dashboard\Services\CompilationErrorBanner.cs" />
|
||||||
<Compile Include="Navigation\Commands\MenuCommands.cs" />
|
<Compile Include="Navigation\Commands\MenuCommands.cs" />
|
||||||
<Compile Include="Navigation\Drivers\AdminMenuPartDriver.cs" />
|
<Compile Include="Navigation\Drivers\AdminMenuPartDriver.cs" />
|
||||||
<Compile Include="Navigation\Handlers\AdminMenuPartHandler.cs" />
|
<Compile Include="Navigation\Handlers\AdminMenuPartHandler.cs" />
|
||||||
|
@@ -1 +1 @@
|
|||||||
<div class="message message-@Model.Type">@Model.Message</div>
|
<div class="message message-@Model.Type">@Html.Raw(Html.Encode(Model.Message).Replace("\n", "<br />"))</div>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Transactions;
|
|
||||||
using Orchard.Caching;
|
using Orchard.Caching;
|
||||||
using Orchard.Environment.Configuration;
|
using Orchard.Environment.Configuration;
|
||||||
using Orchard.Environment.Extensions;
|
using Orchard.Environment.Extensions;
|
||||||
@@ -13,7 +14,7 @@ using Orchard.Logging;
|
|||||||
using Orchard.Utility.Extensions;
|
using Orchard.Utility.Extensions;
|
||||||
|
|
||||||
namespace Orchard.Environment {
|
namespace Orchard.Environment {
|
||||||
public class DefaultOrchardHost : IOrchardHost, IShellSettingsManagerEventHandler, IShellDescriptorManagerEventHandler {
|
public class DefaultOrchardHost : IOrchardHost, IShellSettingsManagerEventHandler, IShellDescriptorManagerEventHandler, ICriticalErrorProvider {
|
||||||
private readonly IHostLocalRestart _hostLocalRestart;
|
private readonly IHostLocalRestart _hostLocalRestart;
|
||||||
private readonly IShellSettingsManager _shellSettingsManager;
|
private readonly IShellSettingsManager _shellSettingsManager;
|
||||||
private readonly IShellContextFactory _shellContextFactory;
|
private readonly IShellContextFactory _shellContextFactory;
|
||||||
@@ -22,6 +23,7 @@ namespace Orchard.Environment {
|
|||||||
private readonly IExtensionLoaderCoordinator _extensionLoaderCoordinator;
|
private readonly IExtensionLoaderCoordinator _extensionLoaderCoordinator;
|
||||||
private readonly IExtensionMonitoringCoordinator _extensionMonitoringCoordinator;
|
private readonly IExtensionMonitoringCoordinator _extensionMonitoringCoordinator;
|
||||||
private readonly ICacheManager _cacheManager;
|
private readonly ICacheManager _cacheManager;
|
||||||
|
private readonly ConcurrentBag<LocalizedString> _errorMessages;
|
||||||
private readonly object _syncLock = new object();
|
private readonly object _syncLock = new object();
|
||||||
|
|
||||||
private IEnumerable<ShellContext> _shellContexts;
|
private IEnumerable<ShellContext> _shellContexts;
|
||||||
@@ -45,6 +47,7 @@ namespace Orchard.Environment {
|
|||||||
_cacheManager = cacheManager;
|
_cacheManager = cacheManager;
|
||||||
_hostLocalRestart = hostLocalRestart;
|
_hostLocalRestart = hostLocalRestart;
|
||||||
_tenantsToRestart = Enumerable.Empty<ShellSettings>();
|
_tenantsToRestart = Enumerable.Empty<ShellSettings>();
|
||||||
|
_errorMessages = new ConcurrentBag<LocalizedString>();
|
||||||
|
|
||||||
T = NullLocalizer.Instance;
|
T = NullLocalizer.Instance;
|
||||||
Logger = NullLogger.Instance;
|
Logger = NullLogger.Instance;
|
||||||
@@ -57,6 +60,12 @@ namespace Orchard.Environment {
|
|||||||
get { return BuildCurrent().ToReadOnlyCollection(); }
|
get { return BuildCurrent().ToReadOnlyCollection(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RegisterErrorMessage(LocalizedString message) {
|
||||||
|
if (_errorMessages != null && _errorMessages.All(m => m.TextHint != message.TextHint)) {
|
||||||
|
_errorMessages.Add(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ShellContext GetShellContext(ShellSettings shellSettings) {
|
public ShellContext GetShellContext(ShellSettings shellSettings) {
|
||||||
return Current
|
return Current
|
||||||
.Single(shellContext => shellContext.Settings.Name.Equals(shellSettings.Name));
|
.Single(shellContext => shellContext.Settings.Name.Equals(shellSettings.Name));
|
||||||
@@ -124,7 +133,7 @@ namespace Orchard.Environment {
|
|||||||
Logger.Information("Start creation of shells");
|
Logger.Information("Start creation of shells");
|
||||||
|
|
||||||
// is there any tenant right now ?
|
// is there any tenant right now ?
|
||||||
var allSettings = _shellSettingsManager.LoadSettings();
|
var allSettings = _shellSettingsManager.LoadSettings().ToArray();
|
||||||
|
|
||||||
// load all tenants, and activate their shell
|
// load all tenants, and activate their shell
|
||||||
if (allSettings.Any()) {
|
if (allSettings.Any()) {
|
||||||
@@ -261,12 +270,12 @@ namespace Orchard.Environment {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor, string tenant) {
|
void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor, string tenant) {
|
||||||
lock (_syncLock) {
|
lock (_syncLock) {
|
||||||
var context = _shellContexts.Where(x => x.Settings.Name == tenant).FirstOrDefault();
|
var context = _shellContexts.FirstOrDefault(x => x.Settings.Name == tenant);
|
||||||
|
|
||||||
// some shells might need to be started, e.g. created by command line
|
// some shells might need to be started, e.g. created by command line
|
||||||
if(context == null) {
|
if(context == null) {
|
||||||
StartUpdatedShells();
|
StartUpdatedShells();
|
||||||
context = _shellContexts.Where(x => x.Settings.Name == tenant).First();
|
context = _shellContexts.First(x => x.Settings.Name == tenant);
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't update the settings themselves here
|
// don't update the settings themselves here
|
||||||
@@ -277,5 +286,9 @@ namespace Orchard.Environment {
|
|||||||
_tenantsToRestart = _tenantsToRestart.Union(new[] { context.Settings });
|
_tenantsToRestart = _tenantsToRestart.Union(new[] { context.Settings });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<LocalizedString> GetErrors() {
|
||||||
|
return _errorMessages;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,19 +19,22 @@ namespace Orchard.Environment.Extensions.Compilers {
|
|||||||
private readonly IDependenciesFolder _dependenciesFolder;
|
private readonly IDependenciesFolder _dependenciesFolder;
|
||||||
private readonly IEnumerable<IExtensionLoader> _loaders;
|
private readonly IEnumerable<IExtensionLoader> _loaders;
|
||||||
private readonly IAssemblyLoader _assemblyLoader;
|
private readonly IAssemblyLoader _assemblyLoader;
|
||||||
|
private readonly IOrchardHost _orchardHost;
|
||||||
|
|
||||||
public DefaultExtensionCompiler(
|
public DefaultExtensionCompiler(
|
||||||
IVirtualPathProvider virtualPathProvider,
|
IVirtualPathProvider virtualPathProvider,
|
||||||
IProjectFileParser projectFileParser,
|
IProjectFileParser projectFileParser,
|
||||||
IDependenciesFolder dependenciesFolder,
|
IDependenciesFolder dependenciesFolder,
|
||||||
IEnumerable<IExtensionLoader> loaders,
|
IEnumerable<IExtensionLoader> loaders,
|
||||||
IAssemblyLoader assemblyLoader) {
|
IAssemblyLoader assemblyLoader,
|
||||||
|
IOrchardHost orchardHost) {
|
||||||
|
|
||||||
_virtualPathProvider = virtualPathProvider;
|
_virtualPathProvider = virtualPathProvider;
|
||||||
_projectFileParser = projectFileParser;
|
_projectFileParser = projectFileParser;
|
||||||
_dependenciesFolder = dependenciesFolder;
|
_dependenciesFolder = dependenciesFolder;
|
||||||
_loaders = loaders;
|
_loaders = loaders;
|
||||||
_assemblyLoader = assemblyLoader;
|
_assemblyLoader = assemblyLoader;
|
||||||
|
_orchardHost = orchardHost;
|
||||||
|
|
||||||
T = NullLocalizer.Instance;
|
T = NullLocalizer.Instance;
|
||||||
Logger = NullLogger.Instance;
|
Logger = NullLogger.Instance;
|
||||||
@@ -88,14 +91,16 @@ namespace Orchard.Environment.Extensions.Compilers {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Logger.Error("Assembly reference '{0}' for project '{1}' cannot be loaded", assemblyReference.FullName, context.VirtualPath);
|
Logger.Error("Assembly reference '{0}' for project '{1}' cannot be loaded", assemblyReference.FullName, context.VirtualPath);
|
||||||
throw new OrchardCoreException(T(
|
_orchardHost.RegisterErrorMessage(T(
|
||||||
"The assembly reference '{0}' could not be loaded.\r\n\r\n" +
|
"The assembly reference '{0}' could not be loaded.\r\n\r\n" +
|
||||||
"There are generally a few ways to solve this issue:\r\n" +
|
"There are generally a few ways to solve this issue:\r\n" +
|
||||||
"1. Remove the assembly reference from the project file if it's not needed.\r\n" +
|
"1. Install any dependent module.\r\n" +
|
||||||
"2. Ensure the assembly reference is present in the 'bin' directory of the module.\r\n" +
|
"2. Remove the assembly reference from the project file if it's not needed.\r\n" +
|
||||||
"3. Ensure the assembly reference is present in the 'bin' directory of the application.\r\n" +
|
"3. Ensure the assembly reference is present in the 'bin' directory of the module.\r\n" +
|
||||||
"4. Specify the strong name of the assembly (name, version, culture, publickey) if the assembly is present in the GAC.",
|
"4. Ensure the assembly reference is present in the 'bin' directory of the application.\r\n" +
|
||||||
|
"5. Specify the strong name of the assembly (name, version, culture, publickey) if the assembly is present in the GAC.",
|
||||||
assemblyReference.FullName));
|
assemblyReference.FullName));
|
||||||
|
throw new OrchardCoreException(T("Assembly reference '{0}' for project '{1}' cannot be loaded", assemblyReference.FullName, context.VirtualPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -109,6 +109,9 @@ namespace Orchard.Environment.Extensions {
|
|||||||
context.AvailableExtensionsProbes[extension.Id] :
|
context.AvailableExtensionsProbes[extension.Id] :
|
||||||
Enumerable.Empty<ExtensionProbeEntry>();
|
Enumerable.Empty<ExtensionProbeEntry>();
|
||||||
|
|
||||||
|
// materializes the list
|
||||||
|
extensionProbes = extensionProbes.ToArray();
|
||||||
|
|
||||||
if (Logger.IsEnabled(LogLevel.Debug)) {
|
if (Logger.IsEnabled(LogLevel.Debug)) {
|
||||||
Logger.Debug("Loaders for extension \"{0}\": ", extension.Id);
|
Logger.Debug("Loaders for extension \"{0}\": ", extension.Id);
|
||||||
foreach (var probe in extensionProbes) {
|
foreach (var probe in extensionProbes) {
|
||||||
@@ -131,14 +134,13 @@ namespace Orchard.Environment.Extensions {
|
|||||||
.Select(e => context.ProcessedExtensions[e.Id])
|
.Select(e => context.ProcessedExtensions[e.Id])
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var activatedExtension = extensionProbes
|
var activatedExtension = extensionProbes.FirstOrDefault(
|
||||||
.Where(e => e.Loader.IsCompatibleWithModuleReferences(extension, processedModuleReferences))
|
e => e.Loader.IsCompatibleWithModuleReferences(extension, processedModuleReferences)
|
||||||
.FirstOrDefault();
|
);
|
||||||
|
|
||||||
var previousDependency = context
|
var previousDependency = context.PreviousDependencies.FirstOrDefault(
|
||||||
.PreviousDependencies
|
d => StringComparer.OrdinalIgnoreCase.Equals(d.Name, extension.Id)
|
||||||
.Where(d => StringComparer.OrdinalIgnoreCase.Equals(d.Name, extension.Id))
|
);
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (activatedExtension == null) {
|
if (activatedExtension == null) {
|
||||||
Logger.Warning("No loader found for extension \"{0}\"!", extension.Id);
|
Logger.Warning("No loader found for extension \"{0}\"!", extension.Id);
|
||||||
@@ -180,7 +182,7 @@ namespace Orchard.Environment.Extensions {
|
|||||||
|
|
||||||
// Check there are no duplicates
|
// Check there are no duplicates
|
||||||
var duplicates = availableExtensions.GroupBy(ed => ed.Id).Where(g => g.Count() >= 2).ToList();
|
var duplicates = availableExtensions.GroupBy(ed => ed.Id).Where(g => g.Count() >= 2).ToList();
|
||||||
if (duplicates.Count() > 0) {
|
if (duplicates.Any()) {
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.Append(T("There are multiple extensions with the same name installed in this instance of Orchard.\r\n"));
|
sb.Append(T("There are multiple extensions with the same name installed in this instance of Orchard.\r\n"));
|
||||||
foreach (var dup in duplicates) {
|
foreach (var dup in duplicates) {
|
||||||
@@ -232,7 +234,7 @@ namespace Orchard.Environment.Extensions {
|
|||||||
availableExtensions.OrderByDependenciesAndPriorities(
|
availableExtensions.OrderByDependenciesAndPriorities(
|
||||||
(item, dep) => referencesByModule.ContainsKey(item.Id) &&
|
(item, dep) => referencesByModule.ContainsKey(item.Id) &&
|
||||||
referencesByModule[item.Id].Any(r => StringComparer.OrdinalIgnoreCase.Equals(dep.Id, r.Name)),
|
referencesByModule[item.Id].Any(r => StringComparer.OrdinalIgnoreCase.Equals(dep.Id, r.Name)),
|
||||||
(item) => 0)
|
item => 0)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
return new ExtensionLoadingContext {
|
return new ExtensionLoadingContext {
|
||||||
@@ -253,7 +255,7 @@ namespace Orchard.Environment.Extensions {
|
|||||||
.OrderByDescending(g => g.Key);
|
.OrderByDescending(g => g.Key);
|
||||||
|
|
||||||
// Select highest priority group with at least one item
|
// Select highest priority group with at least one item
|
||||||
var firstNonEmptyGroup = groupByPriority.FirstOrDefault(g => g.Count() >= 1) ?? Enumerable.Empty<ExtensionProbeEntry>();
|
var firstNonEmptyGroup = groupByPriority.FirstOrDefault(g => g.Any()) ?? Enumerable.Empty<ExtensionProbeEntry>();
|
||||||
|
|
||||||
// No need for further sorting if only 1 item found
|
// No need for further sorting if only 1 item found
|
||||||
if (firstNonEmptyGroup.Count() <= 1)
|
if (firstNonEmptyGroup.Count() <= 1)
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Orchard.Localization;
|
||||||
|
|
||||||
|
namespace Orchard.Environment.Extensions {
|
||||||
|
public interface ICriticalErrorProvider {
|
||||||
|
IEnumerable<LocalizedString> GetErrors();
|
||||||
|
}
|
||||||
|
}
|
@@ -16,7 +16,10 @@ namespace Orchard.Environment {
|
|||||||
private readonly IVirtualPathProvider _virtualPathProvider;
|
private readonly IVirtualPathProvider _virtualPathProvider;
|
||||||
private readonly IAssemblyLoader _assemblyLoader;
|
private readonly IAssemblyLoader _assemblyLoader;
|
||||||
|
|
||||||
public DefaultBuildManager(IVirtualPathProvider virtualPathProvider, IAssemblyLoader assemblyLoader) {
|
public DefaultBuildManager(
|
||||||
|
IVirtualPathProvider virtualPathProvider,
|
||||||
|
IAssemblyLoader assemblyLoader) {
|
||||||
|
|
||||||
_virtualPathProvider = virtualPathProvider;
|
_virtualPathProvider = virtualPathProvider;
|
||||||
_assemblyLoader = assemblyLoader;
|
_assemblyLoader = assemblyLoader;
|
||||||
}
|
}
|
||||||
@@ -39,7 +42,13 @@ namespace Orchard.Environment {
|
|||||||
|
|
||||||
|
|
||||||
public Assembly GetCompiledAssembly(string virtualPath) {
|
public Assembly GetCompiledAssembly(string virtualPath) {
|
||||||
return BuildManager.GetCompiledAssembly(virtualPath);
|
try {
|
||||||
|
return BuildManager.GetCompiledAssembly(virtualPath);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Orchard.Environment.Configuration;
|
using Orchard.Environment.Configuration;
|
||||||
using Orchard.Environment.ShellBuilders;
|
using Orchard.Environment.ShellBuilders;
|
||||||
|
using Orchard.Localization;
|
||||||
|
|
||||||
namespace Orchard.Environment {
|
namespace Orchard.Environment {
|
||||||
public interface IOrchardHost {
|
public interface IOrchardHost {
|
||||||
@@ -25,6 +25,11 @@ namespace Orchard.Environment {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
void EndRequest();
|
void EndRequest();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called by any to notice the system of a critical issue at the system level, e.g. incorrect extensions
|
||||||
|
/// </summary>
|
||||||
|
void RegisterErrorMessage(LocalizedString message);
|
||||||
|
|
||||||
ShellContext GetShellContext(ShellSettings shellSettings);
|
ShellContext GetShellContext(ShellSettings shellSettings);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -71,7 +71,7 @@ namespace Orchard.Environment {
|
|||||||
RegisterVolatileProvider<DefaultVirtualPathMonitor, IVirtualPathMonitor>(builder);
|
RegisterVolatileProvider<DefaultVirtualPathMonitor, IVirtualPathMonitor>(builder);
|
||||||
RegisterVolatileProvider<DefaultVirtualPathProvider, IVirtualPathProvider>(builder);
|
RegisterVolatileProvider<DefaultVirtualPathProvider, IVirtualPathProvider>(builder);
|
||||||
|
|
||||||
builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>().SingleInstance();
|
builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>().As<ICriticalErrorProvider>().SingleInstance();
|
||||||
{
|
{
|
||||||
builder.RegisterType<ShellSettingsManager>().As<IShellSettingsManager>().SingleInstance();
|
builder.RegisterType<ShellSettingsManager>().As<IShellSettingsManager>().SingleInstance();
|
||||||
|
|
||||||
|
@@ -218,6 +218,7 @@
|
|||||||
<Compile Include="Environment\Features\FeatureManager.cs" />
|
<Compile Include="Environment\Features\FeatureManager.cs" />
|
||||||
<Compile Include="Environment\IAssemblyLoader.cs" />
|
<Compile Include="Environment\IAssemblyLoader.cs" />
|
||||||
<Compile Include="Environment\HostComponentsConfigModule.cs" />
|
<Compile Include="Environment\HostComponentsConfigModule.cs" />
|
||||||
|
<Compile Include="Environment\Extensions\ICriticalErrorProvider.cs" />
|
||||||
<Compile Include="Environment\IOrchardFrameworkAssemblies.cs" />
|
<Compile Include="Environment\IOrchardFrameworkAssemblies.cs" />
|
||||||
<Compile Include="Environment\ViewsBackgroundCompilation.cs" />
|
<Compile Include="Environment\ViewsBackgroundCompilation.cs" />
|
||||||
<Compile Include="Environment\Warmup\WarmupUtility.cs" />
|
<Compile Include="Environment\Warmup\WarmupUtility.cs" />
|
||||||
|
Reference in New Issue
Block a user