--HG--
branch : dev
This commit is contained in:
Louis DeJardin
2010-06-23 11:13:17 -07:00
51 changed files with 1068 additions and 98 deletions

View File

@@ -0,0 +1,38 @@
using System.Data;
using NUnit.Framework;
using Orchard.DataMigration.Schema;
namespace Orchard.Tests.DataMigration {
[TestFixture]
public class DataMigrationCommandsTests {
[Test]
public void AllMethodsShouldBeCalledSuccessfully() {
var schemaBuilder = new SchemaBuilder("TEST_");
schemaBuilder
.CreateTable("User", table => table
.ContentPartRecord()
.Column("Id", DbType.Int32, column => column.PrimaryKey())
.Column("Firstname", DbType.String, column => column.Length(255))
.Column("Lastname", DbType.String, column => column.Precision(0).Scale(1))
.ForeignKey("User_Address", fk => fk.On("Id", "Address", "UserId")))
.CreateTable("Address", table => table
.VersionedContentPartRecord()
.Column("City", DbType.String)
.Column("ZIP", DbType.Int32, column => column.Unique())
.Column("UserId", DbType.Int32, column => column.NotNull()))
.AlterTable("User", table => table
.AddColumn("Age", DbType.Int32)
.AlterColumn("Lastname", column => column.Default("John"))
.AlterColumn("Lastname", column => column.Rename("John"))
.DropColumn("Lastname")
.CreateIndex("IDX_XYZ", "NickName")
.DropIndex("IDX_XYZ")
.AddForeignKey("FKL", fk => fk.On("Id", "A", "Id").On("Id", "B", "Id") )
.DropForeignKey("FKL"))
.DropTable("Address")
.ExecuteSql("DROP DATABASE", statement => statement.ForDialect("SQLite").ForDialect("MsSqlServer2008"));
}
}
}

View File

