Adding a rough-cut owner editor

--HG--
extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4043456
This commit is contained in:
loudej
2009-12-08 06:13:08 +00:00
parent ab0fbe20e1
commit 5aae644c6d
15 changed files with 231 additions and 10 deletions

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autofac.Builder;
using Moq;
using NUnit.Framework;
using Orchard.Core.Common.Models;
using Orchard.Core.Common.Providers;
using Orchard.Core.Common.Records;
using Orchard.Models;
using Orchard.Models.Driver;
using Orchard.Models.Records;
using Orchard.Security;
using Orchard.Tests.Packages;
namespace Orchard.Core.Tests.Common.Providers {
[TestFixture]
public class CommonAspectProviderTests : DatabaseEnabledTestsBase {
private Mock<IAuthenticationService> _authn;
private Mock<IAuthorizationService> _authz;
private Mock<IMembershipService> _membership;
public override void Register(ContainerBuilder builder) {
builder.Register<DefaultContentManager>().As<IContentManager>();
builder.Register<TestProvider>().As<IContentProvider>();
builder.Register<CommonAspectProvider>().As<IContentProvider>();
_authn = new Mock<IAuthenticationService>();
_authz = new Mock<IAuthorizationService>();
_membership = new Mock<IMembershipService>();
builder.Register(_authn.Object);
builder.Register(_authz.Object);
builder.Register(_membership.Object);
}
protected override IEnumerable<Type> DatabaseTypes {
get {
return new[] {typeof (ContentTypeRecord), typeof (ContentItemRecord), typeof (CommonRecord)};
}
}
class TestProvider : ContentProvider {
public TestProvider() {
Filters.Add(new ActivatingFilter<CommonAspect>("test-item"));
Filters.Add(new ActivatingFilter<TestUser>("user"));
}
}
class TestUser : ContentPart, IUser {
public int Id { get { return 6655321; } }
public string UserName {get { return "x"; }}
public string Email { get { return "y"; } }
}
[Test]
public void OwnerShouldBeNullAndZeroByDefault() {
var contentManager = _container.Resolve<IContentManager>();
var item = contentManager.Create<CommonAspect>("test-item", init => { });
ClearSession();
Assert.That(item.Owner, Is.Null);
Assert.That(item.Record.OwnerId, Is.EqualTo(0));
}
[Test,Ignore("This testing is still being worked out")]
public void OwnerShouldBeAuthenticatedUserIfAvailable() {
var contentManager = _container.Resolve<IContentManager>();
var user = contentManager.New<IUser>("user");
_authn.Setup(x => x.GetAuthenticatedUser()).Returns(user);
var item = contentManager.Create<CommonAspect>("test-item", init => { });
ClearSession();
Assert.That(item.Record.OwnerId, Is.EqualTo(6655321));
}
}
}

View File

@@ -31,10 +31,30 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Autofac, Version=1.4.4.561, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\autofac\Autofac.dll</HintPath>
</Reference>
<Reference Include="Moq, Version=4.0.812.4, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\moq\Moq.dll</HintPath>
</Reference>
<Reference Include="NHibernate.ByteCode.Castle, Version=2.1.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\fluentnhibernate\NHibernate.ByteCode.Castle.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=2.5.2.9222, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\nunit\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core"> <Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework> <RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference> </Reference>
<Reference Include="System.Data.SQLite, Version=1.0.65.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\sqlite\System.Data.SQLite.DLL</HintPath>
</Reference>
<Reference Include="System.Xml.Linq"> <Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework> <RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference> </Reference>
@@ -45,8 +65,23 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Common\Providers\CommonAspectProviderTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Orchard.Tests.Packages\Orchard.Tests.Packages.csproj">
<Project>{6CB3EB30-F725-45C0-9742-42599BA8E8D2}</Project>
<Name>Orchard.Tests.Packages</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Web\Core\Orchard.Core.csproj">
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
<Name>Orchard.Core</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard\Orchard.csproj">
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
<Name>Orchard</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -29,12 +29,12 @@ namespace Orchard.Core.Common.Models {
set { Record.ModifiedUtc = value;} set { Record.ModifiedUtc = value;}
} }
IUser ICommonAspect.Owner { public IUser Owner {
get { return _owner.Value; } get { return _owner.Value; }
set { _owner.Value = value; } set { _owner.Value = value; }
} }
IContent ICommonAspect.Container { public IContent Container {
get { return _container.Value; } get { return _container.Value; }
set { _container.Value = value; } set { _container.Value = value; }
} }

View File

