Adding an extension manager events interface for enable/disable livecycle of modules. Adding a HackInstallationGenerator which produces Enabling/Enabled events for all modules on app startup. Adding a Roles module Enabled event handler that populates predefined roles with permission stereotypical permissions.

--HG--
extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4045231
This commit is contained in:
loudej
2010-01-11 02:36:25 +00:00
parent 864dd42a08
commit 4272b2609d
21 changed files with 255 additions and 60 deletions

View File

@@ -8,6 +8,7 @@ using System.Web.Routing;
using Autofac;
using Autofac.Integration.Web;
using Autofac.Modules;
using Moq;
using NUnit.Framework;
using Orchard.Environment;
using Orchard.Mvc;
@@ -44,6 +45,7 @@ namespace Orchard.Tests.Environment {
builder.Register(_modelBinderDictionary);
builder.Register(new ViewEngineCollection { new WebFormViewEngine() });
builder.Register(new StuExtensionManager()).As<IExtensionManager>();
builder.Register(new Mock<IHackInstallationGenerator>().Object);
});
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Orchard.Security.Permissions;
namespace Orchard.Core.Common {
@@ -12,5 +13,14 @@ namespace Orchard.Core.Common {
public IEnumerable<Permission> GetPermissions() {
return new[] { ChangeOwner };
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
return new[] {
new PermissionStereotype {
Name = "Administrators",
Permissions = new[] {ChangeOwner}
}
};
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Security.Permissions;
namespace Orchard.Core.Themes {
@@ -18,5 +19,9 @@ namespace Orchard.Core.Themes {
InstallUninstallTheme
};
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
return Enumerable.Empty<PermissionStereotype>();
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Security.Permissions;
namespace Orchard.Blogs {
@@ -36,6 +37,10 @@ namespace Orchard.Blogs {
DeleteBlog
};
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
return Enumerable.Empty<PermissionStereotype>();
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Security.Permissions;
namespace Orchard.Comments {
@@ -18,7 +19,7 @@ namespace Orchard.Comments {
}
public IEnumerable<Permission> GetPermissions() {
return new List<Permission> {
return new[] {
AddComment,
AddCommentWithoutValidation,
EnableComment,
@@ -28,5 +29,19 @@ namespace Orchard.Comments {
ModerateCommentOnOwnItems
};
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
return new[] {
new PermissionStereotype {
Name = "Administrators",
Permissions = new[] {ModerateComment}
},
new PermissionStereotype {
Name = "Anonymous",
Permissions = new[] {AddComment}
},
};
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Security.Permissions;
namespace Orchard.Media {
@@ -26,5 +27,10 @@ namespace Orchard.Media {
RenameMediaFolder
};
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
return Enumerable.Empty<PermissionStereotype>();
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Security.Permissions;
namespace Orchard.Pages {
@@ -32,5 +33,10 @@ namespace Orchard.Pages {
SchedulePages
};
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
return Enumerable.Empty<PermissionStereotype>();
}
}
}

View File

@@ -0,0 +1,57 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Orchard.Extensions;
using Orchard.Roles.Services;
using Orchard.Security.Permissions;
namespace Orchard.Roles {
[UsedImplicitly]
public class Extension : IExtensionManagerEvents {
private readonly IRoleService _roleService;
private readonly IEnumerable<IPermissionProvider> _permissionProviders;
public Extension(
IRoleService roleService,
IEnumerable<IPermissionProvider> permissionProviders) {
_roleService = roleService;
_permissionProviders = permissionProviders;
}
public void Enabling(ExtensionEventContext context) {
}
public void Enabled(ExtensionEventContext context) {
// when another package is being enabled, locate matching permission providers
var providersForEnabledPackage =
_permissionProviders.Where(x => x.PackageName == context.Extension.Descriptor.Name);
foreach (var permissionProvider in providersForEnabledPackage) {
// get and iterate stereotypical groups of permissions
var stereotypes = permissionProvider.GetDefaultStereotypes();
foreach(var stereotype in stereotypes) {
// turn those stereotypes into roles
var role = _roleService.GetRoleByName(stereotype.Name);
if (role == null){
_roleService.CreateRole(stereotype.Name);
role = _roleService.GetRoleByName(stereotype.Name);
}
// and merge the stereotypical permissions into that role
var distinctPermissionNames = role.RolesPermissions.Select(x => x.Permission.Name)
.Union(stereotype.Permissions.Select(x => x.Name))
.Distinct();
_roleService.UpdateRole(role.Id, role.Name, distinctPermissionNames);
}
}
}
public void Disabling(ExtensionEventContext context) {
}
public void Disabled(ExtensionEventContext context) {
}
}
}

View File

@@ -64,6 +64,7 @@
<Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Controllers\UserRolesDriver.cs" />
<Compile Include="Extension.cs" />
<Compile Include="Records\PermissionRecord.cs" />
<Compile Include="Records\RoleRecord.cs" />
<Compile Include="Models\UserRoles.cs" />

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Security.Permissions;
namespace Orchard.Tags {
@@ -22,5 +23,10 @@ namespace Orchard.Tags {
RenameTag,
};
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
return Enumerable.Empty<PermissionStereotype>();
}
}
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using System.Transactions;
using System.Web.Mvc;
using Orchard.Logging;
using Orchard.Mvc.Filters;
namespace Orchard.Data {
@@ -16,21 +17,33 @@ namespace Orchard.Data {
private TransactionScope _scope;
private bool _cancelled;
public TransactionManager() {
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
void ITransactionManager.Demand() {
if (_scope == null) {
Logger.Debug("Creating transaction on Demand");
_scope = new TransactionScope(TransactionScopeOption.Required);
}
}
void ITransactionManager.Cancel() {
Logger.Debug("Transaction cancelled flag set");
_cancelled = true;
}
void IDisposable.Dispose() {
if (_scope != null) {
if (!_cancelled)
if (!_cancelled){
Logger.Debug("Marking transaction as complete");
_scope.Complete();
}
Logger.Debug("Final work for transaction being performed");
_scope.Dispose();
Logger.Debug("Transaction disposed");
}
}

View File

@@ -7,8 +7,10 @@ using Autofac.Builder;
using Autofac.Integration.Web;
using System.Collections.Generic;
using AutofacContrib.DynamicProxy2;
using Orchard.Extensions;
using Orchard.Mvc;
using Orchard.Mvc.ViewEngines;
using Orchard.Tasks;
namespace Orchard.Environment {
public class DefaultOrchardHost : IOrchardHost {
@@ -43,6 +45,22 @@ namespace Orchard.Environment {
ViewEngines.Engines.Insert(0, LayoutViewEngine.CreateShim());
_controllerBuilder.SetControllerFactory(new OrchardControllerFactory());
ServiceLocator.SetLocator(t => _containerProvider.RequestContainer.Resolve(t));
// fire off one-time install events on an alternate container
var tempContainer = CreateShellContainer();
var containerProvider = new FiniteContainerProvider(tempContainer);
try {
var requestContainer = containerProvider.RequestContainer;
var hackInstallationGenerator = requestContainer.Resolve<IHackInstallationGenerator>();
hackInstallationGenerator.GenerateInstallEvents();
}
finally {
// shut everything down again
containerProvider.DisposeRequestContainer();
tempContainer.Dispose();
}
}
protected virtual void EndRequest() {

View File

@@ -6,6 +6,7 @@ using Orchard.Logging;
using Orchard.Mvc.ModelBinders;
using Orchard.Mvc.Routes;
using Orchard.Extensions;
using Orchard.Utility;
namespace Orchard.Environment {
public class DefaultOrchardShell : IOrchardShell {
@@ -39,41 +40,11 @@ namespace Orchard.Environment {
public ILogger Logger { get; set; }
//static IEnumerable<string> OrchardLocationFormats() {
// return new[] {
// "~/Packages/{2}/Views/{1}/{0}.aspx",
// "~/Packages/{2}/Views/{1}/{0}.ascx",
// "~/Packages/{2}/Views/Shared/{0}.aspx",
// "~/Packages/{2}/Views/Shared/{0}.ascx",
// "~/Core/{2}/Views/{1}/{0}.aspx",
// "~/Core/{2}/Views/{1}/{0}.ascx",
// "~/Core/{2}/Views/Shared/{0}.aspx",
// "~/Core/{2}/Views/Shared/{0}.ascx",
// };
//}
public void Activate() {
_routePublisher.Publish(_routeProviders.SelectMany(provider => provider.GetRoutes()));
_modelBinderPublisher.Publish(_modelBinderProviders.SelectMany(provider => provider.GetModelBinders()));
//var viewEngine = _viewEngines.OfType<VirtualPathProviderViewEngine>().Single();
//viewEngine.AreaViewLocationFormats = OrchardLocationFormats()
// .Concat(viewEngine.AreaViewLocationFormats)
// .Distinct()
// .ToArray();
//viewEngine.AreaPartialViewLocationFormats = OrchardLocationFormats()
// .Concat(viewEngine.AreaPartialViewLocationFormats)
// .Distinct()
// .ToArray();
//var activePackageDescriptors = _extensionManager.ActiveExtensions().Select(x => x.Descriptor);
//var sharedLocationFormats = activePackageDescriptors.Select(x => ModelsLocationFormat(x));
//viewEngine.PartialViewLocationFormats = sharedLocationFormats
// .Concat(viewEngine.PartialViewLocationFormats)
// .Distinct()
// .ToArray();
_events.Invoke(x => x.Activated(), Logger);
}

View File

@@ -6,6 +6,8 @@ using ICSharpCode.SharpZipLib.Zip;
using Orchard.Extensions.Helpers;
using Orchard.Extensions.Loaders;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Utility;
using Yaml.Grammar;
using System.Web;
@@ -16,16 +18,20 @@ namespace Orchard.Extensions {
private IEnumerable<ExtensionEntry> _activeExtensions;
public Localizer T { get; set; }
public ILogger Logger { get; set; }
public ExtensionManager(IEnumerable<IExtensionFolders> folders, IEnumerable<IExtensionLoader> loaders) {
public ExtensionManager(
IEnumerable<IExtensionFolders> folders,
IEnumerable<IExtensionLoader> loaders) {
_folders = folders;
_loaders = loaders.OrderBy(x => x.Order);
T = NullLocalizer.Instance;
Logger = NullLogger.Instance;
}
public IEnumerable<ExtensionDescriptor> AvailableExtensions() {
List<ExtensionDescriptor> availableExtensions = new List<ExtensionDescriptor>();
var availableExtensions = new List<ExtensionDescriptor>();
foreach (var folder in _folders) {
foreach (var name in folder.ListNames()) {
availableExtensions.Add(GetDescriptorForExtension(name, folder));

View File

@@ -0,0 +1,41 @@
using System.Collections.Generic;
using Orchard.Logging;
using Orchard.Utility;
namespace Orchard.Extensions {
public interface IHackInstallationGenerator : IDependency {
void GenerateInstallEvents();
}
public class HackInstallationGenerator : IHackInstallationGenerator {
private readonly IExtensionManager _extensionManager;
private readonly IEnumerable<IExtensionManagerEvents> _extensionEvents;
public HackInstallationGenerator(
IExtensionManager extensionManager,
IEnumerable<IExtensionManagerEvents> extensionEvents) {
_extensionManager = extensionManager;
_extensionEvents = extensionEvents;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public void GenerateInstallEvents() {
//TEMP: this is really part of the extension manager's job. an extension
// install event is being simulated here on each web app startup
var enabled = new List<ExtensionEntry>();
foreach (var extension in _extensionManager.ActiveExtensions()) {
var context = new ExtensionEventContext {
Extension = extension,
EnabledExtensions = enabled.ToReadOnlyCollection(),
};
_extensionEvents.Invoke(x => x.Enabling(context), Logger);
enabled.Add(extension);
context.EnabledExtensions = enabled.ToReadOnlyCollection();
_extensionEvents.Invoke(x => x.Enabled(context), Logger);
}
}
}
}

View File

@@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace Orchard.Extensions {
public interface IExtensionManagerEvents : IEvents {
void Enabling(ExtensionEventContext context);
void Enabled(ExtensionEventContext context);
void Disabling(ExtensionEventContext context);
void Disabled(ExtensionEventContext context);
}
public class ExtensionEventContext {
public ExtensionEntry Extension { get; set; }
public IEnumerable<ExtensionEntry> EnabledExtensions { get; set; }
}
}

View File

@@ -178,6 +178,8 @@
<Compile Include="ContentManagement\Records\ContentPartVersionRecord.cs" />
<Compile Include="ContentManagement\Records\ContentTypeRecord.cs" />
<Compile Include="ContentManagement\Records\Utility.cs" />
<Compile Include="Extensions\HackInstallationGenerator.cs" />
<Compile Include="Extensions\IExtensionManagerEvents.cs" />
<Compile Include="Localization\Text.cs" />
<Compile Include="Mvc\ViewModels\ContentItemViewModel.cs" />
<Compile Include="ContentManagement\ViewModels\TemplateViewModel.cs" />
@@ -196,6 +198,7 @@
<Compile Include="Mvc\Html\MvcFormAntiForgeryPost.cs" />
<Compile Include="Mvc\Html\SiteServiceExtensions.cs" />
<Compile Include="Permissions.cs" />
<Compile Include="Tasks\FiniteContainerProvider.cs" />
<Compile Include="Themes\ExtensionManagerExtensions.cs" />
<Compile Include="Extensions\Helpers\PathHelpers.cs" />
<Compile Include="Extensions\IExtensionManager.cs" />

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Security.Permissions;
namespace Orchard {
@@ -12,5 +13,10 @@ namespace Orchard {
public IEnumerable<Permission> GetPermissions() {
return new[] { AccessAdmin };
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
return Enumerable.Empty<PermissionStereotype>();
}
}
}

View File

@@ -8,5 +8,11 @@ namespace Orchard.Security.Permissions {
public interface IPermissionProvider : IDependency {
string PackageName { get; }
IEnumerable<Permission> GetPermissions();
IEnumerable<PermissionStereotype> GetDefaultStereotypes();
}
public class PermissionStereotype {
public string Name { get; set; }
public IEnumerable<Permission> Permissions { get; set; }
}
}

View File

@@ -0,0 +1,27 @@
using Autofac;
using Autofac.Integration.Web;
namespace Orchard.Tasks {
class FiniteContainerProvider : IContainerProvider {
public FiniteContainerProvider(IContainer applicationContainer) {
// explicitly create a request container for the life of this object
ApplicationContainer = applicationContainer;
RequestContainer = applicationContainer.CreateInnerContainer();
// also inject this instance in case anyone asks for the container provider
RequestContainer.Build(builder => builder.Register(this).As<IContainerProvider>());
}
public void DisposeRequestContainer() {
var disposeContainer = RequestContainer;
RequestContainer = null;
if (disposeContainer != null)
disposeContainer.Dispose();
}
public IContainer ApplicationContainer { get; private set; }
public IContainer RequestContainer { get; private set; }
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Timers;
using Autofac;
using Autofac.Integration.Web;
using Orchard.Environment;
using Orchard.Logging;
@@ -57,14 +56,10 @@ namespace Orchard.Tasks {
public void DoWork() {
// makes an inner container, similar to the per-request container
var containerProvider = new ContainerProvider(_container);
var containerProvider = new FiniteContainerProvider(_container);
try {
var requestContainer = containerProvider.RequestContainer;
// also inject this instance in case anyone asks for the container provider
requestContainer.Build(builder => builder.Register(containerProvider).As<IContainerProvider>());
// resolve the manager and invoke it
var manager = requestContainer.Resolve<IBackgroundService>();
manager.Sweep();
@@ -75,24 +70,5 @@ namespace Orchard.Tasks {
}
}
class ContainerProvider : IContainerProvider {
public ContainerProvider(IContainer applicationContainer) {
// explicitly create a request container for the life of this object
ApplicationContainer = applicationContainer;
RequestContainer = applicationContainer.CreateInnerContainer();
}
public void DisposeRequestContainer() {
var disposeContainer = RequestContainer;
RequestContainer = null;
if (disposeContainer != null)
disposeContainer.Dispose();
}
public IContainer ApplicationContainer { get; private set; }
public IContainer RequestContainer { get; private set; }
}
}
}