@@ -6,9 +6,9 @@ using NHibernate;
using NUnit.Framework;
using Orchard.ContentManagement.Records;
using Orchard.Data;
using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Folders;
using Orchard.Environment.Extensions.Loaders;
using Orchard.Environment.Extensions.Models;
using Orchard.Tests.ContentManagement;
using Orchard.DataMigration;
@@ -17,7 +17,6 @@ namespace Orchard.Tests.DataMigration {
[TestFixture]
public class DataMigrationTests {
private IContainer _container;
private IExtensionManager _manager;
private StubFolders _folders;
private IDataMigrationManager _dataMigrationManager;
private IRepository<DataMigrationRecord> _repository;
@@ -25,13 +24,8 @@ namespace Orchard.Tests.DataMigration {
private ISessionFactory _sessionFactory;
private ISession _session;
[SetUp]
public void Init() {
Init(Enumerable.Empty<Type>());
}
public void Init(IEnumerable<Type> dataMigrations) {
[TestFixtureSetUp]
public void CreateDb() {
var databaseFileName = System.IO.Path.GetTempFileName();
_sessionFactory = DataUtility.CreateSessionFactory(
databaseFileName,
@@ -39,9 +33,22 @@ namespace Orchard.Tests.DataMigration {
typeof(ContentItemVersionRecord),
typeof(ContentItemRecord),
typeof(ContentTypeRecord));
}
public void InitDb() {
foreach ( var record in _repository.Fetch(m => true) ) {
_repository.Delete(record);
}
_repository.Flush();
}
public void Init(IEnumerable<Type> dataMigrations) {
var builder = new ContainerBuilder();
_folders = new StubFolders();
builder.RegisterInstance(new ShellSettings { DataTablePrefix = "TEST_"});
builder.RegisterInstance(_folders).As<IExtensionFolders>();
builder.RegisterType<ExtensionManager>().As<IExtensionManager>();
builder.RegisterType<DataMigrationManager>().As<IDataMigrationManager>();
@@ -53,10 +60,11 @@ namespace Orchard.Tests.DataMigration {
builder.RegisterType(type).As<IDataMigration>();
}
_container = builder.Build();
_manager = _container.Resolve<IExtensionManager>();
_container.Resolve<IExtensionManager>();
_dataMigrationManager = _container.Resolve<IDataMigrationManager>();
_repository = _container.Resolve<IRepository<DataMigrationRecord>>();
InitDb();
}
public class StubFolders : IExtensionFolders {
@@ -139,6 +147,7 @@ namespace Orchard.Tests.DataMigration {
return 999;
}
}
public class DataMigrationDependenciesModule2 : IDataMigration {
public string Feature {
get { return "Feature2"; }
@@ -149,9 +158,52 @@ namespace Orchard.Tests.DataMigration {
}
}
public class DataMigrationWithSchemaBuilder : DataMigrationImpl {
public override string Feature {
get { return "Feature1"; }
}
public int Create() {
Assert.That(SchemaBuilder, Is.Not.Null);
Assert.That(SchemaBuilder.TablePrefix, Is.EqualTo("TEST_"));
return 1;
}
}
public class DataMigrationFeatureNeedUpdate1 : IDataMigration {
public string Feature {
get { return "Feature1"; }
}
}
public class DataMigrationFeatureNeedUpdate2 : IDataMigration {
public string Feature {
get { return "Feature2"; }
}
public int Create() {
return 999;
}
}
public class DataMigrationFeatureNeedUpdate3 : IDataMigration {
public string Feature {
get { return "Feature3"; }
}
public int Create() {
return 999;
}
public int UpdateFrom42() {
return 999;
}
}
[Test]
public void DataMigrationShouldDoNothingIfNoDataMigrationIsProvidedForFeature() {
Init(new Type[] {typeof (DataMigrationEmpty)});
Init(new[] {typeof (DataMigrationEmpty)});
_folders.Manifests.Add("Module2", @"
name: Module2
@@ -162,13 +214,13 @@ features:
Description: Feature
");
_dataMigrationManager.Upgrade("Feature1");
_dataMigrationManager.Update("Feature1");
Assert.That(_repository.Table.Count(), Is.EqualTo(0));
}
[Test]
public void DataMigrationShouldDoNothingIfNoUpgradeOrCreateMethodWasFound() {
Init(new Type[] { typeof(DataMigration11) });
Init(new[] { typeof(DataMigration11) });
_folders.Manifests.Add("Module1", @"
name: Module1
@@ -179,13 +231,13 @@ features:
Description: Feature
");
_dataMigrationManager.Upgrade("Feature1");
_dataMigrationManager.Update("Feature1");
Assert.That(_repository.Table.Count(), Is.EqualTo(0));
}
[Test]
public void CreateShouldReturnVersionNumber() {
Init(new Type[] { typeof(DataMigration11Create) });
Init(new[] { typeof(DataMigration11Create) });
_folders.Manifests.Add("Module1", @"
name: Module1
@@ -196,7 +248,7 @@ features:
Description: Feature
");
_dataMigrationManager.Upgrade("Feature1");
_dataMigrationManager.Update("Feature1");
Assert.That(_repository.Table.Count(), Is.EqualTo(1));
Assert.That(_repository.Table.First().Current, Is.EqualTo(999));
Assert.That(_repository.Table.First().DataMigrationClass, Is.EqualTo("Orchard.Tests.DataMigration.DataMigrationTests+DataMigration11Create"));
@@ -204,7 +256,7 @@ features:
[Test]
public void CreateCanBeFollowedByUpdates() {
Init(new Type[] {typeof (DataMigrationCreateCanBeFollowedByUpdates)});
Init(new[] {typeof (DataMigrationCreateCanBeFollowedByUpdates)});
_folders.Manifests.Add("Module1", @"
name: Module1
@@ -215,14 +267,14 @@ features:
Description: Feature
");
_dataMigrationManager.Upgrade("Feature1");
_dataMigrationManager.Update("Feature1");
Assert.That(_repository.Table.Count(), Is.EqualTo(1));
Assert.That(_repository.Table.First().Current, Is.EqualTo(666));
}
[Test]
public void SameMigrationClassCanEvolve() {
Init(new Type[] { typeof(DataMigrationSameMigrationClassCanEvolve) });
Init(new[] { typeof(DataMigrationSameMigrationClassCanEvolve) });
_folders.Manifests.Add("Module1", @"
name: Module1
@@ -232,12 +284,12 @@ features:
Feature1:
Description: Feature
");
_repository.Create(new DataMigrationRecord() {
_repository.Create(new DataMigrationRecord {
Current = 42,
DataMigrationClass = "Orchard.Tests.DataMigration.DataMigrationTests+DataMigrationSameMigrationClassCanEvolve"
});
_dataMigrationManager.Upgrade("Feature1");
_dataMigrationManager.Update("Feature1");
Assert.That(_repository.Table.Count(), Is.EqualTo(1));
Assert.That(_repository.Table.First().Current, Is.EqualTo(999));
}
@@ -245,7 +297,7 @@ features:
[Test]
public void DependenciesShouldBeUpgradedFirst() {
Init(new Type[] { typeof(DataMigrationDependenciesModule1), typeof(DataMigrationDependenciesModule2) });
Init(new[] { typeof(DataMigrationDependenciesModule1), typeof(DataMigrationDependenciesModule2) });
_folders.Manifests.Add("Module1", @"
name: Module1
@@ -265,12 +317,77 @@ features:
Feature2:
Description: Feature
");
_dataMigrationManager.Upgrade("Feature1");
_dataMigrationManager.Update("Feature1");
Assert.That(_repository.Table.Count(), Is.EqualTo(2));
Assert.That(_repository.Fetch(d => d.Current == 999).Count(), Is.EqualTo(2));
Assert.That(_repository.Fetch(d => d.DataMigrationClass == "Orchard.Tests.DataMigration.DataMigrationTests+DataMigrationDependenciesModule1").Count(), Is.EqualTo(1));
Assert.That(_repository.Fetch(d => d.DataMigrationClass == "Orchard.Tests.DataMigration.DataMigrationTests+DataMigrationDependenciesModule2").Count(), Is.EqualTo(1));
}
[Test]
public void DataMigrationImplShouldGetASchemaBuilder() {
Init(new[] { typeof(DataMigrationWithSchemaBuilder) });
_folders.Manifests.Add("Module1", @"
name: Module1
version: 0.1
orchardversion: 1
features:
Feature1:
Description: Feature
");
_dataMigrationManager.Update("Feature1");
Assert.That(_repository.Table.Count(), Is.EqualTo(1));
}
[Test]
public void ShouldDetectFeaturesThatNeedUpdates() {
Init(new[] { typeof(DataMigrationFeatureNeedUpdate1), typeof(DataMigrationFeatureNeedUpdate2), typeof(DataMigrationFeatureNeedUpdate3) });
_folders.Manifests.Add("Module1", @"
name: Module1
version: 0.1
orchardversion: 1
features:
Feature1:
Description: Feature
Feature2:
Description: Feature
Feature3:
Description: Feature
Feature4:
Description: Feature
");
// even if there is a data migration class, as it is empty there should me no migration to do
Assert.That(_dataMigrationManager.GetFeaturesThatNeedUpdate().Contains("Feature1"), Is.False);
// there is no available class for this feature
Assert.That(_dataMigrationManager.GetFeaturesThatNeedUpdate().Contains("Feature4"), Is.False);
// there is a create method and no record in db, so let's create it
Assert.That(_dataMigrationManager.GetFeaturesThatNeedUpdate().Contains("Feature2"), Is.True);
// there is an UpdateFrom42 method, so it should be fired if Current == 42
_repository.Create(new DataMigrationRecord {
Current = 42,
DataMigrationClass = "Orchard.Tests.DataMigration.DataMigrationTests+DataMigrationFeatureNeedUpdate3"
});
Assert.That(_dataMigrationManager.GetFeaturesThatNeedUpdate().Contains("Feature3"), Is.True);
_repository.Delete(_repository.Fetch(m => m.Current == 42).First());
_repository.Flush();
_repository.Create(new DataMigrationRecord {
Current = 43,
DataMigrationClass = "Orchard.Tests.DataMigration.DataMigrationTests+DataMigrationFeatureNeedUpdate3"
});
Assert.That(_dataMigrationManager.GetFeaturesThatNeedUpdate().Contains("Feature3"), Is.False);
}
}
}

View File

@@ -181,6 +181,7 @@
<Compile Include="ContentManagement\Records\GammaRecord.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DataMigration\DataMigrationCommandsTests.cs" />
<Compile Include="DataMigration\DataMigrationTests.cs" />
<Compile Include="DataUtility.cs" />
<Compile Include="Data\Builders\SessionFactoryBuilderTests.cs" />

View File

@@ -0,0 +1,46 @@
using JetBrains.Annotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.Core.Common.Fields;
using Orchard.Core.Common.ViewModels;
namespace Orchard.Core.Common.Drivers {
[UsedImplicitly]
public class ItemReferenceContentFieldDriver : ContentFieldDriver<ItemReferenceContentField> {
public IOrchardServices Services { get; set; }
private const string TemplateName = "Fields/Common.ItemReferenceContentField";
public ItemReferenceContentFieldDriver(IOrchardServices services) {
Services = services;
}
protected override string Prefix {
get { return "ItemReferenceContentField"; }
}
protected override DriverResult Display(ItemReferenceContentField field, string displayType) {
var model = new ItemReferenceContentFieldDisplayViewModel {
Item = Services.ContentManager.Get(field.ContentItemReference.Id)
};
return ContentFieldTemplate(model, TemplateName, Prefix);
}
protected override DriverResult Editor(ItemReferenceContentField field) {
var model = BuildEditorViewModel(field);
return ContentFieldTemplate(model, TemplateName, Prefix).Location("primary", "6");
}
protected override DriverResult Editor(ItemReferenceContentField field, IUpdateModel updater) {
var model = BuildEditorViewModel(field);
updater.TryUpdateModel(model, Prefix, null, null);
return ContentFieldTemplate(model, TemplateName, Prefix).Location("primary", "6");
}
private ItemReferenceContentFieldEditorViewModel BuildEditorViewModel(ItemReferenceContentField field) {
return new ItemReferenceContentFieldEditorViewModel {
Item = Services.ContentManager.Get(field.ContentItemReference.Id)
};
}
}
}

View File

@@ -0,0 +1,8 @@
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;
namespace Orchard.Core.Common.Fields {
public class ItemReferenceContentField : ContentField {
public ContentItemRecord ContentItemReference { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
using System.Collections.Generic;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Handlers;
namespace Orchard.Core.Common.Handlers {
public class ItemReferenceContentFieldHandler : ContentFieldHandler {
public ItemReferenceContentFieldHandler(IEnumerable<IContentFieldDriver> contentFieldDrivers) : base(contentFieldDrivers) { }
}
}

View File

@@ -0,0 +1,12 @@
using Orchard.ContentManagement;
namespace Orchard.Core.Common.ViewModels {
public class ItemReferenceContentFieldDisplayViewModel {
private ContentItem _item;
public ContentItem Item {
get { return _item; }
set { _item = value; }
}
}
}

View File

@@ -0,0 +1,12 @@
using Orchard.ContentManagement;
namespace Orchard.Core.Common.ViewModels {
public class ItemReferenceContentFieldEditorViewModel {
private ContentItem _item;
public ContentItem Item {
get { return _item; }
set { _item = value; }
}
}
}

View File

@@ -0,0 +1,3 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ItemReferenceContentFieldDisplayViewModel>" %>
<%@ Import Namespace="Orchard.Core.Common.ViewModels"%>
<%= Html.ItemDisplayLink(Model.Item) %>

View File

@@ -0,0 +1,3 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<ItemReferenceContentFieldEditorViewModel>" %>
<%@ Import Namespace="Orchard.Core.Common.ViewModels"%>
<%= Html.ItemEditLink(Model.Item) %>

View File

@@ -106,28 +106,97 @@ namespace Orchard.Core.Contents.Controllers {
return EditType(id);
}
var contentTypeDefinitionParts = viewModel.Parts.Select(
p => new ContentTypeDefinition.Part(
new ContentPartDefinition(
p.PartDefinition.Name,
p.PartDefinition.Fields.Select(
f => new ContentPartDefinition.Field(
new ContentFieldDefinition(f.FieldDefinition.Name),
f.Name,
f.Settings
)
),
p.PartDefinition.Settings
),
p.Settings
)
).ToList();
if (viewModel.Fields.Any()) {
var implicitContentTypeDefinitionPart = new ContentTypeDefinition.Part(
new ContentPartDefinition(
viewModel.Name,
viewModel.Fields.Select(
f => new ContentPartDefinition.Field(
new ContentFieldDefinition(f.FieldDefinition.Name),
f.Name,
f.Settings
)
),
null
),
null
);
contentTypeDefinitionParts.Add(implicitContentTypeDefinitionPart);
}
//todo: apply the changes along the lines of but definately not resembling
// for now this _might_ just get a little messy ->
_contentDefinitionService.AlterTypeDefinition(
new ContentTypeDefinition(
viewModel.Name,
viewModel.DisplayName,
viewModel.Parts.Select(
p => new ContentTypeDefinition.Part(
new ContentPartDefinition(
p.PartDefinition.Name,
p.PartDefinition.Fields.Select(
f => new ContentPartDefinition.Field(
new ContentFieldDefinition(f.FieldDefinition.Name),
f.Name,
f.Settings
)
),
p.PartDefinition.Settings
),
p.Settings
)
),
contentTypeDefinitionParts,
viewModel.Settings
)
);
// little == lot
return RedirectToAction("Index");
}
public ActionResult EditPart(string id) {
if (!Services.Authorizer.Authorize(Permissions.CreateContentTypes, T("Not allowed to edit a part.")))
return new HttpUnauthorizedResult();
var contentPartDefinition = _contentDefinitionService.GetPartDefinition(id);
if (contentPartDefinition == null)
return new NotFoundResult();
return View(new EditPartViewModel(contentPartDefinition));
}
[HttpPost, ActionName("EditPart")]
public ActionResult EditPartPOST(string id) {
if (!Services.Authorizer.Authorize(Permissions.CreateContentTypes, T("Not allowed to edit a part.")))
return new HttpUnauthorizedResult();
var contentPartDefinition = _contentDefinitionService.GetPartDefinition(id);
if (contentPartDefinition == null)
return new NotFoundResult();
var viewModel = new EditPartViewModel();
TryUpdateModel(viewModel);
if (!ModelState.IsValid) {
return EditPart(id);
}
//todo: apply the changes along the lines of but definately not resembling
// for now this _might_ just get a little messy ->
_contentDefinitionService.AlterPartDefinition(
new ContentPartDefinition(
viewModel.Name,
viewModel.Fields.Select(
f => new ContentPartDefinition.Field(
new ContentFieldDefinition(f.FieldDefinition.Name),
f.Name,
f.Settings
)
),
viewModel.Settings
)
);

View File

@@ -49,12 +49,33 @@ namespace Orchard.Core.Contents.Services {
public void AlterTypeDefinition(ContentTypeDefinition contentTypeDefinition) {
_contentDefinitionManager.StoreTypeDefinition(contentTypeDefinition);
var implicitTypePart = contentTypeDefinition.Parts.SingleOrDefault(p => p.PartDefinition.Name == contentTypeDefinition.Name);
if (implicitTypePart != null) {
AlterPartDefinition(implicitTypePart.PartDefinition);
}
}
public void RemoveTypeDefinition(string name) {
throw new NotImplementedException();
}
public ContentPartDefinition GetPartDefinition(string name) {
return _contentDefinitionManager.GetPartDefinition(name);
}
public void AddPartDefinition(ContentPartDefinition contentPartDefinition) {
throw new NotImplementedException();
}
public void AlterPartDefinition(ContentPartDefinition contentPartDefinition) {
_contentDefinitionManager.StorePartDefinition(contentPartDefinition);
}
public void RemovePartDefinition(string name) {
throw new NotImplementedException();
}
//gratuitously stolen from the RoutableService
private static string GenerateTypeName(string displayName) {
if (string.IsNullOrWhiteSpace(displayName))

View File

@@ -8,5 +8,10 @@ namespace Orchard.Core.Contents.Services {
void AddTypeDefinition(ContentTypeDefinition contentTypeDefinition);
void AlterTypeDefinition(ContentTypeDefinition contentTypeDefinition);
void RemoveTypeDefinition(string name);
ContentPartDefinition GetPartDefinition(string name);
void AddPartDefinition(ContentPartDefinition contentPartDefinition);
void AlterPartDefinition(ContentPartDefinition contentPartDefinition);
void RemovePartDefinition(string name);
}
}

View File

@@ -3,4 +3,15 @@
}
.manage.add-to-type {
margin-top:-4em;
}
.manage-part h3 {
border-bottom:1px solid #EAEAEA;
}
.manage-part .manage {
font-size:1.4em;
margin-top:-2.4em;
}
.manage-part .manage.minor {
margin-top:-1.7em;
}

View File

@@ -13,13 +13,27 @@ namespace Orchard.Core.Contents.ViewModels {
Name = contentTypeDefinition.Name;
DisplayName = contentTypeDefinition.DisplayName;
Settings = contentTypeDefinition.Settings;
Parts = contentTypeDefinition.Parts.Select(p => new EditTypePartViewModel(p));
Fields = GetTypeFields(contentTypeDefinition);
Parts = GetTypeParts(contentTypeDefinition);
}
public string Name { get; set; }
public string DisplayName { get; set; }
public SettingsDictionary Settings { get; set; }
public IEnumerable<EditPartFieldViewModel> Fields { get; set; }
public IEnumerable<EditTypePartViewModel> Parts { get; set; }
private IEnumerable<EditPartFieldViewModel> GetTypeFields(ContentTypeDefinition contentTypeDefinition) {
var implicitTypePart = contentTypeDefinition.Parts.SingleOrDefault(p => p.PartDefinition.Name == Name);
return implicitTypePart == null
? Enumerable.Empty<EditPartFieldViewModel>()
: implicitTypePart.PartDefinition.Fields.Select(f => new EditPartFieldViewModel(f));
}
private IEnumerable<EditTypePartViewModel> GetTypeParts(ContentTypeDefinition contentTypeDefinition) {
return contentTypeDefinition.Parts.Where(p => p.PartDefinition.Name != Name).Select(p => new EditTypePartViewModel(p));
}
}
public class EditTypePartViewModel {
@@ -35,7 +49,7 @@ namespace Orchard.Core.Contents.ViewModels {
public SettingsDictionary Settings { get; set; }
}
public class EditPartViewModel {
public class EditPartViewModel : BaseViewModel {
public EditPartViewModel() {
Fields = new List<EditPartFieldViewModel>();
Settings = new SettingsDictionary();
@@ -69,7 +83,7 @@ namespace Orchard.Core.Contents.ViewModels {
public class EditFieldViewModel {
public EditFieldViewModel() { }
public EditFieldViewModel(ContentFieldDefinition contentFieldDefinition) {
Name = Name;
Name = contentFieldDefinition.Name;
}
public string Name { get; set; }

View File

@@ -0,0 +1,19 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<EditPartViewModel>" %>
<%@ Import Namespace="Orchard.Core.Contents.ViewModels" %><%
Html.RegisterStyle("admin.css"); %>
<h1><%:Html.TitleForPage(T("Edit Part").ToString())%></h1><%
using (Html.BeginFormAntiForgeryPost()) { %>
<%:Html.ValidationSummary() %>
<fieldset>
<label for="Name"><%:T("Name") %></label>
<%--// has unintended consequences (renamging the part) - changing the name creates a new part of that name--%>
<%:Html.TextBoxFor(m => m.Name, new {@class = "textMedium"}) %>
</fieldset>
<%:Html.EditorFor(m => m.Settings, "Settings", "")%>
<h2><%:T("Fields") %></h2>
<div class="manage add-to-type"><%: Html.ActionLink(T("Add").Text, "AddField", new { }, new { @class = "button" })%></div>
<%:Html.EditorFor(m => m.Fields, "Fields", "") %>
<fieldset>
<button class="primaryAction" type="submit"><%:T("Save") %></button>
</fieldset><%
} %>

View File

@@ -7,8 +7,10 @@ using (Html.BeginFormAntiForgeryPost()) { %>
<fieldset>
<label for="DisplayName"><%:T("Display Name") %></label>
<%:Html.TextBoxFor(m => m.DisplayName, new {@class = "textMedium"}) %>
<%--// has unintended consequences (renamging the type) - changing the name creates a new type of that name--%>
<label for="Name"><%:T("Name") %></label>
<%:Html.TextBoxFor(m => m.Name, new {@class = "textMedium"}) %>
<%:Html.TextBoxFor(m => m.Name, new {@class = "textMedium", disabled = "disabled"}) %>
<%:Html.HiddenFor(m => m.Name) %>
</fieldset>
<%:Html.EditorFor(m => m.Settings) %>
<h2><%:T("Parts") %></h2>
@@ -16,7 +18,7 @@ using (Html.BeginFormAntiForgeryPost()) { %>
<%:Html.EditorFor(m => m.Parts, "Parts", "") %>
<h2><%:T("Fields") %></h2>
<div class="manage add-to-type"><%: Html.ActionLink(T("Add").Text, "AddField", new { }, new { @class = "button" })%></div>
<%--<%:Html.EditorFor(m => m.Fields, "ContentTypeFields")%>--%>
<%:Html.EditorFor(m => m.Fields, "Fields", "")%>
<fieldset>
<button class="primaryAction" type="submit"><%:T("Save") %></button>
</fieldset><%

View File

@@ -2,7 +2,7 @@
<%@ Import Namespace="Orchard.Core.Contents.ViewModels" %>
<h1><%:Html.TitleForPage(T("Content Types").ToString())%></h1>
<div class="manage"><%: Html.ActionLink(T("Create new type").ToString(), "CreateType", null, new { @class = "button primaryAction" })%></div>
<%=Html.UnorderedList(
<%:Html.UnorderedList(
Model.Types,
(t,i) => Html.DisplayFor(m => t),
"contentItems"

View File

@@ -1,4 +1,5 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<Orchard.ContentManagement.MetaData.Models.ContentTypeDefinition>" %>
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<ContentTypeDefinition>" %>
<%@ Import namespace="Orchard.ContentManagement.MetaData.Models" %>
<div class="summary">
<div class="properties">
<h3><%:Model.DisplayName%></h3>

View File

@@ -0,0 +1,8 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<SettingsDictionary>" %>
<%@ import Namespace="Orchard.ContentManagement.MetaData.Models" %>
<dl><%
foreach (var setting in Model) { %>
<dt><%:setting.Key %></dt>
<dd><%:setting.Value %></dd><%
} %>
</dl>

View File

@@ -0,0 +1,18 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<EditPartFieldViewModel>" %>
<%@ Import Namespace="Orchard.Core.Contents.ViewModels" %>
<fieldset>
<h3><%:Model.Name %></h3>
<h4><%:Model.FieldDefinition.Name %></h4>
<div class="manage add-to-type">
<%--// these inline forms can't be here. should probably have some JavaScript in here to build up the forms and add the "remove" link.
// get the antiforgery token from the edit type form and mark up the part in a semantic way so I can get some info from the DOM --%>
<%:Html.Link("[remove]", "#forshowonlyandnotintendedtowork!") %>
<%-- <% using (Html.BeginFormAntiForgeryPost(Url.Action("RemovePart", new { area = "Contents" }), FormMethod.Post, new {@class = "inline link"})) { %>
<%:Html.Hidden("name", Model.PartDefinition.Name, new { id = "" }) %>
<button type="submit" title="<%:T("Remove") %>"><%:T("Remove") %></button>
<% } %> --%>
</div>
<%:Html.EditorFor(m => m.Settings, "Settings", "") %>
<%:Html.HiddenFor(m => m.Name) %>
<%:Html.HiddenFor(m => m.FieldDefinition.Name) %>
</fieldset>

View File

@@ -0,0 +1,12 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<IEnumerable<EditPartFieldViewModel>>" %>
<%@ Import Namespace="Orchard.Core.Contents.ViewModels" %><%
if (Model.Any()) { %>
<fieldset><%
var fi = 0;
foreach (var field in Model) {
var f = field;
var htmlFieldName = string.Format("Fields[{0}]", fi++); %>
<%:Html.EditorFor(m => f, "Field", htmlFieldName) %><%
} %>
</fieldset><%
} %>

View File

@@ -4,7 +4,7 @@ if (Model.Any()) { %>
<fieldset><%
foreach (var field in Model) {
var f = field; %>
<%:Html.EditorFor(m => f, "FieldOnPart") %><%
<%:Html.EditorFor(m => f, "Part.Fields") %><%
} %>
</fieldset><%
} %>

View File

@@ -1,8 +1,8 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<EditTypePartViewModel>" %>
<%@ Import Namespace="Orchard.Core.Contents.ViewModels" %>
<fieldset>
<fieldset class="manage-part">
<h3><%:Model.PartDefinition.Name %></h3>
<div class="manage add-to-type">
<div class="manage">
<%--// these inline forms can't be here. should probably have some JavaScript in here to build up the forms and add the "remove" link.
// get the antiforgery token from the edit type form and mark up the part in a semantic way so I can get some info from the DOM --%>
<%:Html.Link("[remove]", "#forshowonlyandnotintendedtowork") %>
@@ -11,11 +11,12 @@
<button type="submit" title="<%:T("Remove") %>"><%:T("Remove") %></button>
<% } %> --%>
</div>
<%--
what is this settings for?
<%:Html.EditorFor(m => m.PartDefinition.Settings, "Settings") %>--%>
<%:Html.EditorFor(m => m.Settings, "Settings", "") %>
<%:Html.EditorFor(m => m.PartDefinition.Fields, "FieldsOnPart") %>
<%:Html.Hidden("PartDefinition.Name", Model.PartDefinition.Name) %>
<%:Html.EditorFor(m => m.Settings, "Settings", "") %><%
if (Model.PartDefinition.Settings.Any() || Model.PartDefinition.Fields.Any()) { %>
<h4><%:T("Global configuration") %></h4>
<div class="manage minor"><%:Html.ActionLink(T("Edit").Text, "EditPart", new { area = "Contents", id = Model.PartDefinition.Name }) %></div>
<%:Html.DisplayFor(m => m.PartDefinition.Settings, "Settings", "PartDefinition") %>
<%:Html.EditorFor(m => m.PartDefinition.Fields, "Part.Fields") %><%
} %>
<%:Html.Hidden("PartDefinition.Name", Model.PartDefinition.Name) %>
</fieldset>

View File

@@ -1,6 +1,8 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<Orchard.ContentManagement.MetaData.Models.SettingsDictionary>" %><%
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<SettingsDictionary>" %>
<%@ import Namespace="Orchard.ContentManagement.MetaData.Models" %><%
if (Model.Any()) { %>
<fieldset><%
<fieldset>
<legend><%:T("[Settings]") %></legend><%
var si = 0;
foreach (var setting in Model) {
var s = setting;

View File

@@ -208,6 +208,7 @@
<Content Include="Common\Views\EditorTemplates\Parts\Common.TextContentField.ascx" />
<Content Include="Contents\Module.txt" />
<Content Include="Contents\Styles\admin.css" />
<Content Include="Contents\Views\Admin\EditPart.ascx" />
<Content Include="Contents\Views\Admin\EditType.ascx" />
<Content Include="Contents\Views\Admin\CreateType.ascx" />
<Content Include="Contents\Views\Admin\Types.ascx" />
@@ -217,9 +218,12 @@
<Content Include="Contents\Views\Admin\Create.aspx" />
<Content Include="Contents\Views\DisplayTemplates\ContentTypeDefinition.ascx" />
<Content Include="Contents\Views\DisplayTemplates\Items\Contents.Item.ascx" />
<Content Include="Contents\Views\DisplayTemplates\Settings.ascx" />
<Content Include="Contents\Views\EditorTemplates\Field.ascx" />
<Content Include="Contents\Views\EditorTemplates\Fields.ascx" />
<Content Include="Contents\Views\EditorTemplates\Settings.ascx" />
<Content Include="Contents\Views\EditorTemplates\FieldsOnPart.ascx" />
<Content Include="Contents\Views\EditorTemplates\FieldOnPart.ascx" />
<Content Include="Contents\Views\EditorTemplates\Part.Fields.ascx" />
<Content Include="Contents\Views\EditorTemplates\Part.Field.ascx" />
<Content Include="Contents\Views\EditorTemplates\Parts.ascx" />
<Content Include="Contents\Views\EditorTemplates\Part.ascx" />
<Content Include="Contents\Views\EditorTemplates\Items\Contents.Item.ascx" />

View File

@@ -510,7 +510,6 @@ button.remove:focus::-moz-focus-inner, .remove.button:focus::-moz-focus-inner {
.manage {
float:right;
margin:0 0 10px 8px;
overflow:hidden;
}
.actions {
clear:right;

View File

@@ -18,7 +18,7 @@ namespace Orchard.DataMigration.Commands {
[OrchardSwitches("Feature")]
public string UpgradeDatabase() {
try {
_dataMigrationManager.Upgrade(Feature);
_dataMigrationManager.Update(Feature);
}
catch ( Exception ex ) {
Context.Output.WriteLine(T("An error occured while upgrading the database: " + ex.Message));

View File

@@ -1,5 +1,11 @@
namespace Orchard.DataMigration {
public abstract class DataMigration : IDataMigration {
using Orchard.DataMigration.Schema;
namespace Orchard.DataMigration {
/// <summary>
/// Data Migration classes can inherit from this class to get a SchemaBuilder instance configured with the current tenant database prefix
/// </summary>
public abstract class DataMigrationImpl : IDataMigration {
public abstract string Feature { get; }
public SchemaBuilder SchemaBuilder { get; set; }
}
}

View File

@@ -0,0 +1,40 @@
using Orchard.Environment;
using Orchard.Environment.Extensions.Models;
namespace Orchard.DataMigration {
/// <summary>
/// Responsible for executing data migration tasks when a feature is enabled for the first time
/// 1) Initial install of a module:
/// Enable a feature for the first time => run data migration up to the latest version
/// Enable a feature on 2nd time => no data migration run
/// Disable a feature => no data migration
/// 2) Installing a newer version of a module
/// Don't do any data migration by default
/// 2 cases:
/// 1) feature wasn't not enabled when new code was installed
/// 2) feature was enabled when new code was installed
/// </summary>
public class DataMigrationCoordinator : IFeatureEventHandler {
private readonly IDataMigrationManager _dataMigrationManager;
public DataMigrationCoordinator(IDataMigrationManager dataMigrationManager) {
_dataMigrationManager = dataMigrationManager;
}
public void Install(Feature feature) {
var featureName = feature.Descriptor.Name;
if ( !_dataMigrationManager.IsFeatureAlreadyInstalled(featureName) ) {
_dataMigrationManager.Update(featureName);
}
}
public void Enable(Feature feature) {
}
public void Disable(Feature feature) {
}
public void Uninstall(Feature feature) {
}
}
}

View File

@@ -4,8 +4,10 @@ using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Orchard.Data;
using Orchard.DataMigration.Schema;
using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Models;
using Orchard.Environment.State;
using Orchard.Logging;
namespace Orchard.DataMigration {
@@ -17,34 +19,77 @@ namespace Orchard.DataMigration {
private readonly IRepository<DataMigrationRecord> _dataMigrationRepository;
private readonly IDataMigrationGenerator _dataMigrationGenerator;
private readonly IExtensionManager _extensionManager;
private readonly ShellSettings _shellSettings;
public DataMigrationManager(
IEnumerable<IDataMigration> dataMigrations,
IRepository<DataMigrationRecord> dataMigrationRepository,
IDataMigrationGenerator dataMigrationGenerator,
IExtensionManager extensionManager) {
IExtensionManager extensionManager,
ShellSettings shellSettings) {
_dataMigrations = dataMigrations;
_dataMigrationRepository = dataMigrationRepository;
_dataMigrationGenerator = dataMigrationGenerator;
_extensionManager = extensionManager;
_shellSettings = shellSettings;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public void Upgrade(string feature) {
public IEnumerable<string> GetFeaturesThatNeedUpdate() {
var features = new List<string>();
// compare current version and available migration methods for each migration class
foreach ( var dataMigration in _dataMigrations ) {
// get current version for this migration
var dataMigrationRecord = GetDataMigrationRecord(dataMigration);
var current = 0;
if (dataMigrationRecord != null) {
current = dataMigrationRecord.Current;
}
// do we need to call Create() ?
if (current == 0) {
// try to resolve a Create method
if ( GetCreateMethod(dataMigration) != null ) {
features.Add(dataMigration.Feature);
continue;
}
}
var lookupTable = CreateUpgradeLookupTable(dataMigration);
if(lookupTable.ContainsKey(current)) {
features.Add(dataMigration.Feature);
}
}
return features;
}
public void Update(IEnumerable<string> features) {
foreach(var feature in features) {
Update(feature);
}
}
public void Update(string feature){
// proceed with dependent features first, whatever the module it's in
var dependencies = _extensionManager
.AvailableExtensions()
.SelectMany(e => e.Features)
var dependencies = ShellStateCoordinator.OrderByDependencies(_extensionManager.AvailableExtensions()
.SelectMany(ext => ext.Features))
.Where(f => String.Equals(f.Name, feature, StringComparison.OrdinalIgnoreCase))
.Where(f => f.Dependencies != null)
.SelectMany( f => f.Dependencies )
.ToList();
foreach(var dependency in dependencies) {
Upgrade(dependency);
Update(dependency);
}
var migrations = GetDataMigrations(feature);
@@ -55,11 +100,8 @@ namespace Orchard.DataMigration {
var tempMigration = migration;
// get current version for this migration
var dataMigrationRecord = _dataMigrationRepository.Table
.Where(dm => dm.DataMigrationClass == tempMigration.GetType().FullName)
.FirstOrDefault();
var dataMigrationRecord = GetDataMigrationRecord(tempMigration);
var updateMethodNameRegex = new Regex(@"^UpdateFrom(?<version>\d+)$", RegexOptions.Compiled);
var current = 0;
if(dataMigrationRecord != null) {
current = dataMigrationRecord.Current;
@@ -69,8 +111,8 @@ namespace Orchard.DataMigration {
if(current == 0) {
// try to resolve a Create method
var createMethod = migration.GetType().GetMethod("Create", BindingFlags.Public | BindingFlags.Instance);
if(createMethod != null && createMethod.ReturnType == typeof(int)) {
var createMethod = GetCreateMethod(migration);
if(createMethod != null) {
current = (int)createMethod.Invoke(migration, new object[0]);
}
else {
@@ -79,15 +121,7 @@ namespace Orchard.DataMigration {
}
}
// update methods might also be called after Create()
var lookupTable = new Dictionary<int, MethodInfo>();
// construct a lookup table with all managed initial versions
foreach(var methodInfo in migration.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)) {
var match = updateMethodNameRegex.Match(methodInfo.Name);
if(match.Success) {
lookupTable.Add(int.Parse(match.Groups["version"].Value), methodInfo);
}
}
var lookupTable = CreateUpgradeLookupTable(migration);
while(lookupTable.ContainsKey(current)) {
try {
@@ -96,29 +130,82 @@ namespace Orchard.DataMigration {
}
catch (Exception ex) {
Logger.Error(ex, "An unexpected error orccured while applying migration on {0} from version {1}", feature, current);
throw;
}
}
// if current is 0, it means no upgrade/create method was found or succeeded
if ( current != 0 ) {
if (dataMigrationRecord == null) {
_dataMigrationRepository.Create(new DataMigrationRecord {Current = current, DataMigrationClass = migration.GetType().FullName});
}
else {
dataMigrationRecord.Current = current;
_dataMigrationRepository.Update(dataMigrationRecord);
}
if (current == 0) {
continue;
}
if (dataMigrationRecord == null) {
_dataMigrationRepository.Create(new DataMigrationRecord {Current = current, DataMigrationClass = migration.GetType().FullName});
}
else {
dataMigrationRecord.Current = current;
_dataMigrationRepository.Update(dataMigrationRecord);
}
}
}
private DataMigrationRecord GetDataMigrationRecord(IDataMigration tempMigration) {
return _dataMigrationRepository.Table
.Where(dm => dm.DataMigrationClass == tempMigration.GetType().FullName)
.FirstOrDefault();
}
/// <summary>
/// Returns all the available IDataMigration instances for a specific module
/// Returns all the available IDataMigration instances for a specific module, and inject necessary builders
/// </summary>
public IEnumerable<IDataMigration> GetDataMigrations(string feature) {
return _dataMigrations
private IEnumerable<IDataMigration> GetDataMigrations(string feature) {
var migrations = _dataMigrations
.Where(dm => String.Equals(dm.Feature, feature, StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (var migration in migrations.OfType<DataMigrationImpl>()) {
migration.SchemaBuilder = new SchemaBuilder(_shellSettings.DataTablePrefix);
}
return migrations;
}
/// <summary>
/// Whether a feature has already been installed, i.e. one of its Data Migration class has already been processed
/// </summary>
public bool IsFeatureAlreadyInstalled(string feature) {
return GetDataMigrations(feature).Any(dataMigration => GetDataMigrationRecord(dataMigration) != null);
}
/// <summary>
/// Create a list of all available Update methods from a data migration class, indexed by the version number
/// </summary>
private static Dictionary<int, MethodInfo> CreateUpgradeLookupTable(IDataMigration dataMigration) {
var updateMethodNameRegex = new Regex(@"^UpdateFrom(?<version>\d+)$", RegexOptions.Compiled);
// update methods might also be called after Create()
var lookupTable = new Dictionary<int, MethodInfo>();
// construct a lookup table with all managed initial versions
foreach ( var methodInfo in dataMigration.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance) ) {
var match = updateMethodNameRegex.Match(methodInfo.Name);
if ( match.Success ) {
lookupTable.Add(int.Parse(match.Groups["version"].Value), methodInfo);
}
}
return lookupTable;
}
/// <summary>
/// Returns the Create metho from a data migration class if it's found
/// </summary>
private static MethodInfo GetCreateMethod(IDataMigration dataMigration) {
var methodInfo = dataMigration.GetType().GetMethod("Create", BindingFlags.Public | BindingFlags.Instance);
if(methodInfo != null && methodInfo.ReturnType == typeof(int)) {
return methodInfo;
}
return null;
}
}
}

View File

@@ -1,5 +1,25 @@
using System.Collections.Generic;
namespace Orchard.DataMigration {
public interface IDataMigrationManager : IDependency {
void Upgrade(string feature);
/// <summary>
/// Whether a feature has already been installed, i.e. one of its Data Migration class has already been processed
/// </summary>
bool IsFeatureAlreadyInstalled(string feature);
/// <summary>
/// Returns the features which have at least one Data Migration class with a corresponding Upgrade method to be called
/// </summary>
IEnumerable<string> GetFeaturesThatNeedUpdate();
/// <summary>
/// Updates the database to the latest version for the specified feature
/// </summary>
void Update(string feature);
/// <summary>
/// Updates the database to the latest version for the specified features
/// </summary>
void Update(IEnumerable<string> features);
}
}

View File

@@ -0,0 +1,15 @@
namespace Orchard.DataMigration.Schema {
public class AlterColumnCommand : ColumnCommand {
private string _newName;
public AlterColumnCommand(string name)
: base(name) {
}
public AlterColumnCommand Rename(string name) {
_newName = name;
return this;
}
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.Data;
namespace Orchard.DataMigration.Schema {
public class AlterTableCommand : SchemaCommand {
public AlterTableCommand(string name)
: base(name) {
}
public AlterTableCommand AddColumn(string name, DbType dbType, Action<CreateColumnCommand> column = null) {
var command = new CreateColumnCommand(name);
command.Type(dbType);
if(column != null) {
column(command);
}
_tableCommands.Add(command);
return this;
}
public AlterTableCommand DropColumn(string name) {
var command = new DropColumnCommand(name);
_tableCommands.Add(command);
return this;
}
public AlterTableCommand AlterColumn(string name, Action<AlterColumnCommand> column = null) {
var command = new AlterColumnCommand(name);
if ( column != null ) {
column(command);
}
_tableCommands.Add(command);
return this;
}
public AlterTableCommand CreateIndex(string name, params string[] columnNames) {
var command = new CreateIndexCommand(name, columnNames);
_tableCommands.Add(command);
return this;
}
public AlterTableCommand DropIndex(string name) {
var command = new DropIndexCommand(name);
_tableCommands.Add(command);
return this;
}
public AlterTableCommand AddForeignKey(string name, Action<CreateForeignKeyCommand> fk) {
var command = new CreateForeignKeyCommand(name);
fk(command);
_tableCommands.Add(command);
return this;
}
public AlterTableCommand DropForeignKey(string name) {
var command = new DropForeignKeyCommand(name);
_tableCommands.Add(command);
return this;
}
}
}

View File

@@ -0,0 +1,23 @@
using System.Data;
namespace Orchard.DataMigration.Schema {
public class ColumnCommand : TableCommand {
private DbType _dbType;
private object _default;
public ColumnCommand(string name) : base(name) {
_dbType = DbType.Object;
_default = null;
}
public ColumnCommand Type(DbType dbType) {
_dbType = dbType;
return this;
}
public ColumnCommand Default(object @default) {
_default = @default;
return this;
}
}
}

View File

@@ -0,0 +1,60 @@
namespace Orchard.DataMigration.Schema {
public class CreateColumnCommand : ColumnCommand {
private bool _primaryKey;
private byte? _precision;
private byte? _scale;
private int? _length;
private bool _notNull;
private bool _unique;
public CreateColumnCommand(string name) : base(name) {
_precision = null;
_scale = null;
_length = null;
_notNull = false;
_unique = false;
}
public CreateColumnCommand PrimaryKey() {
_primaryKey = true;
return this;
}
public CreateColumnCommand Precision(byte? precision) {
_precision = precision;
return this;
}
public CreateColumnCommand Scale(byte? scale) {
_scale = scale;
return this;
}
public CreateColumnCommand Length(int? length) {
_length = length;
return this;
}
public CreateColumnCommand NotNull() {
_notNull = true;
return this;
}
public CreateColumnCommand Nullable() {
_notNull = false;
return this;
}
public CreateColumnCommand Unique() {
_unique = true;
return this;
}
public CreateColumnCommand NotUnique() {
_unique = false;
return this;
}
}
}

View File

@@ -0,0 +1,31 @@
using System.Collections.Generic;
namespace Orchard.DataMigration.Schema {
public class CreateForeignKeyCommand : TableCommand {
protected readonly List<ForeignKeyClause> _foreignKeyClauses;
public CreateForeignKeyCommand(string name)
: base(name) {
_foreignKeyClauses = new List<ForeignKeyClause>();
}
public CreateForeignKeyCommand On(string srcColumn, string destTable, string destColumn) {
_foreignKeyClauses.Add(new ForeignKeyClause(srcColumn, destTable, destColumn));
return this;
}
}
public class ForeignKeyClause {
public ForeignKeyClause(string srcColumn, string destTable, string destColumn) {
SrcColumn = srcColumn;
DestTable = destTable;
DestColumn = destColumn;
}
public string DestColumn { get; private set; }
public string DestTable { get; private set; }
public string SrcColumn { get; private set; }
}
}

View File

@@ -0,0 +1,10 @@
namespace Orchard.DataMigration.Schema {
public class CreateIndexCommand : TableCommand {
public CreateIndexCommand(string name, params string[] columnNames)
: base(name) {
ColumnNames = columnNames;
}
public string[] ColumnNames { get; private set; }
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Data;
namespace Orchard.DataMigration.Schema {
public class CreateTableCommand : SchemaCommand {
public CreateTableCommand(string name)
: base(name) {
}
public CreateTableCommand Column(string name, DbType dbType, Action<CreateColumnCommand> column = null) {
var command = new CreateColumnCommand(name);
command.Type(dbType);
if ( column != null ) {
column(command);
}
_tableCommands.Add(command);
return this;
}
public CreateTableCommand ContentPartRecord() {
/// TODO: Call Column() with necessary information for content part records
return this;
}
public CreateTableCommand VersionedContentPartRecord() {
/// TODO: Call Column() with necessary information for content part records
return this;
}
public CreateTableCommand ForeignKey(string name, Action<CreateForeignKeyCommand> fk) {
var command = new CreateForeignKeyCommand(name);
fk(command);
_tableCommands.Add(command);
return this;
}
}
}

View File

@@ -0,0 +1,8 @@
namespace Orchard.DataMigration.Schema {
public class DropColumnCommand : ColumnCommand {
public DropColumnCommand(string name)
: base(name) {
}
}
}

View File

@@ -0,0 +1,8 @@
namespace Orchard.DataMigration.Schema {
public class DropForeignKeyCommand : TableCommand {
public DropForeignKeyCommand(string name)
: base(name) {
}
}
}

View File

@@ -0,0 +1,8 @@
namespace Orchard.DataMigration.Schema {
public class DropIndexCommand : TableCommand {
public DropIndexCommand(string name)
: base(name) {
}
}
}

View File

@@ -0,0 +1,7 @@
namespace Orchard.DataMigration.Schema {
public class DropTableCommand : SchemaCommand {
public DropTableCommand(string name)
: base(name) {
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
namespace Orchard.DataMigration.Schema {
public class SchemaBuilder {
private readonly List<SchemaCommand> _schemaCommands;
public SchemaBuilder() {
_schemaCommands = new List<SchemaCommand>();
}
public SchemaBuilder(string tablePrefix) : this() {
TablePrefix = tablePrefix;
}
public string TablePrefix { get; private set; }
public SchemaBuilder CreateTable(string name, Action<CreateTableCommand> table) {
var createTable = new CreateTableCommand(name);
table(createTable);
_schemaCommands.Add(createTable);
return this;
}
public SchemaBuilder AlterTable(string name, Action<AlterTableCommand> table) {
var alterTable = new AlterTableCommand(name);
table(alterTable);
_schemaCommands.Add(alterTable);
return this;
}
public SchemaBuilder DropTable(string name) {
var deleteTable = new DropTableCommand(name);
_schemaCommands.Add(deleteTable);
return this;
}
public SchemaBuilder ExecuteSql(string sql, Action<SqlStatementCommand> statement) {
var sqlStatmentCommand = new SqlStatementCommand(sql);
statement(sqlStatmentCommand);
return this;
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Collections.Generic;
namespace Orchard.DataMigration.Schema {
public class SchemaCommand {
protected readonly List<TableCommand> _tableCommands;
public SchemaCommand(string tableName) {
_tableCommands = new List<TableCommand>();
Name(tableName);
}
public string TableName { get; private set; }
public SchemaCommand Name(string name) {
TableName = name;
return this;
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Collections.Generic;
namespace Orchard.DataMigration.Schema {
public class SqlStatementCommand : SchemaCommand {
protected readonly List<string> _dialects;
public SqlStatementCommand(string sql)
: base("") {
Sql = sql;
_dialects = new List<string>();
}
public string Sql { get; private set; }
public SqlStatementCommand ForDialect(string dialect) {
_dialects.Add(dialect);
return this;
}
}
}

View File

@@ -0,0 +1,10 @@
namespace Orchard.DataMigration.Schema {
public class TableCommand {
private string _name;
public TableCommand(string name) {
_name = name;
}
}
}

View File

@@ -195,7 +195,7 @@ namespace Orchard.Environment.State {
}
}
private static IEnumerable<FeatureDescriptor> OrderByDependencies(IEnumerable<FeatureDescriptor> descriptors) {
public static IEnumerable<FeatureDescriptor> OrderByDependencies(IEnumerable<FeatureDescriptor> descriptors) {
var population = descriptors.Select(d => new Linkage {
Feature = d
}).ToArray();

View File

@@ -353,7 +353,19 @@
<Compile Include="ContentManagement\ViewModels\TemplateViewModel.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DataMigration\Schema\SqlStatementCommand.cs" />
<Compile Include="DataMigration\Schema\CreateColumnCommand.cs" />
<Compile Include="DataMigration\Schema\CreateForeignKeyCommand.cs" />
<Compile Include="DataMigration\Schema\DropForeignKeyCommand.cs" />
<Compile Include="DataMigration\Schema\DropIndexCommand.cs" />
<Compile Include="DataMigration\Schema\CreateIndexCommand.cs" />
<Compile Include="DataMigration\Schema\DropColumnCommand.cs" />
<Compile Include="DataMigration\Schema\AlterColumnCommand.cs" />
<Compile Include="DataMigration\Schema\DropTableCommand.cs" />
<Compile Include="DataMigration\Schema\AlterTableCommand.cs" />
<Compile Include="DataMigration\Commands\DataMigrationCommands.cs" />
<Compile Include="DataMigration\Schema\SchemaBuilder.cs" />
<Compile Include="DataMigration\DataMigrationCoordinator.cs" />
<Compile Include="DataMigration\IDataMigrationCommand.cs" />
<Compile Include="DataMigration\DefaultDataMigrationGenerator.cs" />
<Compile Include="DataMigration\IDataMigrationGenerator.cs" />
@@ -362,6 +374,10 @@
<Compile Include="DataMigration\DataMigrationRecord.cs" />
<Compile Include="DataMigration\IDataMigration.cs" />
<Compile Include="DataMigration\IDataMigrationManager.cs" />
<Compile Include="DataMigration\Schema\ColumnCommand.cs" />
<Compile Include="DataMigration\Schema\CreateTableCommand.cs" />
<Compile Include="DataMigration\Schema\TableCommand.cs" />
<Compile Include="DataMigration\Schema\SchemaCommand.cs" />
<Compile Include="Data\SessionLocator.cs" />
<Compile Include="Data\IRepository.cs" />
<Compile Include="Data\ISessionLocator.cs" />