diff --git a/src/Orchard.Web/Core/Contents/DynamicPermissions.cs b/src/Orchard.Web/Core/Contents/DynamicPermissions.cs index 2c606cafa..02f2c9b5c 100644 --- a/src/Orchard.Web/Core/Contents/DynamicPermissions.cs +++ b/src/Orchard.Web/Core/Contents/DynamicPermissions.cs @@ -38,11 +38,11 @@ namespace Orchard.Core.Contents { } public IEnumerable GetPermissions() { - // manage rights only for Creatable types - var creatableTypes = _contentDefinitionManager.ListTypeDefinitions() - .Where(ctd => ctd.Settings.GetModel().Creatable); + // manage rights only for Securable types + var securableTypes = _contentDefinitionManager.ListTypeDefinitions() + .Where(ctd => ctd.Settings.GetModel().Securable); - foreach (var typeDefinition in creatableTypes) { + foreach (var typeDefinition in securableTypes) { foreach (var permissionTemplate in PermissionTemplates.Values) { yield return CreateDynamicPermission(permissionTemplate, typeDefinition); } diff --git a/src/Orchard.Web/Core/Contents/Extensions/MetaDataExtensions.cs b/src/Orchard.Web/Core/Contents/Extensions/MetaDataExtensions.cs index 42c5c03ca..27f49c8e3 100644 --- a/src/Orchard.Web/Core/Contents/Extensions/MetaDataExtensions.cs +++ b/src/Orchard.Web/Core/Contents/Extensions/MetaDataExtensions.cs @@ -15,6 +15,10 @@ namespace Orchard.Core.Contents.Extensions { return builder.WithSetting("ContentTypeSettings.Draftable", draftable.ToString()); } + public static ContentTypeDefinitionBuilder Securable(this ContentTypeDefinitionBuilder builder, bool securable = true) { + return builder.WithSetting("ContentTypeSettings.Securable", securable.ToString()); + } + public static ContentPartDefinitionBuilder Attachable(this ContentPartDefinitionBuilder builder, bool attachable = true) { return builder.WithSetting("ContentPartSettings.Attachable", attachable.ToString()); } diff --git a/src/Orchard.Web/Core/Contents/Security/AuthorizationEventHandler.cs b/src/Orchard.Web/Core/Contents/Security/AuthorizationEventHandler.cs index 16617160d..9dcadc4cc 100644 --- a/src/Orchard.Web/Core/Contents/Security/AuthorizationEventHandler.cs +++ b/src/Orchard.Web/Core/Contents/Security/AuthorizationEventHandler.cs @@ -25,7 +25,7 @@ namespace Orchard.Core.Contents.Security { var typeDefinition = context.Content.ContentItem.TypeDefinition; // replace permission if a content type specific version exists - if (typeDefinition.Settings.GetModel().Creatable) { + if (typeDefinition.Settings.GetModel().Securable) { var permission = GetContentTypeVariation(context.Permission); if (permission != null) { diff --git a/src/Orchard.Web/Core/Contents/Settings/ContentTypeSettings.cs b/src/Orchard.Web/Core/Contents/Settings/ContentTypeSettings.cs index 25567cc29..c4c3b3a83 100644 --- a/src/Orchard.Web/Core/Contents/Settings/ContentTypeSettings.cs +++ b/src/Orchard.Web/Core/Contents/Settings/ContentTypeSettings.cs @@ -16,5 +16,9 @@ /// Defines the stereotype of the type /// public string Stereotype { get; set; } + /// + /// Used to determine if this content type supports custom permissions + /// + public bool Securable { get; set; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ContentPermissions/Migrations.cs b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Migrations.cs index 805eb8210..979cdd5cc 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentPermissions/Migrations.cs +++ b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Migrations.cs @@ -18,5 +18,11 @@ namespace Orchard.ContentPermissions { return 2; } + + public int UpdateFrom2() { + + // auto-upgrade to 3 as UpdateFrom1 is incorrectly returning 2 + return 3; + } } } diff --git a/src/Orchard.Web/Modules/Orchard.ContentPermissions/Module.txt b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Module.txt index 3faae794f..aa7e41cf2 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentPermissions/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Module.txt @@ -10,4 +10,4 @@ Features: Name: Content Item Permissions Description: Allows item-level front end view permissions. Dependencies: Orchard.Roles - Category: Security \ No newline at end of file + Category: Security diff --git a/src/Orchard.Web/Modules/Orchard.ContentPermissions/Orchard.ContentPermissions.csproj b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Orchard.ContentPermissions.csproj index caf95fb86..03054c6e7 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentPermissions/Orchard.ContentPermissions.csproj +++ b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Orchard.ContentPermissions.csproj @@ -71,6 +71,7 @@ + @@ -95,9 +96,13 @@ - + + + + + @@ -120,6 +125,9 @@ + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/src/Orchard.Web/Modules/Orchard.ContentPermissions/Security/AuthorizationEventHandler.cs b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Security/ContentPermissionsPartAuthorizationEventHandler.cs similarity index 95% rename from src/Orchard.Web/Modules/Orchard.ContentPermissions/Security/AuthorizationEventHandler.cs rename to src/Orchard.Web/Modules/Orchard.ContentPermissions/Security/ContentPermissionsPartAuthorizationEventHandler.cs index e888f8a3b..8d9cf1cad 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentPermissions/Security/AuthorizationEventHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Security/ContentPermissionsPartAuthorizationEventHandler.cs @@ -1,137 +1,137 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Orchard.ContentManagement; -using Orchard.ContentManagement.Aspects; -using Orchard.Roles.Models; -using Orchard.Security; -using Orchard.ContentPermissions.Models; -using Orchard.Security.Permissions; - -namespace Orchard.ContentPermissions.Security { - public class AuthorizationEventHandler : IAuthorizationServiceEventHandler { - private readonly IWorkContextAccessor _workContextAccessor; - private static readonly string[] AnonymousRole = new[] { "Anonymous" }; - private static readonly string[] AuthenticatedRole = new[] { "Authenticated" }; - - public AuthorizationEventHandler(IWorkContextAccessor workContextAccessor) { - _workContextAccessor = workContextAccessor; - } - - public void Checking(CheckAccessContext context) { } - public void Adjust(CheckAccessContext context) { } - - public void Complete(CheckAccessContext context) { - - if (!String.IsNullOrEmpty(_workContextAccessor.GetContext().CurrentSite.SuperUser) - && context.User != null - && String.Equals(context.User.UserName, _workContextAccessor.GetContext().CurrentSite.SuperUser, StringComparison.Ordinal)) { - context.Granted = true; - return; - } - - if (context.Content == null) { - return; - } - - var part = context.Content.As(); - - // if the content item has no right attached, check on the container - if (part == null || !part.Enabled) { - var commonPart = context.Content.As(); - if(commonPart != null && commonPart.Container != null) { - part = commonPart.Container.As(); - } - } - - if (part == null || !part.Enabled) { - return; - } - - var hasOwnership = HasOwnership(context.User, context.Content); - - IEnumerable authorizedRoles; - - var grantingPermissions = PermissionNames(context.Permission, Enumerable.Empty()).Distinct().ToArray(); - - if (grantingPermissions.Any(grantingPermission => String.Equals(Core.Contents.Permissions.ViewContent.Name, grantingPermission, StringComparison.OrdinalIgnoreCase))) - { - authorizedRoles = (hasOwnership ? part.ViewOwnContent : part.ViewContent).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - } - else if (grantingPermissions.Any(grantingPermission => String.Equals(Core.Contents.Permissions.EditContent.Name, grantingPermission, StringComparison.OrdinalIgnoreCase))) - { - authorizedRoles = (hasOwnership ? part.EditOwnContent : part.EditContent).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - } - else if (grantingPermissions.Any(grantingPermission => String.Equals(Core.Contents.Permissions.PublishContent.Name, grantingPermission, StringComparison.OrdinalIgnoreCase))) - { - authorizedRoles = (hasOwnership ? part.PublishOwnContent : part.PublishContent).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - } - else if (grantingPermissions.Any(grantingPermission => String.Equals(Core.Contents.Permissions.DeleteContent.Name, grantingPermission, StringComparison.OrdinalIgnoreCase))) - { - authorizedRoles = (hasOwnership ? part.DeleteOwnContent : part.DeleteContent).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - } - else - { - return; - } - - // determine what set of roles should be examined by the access check - IEnumerable rolesToExamine; - if (context.User == null) { - rolesToExamine = AnonymousRole; - } - else if (context.User.Has()) { - // the current user is not null, so get his roles and add "Authenticated" to it - rolesToExamine = context.User.As().Roles; - - // when it is a simulated anonymous user in the admin - if (!rolesToExamine.Contains(AnonymousRole[0])) { - rolesToExamine = rolesToExamine.Concat(AuthenticatedRole); - } - } - else { - // the user is not null and has no specific role, then it's just "Authenticated" - rolesToExamine = AuthenticatedRole; - } - - context.Granted = rolesToExamine.Any(x => authorizedRoles.Contains(x, StringComparer.OrdinalIgnoreCase)); - context.Adjusted = true; - } - - private static bool HasOwnership(IUser user, IContent content) { - if (user == null || content == null) - return false; - - var common = content.As(); - if (common == null || common.Owner == null) - return false; - - return user.Id == common.Owner.Id; - } - - private static IEnumerable PermissionNames(Permission permission, IEnumerable stack) - { - // the given name is tested - yield return permission.Name; - - // iterate implied permissions to grant, it present - if (permission.ImpliedBy != null && permission.ImpliedBy.Any()) - { - foreach (var impliedBy in permission.ImpliedBy) - { - // avoid potential recursion - if (stack.Contains(impliedBy.Name)) - continue; - - // otherwise accumulate the implied permission names recursively - foreach (var impliedName in PermissionNames(impliedBy, stack.Concat(new[] { permission.Name }))) - { - yield return impliedName; - } - } - } - - yield return StandardPermissions.SiteOwner.Name; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using Orchard.ContentManagement; +using Orchard.ContentManagement.Aspects; +using Orchard.Roles.Models; +using Orchard.Security; +using Orchard.ContentPermissions.Models; +using Orchard.Security.Permissions; + +namespace Orchard.ContentPermissions.Security { + public class ContentPermissionsPartAuthorizationEventHandler : IAuthorizationServiceEventHandler { + private readonly IWorkContextAccessor _workContextAccessor; + private static readonly string[] AnonymousRole = new[] { "Anonymous" }; + private static readonly string[] AuthenticatedRole = new[] { "Authenticated" }; + + public ContentPermissionsPartAuthorizationEventHandler(IWorkContextAccessor workContextAccessor) { + _workContextAccessor = workContextAccessor; + } + + public void Checking(CheckAccessContext context) { } + public void Adjust(CheckAccessContext context) { } + + public void Complete(CheckAccessContext context) { + + if (!String.IsNullOrEmpty(_workContextAccessor.GetContext().CurrentSite.SuperUser) + && context.User != null + && String.Equals(context.User.UserName, _workContextAccessor.GetContext().CurrentSite.SuperUser, StringComparison.Ordinal)) { + context.Granted = true; + return; + } + + if (context.Content == null) { + return; + } + + var part = context.Content.As(); + + // if the content item has no right attached, check on the container + if (part == null || !part.Enabled) { + var commonPart = context.Content.As(); + if(commonPart != null && commonPart.Container != null) { + part = commonPart.Container.As(); + } + } + + if (part == null || !part.Enabled) { + return; + } + + var hasOwnership = HasOwnership(context.User, context.Content); + + IEnumerable authorizedRoles; + + var grantingPermissions = PermissionNames(context.Permission, Enumerable.Empty()).Distinct().ToArray(); + + if (grantingPermissions.Any(grantingPermission => String.Equals(Core.Contents.Permissions.ViewContent.Name, grantingPermission, StringComparison.OrdinalIgnoreCase))) + { + authorizedRoles = (hasOwnership ? part.ViewOwnContent : part.ViewContent).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + } + else if (grantingPermissions.Any(grantingPermission => String.Equals(Core.Contents.Permissions.EditContent.Name, grantingPermission, StringComparison.OrdinalIgnoreCase))) + { + authorizedRoles = (hasOwnership ? part.EditOwnContent : part.EditContent).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + } + else if (grantingPermissions.Any(grantingPermission => String.Equals(Core.Contents.Permissions.PublishContent.Name, grantingPermission, StringComparison.OrdinalIgnoreCase))) + { + authorizedRoles = (hasOwnership ? part.PublishOwnContent : part.PublishContent).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + } + else if (grantingPermissions.Any(grantingPermission => String.Equals(Core.Contents.Permissions.DeleteContent.Name, grantingPermission, StringComparison.OrdinalIgnoreCase))) + { + authorizedRoles = (hasOwnership ? part.DeleteOwnContent : part.DeleteContent).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + } + else + { + return; + } + + // determine what set of roles should be examined by the access check + IEnumerable rolesToExamine; + if (context.User == null) { + rolesToExamine = AnonymousRole; + } + else if (context.User.Has()) { + // the current user is not null, so get his roles and add "Authenticated" to it + rolesToExamine = context.User.As().Roles; + + // when it is a simulated anonymous user in the admin + if (!rolesToExamine.Contains(AnonymousRole[0])) { + rolesToExamine = rolesToExamine.Concat(AuthenticatedRole); + } + } + else { + // the user is not null and has no specific role, then it's just "Authenticated" + rolesToExamine = AuthenticatedRole; + } + + context.Granted = rolesToExamine.Any(x => authorizedRoles.Contains(x, StringComparer.OrdinalIgnoreCase)); + context.Adjusted = true; + } + + private static bool HasOwnership(IUser user, IContent content) { + if (user == null || content == null) + return false; + + var common = content.As(); + if (common == null || common.Owner == null) + return false; + + return user.Id == common.Owner.Id; + } + + private static IEnumerable PermissionNames(Permission permission, IEnumerable stack) + { + // the given name is tested + yield return permission.Name; + + // iterate implied permissions to grant, it present + if (permission.ImpliedBy != null && permission.ImpliedBy.Any()) + { + foreach (var impliedBy in permission.ImpliedBy) + { + // avoid potential recursion + if (stack.Contains(impliedBy.Name)) + continue; + + // otherwise accumulate the implied permission names recursively + foreach (var impliedName in PermissionNames(impliedBy, stack.Concat(new[] { permission.Name }))) + { + yield return impliedName; + } + } + } + + yield return StandardPermissions.SiteOwner.Name; + } + } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ContentPermissions/Security/SecurableContentItemsAuthorizationEventHandler.cs b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Security/SecurableContentItemsAuthorizationEventHandler.cs new file mode 100644 index 000000000..1b841283f --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Security/SecurableContentItemsAuthorizationEventHandler.cs @@ -0,0 +1,82 @@ +using System; +using Orchard.ContentManagement; +using Orchard.ContentManagement.Aspects; +using Orchard.ContentManagement.MetaData.Models; +using Orchard.Localization; +using Orchard.Security; +using Orchard.Security.Permissions; +using System.Linq; +using Orchard.ContentPermissions.Settings; +using Orchard.ContentPermissions.Services; + +namespace Orchard.ContentPermissions.Security { + public class SecurableContentItemsAuthorizationEventHandler : IAuthorizationServiceEventHandler { + private readonly IContentManager _contentManager; + + public SecurableContentItemsAuthorizationEventHandler(IContentManager contentManager) { + _contentManager = contentManager; + } + + public Localizer T { get; set; } + + public void Checking(CheckAccessContext context) { } + public void Complete(CheckAccessContext context) { } + + public void Adjust(CheckAccessContext context) { + if (!context.Granted && + context.Content.Is()) { + + if (OwnerVariationExists(context.Permission) && + HasOwnership(context.User, context.Content)) { + + context.Adjusted = true; + context.Permission = GetOwnerVariation(context.Permission); + } + + var typeDefinition = context.Content.ContentItem.TypeDefinition; + + if (typeDefinition.Settings.GetModel().SecurableContentItems) { + // replace permission if a content item specific version exists + var permission = GetContentTypeVariation(context.Permission); + + if (permission != null) { + context.Adjusted = true; + context.Permission = DynamicPermissions.CreateItemPermission(permission, context.Content, T, _contentManager); + } + } + } + } + + private static bool HasOwnership(IUser user, IContent content) { + if (user == null || content == null) + return false; + + var common = content.As(); + if (common == null || common.Owner == null) + return false; + + return user.Id == common.Owner.Id; + } + + private static bool OwnerVariationExists(Permission permission) { + return GetOwnerVariation(permission) != null; + } + + private static Permission GetOwnerVariation(Permission permission) { + if (permission.Name == Orchard.Core.Contents.Permissions.PublishContent.Name) + return Orchard.Core.Contents.Permissions.PublishOwnContent; + if (permission.Name == Orchard.Core.Contents.Permissions.EditContent.Name) + return Orchard.Core.Contents.Permissions.EditOwnContent; + if (permission.Name == Orchard.Core.Contents.Permissions.DeleteContent.Name) + return Orchard.Core.Contents.Permissions.DeleteOwnContent; + if (permission.Name == Orchard.Core.Contents.Permissions.ViewContent.Name) + return Orchard.Core.Contents.Permissions.ViewOwnContent; + return null; + } + + private static Permission GetContentTypeVariation(Permission permission) { + return DynamicPermissions.ConvertToDynamicPermission(permission); + } + + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ContentPermissions/Services/DynamicPermissions.cs b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Services/DynamicPermissions.cs new file mode 100644 index 000000000..f95dd974d --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Services/DynamicPermissions.cs @@ -0,0 +1,94 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using Orchard.ContentManagement.MetaData; +using Orchard.ContentPermissions.Settings; +using Orchard.Environment.Extensions.Models; +using Orchard.Security.Permissions; +using Orchard.ContentManagement; +using Orchard.Localization; + +namespace Orchard.ContentPermissions.Services { + public class DynamicPermissions : IPermissionProvider { + private static readonly Permission PublishContent = new Permission { Description = "Publish or unpublish {0} for others", Name = "Publish_{0}", ImpliedBy = new[] { Orchard.Core.Contents.Permissions.PublishContent } }; + private static readonly Permission PublishOwnContent = new Permission { Description = "Publish or unpublish {0}", Name = "PublishOwn_{0}", ImpliedBy = new[] { PublishContent, Orchard.Core.Contents.Permissions.PublishOwnContent } }; + private static readonly Permission EditContent = new Permission { Description = "Edit {0} for others", Name = "Edit_{0}", ImpliedBy = new[] { PublishContent, Orchard.Core.Contents.Permissions.EditContent } }; + private static readonly Permission EditOwnContent = new Permission { Description = "Edit {0}", Name = "EditOwn_{0}", ImpliedBy = new[] { EditContent, PublishOwnContent, Orchard.Core.Contents.Permissions.EditOwnContent } }; + private static readonly Permission DeleteContent = new Permission { Description = "Delete {0} for others", Name = "Delete_{0}", ImpliedBy = new[] { Orchard.Core.Contents.Permissions.DeleteContent } }; + private static readonly Permission DeleteOwnContent = new Permission { Description = "Delete {0}", Name = "DeleteOwn_{0}", ImpliedBy = new[] { DeleteContent, Orchard.Core.Contents.Permissions.DeleteOwnContent } }; + private static readonly Permission ViewContent = new Permission { Description = "View {0} by others", Name = "View_{0}", ImpliedBy = new[] { EditContent, Orchard.Core.Contents.Permissions.ViewContent } }; + private static readonly Permission ViewOwnContent = new Permission { Description = "View own {0}", Name = "ViewOwn_{0}", ImpliedBy = new[] { ViewContent, Orchard.Core.Contents.Permissions.ViewOwnContent } }; + + public static readonly Dictionary PermissionTemplates = new Dictionary { + {Orchard.Core.Contents.Permissions.PublishContent.Name, PublishContent}, + {Orchard.Core.Contents.Permissions.PublishOwnContent.Name, PublishOwnContent}, + {Orchard.Core.Contents.Permissions.EditContent.Name, EditContent}, + {Orchard.Core.Contents.Permissions.EditOwnContent.Name, EditOwnContent}, + {Orchard.Core.Contents.Permissions.DeleteContent.Name, DeleteContent}, + {Orchard.Core.Contents.Permissions.DeleteOwnContent.Name, DeleteOwnContent}, + {Orchard.Core.Contents.Permissions.ViewContent.Name, ViewContent}, + {Orchard.Core.Contents.Permissions.ViewOwnContent.Name, ViewOwnContent} + }; + + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentManager _contentManager; + + public virtual Feature Feature { get; set; } + public Localizer T { get; set; } + + public DynamicPermissions(IContentDefinitionManager contentDefinitionManager, IContentManager contentManager) { + _contentDefinitionManager = contentDefinitionManager; + _contentManager = contentManager; + + T = NullLocalizer.Instance; + } + + public IEnumerable GetPermissions() { + + // manage rights only for Securable types + var securableTypes = _contentDefinitionManager.ListTypeDefinitions() + .Where(ctd => ctd.Settings.GetModel().SecurableContentItems) + .ToList(); + + foreach (var typeDefinition in securableTypes) { + foreach (var content in _contentManager.Query(typeDefinition.Name).List()) { + foreach (var permissionTemplate in PermissionTemplates.Values) { + yield return CreateItemPermission(permissionTemplate, content, T, _contentManager); + } + } + } + } + + public IEnumerable GetDefaultStereotypes() { + return Enumerable.Empty(); + } + + + /// + /// Generates a permission dynamically for a content item + /// + public static Permission CreateItemPermission(Permission template, IContent content, Localizer T, IContentManager contentManager) { + var identity = contentManager.GetItemMetadata(content).Identity.ToString(); + var displayText = contentManager.GetItemMetadata(content).DisplayText; + var typeDefinition = content.ContentItem.TypeDefinition; + + return new Permission { + Name = String.Format(template.Name, identity), + Description = String.Format(template.Description, typeDefinition.DisplayName), + Category = T("{0} - {1}", typeDefinition.DisplayName, displayText).ToString(), + ImpliedBy = (template.ImpliedBy ?? new Permission[0]).Select(t => CreateItemPermission(t, content, T, contentManager)) + }; + } + + /// + /// Returns a dynamic permission for a content type, based on a global content permission template + /// + public static Permission ConvertToDynamicPermission(Permission permission) { + if (PermissionTemplates.ContainsKey(permission.Name)) { + return PermissionTemplates[permission.Name]; + } + + return null; + } + } +} diff --git a/src/Orchard.Web/Modules/Orchard.ContentPermissions/Settings/ContentPermissionsTypeSettings.cs b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Settings/ContentPermissionsTypeSettings.cs new file mode 100644 index 000000000..d732ad333 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Settings/ContentPermissionsTypeSettings.cs @@ -0,0 +1,8 @@ +namespace Orchard.ContentPermissions.Settings { + public class ContentPermissionsTypeSettings { + /// + /// Used to determine if instances of this content type supports custom permissions + /// + public bool SecurableContentItems { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ContentPermissions/Settings/SecurableContentItemsEditorEvents.cs b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Settings/SecurableContentItemsEditorEvents.cs new file mode 100644 index 000000000..7522911ef --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Settings/SecurableContentItemsEditorEvents.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Orchard.ContentManagement; +using Orchard.ContentManagement.MetaData; +using Orchard.ContentManagement.MetaData.Builders; +using Orchard.ContentManagement.MetaData.Models; +using Orchard.ContentManagement.ViewModels; +using Orchard.ContentPermissions.ViewModels; + +namespace Orchard.ContentPermissions.Settings { + public class SecurableContentItemsEditorEvents : ContentDefinitionEditorEventsBase { + + public override IEnumerable TypeEditor(ContentTypeDefinition definition) { + var settings = definition.Settings.GetModel(); + var model = new SecurableContentItemsSettingsViewModel { + SecurableContentItems = settings.SecurableContentItems, + }; + + yield return DefinitionTemplate(model); + } + + public override IEnumerable TypeEditorUpdate(ContentTypeDefinitionBuilder builder, IUpdateModel updateModel) { + var model = new SecurableContentItemsSettingsViewModel(); + updateModel.TryUpdateModel(model, "SecurableContentItemsSettingsViewModel", null, null); + + builder.WithSetting("ContentPermissionsTypeSettings.SecurableContentItems", model.SecurableContentItems.ToString()); + + yield return DefinitionTemplate(model); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ContentPermissions/ViewModels/SecurableContentItemsSettingsViewModel.cs b/src/Orchard.Web/Modules/Orchard.ContentPermissions/ViewModels/SecurableContentItemsSettingsViewModel.cs new file mode 100644 index 000000000..aef4a028d --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.ContentPermissions/ViewModels/SecurableContentItemsSettingsViewModel.cs @@ -0,0 +1,5 @@ +namespace Orchard.ContentPermissions.ViewModels { + public class SecurableContentItemsSettingsViewModel { + public bool SecurableContentItems { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ContentPermissions/Views/DefinitionTemplates/SecurableContentItemsSettingsViewModel.cshtml b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Views/DefinitionTemplates/SecurableContentItemsSettingsViewModel.cshtml new file mode 100644 index 000000000..75c269240 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.ContentPermissions/Views/DefinitionTemplates/SecurableContentItemsSettingsViewModel.cshtml @@ -0,0 +1,8 @@ +@model Orchard.ContentPermissions.ViewModels.SecurableContentItemsSettingsViewModel + +
+ @Html.EditorFor(m => m.SecurableContentItems) + + @Html.ValidationMessageFor(m => m.SecurableContentItems) + @T("Determines if an instance of this content type can have custom permissions.") +
diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Migrations.cs b/src/Orchard.Web/Modules/Orchard.ContentTypes/Migrations.cs index a16f9c2ae..6d5592a70 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Migrations.cs +++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Migrations.cs @@ -22,5 +22,15 @@ namespace Orchard.ContentTypes { return 1; } + public int UpgradeFrom1() { + foreach (var typeDefinition in _contentDefinitionManager.ListTypeDefinitions()) { + if (typeDefinition.Settings.ContainsKey("ContentTypeSettings.Creatable") && Convert.ToBoolean(typeDefinition.Settings["ContentTypeSettings.Creatable"], CultureInfo.InvariantCulture)) { + typeDefinition.Settings["ContentTypeSettings.Securable"] = "True"; + _contentDefinitionManager.StoreTypeDefinition(typeDefinition); + } + } + + return 2; + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs b/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs index a4e229447..47c62af62 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs +++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs @@ -87,7 +87,7 @@ namespace Orchard.ContentTypes.Services { var contentTypeDefinition = new ContentTypeDefinition(name, displayName); _contentDefinitionManager.StoreTypeDefinition(contentTypeDefinition); - _contentDefinitionManager.AlterTypeDefinition(name, cfg => cfg.Creatable().Draftable().Listable()); + _contentDefinitionManager.AlterTypeDefinition(name, cfg => cfg.Creatable().Draftable().Listable().Securable()); _contentDefinitionEventHandlers.ContentTypeCreated(new ContentTypeCreatedContext { ContentTypeDefinition = contentTypeDefinition}); return contentTypeDefinition; diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Settings/EditorEvents.cs b/src/Orchard.Web/Modules/Orchard.ContentTypes/Settings/EditorEvents.cs index ae9c7c2a3..dbd9fdb67 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Settings/EditorEvents.cs +++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Settings/EditorEvents.cs @@ -18,6 +18,7 @@ namespace Orchard.ContentTypes.Settings { Creatable = settings.Creatable, Listable = settings.Listable, Draftable = settings.Draftable, + Securable = settings.Securable, }; if(definition.Settings.ContainsKey("Stereotype")) { @@ -34,6 +35,7 @@ namespace Orchard.ContentTypes.Settings { builder.Creatable(model.Creatable); builder.Listable(model.Listable); builder.Draftable(model.Draftable); + builder.Securable(model.Securable); builder.WithSetting("Stereotype", model.Stereotype); yield return DefinitionTemplate(model); diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/ViewModels/ContentTypeSettingsViewModel.cs b/src/Orchard.Web/Modules/Orchard.ContentTypes/ViewModels/ContentTypeSettingsViewModel.cs index a5cf3a2c1..79dccf94c 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentTypes/ViewModels/ContentTypeSettingsViewModel.cs +++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/ViewModels/ContentTypeSettingsViewModel.cs @@ -3,6 +3,7 @@ public bool Creatable { get; set; } public bool Listable { get; set; } public bool Draftable { get; set; } + public bool Securable { get; set; } public string Stereotype { get; set; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/DefinitionTemplates/ContentTypeSettingsViewModel.cshtml b/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/DefinitionTemplates/ContentTypeSettingsViewModel.cshtml index 5afe640f5..3ed5a909b 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/DefinitionTemplates/ContentTypeSettingsViewModel.cshtml +++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/DefinitionTemplates/ContentTypeSettingsViewModel.cshtml @@ -21,6 +21,13 @@ @T("Determines if this content type supports draft versions.") +
+ @Html.EditorFor(m => m.Securable) + + @Html.ValidationMessageFor(m => m.Securable) + @T("Determines if this content type can have custom permissions.") +
+
@Html.TextBoxFor(m => m.Stereotype, new { @class = "text medium"}) diff --git a/src/Orchard.Web/Modules/Orchard.Pages/Migrations.cs b/src/Orchard.Web/Modules/Orchard.Pages/Migrations.cs index 22307acf0..9f6a12240 100644 --- a/src/Orchard.Web/Modules/Orchard.Pages/Migrations.cs +++ b/src/Orchard.Web/Modules/Orchard.Pages/Migrations.cs @@ -17,7 +17,9 @@ namespace Orchard.Pages { .WithSetting("AutorouteSettings.PatternDefinitions", "[{\"Name\":\"Title\",\"Pattern\":\"{Content.Slug}\",\"Description\":\"my-page\"}]") .WithSetting("AutorouteSettings.DefaultPatternIndex", "0")) .WithPart("BodyPart") - .Creatable()); + .Creatable() + .Listable() + .Securable()); return 3; }