Skip to content
Permalink
Browse files

Release CloneModelViewModule #386

  • Loading branch information...
apobekiaris committed Apr 26, 2019
1 parent bc97c5f commit 15b3f0b50187f2fc5ee84a3a023e9a93d4a347ac
@@ -1,4 +1,3 @@
#!/bin/sh

powershell $PWD/.GitHooks/UpdateWiki.ps1
dev
@@ -0,0 +1,17 @@
using System;

namespace Xpand.XAF.Modules.CloneModelView{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)]
public sealed class CloneModelViewAttribute : Attribute {
public CloneModelViewAttribute(CloneViewType viewType, string viewId, bool isDefault = false) {
ViewType = viewType;
ViewId = viewId;
IsDefault = isDefault;
}

public bool IsDefault { get; }
public string ViewId { get; }
public CloneViewType ViewType { get; }
public string DetailView { get; set; }
}
}
@@ -0,0 +1,7 @@
namespace Xpand.XAF.Modules.CloneModelView{
public enum CloneViewType {
DetailView,
ListView,
LookupListView
}
}
@@ -0,0 +1,55 @@
using System;
using System.Linq;
using DevExpress.ExpressApp.Model;
using DevExpress.ExpressApp.Model.Core;
using DevExpress.ExpressApp.Model.NodeGenerators;

namespace Xpand.XAF.Modules.CloneModelView{
public class ModelViewClonerUpdater : ModelNodesGeneratorUpdater<ModelViewsNodesGenerator> {
public override void UpdateNode(ModelNode node) {
var modelClasses = node.Application.BOModel.Where(modelClass => modelClass.TypeInfo.FindAttribute<CloneModelViewAttribute>() != null);

foreach (var modelClass in modelClasses) {
var cloneViewAttributes = modelClass.TypeInfo.FindAttributes<CloneModelViewAttribute>(false).OrderBy(viewAttribute => viewAttribute.ViewType);
foreach (var cloneViewAttribute in cloneViewAttributes) {
if (node.Application.Views[cloneViewAttribute.ViewId]==null) {
var tuple = GetModelView(modelClass, cloneViewAttribute);
var cloneNodeFrom = ((ModelNode)tuple.objectView).Clone(cloneViewAttribute.ViewId);
AssignAsDefaultView(cloneViewAttribute, (IModelObjectView) cloneNodeFrom,tuple.isLookup);
if (tuple.objectView is IModelListView && !(string.IsNullOrEmpty(cloneViewAttribute.DetailView))) {
var modelDetailView =node.Application.Views.OfType<IModelDetailView>().FirstOrDefault(view
=> view.Id == cloneViewAttribute.DetailView);
if (modelDetailView == null)
throw new NullReferenceException(cloneViewAttribute.DetailView);
((IModelListView) cloneNodeFrom).DetailView = modelDetailView;
}
}
}
}
}

void AssignAsDefaultView(CloneModelViewAttribute cloneModelViewAttribute, IModelObjectView modelView,bool isLookup) {
if (cloneModelViewAttribute.IsDefault) {
if (modelView is IModelListView view) {
if (!isLookup){
view.ModelClass.DefaultListView = view;
}
else{
view.ModelClass.DefaultLookupListView = view;
}
}
else {
modelView.ModelClass.DefaultDetailView = (IModelDetailView) modelView;
}
}
}

(IModelObjectView objectView,bool isLookup) GetModelView(IModelClass modelClass, CloneModelViewAttribute cloneModelViewAttribute) {
if (cloneModelViewAttribute.ViewType == CloneViewType.LookupListView)
return (modelClass.DefaultLookupListView,true);
if (cloneModelViewAttribute.ViewType == CloneViewType.DetailView)
return (modelClass.DefaultDetailView,false);
return (modelClass.DefaultListView,false);
}
}
}
@@ -0,0 +1,18 @@
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Model.Core;
using DevExpress.ExpressApp.SystemModule;