@@ -1 +1 @@
Name: Content Name: Common

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.Security.Permissions;
namespace Orchard.Core.Common {
public class Permissions : IPermissionProvider {
public static Permission ChangeOwner = new Permission { Name = "ChangeOwner", Description = "Change the owner of content items" };
public string PackageName {
get { return "Common"; }
}
public IEnumerable<Permission> GetPermissions() {
return new[] { ChangeOwner };
}
}
}

View File

@@ -1,9 +1,13 @@
using System;
using Orchard.Core.Common.Models; using Orchard.Core.Common.Models;
using Orchard.Core.Common.Records; using Orchard.Core.Common.Records;
using Orchard.Core.Common.ViewModels;
using Orchard.Data; using Orchard.Data;
using Orchard.Localization;
using Orchard.Models; using Orchard.Models;
using Orchard.Models.Aspects; using Orchard.Models.Aspects;
using Orchard.Models.Driver; using Orchard.Models.Driver;
using Orchard.Models.ViewModels;
using Orchard.Security; using Orchard.Security;
using Orchard.Services; using Orchard.Services;
@@ -11,24 +15,37 @@ namespace Orchard.Core.Common.Providers {
public class CommonAspectProvider : ContentProvider { public class CommonAspectProvider : ContentProvider {
private readonly IClock _clock; private readonly IClock _clock;
private readonly IAuthenticationService _authenticationService; private readonly IAuthenticationService _authenticationService;
private readonly IAuthorizationService _authorizationService;
private readonly IMembershipService _membershipService;
private readonly IContentManager _contentManager; private readonly IContentManager _contentManager;
public CommonAspectProvider( public CommonAspectProvider(
IRepository<CommonRecord> repository, IRepository<CommonRecord> repository,
IClock clock, IClock clock,
IAuthenticationService authenticationService, IAuthenticationService authenticationService,
IAuthorizationService authorizationService,
IMembershipService membershipService,
IContentManager contentManager) { IContentManager contentManager) {
_clock = clock; _clock = clock;
_authenticationService = authenticationService; _authenticationService = authenticationService;
_authorizationService = authorizationService;
_membershipService = membershipService;
_contentManager = contentManager; _contentManager = contentManager;
T = NullLocalizer.Instance;
OnActivated<CommonAspect>(PropertySetHandlers); OnActivated<CommonAspect>(PropertySetHandlers);
OnCreating<CommonAspect>(DefaultTimestampsAndOwner); OnCreating<CommonAspect>(DefaultTimestampsAndOwner);
OnLoaded<CommonAspect>(LazyLoadHandlers); OnLoaded<CommonAspect>(LazyLoadHandlers);
Filters.Add(new StorageFilter<CommonRecord>(repository)); Filters.Add(new StorageFilter<CommonRecord>(repository));
//OnGetDisplayViewModel<CommonAspect>();
OnGetEditorViewModel<CommonAspect>(GetEditor);
OnUpdateEditorViewModel<CommonAspect>(UpdateEditor);
} }
public Localizer T { get; set; }
void DefaultTimestampsAndOwner(CreateContentContext context, CommonAspect instance) { void DefaultTimestampsAndOwner(CreateContentContext context, CommonAspect instance) {
// assign default create/modified dates // assign default create/modified dates
if (instance.Record.CreatedUtc == null) { if (instance.Record.CreatedUtc == null) {
@@ -75,15 +92,39 @@ namespace Orchard.Core.Common.Providers {
} }
protected override void UpdateEditorViewModel(UpdateEditorViewModelContext context) { private void GetEditor(GetEditorViewModelContext context, CommonAspect instance) {
var part = context.ContentItem.As<CommonAspect>(); var currentUser = _authenticationService.GetAuthenticatedUser();
if (part == null) if (!_authorizationService.CheckAccess(currentUser, Permissions.ChangeOwner)) {
return; return;
}
// this event is hooked so the modified timestamp is changed when an edit-post occurs. var viewModel = new OwnerEditorViewModel { Owner = instance.Owner.UserName };
// kind of a loose rule of thumb. may not be sufficient context.AddEditor(new TemplateViewModel(viewModel, "CommonAspect") );
part.Record.ModifiedUtc = _clock.UtcNow;
} }
private void UpdateEditor(UpdateEditorViewModelContext context, CommonAspect instance) {
// this event is hooked so the modified timestamp is changed when an edit-post occurs.
// kind of a loose rule of thumb. may not be sufficient
instance.Record.ModifiedUtc = _clock.UtcNow;
var currentUser = _authenticationService.GetAuthenticatedUser();
if (!_authorizationService.CheckAccess(currentUser, Permissions.ChangeOwner)) {
return;
}
var viewModel = new OwnerEditorViewModel { Owner = instance.Owner.UserName };
context.Updater.TryUpdateModel(viewModel, "CommonAspect", null, null);
if (viewModel.Owner != instance.Owner.UserName) {
var newOwner = _membershipService.GetUser(viewModel.Owner);
if (newOwner == null) {
context.Updater.AddModelError("CommonAspect.Owner", T("Invalid user name"));
}
else {
instance.Owner = newOwner;
}
}
context.AddEditor(new TemplateViewModel(viewModel, "CommonAspect"));
}
} }
} }

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace Orchard.Core.Common.ViewModels {
public class OwnerEditorViewModel {
[Required]
public string Owner { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<OwnerEditorViewModel>" %>
<%@ Import Namespace="Orchard.Core.Common.ViewModels" %>
<%@ Import Namespace="Orchard.Core.Common.Models" %>
<%@ Import Namespace="Orchard.Core.Settings.ViewModels" %>
<%@ Import Namespace="Orchard.Utility" %>
<fieldset>
<legend>System</legend>
<%=Html.LabelFor(m=>m.Owner) %>
<%=Html.EditorFor(m=>m.Owner) %>
<%=Html.ValidationMessageFor(m=>m.Owner) %>
</fieldset>

View File

@@ -61,6 +61,7 @@
<Reference Include="System.Web.Mobile" /> <Reference Include="System.Web.Mobile" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Common\Permissions.cs" />
<Compile Include="Common\Utilities\LazyField.cs" /> <Compile Include="Common\Utilities\LazyField.cs" />
<Compile Include="Common\Providers\CommonAspectProvider.cs" /> <Compile Include="Common\Providers\CommonAspectProvider.cs" />
<Compile Include="Common\Models\CommonAspect.cs" /> <Compile Include="Common\Models\CommonAspect.cs" />
@@ -73,6 +74,7 @@
<Compile Include="Common\Records\RoutableRecord.cs" /> <Compile Include="Common\Records\RoutableRecord.cs" />
<Compile Include="Common\ViewModels\BodyDisplayViewModel.cs" /> <Compile Include="Common\ViewModels\BodyDisplayViewModel.cs" />
<Compile Include="Common\ViewModels\BodyEditorViewModel.cs" /> <Compile Include="Common\ViewModels\BodyEditorViewModel.cs" />
<Compile Include="Common\ViewModels\OwnerEditorViewModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Settings\AdminMenu.cs" /> <Compile Include="Settings\AdminMenu.cs" />
<Compile Include="Settings\Controllers\AdminController.cs" /> <Compile Include="Settings\Controllers\AdminController.cs" />
@@ -118,6 +120,7 @@
<Content Include="XmlRpc\Views\Web.config" /> <Content Include="XmlRpc\Views\Web.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Common\Views\Models\EditorTemplates\OwnerEditorViewModel.ascx" />
<Content Include="Common\Views\Web.config" /> <Content Include="Common\Views\Web.config" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />

View File

@@ -49,6 +49,10 @@ namespace Orchard.Core.Settings.Controllers {
return TryUpdateModel(model, prefix, includeProperties, excludeProperties); return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
} }
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
ModelState.AddModelError(key, errorMessage.ToString());
}
#endregion #endregion
} }
} }