namespace Xpand.XAF.Modules.CloneModelView{
public sealed class CloneModelViewModule : ModuleBase{
public const string CategoryName = "Xpand.XAF.Modules.CloneModelView";

public CloneModelViewModule(){
RequiredModuleTypes.Add(typeof(SystemModule));
}

public override void AddGeneratorUpdaters(ModelNodesGeneratorUpdaters updaters){
base.AddGeneratorUpdaters(updaters);
updaters.Add(new ModelViewClonerUpdater());
}
}
}
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Xpand.XAF.Modules.CloneModelView")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Xpand.XAF.Modules.CloneModelView")]
[assembly: AssemblyCopyright("Copyright eXpandFramework � 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:

[assembly: AssemblyVersion("0.0.1.0")]
[assembly: AssemblyFileVersion("0.0.1.0")]
//[assembly:AllowPartiallyTrustedCallers]
//[assembly: SecurityTransparent()]
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<DXVersion>18.2.3</DXVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{FB456706-C7F0-455C-B2F1-D75B143FDC28}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xpand.XAF.Modules.CloneModelView</RootNamespace>
<AssemblyName>Xpand.XAF.Modules.CloneModelView</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<LangVersion>latest</LangVersion>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\..\Xpand.key\xpand.snk</AssemblyOriginatorKeyFile>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\..\..\bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>latest</LangVersion>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\..\Xpand.key\xpand.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\..\..\bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>latest</LangVersion>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\..\Xpand.key\xpand.snk</AssemblyOriginatorKeyFile>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Reference Include="DevExpress.ExpressApp.v18.2">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\bin\DevExpress.ExpressApp.v18.2.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\System.ValueTuple.4.5.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CloneModelViewAttribute.cs" />
<Compile Include="CloneViewType.cs" />
<Compile Include="ModelViewClonerUpdater.cs" />
<Compile Include="Module.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
<None Include="Readme.md" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.ValueTuple" version="4.5.0" targetFramework="net452" />
</packages>
@@ -32,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xpand.XAF.Modules.SuppressC
{58399364-9ACA-429F-A276-7F01E9E61D7D} = {58399364-9ACA-429F-A276-7F01E9E61D7D}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xpand.XAF.Modules.CloneModelView", "Agnostic\CloneModelView\Xpand.XAF.Modules.CloneModelView.csproj", "{FB456706-C7F0-455C-B2F1-D75B143FDC28}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -81,6 +83,12 @@ Global
{7D3556B8-8CF9-4622-94F1-0F28A7765D0E}.EasyTest|Any CPU.Build.0 = Release|Any CPU
{7D3556B8-8CF9-4622-94F1-0F28A7765D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D3556B8-8CF9-4622-94F1-0F28A7765D0E}.Release|Any CPU.Build.0 = Release|Any CPU
{FB456706-C7F0-455C-B2F1-D75B143FDC28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FB456706-C7F0-455C-B2F1-D75B143FDC28}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FB456706-C7F0-455C-B2F1-D75B143FDC28}.EasyTest|Any CPU.ActiveCfg = Release|Any CPU
{FB456706-C7F0-455C-B2F1-D75B143FDC28}.EasyTest|Any CPU.Build.0 = Release|Any CPU
{FB456706-C7F0-455C-B2F1-D75B143FDC28}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FB456706-C7F0-455C-B2F1-D75B143FDC28}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -47,16 +47,16 @@ public class CloneMemberValueTests : BaseTest{
}

var detailViews = CloneMemberValueService.DetailViewPairs.Replay();
var disposable = detailViews.Connect();

var objectSpace1 = application.CreateObjectSpace();
var detailView1 = application.CreateDetailView(objectSpace1, objectSpace1.CreateObject<ACmv>());
var objectSpace2 = application.CreateObjectSpace();
var detailView2 = application.CreateDetailView(objectSpace2, objectSpace2.CreateObject<ACmv>());

var viewsTuple = await detailViews.FirstAsync();
viewsTuple.previous.ShouldBe(detailView1);
viewsTuple.current.ShouldBe(detailView2);
using (detailViews.Connect()){
var objectSpace1 = application.CreateObjectSpace();
var detailView1 = application.CreateDetailView(objectSpace1, objectSpace1.CreateObject<ACmv>());
var objectSpace2 = application.CreateObjectSpace();
var detailView2 = application.CreateDetailView(objectSpace2, objectSpace2.CreateObject<ACmv>());

var viewsTuple = await detailViews.FirstAsync();
viewsTuple.previous.ShouldBe(detailView1);
viewsTuple.current.ShouldBe(detailView2);
}
}

[Fact]
@@ -0,0 +1,9 @@
using DevExpress.Xpo;
using Xpand.XAF.Persistent.BaseImpl;

namespace Xpand.XAF.Agnostic.Tests.Modules.CloneModelView.BOModel{
public class CMV:CustomBaseObject{
public CMV(Session session) : base(session){
}
}
}
@@ -0,0 +1,104 @@
using System;
using System.Linq;
using System.Reactive.Linq;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.DC;
using DevExpress.ExpressApp.Model;
using Fasterflect;
using Shouldly;
using Xpand.XAF.Agnostic.Tests.Artifacts;
using Xpand.XAF.Agnostic.Tests.Modules.CloneModelView.BOModel;
using Xpand.XAF.Modules.CloneModelView;
using Xpand.XAF.Modules.Reactive;
using Xpand.XAF.Modules.Reactive.Services;
using Xunit;

namespace Xpand.XAF.Agnostic.Tests.Modules.CloneModelView{
[Collection(nameof(XafTypesInfo))]
public class CloneModelViewTests : BaseTest{
[Theory]
[InlineData(CloneViewType.LookupListView)]
[InlineData(CloneViewType.ListView)]
[InlineData(CloneViewType.DetailView)]
public void Clone_Model_View(CloneViewType cloneViewType){
var cloneViewId = $"test_{cloneViewType}";

using (var application = DefaultCloneModelViewModule(info => {
var cloneModelViewAttribute = new CloneModelViewAttribute(cloneViewType, cloneViewId);
info.FindTypeInfo(typeof(CMV)).AddAttribute(cloneModelViewAttribute);
}).Application){
var modelView = application.Model.Views[cloneViewId];
modelView.ShouldNotBeNull();
modelView.GetType().Name.ShouldBe($"Model{cloneViewType.ToString().Replace("Lookup", "")}");
modelView.Id.ShouldBe(cloneViewId);
}
}

[Fact]
public void Clone_multiple_Model_Views(){
var cloneViewId = "test_";
var cloneViewTypes = Enum.GetValues(typeof(CloneViewType)).Cast<CloneViewType>();
using (var application = DefaultCloneModelViewModule(info => {
foreach (var cloneViewType in cloneViewTypes){
var cloneModelViewAttribute = new CloneModelViewAttribute(cloneViewType, $"{cloneViewId}{cloneViewType}");
info.FindTypeInfo(typeof(CMV)).AddAttribute(cloneModelViewAttribute);
}

}).Application){
foreach (var cloneViewType in cloneViewTypes){
var viewId = $"{cloneViewId}{cloneViewType}";
var modelView = application.Model.Views[viewId];
modelView.ShouldNotBeNull();
modelView.GetType().Name.ShouldBe($"Model{cloneViewType.ToString().Replace("Lookup", "")}");
modelView.Id.ShouldBe(viewId);
}
}
}

[Theory]
[InlineData(CloneViewType.LookupListView)]
[InlineData(CloneViewType.ListView)]
[InlineData(CloneViewType.DetailView)]
public void Clone_Model_View_and_make_it_default(CloneViewType cloneViewType){
var cloneViewId = $"test_{cloneViewType}";

using (var application = DefaultCloneModelViewModule(info => {
var cloneModelViewAttribute = new CloneModelViewAttribute(cloneViewType, cloneViewId,true);
info.FindTypeInfo(typeof(CMV)).AddAttribute(cloneModelViewAttribute);
}).Application){
var modelView = application.Model.Views[cloneViewId].AsObjectView;

((IModelView) modelView.ModelClass.GetPropertyValue($"Default{cloneViewType}")).Id.ShouldBe(cloneViewId);
}
}

[Theory]
[InlineData(CloneViewType.LookupListView)]
[InlineData(CloneViewType.ListView)]
public void Clone_Model_ListView_and_change_its_detailview(CloneViewType cloneViewType){
var cloneViewId = "test_";
var listViewId = $"{cloneViewId}{cloneViewType}";
var detailViewId = $"{cloneViewType}DetailView";
using (var application = DefaultCloneModelViewModule(info => {
var typeInfo = info.FindTypeInfo(typeof(CMV));
typeInfo.AddAttribute(new CloneModelViewAttribute(CloneViewType.DetailView, detailViewId));
typeInfo.AddAttribute(new CloneModelViewAttribute(cloneViewType, listViewId){DetailView = detailViewId});
}).Application){
var modelListView = (IModelListView) application.Model.Views[listViewId];
modelListView.DetailView.Id.ShouldBe(detailViewId);
}
}

private CloneModelViewModule DefaultCloneModelViewModule(Action<ITypesInfo> customizeTypesInfo){
var application = new XafApplicationMock().Object;
application.WhenCustomizingTypesInfo().FirstAsync()
.Do(customizeTypesInfo)
.Subscribe();
var cloneModelViewModule = new CloneModelViewModule();
cloneModelViewModule.AdditionalExportedTypes.AddRange(new[]{typeof(CMV)});
cloneModelViewModule.RequiredModuleTypes.Add(typeof(ReactiveModule));
application.SetupDefaults(cloneModelViewModule);
return cloneModelViewModule;
}
}
}

0 comments on commit 15b3f0b

Please sign in to comment.
You can’t perform that action at this time.