View File

@@ -150,5 +150,9 @@ namespace Orchard.Blogs.Controllers {
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) { bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
return TryUpdateModel(model, prefix, includeProperties, excludeProperties); return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
} }
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
ModelState.AddModelError(key, errorMessage.ToString());
}
} }
} }

View File

@@ -192,5 +192,8 @@ namespace Orchard.Blogs.Controllers {
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) { bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
return TryUpdateModel(model, prefix, includeProperties, excludeProperties); return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
} }
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
ModelState.AddModelError(key, errorMessage.ToString());
}
} }
} }

View File

@@ -106,5 +106,8 @@ namespace Orchard.Sandbox.Controllers {
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) { bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
return TryUpdateModel(model, prefix, includeProperties, excludeProperties); return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
} }
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
ModelState.AddModelError(key, errorMessage.ToString());
}
} }
} }

View File

@@ -101,6 +101,9 @@ namespace Orchard.Users.Controllers {
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) { bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
return TryUpdateModel(model, prefix, includeProperties, excludeProperties); return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
} }
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
ModelState.AddModelError(key, errorMessage.ToString());
}
} }
} }

View File

@@ -1,5 +1,8 @@
using Orchard.Localization;
namespace Orchard.Models.Driver { namespace Orchard.Models.Driver {
public interface IUpdateModel { public interface IUpdateModel {
bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) where TModel : class; bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) where TModel : class;
void AddModelError(string key, LocalizedString errorMessage);
} }
} }