mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
Merge
--HG-- branch : dev
This commit is contained in:
@@ -6,7 +6,7 @@ using JetBrains.Annotations;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.ContentManagement.MetaData.Records;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.Core.Common;
|
||||
using Orchard.Core.Common.Handlers;
|
||||
using Orchard.Core.Common.Models;
|
||||
@@ -26,6 +26,7 @@ namespace Orchard.Core.Tests.Common.Providers {
|
||||
private Mock<IAuthenticationService> _authn;
|
||||
private Mock<IAuthorizationService> _authz;
|
||||
private Mock<IMembershipService> _membership;
|
||||
private Mock<IContentDefinitionManager> _contentDefinitionManager;
|
||||
|
||||
public override void Register(ContainerBuilder builder) {
|
||||
builder.RegisterType<DefaultContentManager>().As<IContentManager>();
|
||||
@@ -36,19 +37,18 @@ namespace Orchard.Core.Tests.Common.Providers {
|
||||
_authn = new Mock<IAuthenticationService>();
|
||||
_authz = new Mock<IAuthorizationService>();
|
||||
_membership = new Mock<IMembershipService>();
|
||||
_contentDefinitionManager = new Mock<IContentDefinitionManager>();
|
||||
|
||||
builder.RegisterInstance(_authn.Object);
|
||||
builder.RegisterInstance(_authz.Object);
|
||||
builder.RegisterInstance(_membership.Object);
|
||||
|
||||
builder.RegisterInstance(_contentDefinitionManager.Object);
|
||||
}
|
||||
|
||||
protected override IEnumerable<Type> DatabaseTypes {
|
||||
get {
|
||||
return new[] {
|
||||
typeof(ContentTypeRecord),
|
||||
typeof(ContentTypePartRecord),
|
||||
typeof(ContentTypePartNameRecord),
|
||||
typeof(ContentItemRecord),
|
||||
typeof(ContentItemVersionRecord),
|
||||
typeof(CommonRecord),
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
using System.Collections.Generic;
|
||||
using Autofac;
|
||||
using JetBrains.Annotations;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Drivers;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.ContentManagement.MetaData.Records;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Common.Services;
|
||||
@@ -28,6 +29,7 @@ namespace Orchard.Core.Tests.Common.Services {
|
||||
public override void Register(ContainerBuilder builder) {
|
||||
builder.RegisterType<DefaultContentManager>().As<IContentManager>();
|
||||
builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
|
||||
builder.RegisterInstance(new Mock<IContentDefinitionManager>().Object);
|
||||
|
||||
builder.RegisterType<ThingHandler>().As<IContentHandler>();
|
||||
builder.RegisterType<StuffHandler>().As<IContentHandler>();
|
||||
@@ -182,8 +184,6 @@ namespace Orchard.Core.Tests.Common.Services {
|
||||
return new[] {
|
||||
typeof(RoutableRecord),
|
||||
typeof(ContentTypeRecord),
|
||||
typeof(ContentTypePartRecord),
|
||||
typeof(ContentTypePartNameRecord),
|
||||
typeof(ContentItemRecord),
|
||||
typeof(ContentItemVersionRecord),
|
||||
typeof(CommonRecord),
|
||||
|
||||
@@ -9,6 +9,8 @@ using Moq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.ContentManagement.MetaData.Builders;
|
||||
using Orchard.ContentManagement.MetaData.Models;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Feeds;
|
||||
using Orchard.Core.Feeds.Controllers;
|
||||
@@ -145,7 +147,7 @@ namespace Orchard.Core.Tests.Feeds.Controllers {
|
||||
[Test]
|
||||
public void CorePartValuesAreExtracted() {
|
||||
var clock = new StubClock();
|
||||
var hello = new ContentItemBuilder("hello")
|
||||
var hello = new ContentItemBuilder(new ContentTypeDefinitionBuilder().Named("hello").Build())
|
||||
.Weld<CommonAspect>()
|
||||
.Weld<RoutableAspect>()
|
||||
.Weld<BodyAspect>()
|
||||
|
||||
@@ -58,7 +58,11 @@ namespace Orchard.Tests.Indexing {
|
||||
[Test]
|
||||
public void IndexProviderShouldOverwriteAlreadyExistingIndex() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1).Add("body", null));
|
||||
Assert.That(_provider.IsEmpty("default"), Is.False);
|
||||
|
||||
_provider.CreateIndex("default");
|
||||
Assert.That(_provider.IsEmpty("default"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -168,5 +172,82 @@ namespace Orchard.Tests.Indexing {
|
||||
Assert.That(searchBuilder.WithField("body", "hr").Search().Count(), Is.EqualTo(1));
|
||||
Assert.That(searchBuilder.WithField("body", "hr").Search().First().Id, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test] public void ShouldAllowNullOrEmptyStrings() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1).Add("body", null));
|
||||
_provider.Store("default", _provider.New(2).Add("body", ""));
|
||||
_provider.Store("default", _provider.New(3).Add("body", "<hr></hr>", true));
|
||||
|
||||
var searchBuilder = _provider.CreateSearchBuilder("default");
|
||||
|
||||
Assert.That(searchBuilder.Get(1).Id, Is.EqualTo(1));
|
||||
Assert.That(searchBuilder.Get(2).Id, Is.EqualTo(2));
|
||||
Assert.That(searchBuilder.Get(3).Id, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ProviderShouldStoreSettings() {
|
||||
_provider.CreateIndex("default");
|
||||
Assert.That(_provider.GetLastIndexUtc("default"), Is.EqualTo(DefaultIndexProvider.DefaultMinDateTime));
|
||||
|
||||
_provider.SetLastIndexUtc("default", new DateTime(2010, 1, 1, 1, 1, 1, 1));
|
||||
Assert.That(_provider.GetLastIndexUtc("default"), Is.EqualTo(new DateTime(2010, 1, 1, 1, 1, 1, 0)));
|
||||
|
||||
_provider.SetLastIndexUtc("default", new DateTime(1901, 1, 1, 1, 1, 1, 1));
|
||||
Assert.That(_provider.GetLastIndexUtc("default"), Is.EqualTo(DefaultIndexProvider.DefaultMinDateTime));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsEmptyShouldBeTrueForNoneExistingIndexes() {
|
||||
_provider.IsEmpty("dummy");
|
||||
Assert.That(_provider.IsEmpty("default"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsEmptyShouldBeTrueForJustNewIndexes() {
|
||||
_provider.CreateIndex("default");
|
||||
Assert.That(_provider.IsEmpty("default"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsEmptyShouldBeFalseWhenThereIsADocument() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1).Add("body", null));
|
||||
Assert.That(_provider.IsEmpty("default"), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDirtyShouldBeFalseForNewDocuments() {
|
||||
IIndexDocument doc = _provider.New(1);
|
||||
Assert.That(doc.IsDirty, Is.False);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void IsDirtyShouldBeTrueWhenIndexIsModified() {
|
||||
IIndexDocument doc;
|
||||
|
||||
doc = _provider.New(1);
|
||||
doc.Add("foo", "value");
|
||||
Assert.That(doc.IsDirty, Is.True);
|
||||
|
||||
doc = _provider.New(1);
|
||||
doc.Add("foo", false);
|
||||
Assert.That(doc.IsDirty, Is.True);
|
||||
|
||||
doc = _provider.New(1);
|
||||
doc.Add("foo", (float)1.0);
|
||||
Assert.That(doc.IsDirty, Is.True);
|
||||
|
||||
doc = _provider.New(1);
|
||||
doc.Add("foo", 1);
|
||||
Assert.That(doc.IsDirty, Is.True);
|
||||
|
||||
doc = _provider.New(1);
|
||||
doc.Add("foo", DateTime.Now);
|
||||
Assert.That(doc.IsDirty, Is.True);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,5 +177,19 @@ namespace Orchard.Tests.Indexing {
|
||||
Assert.That(date[0].GetDateTime("date") < date[1].GetDateTime("date"), Is.True);
|
||||
Assert.That(date[1].GetDateTime("date") < date[2].GetDateTime("date"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldEscapeSpecialChars() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1).Add("body", "Orchard has been developped in C#"));
|
||||
_provider.Store("default", _provider.New(2).Add("body", "Windows has been developped in C++"));
|
||||
|
||||
var cs = _searchBuilder.WithField("body", "C#").Search().ToList();
|
||||
Assert.That(cs.Count(), Is.EqualTo(2));
|
||||
|
||||
var cpp = _searchBuilder.WithField("body", "C++").Search().ToList();
|
||||
Assert.That(cpp.Count(), Is.EqualTo(2));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Scheduling\ScheduledTaskManagerTests.cs" />
|
||||
<Compile Include="Scheduling\ScheduledTaskExecutorTests.cs" />
|
||||
<Compile Include="Settings\Metadata\ContentDefinitionManagerTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Orchard.Tests.Modules\Orchard.Tests.Modules.csproj">
|
||||
|
||||
@@ -4,7 +4,7 @@ using Autofac;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.MetaData.Records;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using Orchard.Core.Scheduling.Models;
|
||||
using Orchard.Core.Scheduling.Services;
|
||||
@@ -30,6 +30,7 @@ namespace Orchard.Core.Tests.Scheduling {
|
||||
builder.RegisterInstance(new Mock<IOrchardServices>().Object);
|
||||
builder.RegisterType<DefaultContentManager>().As<IContentManager>();
|
||||
builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
|
||||
builder.RegisterInstance(new Mock<IContentDefinitionManager>().Object);
|
||||
|
||||
builder.RegisterType<ScheduledTaskExecutor>().As<IBackgroundTask>().Named("ScheduledTaskExecutor", typeof(IBackgroundTask));
|
||||
builder.RegisterInstance(_handler).As<IScheduledTaskHandler>();
|
||||
@@ -39,8 +40,6 @@ namespace Orchard.Core.Tests.Scheduling {
|
||||
get {
|
||||
return new[] {
|
||||
typeof(ContentTypeRecord),
|
||||
typeof(ContentTypePartRecord),
|
||||
typeof(ContentTypePartNameRecord),
|
||||
typeof(ContentItemRecord),
|
||||
typeof(ContentItemVersionRecord),
|
||||
typeof(ScheduledTaskRecord),
|
||||
|
||||
@@ -5,7 +5,7 @@ using Autofac;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.MetaData.Records;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using Orchard.Core.Scheduling.Models;
|
||||
using Orchard.Core.Scheduling.Services;
|
||||
@@ -34,6 +34,7 @@ namespace Orchard.Core.Tests.Scheduling {
|
||||
builder.RegisterInstance(_mockServices.Object);
|
||||
builder.RegisterType<DefaultContentManager>().As<IContentManager>();
|
||||
builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
|
||||
builder.RegisterInstance(new Mock<IContentDefinitionManager>().Object);
|
||||
|
||||
builder.RegisterType<ScheduledTaskManager>().As<IScheduledTaskManager>();
|
||||
}
|
||||
@@ -42,8 +43,6 @@ namespace Orchard.Core.Tests.Scheduling {
|
||||
get {
|
||||
return new[] {
|
||||
typeof(ContentTypeRecord),
|
||||
typeof(ContentTypePartRecord),
|
||||
typeof(ContentTypePartNameRecord),
|
||||
typeof(ContentItemRecord),
|
||||
typeof(ContentItemVersionRecord),
|
||||
typeof(ScheduledTaskRecord),
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Autofac;
|
||||
using Moq;
|
||||
using NHibernate;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.ContentManagement.MetaData.Builders;
|
||||
using Orchard.ContentManagement.MetaData.Models;
|
||||
using Orchard.ContentManagement.MetaData.Services;
|
||||
using Orchard.Core.Settings.Metadata;
|
||||
using Orchard.Core.Settings.Metadata.Records;
|
||||
using Orchard.Data;
|
||||
using Orchard.Tests;
|
||||
using Orchard.Tests.Utility;
|
||||
|
||||
namespace Orchard.Core.Tests.Settings.Metadata {
|
||||
[TestFixture]
|
||||
public class ContentDefinitionManagerTests {
|
||||
private string _databaseFileName;
|
||||
private ISessionFactory _sessionFactory;
|
||||
private ISession _session;
|
||||
private IContainer _container;
|
||||
|
||||
[TestFixtureSetUp]
|
||||
public void InitFixture() {
|
||||
_databaseFileName = Path.GetTempFileName();
|
||||
_sessionFactory = DataUtility.CreateSessionFactory(
|
||||
_databaseFileName,
|
||||
typeof(ContentTypeDefinitionRecord),
|
||||
typeof(ContentTypePartDefinitionRecord),
|
||||
typeof(ContentPartDefinitionRecord),
|
||||
typeof(ContentPartFieldDefinitionRecord),
|
||||
typeof(ContentFieldDefinitionRecord)
|
||||
);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void Init() {
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterAutoMocking();
|
||||
builder.RegisterType<ContentDefinitionManager>().As<IContentDefinitionManager>();
|
||||
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
|
||||
builder.RegisterType(typeof(SettingsFormatter))
|
||||
.As(typeof(IMapper<XElement, IDictionary<string, string>>))
|
||||
.As(typeof(IMapper<IDictionary<string, string>, XElement>));
|
||||
_container = builder.Build();
|
||||
|
||||
_container.Mock<ISessionLocator>()
|
||||
.Setup(x => x.For(It.IsAny<Type>()))
|
||||
.Returns(() => _session);
|
||||
|
||||
_session = _sessionFactory.OpenSession();
|
||||
foreach (var killType in new[] { typeof(ContentTypeDefinitionRecord), typeof(ContentPartDefinitionRecord), typeof(ContentFieldDefinitionRecord) }) {
|
||||
foreach (var killRecord in _session.CreateCriteria(killType).List()) {
|
||||
_session.Delete(killRecord);
|
||||
}
|
||||
}
|
||||
_session.Flush();
|
||||
}
|
||||
|
||||
void ResetSession() {
|
||||
_session.Flush();
|
||||
_session.Dispose();
|
||||
_session = _sessionFactory.OpenSession();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void Term() {
|
||||
_session.Dispose();
|
||||
}
|
||||
|
||||
[TestFixtureTearDown]
|
||||
public void TermFixture() {
|
||||
File.Delete(_databaseFileName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoTypesAreAvailableByDefault() {
|
||||
var types = _container.Resolve<IContentDefinitionManager>().ListTypeDefinitions();
|
||||
Assert.That(types.Count(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TypeRecordsAreReturned() {
|
||||
var repository = _container.Resolve<IRepository<ContentTypeDefinitionRecord>>();
|
||||
repository.Create(new ContentTypeDefinitionRecord { Name = "alpha" });
|
||||
repository.Create(new ContentTypeDefinitionRecord { Name = "beta" });
|
||||
ResetSession();
|
||||
var types = _container.Resolve<IContentDefinitionManager>().ListTypeDefinitions();
|
||||
Assert.That(types.Count(), Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TypeSettingsAreParsed() {
|
||||
var repository = _container.Resolve<IRepository<ContentTypeDefinitionRecord>>();
|
||||
repository.Create(new ContentTypeDefinitionRecord { Name = "alpha", Settings = "<settings a='1' b='2'/>" });
|
||||
ResetSession();
|
||||
var alpha = _container.Resolve<IContentDefinitionManager>().ListTypeDefinitions().Single();
|
||||
Assert.That(alpha.Settings["a"], Is.EqualTo("1"));
|
||||
Assert.That(alpha.Settings["b"], Is.EqualTo("2"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContentTypesWithSettingsCanBeCreatedAndModified() {
|
||||
var manager = _container.Resolve<IContentDefinitionManager>();
|
||||
manager.StoreTypeDefinition(new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.WithSetting("a", "1")
|
||||
.WithSetting("b", "2")
|
||||
.Build());
|
||||
|
||||
manager.StoreTypeDefinition(new ContentTypeDefinitionBuilder()
|
||||
.Named("beta")
|
||||
.WithSetting("c", "3")
|
||||
.WithSetting("d", "4")
|
||||
.Build());
|
||||
|
||||
ResetSession();
|
||||
|
||||
var types1 = manager.ListTypeDefinitions();
|
||||
Assert.That(types1.Count(), Is.EqualTo(2));
|
||||
var alpha1 = types1.Single(t => t.Name == "alpha");
|
||||
Assert.That(alpha1.Settings["a"], Is.EqualTo("1"));
|
||||
manager.StoreTypeDefinition(new ContentTypeDefinitionBuilder(alpha1).WithSetting("a", "5").Build());
|
||||
ResetSession();
|
||||
|
||||
var types2 = manager.ListTypeDefinitions();
|
||||
Assert.That(types2.Count(), Is.EqualTo(2));
|
||||
var alpha2 = types2.Single(t => t.Name == "alpha");
|
||||
Assert.That(alpha2.Settings["a"], Is.EqualTo("5"));
|
||||
Assert.That(alpha2.Settings["a"], Is.EqualTo("5"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StubPartDefinitionsAreCreatedWhenContentTypesAreStored() {
|
||||
var manager = _container.Resolve<IContentDefinitionManager>();
|
||||
manager.StoreTypeDefinition(new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.WithPart("foo", pb => { })
|
||||
.Build());
|
||||
|
||||
ResetSession();
|
||||
|
||||
var fooRecord = _container.Resolve<IRepository<ContentPartDefinitionRecord>>().Fetch(r => r.Name == "foo").SingleOrDefault();
|
||||
Assert.That(fooRecord, Is.Not.Null);
|
||||
Assert.That(fooRecord.Name, Is.EqualTo("foo"));
|
||||
|
||||
var foo = manager.GetPartDefinition("foo");
|
||||
Assert.That(foo, Is.Not.Null);
|
||||
Assert.That(foo.Name, Is.EqualTo("foo"));
|
||||
|
||||
var alpha = manager.GetTypeDefinition("alpha");
|
||||
Assert.That(alpha, Is.Not.Null);
|
||||
Assert.That(alpha.Parts.Count(), Is.EqualTo(1));
|
||||
Assert.That(alpha.Parts.Single().PartDefinition.Name, Is.EqualTo("foo"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GettingDefinitionsByNameCanReturnNullAndWillAcceptNullEmptyOrInvalidNames() {
|
||||
var manager = _container.Resolve<IContentDefinitionManager>();
|
||||
Assert.That(manager.GetTypeDefinition("no such name"), Is.Null);
|
||||
Assert.That(manager.GetTypeDefinition(string.Empty), Is.Null);
|
||||
Assert.That(manager.GetTypeDefinition(null), Is.Null);
|
||||
Assert.That(manager.GetPartDefinition("no such name"), Is.Null);
|
||||
Assert.That(manager.GetPartDefinition(string.Empty), Is.Null);
|
||||
Assert.That(manager.GetPartDefinition(null), Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartsAreRemovedWhenNotReferencedButPartDefinitionRemains() {
|
||||
var manager = _container.Resolve<IContentDefinitionManager>();
|
||||
manager.StoreTypeDefinition(
|
||||
new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.WithPart("foo", pb => { })
|
||||
.WithPart("bar", pb => { })
|
||||
.Build());
|
||||
|
||||
AssertThatTypeHasParts("alpha","foo","bar");
|
||||
Assert.That(manager.ListPartDefinitions().Count(), Is.EqualTo(2));
|
||||
ResetSession();
|
||||
AssertThatTypeHasParts("alpha","foo","bar");
|
||||
Assert.That(manager.ListPartDefinitions().Count(), Is.EqualTo(2));
|
||||
|
||||
manager.StoreTypeDefinition(
|
||||
new ContentTypeDefinitionBuilder(manager.GetTypeDefinition("alpha"))
|
||||
.WithPart("frap", pb => { })
|
||||
.RemovePart("bar")
|
||||
.Build());
|
||||
|
||||
AssertThatTypeHasParts("alpha","foo","frap");
|
||||
Assert.That(manager.ListPartDefinitions().Count(), Is.EqualTo(3));
|
||||
ResetSession();
|
||||
AssertThatTypeHasParts("alpha","foo","frap");
|
||||
Assert.That(manager.ListPartDefinitions().Count(), Is.EqualTo(3));
|
||||
}
|
||||
|
||||
private void AssertThatTypeHasParts(string typeName, params string[] partNames) {
|
||||
var type = _container.Resolve<IContentDefinitionManager>().GetTypeDefinition(typeName);
|
||||
Assert.That(type, Is.Not.Null);
|
||||
Assert.That(type.Parts.Count(), Is.EqualTo(partNames.Count()));
|
||||
foreach(var partName in partNames) {
|
||||
Assert.That(type.Parts.Select(p=>p.PartDefinition.Name), Has.Some.EqualTo(partName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,15 +7,9 @@ Scenario: Installed modules are listed
|
||||
Given I have installed Orchard
|
||||
When I go to "admin/modules"
|
||||
Then I should see "<h1>Installed Modules</h1>"
|
||||
And I should see "<h3>Themes</h3>"
|
||||
And I should see "<h2>Themes"
|
||||
And the status should be 200 OK
|
||||
|
||||
Scenario: Edit module shows its features
|
||||
Given I have installed Orchard
|
||||
When I go to "admin/modules/Edit/Orchard.Themes"
|
||||
Then I should see "<h1>Edit Module: Themes</h1>"
|
||||
And the status should be 200 OK
|
||||
|
||||
Scenario: Features of installed modules are listed
|
||||
Given I have installed Orchard
|
||||
When I go to "admin/modules/features"
|
||||
|
||||
@@ -7,7 +7,7 @@ Scenario: Default site is listed
|
||||
Given I have installed Orchard
|
||||
And I have installed "Orchard.MultiTenancy"
|
||||
When I go to "Admin/MultiTenancy"
|
||||
Then I should see "List of Site's Tenants"
|
||||
Then I should see "List of Site's Tenants"
|
||||
And I should see "<span class="tenantName">Default</span>"
|
||||
And the status should be 200 OK
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.Web.Routing;
|
||||
using Autofac;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement.MetaData.Records;
|
||||
using Orchard.Data;
|
||||
using Orchard.Environment;
|
||||
using Orchard.ContentManagement;
|
||||
@@ -47,8 +46,6 @@ namespace Orchard.Tests.Modules.Users.Controllers {
|
||||
get {
|
||||
return new[] { typeof(UserRecord),
|
||||
typeof(ContentTypeRecord),
|
||||
typeof(ContentTypePartRecord),
|
||||
typeof(ContentTypePartNameRecord),
|
||||
typeof(ContentItemRecord),
|
||||
typeof(ContentItemVersionRecord),
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Web.Security;
|
||||
using Autofac;
|
||||
using NHibernate;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement.MetaData.Records;
|
||||
using Orchard.Data;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
@@ -42,9 +41,7 @@ namespace Orchard.Tests.Modules.Users.Services {
|
||||
typeof(UserRecord),
|
||||
typeof(ContentItemVersionRecord),
|
||||
typeof(ContentItemRecord),
|
||||
typeof(ContentTypeRecord),
|
||||
typeof(ContentTypePartRecord),
|
||||
typeof(ContentTypePartNameRecord));
|
||||
typeof(ContentTypeRecord));
|
||||
}
|
||||
|
||||
[TestFixtureTearDown]
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System.Linq;
|
||||
using Autofac;
|
||||
using Moq;
|
||||
using NHibernate;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement.MetaData.Records;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.Data;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
@@ -28,9 +29,7 @@ namespace Orchard.Tests.ContentManagement {
|
||||
typeof(EpsilonRecord),
|
||||
typeof(ContentItemVersionRecord),
|
||||
typeof(ContentItemRecord),
|
||||
typeof(ContentTypeRecord),
|
||||
typeof(ContentTypePartRecord),
|
||||
typeof(ContentTypePartNameRecord));
|
||||
typeof(ContentTypeRecord));
|
||||
}
|
||||
|
||||
[TestFixtureTearDown]
|
||||
@@ -47,6 +46,7 @@ namespace Orchard.Tests.ContentManagement {
|
||||
builder.RegisterModule(new ContentModule());
|
||||
builder.RegisterType<DefaultContentManager>().As<IContentManager>().SingleInstance();
|
||||
builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
|
||||
builder.RegisterInstance(new Mock<IContentDefinitionManager>().Object);
|
||||
|
||||
builder.RegisterType<AlphaHandler>().As<IContentHandler>();
|
||||
builder.RegisterType<BetaHandler>().As<IContentHandler>();
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
using Autofac;
|
||||
using NHibernate;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.MetaData.Records;
|
||||
using Orchard.ContentManagement.MetaData.Services;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using Orchard.Data;
|
||||
|
||||
|
||||
namespace Orchard.Tests.ContentManagement{
|
||||
[TestFixture]
|
||||
public class ContentTypeMetaDataTests
|
||||
{
|
||||
private IContainer _container;
|
||||
private ISessionFactory _sessionFactory;
|
||||
private ISession _session;
|
||||
|
||||
[TestFixtureSetUp]
|
||||
public void InitFixture()
|
||||
{
|
||||
var databaseFileName = System.IO.Path.GetTempFileName();
|
||||
_sessionFactory = DataUtility.CreateSessionFactory(
|
||||
databaseFileName,
|
||||
typeof(ContentTypeRecord),
|
||||
typeof(ContentItemRecord),
|
||||
typeof(ContentTypePartRecord),
|
||||
typeof(ContentTypePartNameRecord),
|
||||
typeof(ContentItemVersionRecord));
|
||||
}
|
||||
|
||||
[TestFixtureTearDown]
|
||||
public void TermFixture()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void Init()
|
||||
{
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterType<DefaultContentManager>().As<IContentManager>();
|
||||
builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
|
||||
builder.RegisterType<ContentTypeService>().As<IContentTypeService>();
|
||||
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
|
||||
_session = _sessionFactory.OpenSession();
|
||||
builder.RegisterInstance(new DefaultContentManagerTests.TestSessionLocator(_session)).As<ISessionLocator>();
|
||||
|
||||
_container = builder.Build();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MapandUnMapContentTypeToContentPart()
|
||||
{
|
||||
var contentTypeService = _container.Resolve<IContentTypeService>();
|
||||
contentTypeService.MapContentTypeToContentPart("foo", "bar");
|
||||
Assert.IsTrue(contentTypeService.ValidateContentTypeToContentPartMapping("foo","bar"),"Content Type not successfully mapped");
|
||||
contentTypeService.UnMapContentTypeToContentPart("foo", "bar");
|
||||
Assert.IsFalse(contentTypeService.ValidateContentTypeToContentPartMapping("foo", "bar"), "Content Type mapping not successfully deleted");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,11 @@
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Autofac;
|
||||
using Moq;
|
||||
using NHibernate;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement.MetaData.Records;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.ContentManagement.MetaData.Builders;
|
||||
using Orchard.Data;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
@@ -19,6 +21,7 @@ namespace Orchard.Tests.ContentManagement {
|
||||
private IContentManager _manager;
|
||||
private ISessionFactory _sessionFactory;
|
||||
private ISession _session;
|
||||
private Mock<IContentDefinitionManager> _contentDefinitionManager;
|
||||
|
||||
[TestFixtureSetUp]
|
||||
public void InitFixture() {
|
||||
@@ -26,8 +29,6 @@ namespace Orchard.Tests.ContentManagement {
|
||||
_sessionFactory = DataUtility.CreateSessionFactory(
|
||||
databaseFileName,
|
||||
typeof(ContentTypeRecord),
|
||||
typeof(ContentTypePartRecord),
|
||||
typeof(ContentTypePartNameRecord),
|
||||
typeof(ContentItemRecord),
|
||||
typeof(ContentItemVersionRecord),
|
||||
typeof(GammaRecord),
|
||||
@@ -42,10 +43,12 @@ namespace Orchard.Tests.ContentManagement {
|
||||
|
||||
[SetUp]
|
||||
public void Init() {
|
||||
_contentDefinitionManager = new Mock<IContentDefinitionManager>();
|
||||
|
||||
var builder = new ContainerBuilder();
|
||||
//builder.RegisterModule(new ImplicitCollectionSupportModule());
|
||||
builder.RegisterType<DefaultContentManager>().As<IContentManager>();
|
||||
builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
|
||||
builder.RegisterInstance(_contentDefinitionManager.Object);
|
||||
|
||||
builder.RegisterType<AlphaHandler>().As<IContentHandler>();
|
||||
builder.RegisterType<BetaHandler>().As<IContentHandler>();
|
||||
@@ -462,6 +465,40 @@ namespace Orchard.Tests.ContentManagement {
|
||||
Assert.That(gammas[3].Version, Is.EqualTo(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EmptyTypeDefinitionShouldBeCreatedIfNotAlreadyDefined() {
|
||||
var contentItem = _manager.New("no-such-type");
|
||||
Assert.That(contentItem.ContentType, Is.EqualTo("no-such-type"));
|
||||
Assert.That(contentItem.TypeDefinition, Is.Not.Null);
|
||||
Assert.That(contentItem.TypeDefinition.Name, Is.EqualTo("no-such-type"));
|
||||
Assert.That(contentItem.TypeDefinition.Settings.Count(), Is.EqualTo(0));
|
||||
Assert.That(contentItem.TypeDefinition.Parts.Count(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void ExistingTypeAndPartDefinitionShouldBeUsed() {
|
||||
var alphaType = new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.WithSetting("x", "1")
|
||||
.WithPart("foo")
|
||||
.WithPart("Flavored", part => part.WithSetting("spin", "clockwise"))
|
||||
.Build();
|
||||
|
||||
_contentDefinitionManager
|
||||
.Setup(x => x.GetTypeDefinition("alpha"))
|
||||
.Returns(alphaType);
|
||||
|
||||
var contentItem = _manager.New("alpha");
|
||||
Assert.That(contentItem.ContentType, Is.EqualTo("alpha"));
|
||||
Assert.That(contentItem.TypeDefinition, Is.Not.Null);
|
||||
Assert.That(contentItem.TypeDefinition, Is.SameAs(alphaType));
|
||||
|
||||
var flavored = contentItem.As<Flavored>();
|
||||
Assert.That(flavored, Is.Not.Null);
|
||||
Assert.That(flavored.TypePartDefinition, Is.Not.Null);
|
||||
Assert.That(flavored.TypePartDefinition.Settings["spin"], Is.EqualTo("clockwise"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.ContentManagement.MetaData.Builders;
|
||||
|
||||
namespace Orchard.Tests.ContentManagement.Handlers {
|
||||
|
||||
@@ -22,7 +23,7 @@ namespace Orchard.Tests.ContentManagement.Handlers {
|
||||
public void PartShouldBeAddedBasedOnSimplePredicate() {
|
||||
var modelDriver = new TestModelHandler();
|
||||
|
||||
var builder = new ContentItemBuilder("testing");
|
||||
var builder = new ContentItemBuilder(new ContentTypeDefinitionBuilder().Named("testing").Build());
|
||||
((IContentHandler)modelDriver).Activating(new ActivatingContentContext { Builder = builder, ContentType = "testing" });
|
||||
var model = builder.Build();
|
||||
Assert.That(model.Is<TestModelPart>(), Is.True);
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.ContentManagement.MetaData.Builders;
|
||||
using Orchard.Tests.ContentManagement.Models;
|
||||
|
||||
namespace Orchard.Tests.ContentManagement.Handlers {
|
||||
@@ -12,21 +13,21 @@ namespace Orchard.Tests.ContentManagement.Handlers {
|
||||
public class ModelBuilderTests {
|
||||
[Test]
|
||||
public void BuilderShouldReturnWorkingModelWithTypeAndId() {
|
||||
var builder = new ContentItemBuilder("foo");
|
||||
var builder = new ContentItemBuilder(new ContentTypeDefinitionBuilder().Named("foo").Build());
|
||||
var model = builder.Build();
|
||||
Assert.That(model.ContentType, Is.EqualTo("foo"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IdShouldDefaultToZero() {
|
||||
var builder = new ContentItemBuilder("foo");
|
||||
var builder = new ContentItemBuilder(new ContentTypeDefinitionBuilder().Named("foo").Build());
|
||||
var model = builder.Build();
|
||||
Assert.That(model.Id, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WeldShouldAddPartToModel() {
|
||||
var builder = new ContentItemBuilder("foo");
|
||||
var builder = new ContentItemBuilder(new ContentTypeDefinitionBuilder().Named("foo").Build());
|
||||
builder.Weld<Alpha>();
|
||||
var model = builder.Build();
|
||||
|
||||
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement.MetaData.Builders;
|
||||
|
||||
namespace Orchard.Tests.ContentManagement.MetaData.Builders {
|
||||
[TestFixture]
|
||||
public class ContentTypeDefinitionBuilderTests {
|
||||
[Test]
|
||||
public void ContentTypeNameAndSettingsFromScratch() {
|
||||
var contentTypeDefinition = new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.WithSetting("a", "1")
|
||||
.WithSetting("b", "2")
|
||||
.Build();
|
||||
Assert.That(contentTypeDefinition.Name, Is.EqualTo("alpha"));
|
||||
Assert.That(contentTypeDefinition.Settings.Count(), Is.EqualTo(2));
|
||||
Assert.That(contentTypeDefinition.Settings["a"], Is.EqualTo("1"));
|
||||
Assert.That(contentTypeDefinition.Settings["b"], Is.EqualTo("2"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContentRebuildWithoutModification() {
|
||||
var contentTypeDefinition1 = new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.WithSetting("a", "1")
|
||||
.WithSetting("b", "2")
|
||||
.Build();
|
||||
var contentTypeDefinition2 = new ContentTypeDefinitionBuilder(contentTypeDefinition1)
|
||||
.Build();
|
||||
Assert.That(contentTypeDefinition1, Is.Not.SameAs(contentTypeDefinition2));
|
||||
Assert.That(contentTypeDefinition2.Name, Is.EqualTo("alpha"));
|
||||
Assert.That(contentTypeDefinition2.Settings.Count(), Is.EqualTo(2));
|
||||
Assert.That(contentTypeDefinition2.Settings["a"], Is.EqualTo("1"));
|
||||
Assert.That(contentTypeDefinition2.Settings["b"], Is.EqualTo("2"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContentRebuildWithModification() {
|
||||
var contentTypeDefinition1 = new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.WithSetting("a", "1")
|
||||
.WithSetting("b", "2")
|
||||
.Build();
|
||||
var contentTypeDefinition2 = new ContentTypeDefinitionBuilder(contentTypeDefinition1)
|
||||
.Named("beta")
|
||||
.WithSetting("b", "22")
|
||||
.WithSetting("c", "3")
|
||||
.Build();
|
||||
Assert.That(contentTypeDefinition1, Is.Not.SameAs(contentTypeDefinition2));
|
||||
Assert.That(contentTypeDefinition1.Name, Is.EqualTo("alpha"));
|
||||
Assert.That(contentTypeDefinition1.Settings.Count(), Is.EqualTo(2));
|
||||
Assert.That(contentTypeDefinition1.Settings["a"], Is.EqualTo("1"));
|
||||
Assert.That(contentTypeDefinition1.Settings["b"], Is.EqualTo("2"));
|
||||
Assert.That(contentTypeDefinition2.Name, Is.EqualTo("beta"));
|
||||
Assert.That(contentTypeDefinition2.Settings.Count(), Is.EqualTo(3));
|
||||
Assert.That(contentTypeDefinition2.Settings["a"], Is.EqualTo("1"));
|
||||
Assert.That(contentTypeDefinition2.Settings["b"], Is.EqualTo("22"));
|
||||
Assert.That(contentTypeDefinition2.Settings["c"], Is.EqualTo("3"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddingPartWithSettings() {
|
||||
var contentTypeDefinition = new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.WithSetting("a", "1")
|
||||
.WithSetting("b", "2")
|
||||
.WithPart("foo", pb => pb.WithSetting("x", "10").WithSetting("y", "11"))
|
||||
.Build();
|
||||
|
||||
Assert.That(contentTypeDefinition.Name, Is.EqualTo("alpha"));
|
||||
Assert.That(contentTypeDefinition.Parts.Count(), Is.EqualTo(1));
|
||||
Assert.That(contentTypeDefinition.Parts.Single().PartDefinition.Name, Is.EqualTo("foo"));
|
||||
Assert.That(contentTypeDefinition.Parts.Single().Settings.Count(), Is.EqualTo(2));
|
||||
Assert.That(contentTypeDefinition.Parts.Single().Settings["x"], Is.EqualTo("10"));
|
||||
Assert.That(contentTypeDefinition.Parts.Single().Settings["y"], Is.EqualTo("11"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanAlterPartSettingsByNameDuringBuild() {
|
||||
var contentTypeDefinition = new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.WithSetting("a", "1")
|
||||
.WithSetting("b", "2")
|
||||
.WithPart("foo", pb => pb.WithSetting("x", "10"))
|
||||
.WithPart("foo", pb => pb.WithSetting("y", "11"))
|
||||
.Build();
|
||||
|
||||
Assert.That(contentTypeDefinition.Name, Is.EqualTo("alpha"));
|
||||
Assert.That(contentTypeDefinition.Parts.Count(), Is.EqualTo(1));
|
||||
Assert.That(contentTypeDefinition.Parts.Single().PartDefinition.Name, Is.EqualTo("foo"));
|
||||
Assert.That(contentTypeDefinition.Parts.Single().Settings.Count(), Is.EqualTo(2));
|
||||
Assert.That(contentTypeDefinition.Parts.Single().Settings["x"], Is.EqualTo("10"));
|
||||
Assert.That(contentTypeDefinition.Parts.Single().Settings["y"], Is.EqualTo("11"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanAlterPartSettingsByNameDuringRebuild() {
|
||||
var contentTypeDefinition1 = new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.WithPart("foo", pb => pb.WithSetting("x", "10").WithSetting("y", "11"))
|
||||
.Build();
|
||||
|
||||
var contentTypeDefinition2 = new ContentTypeDefinitionBuilder(contentTypeDefinition1)
|
||||
.WithPart("foo", pb => pb.WithSetting("x", "12").WithSetting("z", "13"))
|
||||
.Build();
|
||||
|
||||
Assert.That(contentTypeDefinition1.Name, Is.EqualTo("alpha"));
|
||||
Assert.That(contentTypeDefinition1.Parts.Count(), Is.EqualTo(1));
|
||||
Assert.That(contentTypeDefinition1.Parts.Single().PartDefinition.Name, Is.EqualTo("foo"));
|
||||
Assert.That(contentTypeDefinition1.Parts.Single().Settings.Count(), Is.EqualTo(2));
|
||||
Assert.That(contentTypeDefinition1.Parts.Single().Settings["x"], Is.EqualTo("10"));
|
||||
Assert.That(contentTypeDefinition1.Parts.Single().Settings["y"], Is.EqualTo("11"));
|
||||
Assert.That(contentTypeDefinition2.Name, Is.EqualTo("alpha"));
|
||||
Assert.That(contentTypeDefinition2.Parts.Count(), Is.EqualTo(1));
|
||||
Assert.That(contentTypeDefinition2.Parts.Single().PartDefinition.Name, Is.EqualTo("foo"));
|
||||
Assert.That(contentTypeDefinition2.Parts.Single().Settings.Count(), Is.EqualTo(3));
|
||||
Assert.That(contentTypeDefinition2.Parts.Single().Settings["x"], Is.EqualTo("12"));
|
||||
Assert.That(contentTypeDefinition2.Parts.Single().Settings["y"], Is.EqualTo("11"));
|
||||
Assert.That(contentTypeDefinition2.Parts.Single().Settings["z"], Is.EqualTo("13"));
|
||||
}
|
||||
|
||||
[Test, IgnoreAttribute("Merging not yet implemented")]
|
||||
public void ContentMergeOverlaysSettings() {
|
||||
Assert.Fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.ContentManagement.MetaData.Builders;
|
||||
using Orchard.ContentManagement.MetaData.Services;
|
||||
|
||||
namespace Orchard.Tests.ContentManagement.MetaData.Services {
|
||||
[TestFixture]
|
||||
public class ContentDefinitionReaderTests {
|
||||
private IContentDefinitionReader _reader;
|
||||
|
||||
[SetUp]
|
||||
public void Init() {
|
||||
_reader = new ContentDefinitionReader(new SettingsFormatter());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReadingElementSetsName() {
|
||||
var builder = new ContentTypeDefinitionBuilder();
|
||||
_reader.Merge(new XElement("foo"), builder);
|
||||
var type = builder.Build();
|
||||
Assert.That(type.Name, Is.EqualTo("foo"));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void AttributesAreAppliedAsSettings() {
|
||||
var builder = new ContentTypeDefinitionBuilder();
|
||||
_reader.Merge(new XElement("foo", new XAttribute("x", "1")), builder);
|
||||
var type = builder.Build();
|
||||
Assert.That(type.Settings["x"], Is.EqualTo("1"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChildElementsAreAddedAsPartsWithSettings() {
|
||||
var builder = new ContentTypeDefinitionBuilder();
|
||||
_reader.Merge(new XElement("foo", new XElement("bar", new XAttribute("y", "2"))), builder);
|
||||
var type = builder.Build();
|
||||
Assert.That(type.Parts.Single().PartDefinition.Name, Is.EqualTo("bar"));
|
||||
Assert.That(type.Parts.Single().Settings["y"], Is.EqualTo("2"));
|
||||
}
|
||||
|
||||
[Test, Ignore("Parts can be removed by name")]
|
||||
public void PartsCanBeRemovedByNameWhenImporting() {
|
||||
Assert.Fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.ContentManagement.MetaData.Builders;
|
||||
using Orchard.ContentManagement.MetaData.Services;
|
||||
|
||||
namespace Orchard.Tests.ContentManagement.MetaData.Services {
|
||||
[TestFixture]
|
||||
public class ContentDefinitionWriterTests {
|
||||
private ContentDefinitionWriter _writer;
|
||||
|
||||
[SetUp]
|
||||
public void Init() {
|
||||
_writer = new ContentDefinitionWriter(new SettingsFormatter());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreatesElementWithEncodedContentTypeName() {
|
||||
var alphaInfoset = _writer.Export(new ContentTypeDefinitionBuilder().Named("alpha").Build());
|
||||
var betaInfoset = _writer.Export(new ContentTypeDefinitionBuilder().Named(":beta").Build());
|
||||
var gammaInfoset = _writer.Export(new ContentTypeDefinitionBuilder().Named(" g a m m a ").Build());
|
||||
var deltaInfoset = _writer.Export(new ContentTypeDefinitionBuilder().Named("del\r\nta").Build());
|
||||
|
||||
Assert.That(XmlConvert.DecodeName(alphaInfoset.Name.LocalName), Is.EqualTo("alpha"));
|
||||
Assert.That(XmlConvert.DecodeName(betaInfoset.Name.LocalName), Is.EqualTo(":beta"));
|
||||
Assert.That(XmlConvert.DecodeName(gammaInfoset.Name.LocalName), Is.EqualTo(" g a m m a "));
|
||||
Assert.That(XmlConvert.DecodeName(deltaInfoset.Name.LocalName), Is.EqualTo("del\r\nta"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChildElementsArePartNames() {
|
||||
var alphaInfoset = _writer.Export(new ContentTypeDefinitionBuilder().Named("alpha").WithPart(":beta").WithPart("del\r\nta").Build());
|
||||
|
||||
Assert.That(XmlConvert.DecodeName(alphaInfoset.Name.LocalName), Is.EqualTo("alpha"));
|
||||
Assert.That(alphaInfoset.Elements().Count(), Is.EqualTo(2));
|
||||
Assert.That(alphaInfoset.Elements().Select(elt => elt.Name.LocalName), Has.Some.EqualTo(XmlConvert.EncodeLocalName(":beta")));
|
||||
Assert.That(alphaInfoset.Elements().Select(elt => elt.Name.LocalName), Has.Some.EqualTo(XmlConvert.EncodeLocalName("del\r\nta")));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TypeAndTypePartSettingsAreAttributes() {
|
||||
|
||||
var alpha = new ContentTypeDefinitionBuilder()
|
||||
.Named("alpha")
|
||||
.WithSetting("x", "1")
|
||||
.WithPart("beta", part => part.WithSetting(" y ", "2"))
|
||||
.Build();
|
||||
|
||||
var alphaInfoset = _writer.Export(alpha);
|
||||
Assert.That(alphaInfoset.Attributes("x").Single().Value, Is.EqualTo("1"));
|
||||
Assert.That(alphaInfoset.Elements("beta").Attributes(XmlConvert.EncodeLocalName(" y ")).Single().Value, Is.EqualTo("2"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,12 +148,14 @@
|
||||
<Compile Include="ContentManagement\ContentQueryTests.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ContentManagement\ContentTypeMetaDataTests.cs" />
|
||||
<Compile Include="ContentManagement\DefaultContentManagerTests.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ContentManagement\Handlers\ContentHandlerTests.cs" />
|
||||
<Compile Include="ContentManagement\Handlers\ModelBuilderTests.cs" />
|
||||
<Compile Include="ContentManagement\MetaData\Builders\ContentTypeDefinitionBuilderTests.cs" />
|
||||
<Compile Include="ContentManagement\MetaData\Services\ContentDefinitionReaderTests.cs" />
|
||||
<Compile Include="ContentManagement\MetaData\Services\ContentDefinitionWriterTests.cs" />
|
||||
<Compile Include="ContentManagement\Models\Alpha.cs" />
|
||||
<Compile Include="ContentManagement\Models\AlphaHandler.cs" />
|
||||
<Compile Include="ContentManagement\Models\Beta.cs" />
|
||||
|
||||
@@ -23,6 +23,21 @@ msgstr "[more]"
|
||||
msgid "Welcome to Orchard"
|
||||
msgstr "Welcome to Orchard"
|
||||
|
||||
#: ~/Core/Dashboard/Views/Admin/Index.ascx
|
||||
#| msgid : "The Orchard Team"
|
||||
msgid "The Orchard Team"
|
||||
msgstr "The Orchard Team"
|
||||
|
||||
#: ~/Core/Dashboard/Views/Admin/Index.ascx
|
||||
#| msgid : "This is the place where you can manage your web site, its appearance and its contents. Please take a moment to explore the different menu items on the left of the screen to familiarize yourself with the features of the application. For example, try to change the theme through the “Manage Themes” menu entry. You can also create new pages and manage existing ones through the “Manage Pages” menu entry or create blogs through “Manage Blogs”."
|
||||
msgid "This is the place where you can manage your web site, its appearance and its contents. Please take a moment to explore the different menu items on the left of the screen to familiarize yourself with the features of the application. For example, try to change the theme through the “Manage Themes” menu entry. You can also create new pages and manage existing ones through the “Manage Pages” menu entry or create blogs through “Manage Blogs”."
|
||||
msgstr "This is the place where you can manage your web site, its appearance and its contents. Please take a moment to explore the different menu items on the left of the screen to familiarize yourself with the features of the application. For example, try to change the theme through the “Manage Themes” menu entry. You can also create new pages and manage existing ones through the “Manage Pages” menu entry or create blogs through “Manage Blogs”."
|
||||
|
||||
#: ~/Core/Dashboard/Views/Admin/Index.ascx
|
||||
#| msgid : "Have fun!"
|
||||
msgid "Have fun!"
|
||||
msgstr "Have fun!"
|
||||
|
||||
#: ~/Core/Navigation/Controllers/AdminController.cs
|
||||
#| msgid : "Not allowed to manage the main menu"
|
||||
msgid "Not allowed to manage the main menu"
|
||||
@@ -278,6 +293,16 @@ msgstr "Edit Post"
|
||||
msgid "Discard Draft"
|
||||
msgstr "Discard Draft"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/AdminMenu.cs
|
||||
#| msgid : "Comments"
|
||||
msgid "Comments"
|
||||
msgstr "Comments"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/AdminMenu.cs
|
||||
#| msgid : "Manage Comments"
|
||||
msgid "Manage Comments"
|
||||
msgstr "Manage Comments"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Controllers/AdminController.cs
|
||||
#| msgid : "Listing comments failed: "
|
||||
msgid "Listing comments failed: "
|
||||
@@ -438,11 +463,36 @@ msgstr "Manage Comments"
|
||||
msgid "log on"
|
||||
msgstr "log on"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Add a Comment"
|
||||
msgid "Add a Comment"
|
||||
msgstr "Add a Comment"
|
||||
|
||||
#: ~/Themes/Contoso/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "You must {0} to comment."
|
||||
msgid "You must {0} to comment."
|
||||
msgstr "You must {0} to comment."
|
||||
|
||||
#: ~/Themes/Corporate/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Comments have been disabled for this content."
|
||||
msgid "Comments have been disabled for this content."
|
||||
msgstr "Comments have been disabled for this content."
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Hi, {0}!"
|
||||
msgid "Hi, {0}!"
|
||||
msgstr "Hi, {0}!"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Comment"
|
||||
msgid "Comment"
|
||||
msgstr "Comment"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Submit Comment"
|
||||
msgid "Submit Comment"
|
||||
msgstr "Submit Comment"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Views/EditorTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Comments are shown. Existing comments are displayed."
|
||||
msgid "Comments are shown. Existing comments are displayed."
|
||||
@@ -1748,11 +1798,41 @@ msgstr "User deleted"
|
||||
msgid "Access Denied"
|
||||
msgstr "Access Denied"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/AccessDenied.ascx
|
||||
#| msgid : "You do not have permission to complete your request."
|
||||
msgid "You do not have permission to complete your request."
|
||||
msgstr "You do not have permission to complete your request."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "Change Password"
|
||||
msgid "Change Password"
|
||||
msgstr "Change Password"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "Use the form below to change your password."
|
||||
msgid "Use the form below to change your password."
|
||||
msgstr "Use the form below to change your password."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "New passwords are required to be a minimum of {0} characters in length."
|
||||
msgid "New passwords are required to be a minimum of {0} characters in length."
|
||||
msgstr "New passwords are required to be a minimum of {0} characters in length."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "Current password:"
|
||||
msgid "Current password:"
|
||||
msgstr "Current password:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "New password:"
|
||||
msgid "New password:"
|
||||
msgstr "New password:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "Confirm new password:"
|
||||
msgid "Confirm new password:"
|
||||
msgstr "Confirm new password:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "Password change was unsuccessful. Please correct the errors and try again."
|
||||
msgid "Password change was unsuccessful. Please correct the errors and try again."
|
||||
@@ -1763,16 +1843,81 @@ msgstr "Password change was unsuccessful. Please correct the errors and try agai
|
||||
msgid "Change Password"
|
||||
msgstr "Change Password"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePasswordSuccess.ascx
|
||||
#| msgid : "Your password has been changed successfully."
|
||||
msgid "Your password has been changed successfully."
|
||||
msgstr "Your password has been changed successfully."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Login was unsuccessful. Please correct the errors and try again."
|
||||
msgid "Login was unsuccessful. Please correct the errors and try again."
|
||||
msgstr "Login was unsuccessful. Please correct the errors and try again."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Please enter your username and password."
|
||||
msgid "Please enter your username and password."
|
||||
msgstr "Please enter your username and password."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Register"
|
||||
msgid "Register"
|
||||
msgstr "Register"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : " if you don't have an account."
|
||||
msgid " if you don't have an account."
|
||||
msgstr " if you don't have an account."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Account Information"
|
||||
msgid "Account Information"
|
||||
msgstr "Account Information"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Username or Email:"
|
||||
msgid "Username or Email:"
|
||||
msgstr "Username or Email:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Password:"
|
||||
msgid "Password:"
|
||||
msgstr "Password:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Remember me?"
|
||||
msgid "Remember me?"
|
||||
msgstr "Remember me?"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Create a New Account"
|
||||
msgid "Create a New Account"
|
||||
msgstr "Create a New Account"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Use the form below to create a new account."
|
||||
msgid "Use the form below to create a new account."
|
||||
msgstr "Use the form below to create a new account."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Passwords are required to be a minimum of {0} characters in length."
|
||||
msgid "Passwords are required to be a minimum of {0} characters in length."
|
||||
msgstr "Passwords are required to be a minimum of {0} characters in length."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Username:"
|
||||
msgid "Username:"
|
||||
msgstr "Username:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Email:"
|
||||
msgid "Email:"
|
||||
msgstr "Email:
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Confirm password:"
|
||||
msgid "Confirm password:"
|
||||
msgstr "Confirm password:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Account creation was unsuccessful. Please correct the errors and try again."
|
||||
msgid "Account creation was unsuccessful. Please correct the errors and try again."
|
||||
@@ -1823,6 +1968,41 @@ msgstr "nobody(?)"
|
||||
msgid "Login was unsuccessful. Please correct the errors and try again."
|
||||
msgstr "Login was unsuccessful. Please correct the errors and try again."
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : "Please enter your username and password."
|
||||
msgid "Please enter your username and password."
|
||||
msgstr "Please enter your username and password."
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : "Register"
|
||||
msgid "Register"
|
||||
msgstr "Register"
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : " if you don't have an account."
|
||||
msgid " if you don't have an account."
|
||||
msgstr " if you don't have an account."
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : "Account Information"
|
||||
msgid "Account Information"
|
||||
msgstr "Account Information"
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : "Username or Email:"
|
||||
msgid "Username or Email:"
|
||||
msgstr "Username or Email:"
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : "Password:"
|
||||
msgid "Password:"
|
||||
msgstr "Password:"
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : "Remember me?"
|
||||
msgid "Remember me?"
|
||||
msgstr "Remember me?"
|
||||
|
||||
#: ~/Themes/Contoso/Views/User.ascx
|
||||
#| msgid : "Log Off"
|
||||
msgid "Log Off"
|
||||
@@ -1848,6 +2028,21 @@ msgstr "Archives"
|
||||
msgid "log on"
|
||||
msgstr "log on"
|
||||
|
||||
#: ~/Themes/Contoso/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Add a Comment"
|
||||
msgid "Add a Comment"
|
||||
msgstr "Add a Comment"
|
||||
|
||||
#: ~/Themes/Contoso/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "You must {0} to comment."
|
||||
msgid "You must {0} to comment."
|
||||
msgstr "You must {0} to comment."
|
||||
|
||||
#: ~/Themes/Corporate/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Comments have been disabled for this content."
|
||||
msgid "Comments have been disabled for this content."
|
||||
msgstr "Comments have been disabled for this content."
|
||||
|
||||
#: ~/Themes/Contoso/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Hi, {0}!"
|
||||
msgid "Hi, {0}!"
|
||||
@@ -1863,6 +2058,41 @@ msgstr "nobody(?)"
|
||||
msgid "Login was unsuccessful. Please correct the errors and try again."
|
||||
msgstr "Login was unsuccessful. Please correct the errors and try again."
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : "Please enter your username and password."
|
||||
msgid "Please enter your username and password."
|
||||
msgstr "Please enter your username and password."
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : "Register"
|
||||
msgid "Register"
|
||||
msgstr "Register"
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : " if you don't have an account."
|
||||
msgid " if you don't have an account."
|
||||
msgstr " if you don't have an account."
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : "Account Information"
|
||||
msgid "Account Information"
|
||||
msgstr "Account Information"
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : "Username or Email:"
|
||||
msgid "Username or Email:"
|
||||
msgstr "Username or Email:"
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : "Password:"
|
||||
msgid "Password:"
|
||||
msgstr "Password:"
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : "Remember me?"
|
||||
msgid "Remember me?"
|
||||
msgstr "Remember me?"
|
||||
|
||||
#: ~/Themes/Corporate/Views/User.ascx
|
||||
#| msgid : "Log Off"
|
||||
msgid "Log Off"
|
||||
@@ -1893,6 +2123,21 @@ msgstr "log on"
|
||||
msgid "Hi, {0}!"
|
||||
msgstr "Hi, {0}!"
|
||||
|
||||
#: ~/Themes/Corporate/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Add a Comment"
|
||||
msgid "Add a Comment"
|
||||
msgstr "Add a Comment"
|
||||
|
||||
#: ~/Themes/Corporate/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "You must {0} to comment."
|
||||
msgid "You must {0} to comment."
|
||||
msgstr "You must {0} to comment."
|
||||
|
||||
#: ~/Themes/Corporate/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Comments have been disabled for this content."
|
||||
msgid "Comments have been disabled for this content."
|
||||
msgstr "Comments have been disabled for this content."
|
||||
|
||||
#: ~/Themes/Corporate/Views/DisplayTemplates/Parts/Pages.Page.Metadata.ascx
|
||||
#| msgid : "nobody(?)"
|
||||
msgid "nobody(?)"
|
||||
@@ -1918,6 +2163,11 @@ msgstr "Your Site"
|
||||
msgid "Logout"
|
||||
msgstr "Logout"
|
||||
|
||||
#: ~/Themes/TheAdmin/Views/User.ascx
|
||||
#| msgid : "User:"
|
||||
msgid "User:"
|
||||
msgstr "User:"
|
||||
|
||||
#: ~/Commands/DefaultOrchardCommandHandler.cs
|
||||
#| msgid : "Switch was not found: "
|
||||
msgid "Switch was not found: "
|
||||
|
||||
@@ -23,6 +23,21 @@ msgstr "[En voir plus]"
|
||||
msgid "Welcome to Orchard"
|
||||
msgstr "Bienvenue dans Orchard"
|
||||
|
||||
#: ~/Core/Dashboard/Views/Admin/Index.ascx
|
||||
#| msgid : "The Orchard Team"
|
||||
msgid "The Orchard Team"
|
||||
msgstr "L'équipe Orchard"
|
||||
|
||||
#: ~/Core/Dashboard/Views/Admin/Index.ascx
|
||||
#| msgid : "This is the place where you can manage your web site, its appearance and its contents. Please take a moment to explore the different menu items on the left of the screen to familiarize yourself with the features of the application. For example, try to change the theme through the “Manage Themes” menu entry. You can also create new pages and manage existing ones through the “Manage Pages” menu entry or create blogs through “Manage Blogs”."
|
||||
msgid "This is the place where you can manage your web site, its appearance and its contents. Please take a moment to explore the different menu items on the left of the screen to familiarize yourself with the features of the application. For example, try to change the theme through the “Manage Themes” menu entry. You can also create new pages and manage existing ones through the “Manage Pages” menu entry or create blogs through “Manage Blogs”."
|
||||
msgstr "Ceci est l'endroit où vous pouvez gérer votre site, son apparence et son contenu. Veuillez passer un instant à explorer le menu à gauche de l'écran pour vous familiariser avec les différents aspects de l'application. Par exemple, vous pouvez changer le thème en utilisant “Gérer les thèmes”. Vous pouvez également créer de nouvelles pages et gérer les pages existantes via “Gérer les pages” ou bien créer de nouveaux blogs via “Gérer les blogs”."
|
||||
|
||||
#: ~/Core/Dashboard/Views/Admin/Index.ascx
|
||||
#| msgid : "Have fun!"
|
||||
msgid "Have fun!"
|
||||
msgstr "Amusez-vous bien!"
|
||||
|
||||
#: ~/Core/Navigation/Controllers/AdminController.cs
|
||||
#| msgid : "Not allowed to manage the main menu"
|
||||
msgid "Not allowed to manage the main menu"
|
||||
@@ -278,6 +293,16 @@ msgstr "Modifier un billet"
|
||||
msgid "Discard Draft"
|
||||
msgstr "Effacer le brouillon"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/AdminMenu.cs
|
||||
#| msgid : "Comments"
|
||||
msgid "Comments"
|
||||
msgstr "Commentaires"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/AdminMenu.cs
|
||||
#| msgid : "Manage Comments"
|
||||
msgid "Manage Comments"
|
||||
msgstr "Gérer les Commentaires"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Controllers/AdminController.cs
|
||||
#| msgid : "Listing comments failed: "
|
||||
msgid "Listing comments failed: "
|
||||
@@ -436,13 +461,38 @@ msgstr "Gérer les commentaires"
|
||||
#: ~/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "log on"
|
||||
msgid "log on"
|
||||
msgstr "S'authentifier"
|
||||
msgstr "s'authentifier"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Add a Comment"
|
||||
msgid "Add a Comment"
|
||||
msgstr "Ajouter un commentaire"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "You must {0} to comment."
|
||||
msgid "You must {0} to comment."
|
||||
msgstr "Vous devez {0} pour ajouter un commentaire."
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Comments have been disabled for this content."
|
||||
msgid "Comments have been disabled for this content."
|
||||
msgstr "Les commentaires ont été désactivés pour ce contenu."
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Hi, {0}!"
|
||||
msgid "Hi, {0}!"
|
||||
msgstr "Bonjour {0}!"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Comment"
|
||||
msgid "Comment"
|
||||
msgstr "Commentaire"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Submit Comment"
|
||||
msgid "Submit Comment"
|
||||
msgstr "Envoyer le commentaire"
|
||||
|
||||
#: ~/Modules/Orchard.Comments/Views/EditorTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Comments are shown. Existing comments are displayed."
|
||||
msgid "Comments are shown. Existing comments are displayed."
|
||||
@@ -1748,11 +1798,41 @@ msgstr "Utilisateur effacé"
|
||||
msgid "Access Denied"
|
||||
msgstr "Accès refusé"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/AccessDenied.ascx
|
||||
#| msgid : "You do not have permission to complete your request."
|
||||
msgid "You do not have permission to complete your request."
|
||||
msgstr "Vous n'avez pas la permission de compléter cette requête."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "Change Password"
|
||||
msgid "Change Password"
|
||||
msgstr "Changer le mot de passe"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "Use the form below to change your password."
|
||||
msgid "Use the form below to change your password."
|
||||
msgstr "Veuillez utiliser le forumulaire ci-dessous pour changer votre mot de passe."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "New passwords are required to be a minimum of {0} characters in length."
|
||||
msgid "New passwords are required to be a minimum of {0} characters in length."
|
||||
msgstr "Les mots de passe doivent contenir au moins {0} caractères."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "Current password:"
|
||||
msgid "Current password:"
|
||||
msgstr "Mot de passe courant:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "New password:"
|
||||
msgid "New password:"
|
||||
msgstr "Nouveau mot de passe:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "Confirm new password:"
|
||||
msgid "Confirm new password:"
|
||||
msgstr "Confirmez le nouveau mot de passe:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePassword.ascx
|
||||
#| msgid : "Password change was unsuccessful. Please correct the errors and try again."
|
||||
msgid "Password change was unsuccessful. Please correct the errors and try again."
|
||||
@@ -1763,16 +1843,81 @@ msgstr "Le changement de mot de passe a échoué. Veuillez corriger les erreurs
|
||||
msgid "Change Password"
|
||||
msgstr "Changer le mot de passe"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/ChangePasswordSuccess.ascx
|
||||
#| msgid : "Your password has been changed successfully."
|
||||
msgid "Your password has been changed successfully."
|
||||
msgstr "Votre mot de passe a été changé."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Login was unsuccessful. Please correct the errors and try again."
|
||||
msgid "Login was unsuccessful. Please correct the errors and try again."
|
||||
msgstr "La connexion a échoué. Veuillez corriger les erreurs et réessayer."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Please enter your username and password."
|
||||
msgid "Please enter your username and password."
|
||||
msgstr "Veuillez saisir votre nom d'utilisateur et votre mot de passe."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Register"
|
||||
msgid "Register"
|
||||
msgstr "Enregistrer"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : " if you don't have an account."
|
||||
msgid " if you don't have an account."
|
||||
msgstr " si vous n'avez pas de compte."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Account Information"
|
||||
msgid "Account Information"
|
||||
msgstr "Données du compte"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Username or Email:"
|
||||
msgid "Username or Email:"
|
||||
msgstr "Nom d'utilisateur ou e-mail:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Password:"
|
||||
msgid "Password:"
|
||||
msgstr "Mot de passe:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/LogOn.ascx
|
||||
#| msgid : "Remember me?"
|
||||
msgid "Remember me?"
|
||||
msgstr "Se souvenir de moi?"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Create a New Account"
|
||||
msgid "Create a New Account"
|
||||
msgstr "Créer un nouveau compte utilisateur"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Use the form below to create a new account."
|
||||
msgid "Use the form below to create a new account."
|
||||
msgstr "Veuillez utiliser le formulaire ci-dessous pour créer un nouveau compte."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Passwords are required to be a minimum of {0} characters in length."
|
||||
msgid "Passwords are required to be a minimum of {0} characters in length."
|
||||
msgstr "Les mots de passe doivent contenir au moins {0} caractères."
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Username:"
|
||||
msgid "Username:"
|
||||
msgstr "Nom d'utilisateur:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Email:"
|
||||
msgid "Email:"
|
||||
msgstr "E-mail:
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Confirm password:"
|
||||
msgid "Confirm password:"
|
||||
msgstr "Confirmez le mot de passe:"
|
||||
|
||||
#: ~/Modules/Orchard.Users/Views/Account/Register.ascx
|
||||
#| msgid : "Account creation was unsuccessful. Please correct the errors and try again."
|
||||
msgid "Account creation was unsuccessful. Please correct the errors and try again."
|
||||
@@ -1823,6 +1968,41 @@ msgstr "personne(?)"
|
||||
msgid "Login was unsuccessful. Please correct the errors and try again."
|
||||
msgstr "La connexion a échoué. Veuillez corriger les erreurs et réessayer."
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : "Please enter your username and password."
|
||||
msgid "Please enter your username and password."
|
||||
msgstr "Veuillez saisir votre nom d'utilisateur et votre mot de passe."
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : "Register"
|
||||
msgid "Register"
|
||||
msgstr "S'enregistrer"
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : " if you don't have an account."
|
||||
msgid " if you don't have an account."
|
||||
msgstr " si vous n'avez pas de compte."
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : "Account Information"
|
||||
msgid "Account Information"
|
||||
msgstr "Données du Compte"
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : "Username or Email:"
|
||||
msgid "Username or Email:"
|
||||
msgstr "Nom d'utilisateur ou e-mail:"
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : "Password:"
|
||||
msgid "Password:"
|
||||
msgstr "Mot de passe:"
|
||||
|
||||
#: ~/Themes/Contoso/Views/LogOn.ascx
|
||||
#| msgid : "Remember me?"
|
||||
msgid "Remember me?"
|
||||
msgstr "Se souvenir de moi?"
|
||||
|
||||
#: ~/Themes/Contoso/Views/User.ascx
|
||||
#| msgid : "Log Off"
|
||||
msgid "Log Off"
|
||||
@@ -1848,6 +2028,21 @@ msgstr "Archives"
|
||||
msgid "log on"
|
||||
msgstr "se connecter"
|
||||
|
||||
#: ~/Themes/Contoso/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Add a Comment"
|
||||
msgid "Add a Comment"
|
||||
msgstr "Ajouter un commentaire"
|
||||
|
||||
#: ~/Themes/Contoso/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "You must {0} to comment."
|
||||
msgid "You must {0} to comment."
|
||||
msgstr "Vous devez {0} pour ajouter un commentaire."
|
||||
|
||||
#: ~/Themes/Contoso/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Comments have been disabled for this content."
|
||||
msgid "Comments have been disabled for this content."
|
||||
msgstr "Les commentaires ont été désactivés pour ce contenu."
|
||||
|
||||
#: ~/Themes/Contoso/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Hi, {0}!"
|
||||
msgid "Hi, {0}!"
|
||||
@@ -1863,6 +2058,41 @@ msgstr "personne(?)"
|
||||
msgid "Login was unsuccessful. Please correct the errors and try again."
|
||||
msgstr "La connexion a échoué. Veuillez corriger les erreurs et réessayer."
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : "Please enter your username and password."
|
||||
msgid "Please enter your username and password."
|
||||
msgstr "Veuillez saisir votre nom d'utilisateur et votre mot de passe."
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : "Register"
|
||||
msgid "Register"
|
||||
msgstr "S'enregistrer"
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : " if you don't have an account."
|
||||
msgid " if you don't have an account."
|
||||
msgstr " si vous n'avez pas de compte."
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : "Account Information"
|
||||
msgid "Account Information"
|
||||
msgstr "Données du Compte"
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : "Username or Email:"
|
||||
msgid "Username or Email:"
|
||||
msgstr "Nom d'utilisateur ou e-mail:"
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : "Password:"
|
||||
msgid "Password:"
|
||||
msgstr "Mot de passe:"
|
||||
|
||||
#: ~/Themes/Corporate/Views/LogOn.ascx
|
||||
#| msgid : "Remember me?"
|
||||
msgid "Remember me?"
|
||||
msgstr "Se souvenir de moi?"
|
||||
|
||||
#: ~/Themes/Corporate/Views/User.ascx
|
||||
#| msgid : "Log Off"
|
||||
msgid "Log Off"
|
||||
@@ -1888,6 +2118,21 @@ msgstr "Archives"
|
||||
msgid "log on"
|
||||
msgstr "se connecter"
|
||||
|
||||
#: ~/Themes/Corporate/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Add a Comment"
|
||||
msgid "Add a Comment"
|
||||
msgstr "Ajouter un commentaire"
|
||||
|
||||
#: ~/Themes/Corporate/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "You must {0} to comment."
|
||||
msgid "You must {0} to comment."
|
||||
msgstr "Vous devez {0} pour ajouter un commentaire."
|
||||
|
||||
#: ~/Themes/Corporate/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Comments have been disabled for this content."
|
||||
msgid "Comments have been disabled for this content."
|
||||
msgstr "Les commentaires ont été désactivés pour ce contenu."
|
||||
|
||||
#: ~/Themes/Corporate/Views/DisplayTemplates/Parts/Comments.HasComments.ascx
|
||||
#| msgid : "Hi, {0}!"
|
||||
msgid "Hi, {0}!"
|
||||
@@ -1918,6 +2163,11 @@ msgstr "Votre site"
|
||||
msgid "Logout"
|
||||
msgstr "Déconnexion"
|
||||
|
||||
#: ~/Themes/TheAdmin/Views/User.ascx
|
||||
#| msgid : "User:"
|
||||
msgid "User:"
|
||||
msgstr "Utilisateur:"
|
||||
|
||||
#: ~/Commands/DefaultOrchardCommandHandler.cs
|
||||
#| msgid : "Switch was not found: "
|
||||
msgid "Switch was not found: "
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
|
||||
namespace Orchard.Core.Common.Models {
|
||||
public class RoutableAspect : ContentPart<RoutableRecord> {
|
||||
public class RoutableAspect : ContentPart<RoutableRecord>, IRoutableAspect {
|
||||
public string ContentItemBasePath { get; set; }
|
||||
|
||||
public string Title {
|
||||
@@ -14,4 +15,4 @@ namespace Orchard.Core.Common.Models {
|
||||
set { Record.Slug = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Orchard.ContentManagement.Records;
|
||||
|
||||
namespace Orchard.Core.Common.Models {
|
||||
@@ -7,5 +8,8 @@ namespace Orchard.Core.Common.Models {
|
||||
public virtual string Title { get; set; }
|
||||
|
||||
public virtual string Slug { get; set; }
|
||||
|
||||
[StringLength(2048)]
|
||||
public virtual string Path { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -1,3 +1,7 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<BodyDisplayViewModel>" %>
|
||||
<%@ Import Namespace="Orchard.Core.Common.ViewModels"%>
|
||||
</div>
|
||||
<%-- begin: knowingly broken HTML (hence the ManageWrapperPre and ManageWrapperPost templates)
|
||||
we need "wrapper templates" (among other functionality) in the future of UI composition
|
||||
please do not delete or the front end will be broken when the user is authenticated. --%>
|
||||
</div>
|
||||
<%-- begin: knowingly broken HTML --%>
|
||||
@@ -24,7 +24,8 @@
|
||||
url:"<%=Url.Slugify() %>",
|
||||
contentType:"<%=Model.RoutableAspect.ContentItem.ContentType %>",
|
||||
id:"<%=Model.RoutableAspect.ContentItem.Id %>"<%
|
||||
var container = Model.RoutableAspect.ContentItem.As<ICommonAspect>().Container;
|
||||
var commonAspect = Model.RoutableAspect.ContentItem.As<ICommonAspect>();
|
||||
var container = commonAspect != null ? commonAspect.Container : null;
|
||||
if (container != null) { %>,
|
||||
containerId:<%=container.ContentItem.Id %><%
|
||||
} %>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using Orchard.Localization;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Core.Contents {
|
||||
public class AdminMenu : INavigationProvider {
|
||||
public Localizer T { get; set; }
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add(T("Content"), "1",
|
||||
menu => {
|
||||
menu.Add(T("Create"), "1.1", item => item.Action("Create", "Admin", new { area = "Contents" }));
|
||||
menu.Add(T("List"), "1.2", item => item.Action("List", "Admin", new { area = "Contents" }));
|
||||
menu.Add(T("Types"), "1.3", item => item.Action("Types", "Admin", new { area = "Contents" }));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using Orchard.Core.Contents.ViewModels;
|
||||
using Orchard.Data;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Mvc.ViewModels;
|
||||
using Orchard.UI.Notify;
|
||||
|
||||
namespace Orchard.Core.Contents.Controllers {
|
||||
[ValidateInput(false)]
|
||||
public class AdminController : Controller, IUpdateModel {
|
||||
private readonly INotifier _notifier;
|
||||
private readonly IContentDefinitionManager _contentDefinitionManager;
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly ITransactionManager _transactionManager;
|
||||
|
||||
public AdminController(
|
||||
INotifier notifier,
|
||||
IContentDefinitionManager contentDefinitionManager,
|
||||
IContentManager contentManager,
|
||||
ITransactionManager transactionManager) {
|
||||
_notifier = notifier;
|
||||
_contentDefinitionManager = contentDefinitionManager;
|
||||
_contentManager = contentManager;
|
||||
_transactionManager = transactionManager;
|
||||
T = NullLocalizer.Instance;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public ActionResult Index() {
|
||||
return Types();
|
||||
}
|
||||
|
||||
public ActionResult Types() {
|
||||
return View("Types", new ContentTypeListViewModel {
|
||||
Types = _contentDefinitionManager.ListTypeDefinitions()
|
||||
});
|
||||
}
|
||||
|
||||
public ActionResult List(ListContentViewModel model) {
|
||||
const int pageSize = 20;
|
||||
var skip = (Math.Max(model.Page ?? 0, 1) - 1) * pageSize;
|
||||
|
||||
var query = _contentManager.Query(VersionOptions.Latest);
|
||||
|
||||
if (!string.IsNullOrEmpty(model.Id)) {
|
||||
query = query.ForType(model.Id);
|
||||
}
|
||||
|
||||
var contentItems = query.Slice(skip, pageSize);
|
||||
|
||||
model.Entries = contentItems.Select(BuildEntry).ToList();
|
||||
|
||||
return View("List", model);
|
||||
}
|
||||
|
||||
private ListContentViewModel.Entry BuildEntry(ContentItem contentItem) {
|
||||
var entry = new ListContentViewModel.Entry {
|
||||
ContentItem = contentItem,
|
||||
ContentItemMetadata = _contentManager.GetItemMetadata(contentItem),
|
||||
ViewModel = _contentManager.BuildDisplayModel(contentItem, "List"),
|
||||
};
|
||||
if (string.IsNullOrEmpty(entry.ContentItemMetadata.DisplayText)) {
|
||||
entry.ContentItemMetadata.DisplayText = string.Format("[{0}#{1}]", contentItem.ContentType, contentItem.Id);
|
||||
}
|
||||
if (entry.ContentItemMetadata.EditorRouteValues == null) {
|
||||
entry.ContentItemMetadata.EditorRouteValues = new RouteValueDictionary {
|
||||
{"Area", "Contents"},
|
||||
{"Controller", "Admin"},
|
||||
{"Action", "Edit"},
|
||||
{"Id", contentItem.Id}
|
||||
};
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
ActionResult CreatableTypeList() {
|
||||
var model = new ContentTypeListViewModel {
|
||||
Types = _contentDefinitionManager.ListTypeDefinitions()
|
||||
};
|
||||
|
||||
return View("CreatableTypeList", model);
|
||||
}
|
||||
|
||||
public ActionResult Create(string id) {
|
||||
if (string.IsNullOrEmpty(id))
|
||||
return CreatableTypeList();
|
||||
|
||||
var contentItem = _contentManager.New(id);
|
||||
var model = new CreateItemViewModel {
|
||||
Id = id,
|
||||
Content = _contentManager.BuildEditorModel(contentItem)
|
||||
};
|
||||
PrepareEditorViewModel(model.Content);
|
||||
return View("Create", model);
|
||||
}
|
||||
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Create(CreateItemViewModel model) {
|
||||
var contentItem = _contentManager.New(model.Id);
|
||||
model.Content = _contentManager.UpdateEditorModel(contentItem, this);
|
||||
if (ModelState.IsValid) {
|
||||
_contentManager.Create(contentItem, VersionOptions.Draft);
|
||||
model.Content = _contentManager.UpdateEditorModel(contentItem, this);
|
||||
}
|
||||
if (ModelState.IsValid) {
|
||||
_contentManager.Publish(contentItem);
|
||||
}
|
||||
if (!ModelState.IsValid) {
|
||||
_transactionManager.Cancel();
|
||||
PrepareEditorViewModel(model.Content);
|
||||
return View("Create", model);
|
||||
}
|
||||
|
||||
_notifier.Information(T("Created content item"));
|
||||
return RedirectToAction("Edit", new RouteValueDictionary { { "Id", contentItem.Id } });
|
||||
}
|
||||
|
||||
public ActionResult Edit(int id) {
|
||||
var contentItem = _contentManager.Get(id, VersionOptions.Latest);
|
||||
var model = new EditItemViewModel {
|
||||
Id = id,
|
||||
Content = _contentManager.BuildEditorModel(contentItem)
|
||||
};
|
||||
PrepareEditorViewModel(model.Content);
|
||||
return View("Edit", model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Edit(EditItemViewModel model) {
|
||||
var contentItem = _contentManager.Get(model.Id, VersionOptions.DraftRequired);
|
||||
model.Content = _contentManager.UpdateEditorModel(contentItem, this);
|
||||
if (!ModelState.IsValid) {
|
||||
_transactionManager.Cancel();
|
||||
PrepareEditorViewModel(model.Content);
|
||||
return View("Edit", model);
|
||||
}
|
||||
_contentManager.Publish(contentItem);
|
||||
return RedirectToAction("Edit", new RouteValueDictionary { { "Id", contentItem.Id } });
|
||||
}
|
||||
|
||||
private void PrepareEditorViewModel(ContentItemViewModel itemViewModel) {
|
||||
if (string.IsNullOrEmpty(itemViewModel.TemplateName)) {
|
||||
itemViewModel.TemplateName = "Items/Contents.Item";
|
||||
}
|
||||
}
|
||||
|
||||
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
|
||||
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
|
||||
}
|
||||
|
||||
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
|
||||
ModelState.AddModelError(key, errorMessage.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Core.Contents.ViewModels;
|
||||
using Orchard.Mvc.ViewModels;
|
||||
|
||||
namespace Orchard.Core.Contents.Controllers {
|
||||
public class ItemController : Controller {
|
||||
private readonly IContentManager _contentManager;
|
||||
|
||||
public ItemController(IContentManager contentManager) {
|
||||
_contentManager = contentManager;
|
||||
}
|
||||
|
||||
public ActionResult Display(int id) {
|
||||
var contentItem = _contentManager.Get(id, VersionOptions.Published);
|
||||
|
||||
var model = new DisplayItemViewModel {
|
||||
Content = _contentManager.BuildDisplayModel(contentItem, "Detail")
|
||||
};
|
||||
PrepareDisplayViewModel(model.Content);
|
||||
return View("Display", model);
|
||||
}
|
||||
|
||||
public ActionResult Preview(int id, int? version) {
|
||||
var versionOptions = VersionOptions.Latest;
|
||||
if (version != null) {
|
||||
versionOptions = VersionOptions.Number((int)version);
|
||||
}
|
||||
|
||||
var contentItem = _contentManager.Get(id, versionOptions);
|
||||
|
||||
var model = new DisplayItemViewModel {
|
||||
Content = _contentManager.BuildDisplayModel(contentItem, "Detail")
|
||||
};
|
||||
PrepareDisplayViewModel(model.Content);
|
||||
return View("Preview", model);
|
||||
}
|
||||
|
||||
private static void PrepareDisplayViewModel(ContentItemViewModel itemViewModel) {
|
||||
if (string.IsNullOrEmpty(itemViewModel.TemplateName)) {
|
||||
itemViewModel.TemplateName = "Items/Contents.Item";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Routing;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
|
||||
namespace Orchard.Core.Contents.Handlers {
|
||||
public class ContentsModuleHandler : ContentHandlerBase {
|
||||
public override void GetContentItemMetadata(GetContentItemMetadataContext context) {
|
||||
if (context.Metadata.EditorRouteValues == null) {
|
||||
context.Metadata.EditorRouteValues = new RouteValueDictionary {
|
||||
{"Area", "Contents"},
|
||||
{"Controller", "Admin"},
|
||||
{"Action", "Edit"},
|
||||
{"Id", context.ContentItem.Id}
|
||||
};
|
||||
}
|
||||
if (context.Metadata.DisplayRouteValues == null) {
|
||||
context.Metadata.DisplayRouteValues = new RouteValueDictionary {
|
||||
{"Area", "Contents"},
|
||||
{"Controller", "Item"},
|
||||
{"Action", "Display"},
|
||||
{"Id", context.ContentItem.Id}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
Name: Contents
|
||||
antiforgery: enabled
|
||||
author: The Orchard Team
|
||||
website: http://orchardproject.net
|
||||
version: 0.1
|
||||
orchardversion: 0.1.2010.0312
|
||||
description: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas consectetur consequat risus, vel blandit arcu tincidunt eget. Nam rutrum nulla vestibulum dolor dapibus sagittis. Vivamus convallis faucibus accumsan. Suspendisse sapien enim, cursus at dignissim a, sollicitudin sit amet est. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec sed urna magna, in luctus nulla. Pellentesque erat ipsum, convallis sed molestie tempus, mattis vel leo metus.
|
||||
features:
|
||||
Contents:
|
||||
Description: Default controllers for some content types.
|
||||
Category: Core
|
||||
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement.MetaData.Models;
|
||||
using Orchard.Mvc.ViewModels;
|
||||
|
||||
namespace Orchard.Core.Contents.ViewModels {
|
||||
public class ContentTypeListViewModel : BaseViewModel {
|
||||
public IEnumerable<ContentTypeDefinition> Types { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Orchard.Mvc.ViewModels;
|
||||
|
||||
namespace Orchard.Core.Contents.ViewModels {
|
||||
public class CreateItemViewModel : BaseViewModel {
|
||||
public string Id { get; set; }
|
||||
public ContentItemViewModel Content { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Orchard.Mvc.ViewModels;
|
||||
|
||||
namespace Orchard.Core.Contents.ViewModels {
|
||||
public class EditItemViewModel : BaseViewModel {
|
||||
public int Id { get; set; }
|
||||
public ContentItemViewModel Content { get; set; }
|
||||
}
|
||||
public class DisplayItemViewModel : BaseViewModel {
|
||||
public ContentItemViewModel Content { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Mvc.ViewModels;
|
||||
|
||||
namespace Orchard.Core.Contents.ViewModels {
|
||||
public class ListContentViewModel : BaseViewModel {
|
||||
public string Id { get; set; }
|
||||
public int? Page { get; set; }
|
||||
public IList<Entry> Entries { get; set; }
|
||||
|
||||
public class Entry {
|
||||
public ContentItem ContentItem { get; set; }
|
||||
public ContentItemMetadata ContentItemMetadata { get; set; }
|
||||
public ContentItemViewModel ViewModel { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage<ContentTypeListViewModel>" %>
|
||||
|
||||
<%@ Import Namespace="Orchard.Core.Contents.ViewModels" %>
|
||||
<% Html.AddTitleParts(T("Create Content").ToString()); %>
|
||||
<p>
|
||||
Create content</p>
|
||||
<ul>
|
||||
<% foreach (var t in Model.Types) {%>
|
||||
<li>
|
||||
<%:Html.ActionLink(t.Name, "Create", new RouteValueDictionary{{"Area","Contents"},{"Id",t.Name}}) %></li>
|
||||
<%} %>
|
||||
</ul>
|
||||
@@ -0,0 +1,8 @@
|
||||
<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage<CreateItemViewModel>" %>
|
||||
|
||||
<%@ Import Namespace="Orchard.Core.Contents.ViewModels" %>
|
||||
<% Html.AddTitleParts(T("Create Content").ToString()); %>
|
||||
<% using (Html.BeginFormAntiForgeryPost()) { %>
|
||||
<%:Html.ValidationSummary() %>
|
||||
<%:Html.EditorForItem(m=>m.Content) %>
|
||||
<%} %>
|
||||
@@ -0,0 +1,8 @@
|
||||
<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage<EditItemViewModel>" %>
|
||||
|
||||
<%@ Import Namespace="Orchard.Core.Contents.ViewModels" %>
|
||||
<% Html.AddTitleParts(T("Edit Content").ToString()); %>
|
||||
<% using (Html.BeginFormAntiForgeryPost()) { %>
|
||||
<%:Html.ValidationSummary() %>
|
||||
<%:Html.EditorForItem(m=>m.Content) %>
|
||||
<%} %>
|
||||
@@ -0,0 +1,33 @@
|
||||
<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage<ListContentViewModel>" %>
|
||||
|
||||
<%@ Import Namespace="Orchard.Core.Contents.ViewModels" %>
|
||||
<% Html.AddTitleParts(T("Browse Contents").ToString()); %>
|
||||
<p>
|
||||
Browse Contents</p>
|
||||
<table>
|
||||
<% foreach (var t in Model.Entries) {%>
|
||||
<tr>
|
||||
<td>
|
||||
<%:t.ContentItem.Id %>.
|
||||
</td>
|
||||
<td>
|
||||
<%:t.ContentItem.ContentType %>
|
||||
</td>
|
||||
<td>
|
||||
ver #<%:t.ContentItem.Version %>
|
||||
</td>
|
||||
<td>
|
||||
<%if (t.ContentItemMetadata.DisplayRouteValues != null) {%>
|
||||
<%:Html.ActionLink(t.ContentItemMetadata.DisplayText, t.ContentItemMetadata.DisplayRouteValues["Action"].ToString(), t.ContentItemMetadata.DisplayRouteValues)%>
|
||||
<%}%>
|
||||
</td>
|
||||
<td>
|
||||
<%if (t.ContentItemMetadata.EditorRouteValues != null) {%>
|
||||
<%:Html.ActionLink("edit", t.ContentItemMetadata.EditorRouteValues["Action"].ToString(), t.ContentItemMetadata.EditorRouteValues)%>
|
||||
<%}%>
|
||||
</td>
|
||||
</tr>
|
||||
<%} %>
|
||||
</table>
|
||||
<p>
|
||||
<%:Html.ActionLink("Create new item", "Create", "Admin", new RouteValueDictionary{{"Area","Contents"},{"Id",Model.Id}}, new Dictionary<string, object>()) %></p>
|
||||
@@ -0,0 +1,24 @@
|
||||
<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage<ContentTypeListViewModel>" %>
|
||||
|
||||
<%@ Import Namespace="Orchard.Core.Contents.ViewModels" %>
|
||||
<% Html.AddTitleParts(T("Create Content").ToString()); %>
|
||||
<p>
|
||||
Create content</p>
|
||||
<table>
|
||||
<% foreach (var t in Model.Types) {%>
|
||||
<tr>
|
||||
<td>
|
||||
<%:t.Name %>
|
||||
</td>
|
||||
<td>
|
||||
<%:Html.ActionLink(T("List Items").ToString(), "List", "Admin", new RouteValueDictionary{{"Area","Contents"},{"Id",t.Name}}, new Dictionary<string, object>()) %>
|
||||
</td>
|
||||
<td>
|
||||
<%:Html.ActionLink(T("Create Item").ToString(), "Create", "Admin", new RouteValueDictionary{{"Area","Contents"},{"Id",t.Name}}, new Dictionary<string, object>()) %>
|
||||
</td>
|
||||
<td>
|
||||
<%:Html.ActionLink(T("Edit Type").ToString(), "ContentTypeList", "Admin", new RouteValueDictionary{{"Area","Orchard.MetaData"},{"Id",t.Name}}, new Dictionary<string, object>()) %>
|
||||
</td>
|
||||
</tr>
|
||||
<%} %>
|
||||
</table>
|
||||
@@ -0,0 +1,11 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<ContentItemViewModel>" %>
|
||||
<%@ Import Namespace="Orchard.Mvc.ViewModels" %>
|
||||
<%@ Import Namespace="Orchard.ContentManagement.Aspects" %>
|
||||
<%@ Import Namespace="Orchard.ContentManagement" %>
|
||||
<%var routable = Model.Item.As<IRoutableAspect>();
|
||||
if (routable != null && !string.IsNullOrEmpty(routable.Title)) {%>
|
||||
<h1>
|
||||
<%:routable.Title%></h1>
|
||||
<%} %>
|
||||
<% Html.Zone("primary", ":manage :metadata");
|
||||
Html.ZonesAny(); %>
|
||||
@@ -0,0 +1,14 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<ContentItemViewModel>" %>
|
||||
<%@ Import Namespace="Orchard.Mvc.ViewModels"%>
|
||||
<div class="sections">
|
||||
<div class="primary"><%
|
||||
Html.Zone("primary");
|
||||
Html.ZonesExcept("secondary"); %>
|
||||
</div>
|
||||
<div class="secondary">
|
||||
<% Html.Zone("secondary");%>
|
||||
<fieldset>
|
||||
<input class="button primaryAction" type="submit" name="submit.Save" value="<%=_Encoded("Save") %>"/>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage<Orchard.Core.Contents.ViewModels.DisplayItemViewModel>" %>
|
||||
|
||||
<div class="preview">
|
||||
<%=Html.DisplayForItem(m=>m.Content) %>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage<Orchard.Core.Contents.ViewModels.DisplayItemViewModel>" %>
|
||||
|
||||
<%=Html.DisplayForItem(m=>m.Content) %>
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
<add path="*" verb="*"
|
||||
type="System.Web.HttpNotFoundHandler"/>
|
||||
</httpHandlers>
|
||||
|
||||
<!--
|
||||
Enabling request validation in view pages would cause validation to occur
|
||||
after the input has already been processed by the controller. By default
|
||||
MVC performs request validation before a controller processes the input.
|
||||
To change this behavior apply the ValidateInputAttribute to a
|
||||
controller or action.
|
||||
-->
|
||||
<pages
|
||||
validateRequest="false"
|
||||
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
|
||||
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
|
||||
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||
<controls>
|
||||
<add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
|
||||
</controls>
|
||||
</pages>
|
||||
</system.web>
|
||||
|
||||
<system.webServer>
|
||||
<validation validateIntegratedModeConfiguration="false"/>
|
||||
<handlers>
|
||||
<remove name="BlockViewHandler"/>
|
||||
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler"/>
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
@@ -1,13 +1,15 @@
|
||||
using Orchard.Security;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Core.Dashboard {
|
||||
public class AdminMenu : INavigationProvider {
|
||||
public Localizer T { get; set; }
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add("Orchard", "0",
|
||||
menu => menu.Add("Dashboard", "0", item => item.Action("Index", "Admin", new { area = "Dashboard" }).Permission(StandardPermissions.AccessAdminPanel)));
|
||||
builder.Add(T("Orchard"), "0",
|
||||
menu => menu.Add(T("Dashboard"), "0", item => item.Action("Index", "Admin", new { area = "Dashboard" }).Permission(StandardPermissions.AccessAdminPanel)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Commands;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Indexing;
|
||||
using Orchard.Security;
|
||||
using Orchard.Tasks.Indexing;
|
||||
|
||||
namespace Orchard.Core.Indexing.Commands {
|
||||
public class IndexingCommands : DefaultOrchardCommandHandler {
|
||||
private readonly IEnumerable<IIndexNotifierHandler> _indexNotifierHandlers;
|
||||
private readonly IIndexManager _indexManager;
|
||||
private readonly IIndexingTaskManager _indexingTaskManager;
|
||||
private readonly IContentManager _contentManager;
|
||||
private const string SearchIndexName = "Search";
|
||||
|
||||
public IndexingCommands(
|
||||
IEnumerable<IIndexNotifierHandler> indexNotifierHandlers,
|
||||
IIndexManager indexManager,
|
||||
IIndexingTaskManager indexingTaskManager,
|
||||
IContentManager contentManager) {
|
||||
_indexNotifierHandlers = indexNotifierHandlers;
|
||||
_indexingTaskManager = indexingTaskManager;
|
||||
_contentManager = contentManager;
|
||||
_indexManager = indexManager;
|
||||
}
|
||||
|
||||
[OrchardSwitch]
|
||||
public string IndexName { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Query { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string ContentItemId { get; set; }
|
||||
|
||||
[CommandName("index update")]
|
||||
[CommandHelp("index update [/IndexName:<index name>]\r\n\t" + "Updates the index with the specified <index name>, or the search index if not specified")]
|
||||
[OrchardSwitches("IndexName")]
|
||||
public string Update() {
|
||||
if ( !_indexManager.HasIndexProvider() ) {
|
||||
return "No index available";
|
||||
}
|
||||
|
||||
var indexName = String.IsNullOrWhiteSpace(IndexName) ? SearchIndexName : IndexName;
|
||||
foreach ( var handler in _indexNotifierHandlers ) {
|
||||
handler.UpdateIndex(indexName);
|
||||
}
|
||||
|
||||
return "Index is now being updated...";
|
||||
}
|
||||
|
||||
[CommandName("index rebuild")]
|
||||
[CommandHelp("index rebuild [/IndexName:<index name>]\r\n\t" + "Rebuilds the index with the specified <index name>, or the search index if not specified")]
|
||||
[OrchardSwitches("IndexName")]
|
||||
public string Rebuild() {
|
||||
if ( !_indexManager.HasIndexProvider() ) {
|
||||
return "No index available";
|
||||
}
|
||||
|
||||
var indexName = String.IsNullOrWhiteSpace(IndexName) ? SearchIndexName : IndexName;
|
||||
var searchProvider = _indexManager.GetSearchIndexProvider();
|
||||
if ( searchProvider.Exists(indexName) )
|
||||
searchProvider.DeleteIndex(indexName);
|
||||
|
||||
searchProvider.CreateIndex(indexName);
|
||||
return "Index is now being rebuilt...";
|
||||
}
|
||||
|
||||
[CommandName("index search")]
|
||||
[CommandHelp("index search /Query:<query> [/IndexName:<index name>]\r\n\t" + "Searches the specified <query> terms in the index with the specified <index name>, or in the search index if not specified")]
|
||||
[OrchardSwitches("Query,IndexName")]
|
||||
public string Search() {
|
||||
if ( !_indexManager.HasIndexProvider() ) {
|
||||
return "No index available";
|
||||
}
|
||||
var indexName = String.IsNullOrWhiteSpace(IndexName) ? SearchIndexName : IndexName;
|
||||
var searchBuilder = _indexManager.GetSearchIndexProvider().CreateSearchBuilder(indexName);
|
||||
var results = searchBuilder.WithField("body", Query).WithField("title", Query).Search();
|
||||
|
||||
Context.Output.WriteLine("{0} result{1}\r\n-----------------\r\n", results.Count(), results.Count() > 0 ? "s" : "");
|
||||
|
||||
Context.Output.WriteLine("┌──────────────────────────────────────────────────────────────┬────────┐");
|
||||
Context.Output.WriteLine("│ {0} │ {1,6} │", "Title" + new string(' ', 60 - "Title".Length), "Score");
|
||||
Context.Output.WriteLine("├──────────────────────────────────────────────────────────────┼────────┤");
|
||||
foreach ( var searchHit in results ) {
|
||||
var title = searchHit.GetString("title");
|
||||
title = title.Substring(0, Math.Min(60, title.Length)) ?? "- no title -";
|
||||
var score = searchHit.Score;
|
||||
Context.Output.WriteLine("│ {0} │ {1,6} │", title + new string(' ', 60 - title.Length), score);
|
||||
}
|
||||
Context.Output.WriteLine("└──────────────────────────────────────────────────────────────┴────────┘");
|
||||
|
||||
Context.Output.WriteLine();
|
||||
return "End of search results";
|
||||
}
|
||||
|
||||
[CommandName("index stats")]
|
||||
[CommandHelp("index stats [/IndexName:<index name>]\r\n\t" + "Displays some statistics about the index with the specified <index name>, or in the search index if not specified")]
|
||||
[OrchardSwitches("IndexName")]
|
||||
public string Stats() {
|
||||
if ( !_indexManager.HasIndexProvider() ) {
|
||||
return "No index available";
|
||||
}
|
||||
var indexName = String.IsNullOrWhiteSpace(IndexName) ? SearchIndexName : IndexName;
|
||||
Context.Output.WriteLine("Number of indexed documents: {0}", _indexManager.GetSearchIndexProvider().NumDocs(indexName));
|
||||
return "";
|
||||
}
|
||||
|
||||
[CommandName("index refresh")]
|
||||
[CommandHelp("index refresh /ContenItem:<content item id> \r\n\t" + "Refreshes the index for the specifed <content item id>")]
|
||||
[OrchardSwitches("ContentItem")]
|
||||
public string Refresh() {
|
||||
int contenItemId;
|
||||
if ( !int.TryParse(ContentItemId, out contenItemId) ) {
|
||||
return "Invalid content item id. Not an integer.";
|
||||
}
|
||||
|
||||
var contentItem = _contentManager.Get(contenItemId);
|
||||
_indexingTaskManager.CreateUpdateIndexTask(contentItem);
|
||||
|
||||
return "Content Item marked for reindexing";
|
||||
}
|
||||
|
||||
[CommandName("index delete")]
|
||||
[CommandHelp("index delete /ContenItem:<content item id>\r\n\t" + "Deletes the specifed <content item id> fromthe index")]
|
||||
[OrchardSwitches("ContentItem")]
|
||||
public string Delete() {
|
||||
int contenItemId;
|
||||
if(!int.TryParse(ContentItemId, out contenItemId)) {
|
||||
return "Invalid content item id. Not an integer.";
|
||||
}
|
||||
|
||||
var contentItem = _contentManager.Get(contenItemId);
|
||||
_indexingTaskManager.CreateDeleteIndexTask(contentItem);
|
||||
|
||||
return "Content Item marked for deletion";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -12,54 +12,69 @@ namespace Orchard.Core.Indexing.Lucene {
|
||||
|
||||
public List<AbstractField> Fields { get; private set; }
|
||||
private AbstractField _previousField;
|
||||
|
||||
public int Id { get; private set; }
|
||||
|
||||
public DefaultIndexDocument(int documentId) {
|
||||
Fields = new List<AbstractField>();
|
||||
SetContentItemId(documentId);
|
||||
IsDirty = false;
|
||||
}
|
||||
|
||||
public bool IsDirty { get; private set; }
|
||||
|
||||
public IIndexDocument Add(string name, string value) {
|
||||
return Add(name, value, false);
|
||||
}
|
||||
|
||||
public IIndexDocument Add(string name, string value, bool removeTags) {
|
||||
AppendPreviousField();
|
||||
|
||||
if(value == null) {
|
||||
value = String.Empty;
|
||||
}
|
||||
|
||||
if(removeTags) {
|
||||
value = value.RemoveTags();
|
||||
}
|
||||
|
||||
_previousField = new Field(name, value, Field.Store.YES, Field.Index.ANALYZED);
|
||||
IsDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIndexDocument Add(string name, DateTime value) {
|
||||
AppendPreviousField();
|
||||
_previousField = new Field(name, DateTools.DateToString(value, DateTools.Resolution.SECOND), Field.Store.YES, Field.Index.NOT_ANALYZED);
|
||||
IsDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIndexDocument Add(string name, int value) {
|
||||
AppendPreviousField();
|
||||
_previousField = new NumericField(name, Field.Store.YES, true).SetIntValue(value);
|
||||
IsDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIndexDocument Add(string name, bool value) {
|
||||
AppendPreviousField();
|
||||
_previousField = new Field(name, value.ToString().ToLower(), Field.Store.YES, Field.Index.NOT_ANALYZED);
|
||||
IsDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIndexDocument Add(string name, float value) {
|
||||
AppendPreviousField();
|
||||
_previousField = new NumericField(name, Field.Store.YES, true).SetFloatValue(value);
|
||||
IsDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIndexDocument Add(string name, object value) {
|
||||
AppendPreviousField();
|
||||
_previousField = new Field(name, value.ToString(), Field.Store.NO, Field.Index.NOT_ANALYZED);
|
||||
IsDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Lucene.Net.Analysis;
|
||||
using Lucene.Net.Analysis.Standard;
|
||||
using Lucene.Net.Documents;
|
||||
@@ -11,23 +13,28 @@ using Orchard.Indexing;
|
||||
using Directory = Lucene.Net.Store.Directory;
|
||||
using Version = Lucene.Net.Util.Version;
|
||||
using Orchard.Logging;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Orchard.Core.Indexing.Lucene {
|
||||
/// <summary>
|
||||
/// Represents the default implementation of an IIndexProvider based on Lucene
|
||||
/// Represents the default implementation of an IIndexProvider, based on Lucene
|
||||
/// </summary>
|
||||
public class DefaultIndexProvider : IIndexProvider {
|
||||
private readonly IAppDataFolder _appDataFolder;
|
||||
private readonly ShellSettings _shellSettings;
|
||||
public static readonly Version LuceneVersion = Version.LUCENE_29;
|
||||
private readonly Analyzer _analyzer = new StandardAnalyzer(LuceneVersion);
|
||||
private readonly Analyzer _analyzer ;
|
||||
private readonly string _basePath;
|
||||
public static readonly DateTime DefaultMinDateTime = new DateTime(1980, 1, 1);
|
||||
public static readonly string Settings = "Settings";
|
||||
public static readonly string LastIndexUtc = "LastIndexedUtc";
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public DefaultIndexProvider(IAppDataFolder appDataFolder, ShellSettings shellSettings) {
|
||||
_appDataFolder = appDataFolder;
|
||||
_shellSettings = shellSettings;
|
||||
_analyzer = CreateAnalyzer();
|
||||
|
||||
// TODO: (sebros) Find a common way to get where tenant's specific files should go. "Sites/Tenant" is hard coded in multiple places
|
||||
_basePath = Path.Combine("Sites", _shellSettings.Name, "Indexes");
|
||||
@@ -35,6 +42,15 @@ namespace Orchard.Core.Indexing.Lucene {
|
||||
Logger = NullLogger.Instance;
|
||||
|
||||
// Ensures the directory exists
|
||||
EnsureDirectoryExists();
|
||||
}
|
||||
|
||||
public static Analyzer CreateAnalyzer() {
|
||||
// StandardAnalyzer does lower-case and stop-word filtering. It also removes punctuation
|
||||
return new StandardAnalyzer(LuceneVersion);
|
||||
}
|
||||
|
||||
private void EnsureDirectoryExists() {
|
||||
var directory = new DirectoryInfo(_appDataFolder.MapPath(_basePath));
|
||||
if(!directory.Exists) {
|
||||
directory.Create();
|
||||
@@ -60,6 +76,36 @@ namespace Orchard.Core.Indexing.Lucene {
|
||||
return new DirectoryInfo(_appDataFolder.MapPath(Path.Combine(_basePath, indexName))).Exists;
|
||||
}
|
||||
|
||||
public bool IsEmpty(string indexName) {
|
||||
if ( !Exists(indexName) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var reader = IndexReader.Open(GetDirectory(indexName), true);
|
||||
|
||||
try {
|
||||
return reader.NumDocs() == 0;
|
||||
}
|
||||
finally {
|
||||
reader.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public int NumDocs(string indexName) {
|
||||
if ( !Exists(indexName) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var reader = IndexReader.Open(GetDirectory(indexName), true);
|
||||
|
||||
try {
|
||||
return reader.NumDocs();
|
||||
}
|
||||
finally {
|
||||
reader.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateIndex(string indexName) {
|
||||
var writer = new IndexWriter(GetDirectory(indexName), _analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED);
|
||||
writer.Close();
|
||||
@@ -70,43 +116,72 @@ namespace Orchard.Core.Indexing.Lucene {
|
||||
public void DeleteIndex(string indexName) {
|
||||
new DirectoryInfo(Path.Combine(_appDataFolder.MapPath(Path.Combine(_basePath, indexName))))
|
||||
.Delete(true);
|
||||
|
||||
var settingsFileName = GetSettingsFileName(indexName);
|
||||
if(File.Exists(settingsFileName)) {
|
||||
File.Delete(settingsFileName);
|
||||
}
|
||||
}
|
||||
|
||||
public void Store(string indexName, IIndexDocument indexDocument) {
|
||||
Store(indexName, (DefaultIndexDocument)indexDocument);
|
||||
Store(indexName, new [] { (DefaultIndexDocument)indexDocument });
|
||||
}
|
||||
|
||||
public void Store(string indexName, DefaultIndexDocument indexDocument) {
|
||||
public void Store(string indexName, IEnumerable<IIndexDocument> indexDocuments) {
|
||||
Store(indexName, indexDocuments.Cast<DefaultIndexDocument>());
|
||||
}
|
||||
|
||||
public void Store(string indexName, IEnumerable<DefaultIndexDocument> indexDocuments) {
|
||||
if(indexDocuments.AsQueryable().Count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new IndexWriter(GetDirectory(indexName), _analyzer, false, IndexWriter.MaxFieldLength.UNLIMITED);
|
||||
DefaultIndexDocument current = null;
|
||||
|
||||
try {
|
||||
var doc = CreateDocument(indexDocument);
|
||||
writer.AddDocument(doc);
|
||||
Logger.Debug("Document [{0}] indexed", indexDocument.Id);
|
||||
foreach ( var indexDocument in indexDocuments ) {
|
||||
current = indexDocument;
|
||||
var doc = CreateDocument(indexDocument);
|
||||
writer.AddDocument(doc);
|
||||
Logger.Debug("Document [{0}] indexed", indexDocument.Id);
|
||||
}
|
||||
}
|
||||
catch ( Exception ex ) {
|
||||
Logger.Error(ex, "An unexpected error occured while removing the document [{0}] from the index [{1}].", indexDocument.Id, indexName);
|
||||
Logger.Error(ex, "An unexpected error occured while add the document [{0}] from the index [{1}].", current.Id, indexName);
|
||||
}
|
||||
finally {
|
||||
writer.Optimize();
|
||||
writer.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Delete(string indexName, int id) {
|
||||
var reader = IndexReader.Open(GetDirectory(indexName), false);
|
||||
public void Delete(string indexName, int documentId) {
|
||||
Delete(indexName, new[] { documentId });
|
||||
}
|
||||
|
||||
public void Delete(string indexName, IEnumerable<int> documentIds) {
|
||||
if ( documentIds.AsQueryable().Count() == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var reader = IndexReader.Open(GetDirectory(indexName), false);
|
||||
|
||||
try {
|
||||
var term = new Term("id", id.ToString());
|
||||
if ( reader.DeleteDocuments(term) != 0 ) {
|
||||
Logger.Error("The document [{0}] could not be removed from the index [{1}]", id, indexName);
|
||||
foreach (var id in documentIds) {
|
||||
try {
|
||||
var term = new Term("id", id.ToString());
|
||||
if (reader.DeleteDocuments(term) != 0) {
|
||||
Logger.Error("The document [{0}] could not be removed from the index [{1}]", id, indexName);
|
||||
}
|
||||
else {
|
||||
Logger.Debug("Document [{0}] removed from index", id);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Error(ex, "An unexpected error occured while removing the document [{0}] from the index [{1}].", id, indexName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Logger.Debug("Document [{0}] removed from index", id);
|
||||
}
|
||||
}
|
||||
catch ( Exception ex ) {
|
||||
Logger.Error(ex, "An unexpected error occured while removing the document [{0}] from the index [{1}].", id, indexName);
|
||||
}
|
||||
finally {
|
||||
reader.Close();
|
||||
@@ -121,8 +196,38 @@ namespace Orchard.Core.Indexing.Lucene {
|
||||
return new DefaultSearchBuilder(GetDirectory(indexName));
|
||||
}
|
||||
|
||||
public IIndexDocument Get(string indexName, int id) {
|
||||
throw new NotImplementedException();
|
||||
private string GetSettingsFileName(string indexName) {
|
||||
return Path.Combine(_appDataFolder.MapPath(_basePath), indexName + ".settings.xml");
|
||||
}
|
||||
|
||||
public DateTime GetLastIndexUtc(string indexName) {
|
||||
var settingsFileName = GetSettingsFileName(indexName);
|
||||
|
||||
return File.Exists(settingsFileName)
|
||||
? DateTime.Parse(XDocument.Load(settingsFileName).Descendants(LastIndexUtc).First().Value)
|
||||
: DefaultMinDateTime;
|
||||
}
|
||||
|
||||
public void SetLastIndexUtc(string indexName, DateTime lastIndexUtc) {
|
||||
if ( lastIndexUtc < DefaultMinDateTime ) {
|
||||
lastIndexUtc = DefaultMinDateTime;
|
||||
}
|
||||
|
||||
XDocument doc;
|
||||
var settingsFileName = GetSettingsFileName(indexName);
|
||||
if ( !File.Exists(settingsFileName) ) {
|
||||
EnsureDirectoryExists();
|
||||
doc = new XDocument(
|
||||
new XElement(Settings,
|
||||
new XElement(LastIndexUtc, lastIndexUtc.ToString("s"))));
|
||||
}
|
||||
else {
|
||||
doc = XDocument.Load(settingsFileName);
|
||||
doc.Element(Settings).Element(LastIndexUtc).Value = lastIndexUtc.ToString("s");
|
||||
}
|
||||
|
||||
doc.Save(settingsFileName);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Lucene.Net.Analysis;
|
||||
using Lucene.Net.Analysis.Tokenattributes;
|
||||
using Lucene.Net.Index;
|
||||
using Lucene.Net.Search;
|
||||
using Lucene.Net.Store;
|
||||
using Orchard.Logging;
|
||||
using Lucene.Net.Documents;
|
||||
using Orchard.Indexing;
|
||||
using Lucene.Net.QueryParsers;
|
||||
|
||||
namespace Orchard.Core.Indexing.Lucene {
|
||||
public class DefaultSearchBuilder : ISearchBuilder {
|
||||
@@ -23,6 +26,9 @@ namespace Orchard.Core.Indexing.Lucene {
|
||||
private readonly Dictionary<string, DateTime> _after;
|
||||
private string _sort;
|
||||
private bool _sortDescending;
|
||||
private string _parse;
|
||||
private readonly Analyzer _analyzer;
|
||||
private string _defaultField;
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
@@ -37,9 +43,21 @@ namespace Orchard.Core.Indexing.Lucene {
|
||||
_fields = new Dictionary<string, Query[]>();
|
||||
_sort = String.Empty;
|
||||
_sortDescending = true;
|
||||
_parse = String.Empty;
|
||||
_analyzer = DefaultIndexProvider.CreateAnalyzer();
|
||||
}
|
||||
|
||||
public ISearchBuilder Parse(string query) {
|
||||
public ISearchBuilder Parse(string defaultField, string query) {
|
||||
if ( String.IsNullOrWhiteSpace(defaultField) ) {
|
||||
throw new ArgumentException("Default field can't be empty");
|
||||
}
|
||||
|
||||
if ( String.IsNullOrWhiteSpace(query) ) {
|
||||
throw new ArgumentException("Query can't be empty");
|
||||
}
|
||||
|
||||
_defaultField = defaultField;
|
||||
_parse = query;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -49,8 +67,17 @@ namespace Orchard.Core.Indexing.Lucene {
|
||||
|
||||
public ISearchBuilder WithField(string field, string value, bool wildcardSearch) {
|
||||
|
||||
_fields[field] = value.Split(' ')
|
||||
var tokens = new List<string>();
|
||||
using(var sr = new System.IO.StringReader(value)) {
|
||||
var stream = _analyzer.TokenStream(field, sr);
|
||||
while(stream.IncrementToken()) {
|
||||
tokens.Add(((TermAttribute)stream.GetAttribute(typeof(TermAttribute))).Term());
|
||||
}
|
||||
}
|
||||
|
||||
_fields[field] = tokens
|
||||
.Where(k => !String.IsNullOrWhiteSpace(k))
|
||||
.Select(QueryParser.Escape)
|
||||
.Select(k => wildcardSearch ? (Query)new PrefixQuery(new Term(field, k)) : new TermQuery(new Term(k)))
|
||||
.ToArray();
|
||||
|
||||
@@ -93,6 +120,10 @@ namespace Orchard.Core.Indexing.Lucene {
|
||||
}
|
||||
|
||||
private Query CreateQuery() {
|
||||
if(!String.IsNullOrWhiteSpace(_parse)) {
|
||||
return new QueryParser(DefaultIndexProvider.LuceneVersion, _defaultField, DefaultIndexProvider.CreateAnalyzer()).Parse(_parse);
|
||||
}
|
||||
|
||||
var query = new BooleanQuery();
|
||||
|
||||
if ( _fields.Keys.Count > 0 ) { // apply specific filters if defined
|
||||
@@ -124,7 +155,16 @@ namespace Orchard.Core.Indexing.Lucene {
|
||||
public IEnumerable<ISearchHit> Search() {
|
||||
var query = CreateQuery();
|
||||
|
||||
var searcher = new IndexSearcher(_directory, true);
|
||||
IndexSearcher searcher;
|
||||
|
||||
try {
|
||||
searcher = new IndexSearcher(_directory, true);
|
||||
}
|
||||
catch {
|
||||
// index might not exist if it has been rebuilt
|
||||
Logger.Information("Attempt to read a none existing index");
|
||||
return Enumerable.Empty<ISearchHit>();
|
||||
}
|
||||
|
||||
try {
|
||||
var sort = String.IsNullOrEmpty(_sort)
|
||||
@@ -157,8 +197,17 @@ namespace Orchard.Core.Indexing.Lucene {
|
||||
|
||||
public int Count() {
|
||||
var query = CreateQuery();
|
||||
IndexSearcher searcher;
|
||||
|
||||
try {
|
||||
searcher = new IndexSearcher(_directory, true);
|
||||
}
|
||||
catch {
|
||||
// index might not exist if it has been rebuilt
|
||||
Logger.Information("Attempt to read a none existing index");
|
||||
return 0;
|
||||
}
|
||||
|
||||
var searcher = new IndexSearcher(_directory, true);
|
||||
try {
|
||||
var hits = searcher.Search(query, Int16.MaxValue);
|
||||
Logger.Information("Search results: {0}", hits.scoreDocs.Length);
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Orchard.Core.Indexing.Models {
|
||||
public class IndexingSettingsRecord {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual DateTime? LatestIndexingUtc { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,12 @@ using Orchard.ContentManagement.Records;
|
||||
|
||||
namespace Orchard.Core.Indexing.Models {
|
||||
public class IndexingTaskRecord {
|
||||
|
||||
public const int Update = 0;
|
||||
public const int Delete = 1;
|
||||
|
||||
public virtual int Id { get; set; }
|
||||
public virtual int Action { get; set; }
|
||||
public virtual DateTime? CreatedUtc { get; set; }
|
||||
public virtual ContentItemRecord ContentItemRecord { get; set; }
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@ namespace Orchard.Core.Indexing.Services {
|
||||
}
|
||||
|
||||
void CreateIndexingTask(PublishContentContext context, ContentPart<CommonRecord> part) {
|
||||
_indexingTaskManager.CreateTask(context.ContentItem);
|
||||
_indexingTaskManager.CreateUpdateIndexTask(context.ContentItem);
|
||||
}
|
||||
|
||||
void RemoveIndexingTask(RemoveContentContext context, ContentPart<CommonRecord> part) {
|
||||
_indexingTaskManager.DeleteTasks(context.ContentItem);
|
||||
_indexingTaskManager.CreateDeleteIndexTask(context.ContentItem);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,99 +10,171 @@ using Orchard.Logging;
|
||||
using Orchard.Services;
|
||||
using Orchard.Tasks;
|
||||
using Orchard.Core.Indexing.Models;
|
||||
using Orchard.Tasks.Indexing;
|
||||
using Orchard.Indexing;
|
||||
|
||||
namespace Orchard.Core.Indexing.Services {
|
||||
/// <summary>
|
||||
/// Contains the logic which is regularly executed to retrieve index information from multiple content handlers.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class IndexingTaskExecutor : IBackgroundTask {
|
||||
public class IndexingTaskExecutor : IBackgroundTask, IIndexNotifierHandler {
|
||||
private readonly IClock _clock;
|
||||
private readonly IRepository<IndexingTaskRecord> _repository;
|
||||
private readonly IRepository<IndexingSettingsRecord> _settings;
|
||||
private readonly IEnumerable<IContentHandler> _handlers;
|
||||
private IIndexProvider _indexProvider;
|
||||
private IIndexManager _indexManager;
|
||||
private readonly IIndexManager _indexManager;
|
||||
private readonly IIndexingTaskManager _indexingTaskManager;
|
||||
private readonly IContentManager _contentManager;
|
||||
private const string SearchIndexName = "search";
|
||||
private const string SearchIndexName = "Search";
|
||||
|
||||
private readonly object _synLock = new object();
|
||||
|
||||
public IndexingTaskExecutor(
|
||||
IClock clock,
|
||||
IRepository<IndexingTaskRecord> repository,
|
||||
IRepository<IndexingSettingsRecord> settings,
|
||||
IEnumerable<IContentHandler> handlers,
|
||||
IIndexManager indexManager,
|
||||
IIndexingTaskManager indexingTaskManager,
|
||||
IContentManager contentManager) {
|
||||
_clock = clock;
|
||||
_repository = repository;
|
||||
_settings = settings;
|
||||
_indexManager = indexManager;
|
||||
_handlers = handlers;
|
||||
_indexingTaskManager = indexingTaskManager;
|
||||
_contentManager = contentManager;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public void UpdateIndex(string indexName) {
|
||||
if (indexName == SearchIndexName) {
|
||||
Sweep();
|
||||
}
|
||||
}
|
||||
|
||||
public void Sweep() {
|
||||
|
||||
if(!_indexManager.HasIndexProvider()) {
|
||||
if ( !System.Threading.Monitor.TryEnter(_synLock) ) {
|
||||
Logger.Information("Index was requested but was already running");
|
||||
return;
|
||||
}
|
||||
|
||||
_indexProvider = _indexManager.GetSearchIndexProvider();
|
||||
try {
|
||||
|
||||
// retrieve last processed index time
|
||||
var settingsRecord = _settings.Table.FirstOrDefault();
|
||||
if (!_indexManager.HasIndexProvider()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (settingsRecord == null) {
|
||||
_settings.Create(settingsRecord = new IndexingSettingsRecord { LatestIndexingUtc = new DateTime(1980, 1, 1)});
|
||||
}
|
||||
_indexProvider = _indexManager.GetSearchIndexProvider();
|
||||
var updateIndexDocuments = new List<IIndexDocument>();
|
||||
var lastIndexing = DateTime.UtcNow;
|
||||
|
||||
var lastIndexing = settingsRecord.LatestIndexingUtc;
|
||||
settingsRecord.LatestIndexingUtc = _clock.UtcNow;
|
||||
// Do we need to rebuild the full index (first time module is used, or rebuild index requested) ?
|
||||
if (_indexProvider.IsEmpty(SearchIndexName)) {
|
||||
Logger.Information("Rebuild index started");
|
||||
|
||||
// retrieved not yet processed tasks
|
||||
var taskRecords = _repository.Fetch(x => x.CreatedUtc >= lastIndexing)
|
||||
.ToArray();
|
||||
|
||||
if (taskRecords.Length == 0)
|
||||
return;
|
||||
// mark current last task, as we should process older ones (in case of rebuild index only)
|
||||
lastIndexing = _indexingTaskManager.GetLastTaskDateTime();
|
||||
|
||||
Logger.Information("Processing {0} indexing tasks", taskRecords.Length);
|
||||
// get every existing content item to index it
|
||||
foreach (var contentItem in _contentManager.Query(VersionOptions.Published).List()) {
|
||||
try {
|
||||
var context = new IndexContentContext {
|
||||
ContentItem = contentItem,
|
||||
IndexDocument = _indexProvider.New(contentItem.Id)
|
||||
};
|
||||
|
||||
|
||||
if(!_indexProvider.Exists(SearchIndexName)) {
|
||||
_indexProvider.CreateIndex(SearchIndexName);
|
||||
}
|
||||
// dispatch to handlers to retrieve index information
|
||||
foreach (var handler in _handlers) {
|
||||
handler.Indexing(context);
|
||||
}
|
||||
|
||||
foreach (var taskRecord in taskRecords) {
|
||||
if ( context.IndexDocument.IsDirty ) {
|
||||
updateIndexDocuments.Add(context.IndexDocument);
|
||||
|
||||
foreach ( var handler in _handlers ) {
|
||||
handler.Indexed(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Warning(ex, "Unable to index content item #{0} during rebuild", contentItem.Id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// retrieve last processed index time
|
||||
lastIndexing = _indexProvider.GetLastIndexUtc(SearchIndexName);
|
||||
}
|
||||
|
||||
_indexProvider.SetLastIndexUtc(SearchIndexName, _clock.UtcNow);
|
||||
|
||||
// retrieve not yet processed tasks
|
||||
var taskRecords = _repository.Fetch(x => x.CreatedUtc > lastIndexing)
|
||||
.ToArray();
|
||||
|
||||
// nothing to do ?
|
||||
if (taskRecords.Length + updateIndexDocuments.Count == 0)
|
||||
return;
|
||||
|
||||
Logger.Information("Processing {0} indexing tasks", taskRecords.Length);
|
||||
|
||||
if (!_indexProvider.Exists(SearchIndexName)) {
|
||||
_indexProvider.CreateIndex(SearchIndexName);
|
||||
}
|
||||
|
||||
// process Delete tasks
|
||||
try {
|
||||
var task = new IndexingTask(_contentManager, taskRecord);
|
||||
var context = new IndexContentContext {
|
||||
ContentItem = task.ContentItem,
|
||||
IndexDocument = _indexProvider.New(task.ContentItem.Id)
|
||||
};
|
||||
|
||||
// dispatch to handlers to retrieve index information
|
||||
foreach (var handler in _handlers) {
|
||||
handler.Indexing(context);
|
||||
}
|
||||
|
||||
_indexProvider.Store(SearchIndexName, context.IndexDocument);
|
||||
|
||||
foreach ( var handler in _handlers ) {
|
||||
handler.Indexed(context);
|
||||
}
|
||||
_indexProvider.Delete(SearchIndexName, taskRecords.Where(t => t.Action == IndexingTaskRecord.Delete).Select(t => t.Id));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Warning(ex, "Unable to process indexing task #{0}", taskRecord.Id);
|
||||
Logger.Warning(ex, "An error occured while removing a document from the index");
|
||||
}
|
||||
|
||||
}
|
||||
// process Update tasks
|
||||
foreach (var taskRecord in taskRecords.Where(t => t.Action == IndexingTaskRecord.Update)) {
|
||||
var task = new IndexingTask(_contentManager, taskRecord);
|
||||
|
||||
_settings.Update(settingsRecord);
|
||||
try {
|
||||
var context = new IndexContentContext {
|
||||
ContentItem = task.ContentItem,
|
||||
IndexDocument = _indexProvider.New(task.ContentItem.Id)
|
||||
};
|
||||
|
||||
// dispatch to handlers to retrieve index information
|
||||
foreach (var handler in _handlers) {
|
||||
handler.Indexing(context);
|
||||
}
|
||||
|
||||
if ( context.IndexDocument.IsDirty ) {
|
||||
updateIndexDocuments.Add(context.IndexDocument);
|
||||
|
||||
foreach (var handler in _handlers) {
|
||||
handler.Indexed(context);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Warning(ex, "Unable to process indexing task #{0}", taskRecord.Id);
|
||||
}
|
||||
}
|
||||
|
||||
if (updateIndexDocuments.Count > 0) {
|
||||
try {
|
||||
_indexProvider.Store(SearchIndexName, updateIndexDocuments);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Warning(ex, "An error occured while adding a document to the index");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
System.Threading.Monitor.Exit(_synLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,11 @@ namespace Orchard.Core.Indexing.Services {
|
||||
public class IndexingTaskManager : IIndexingTaskManager {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IRepository<IndexingTaskRecord> _repository;
|
||||
private readonly IRepository<IndexingSettingsRecord> _settings;
|
||||
private readonly IClock _clock;
|
||||
|
||||
public IndexingTaskManager(
|
||||
IContentManager contentManager,
|
||||
IRepository<IndexingTaskRecord> repository,
|
||||
IRepository<IndexingSettingsRecord> settings,
|
||||
IClock clock) {
|
||||
_clock = clock;
|
||||
_repository = repository;
|
||||
@@ -32,70 +30,49 @@ namespace Orchard.Core.Indexing.Services {
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public void CreateTask(ContentItem contentItem) {
|
||||
if (contentItem == null) {
|
||||
private void CreateTask(ContentItem contentItem, int action) {
|
||||
if ( contentItem == null ) {
|
||||
throw new ArgumentNullException("contentItem");
|
||||
}
|
||||
|
||||
// remove previous tasks for the same content item
|
||||
var tasks = _repository
|
||||
.Fetch(x => x.Id == contentItem.Id )
|
||||
.ToArray();
|
||||
|
||||
foreach (var task in tasks) {
|
||||
_repository.Delete(task);
|
||||
}
|
||||
DeleteTasks(contentItem);
|
||||
|
||||
var taskRecord = new IndexingTaskRecord {
|
||||
CreatedUtc = _clock.UtcNow,
|
||||
ContentItemRecord = contentItem.Record
|
||||
};
|
||||
CreatedUtc = _clock.UtcNow,
|
||||
ContentItemRecord = contentItem.Record,
|
||||
Action = action
|
||||
};
|
||||
|
||||
_repository.Create(taskRecord);
|
||||
|
||||
Logger.Information("Indexing task created for [{0}:{1}]", contentItem.ContentType, contentItem.Id);
|
||||
|
||||
}
|
||||
|
||||
public IEnumerable<IIndexingTask> GetTasks(DateTime? createdAfter) {
|
||||
return _repository
|
||||
.Fetch(x => x.CreatedUtc > createdAfter)
|
||||
.Select(x => new IndexingTask(_contentManager, x))
|
||||
.Cast<IIndexingTask>()
|
||||
.ToReadOnlyCollection();
|
||||
}
|
||||
|
||||
public void DeleteTasks(DateTime? createdBefore) {
|
||||
Logger.Debug("Deleting Indexing tasks created before {0}", createdBefore);
|
||||
|
||||
var tasks = _repository
|
||||
.Fetch(x => x.CreatedUtc <= createdBefore);
|
||||
|
||||
foreach (var task in tasks) {
|
||||
_repository.Delete(task);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteTasks(ContentItem contentItem) {
|
||||
Logger.Debug("Deleting Indexing tasks for ContentItem [{0}:{1}]", contentItem.ContentType, contentItem.Id);
|
||||
|
||||
var tasks = _repository
|
||||
.Fetch(x => x.Id == contentItem.Id);
|
||||
|
||||
foreach (var task in tasks) {
|
||||
_repository.Delete(task);
|
||||
}
|
||||
}
|
||||
|
||||
public void RebuildIndex() {
|
||||
var settingsRecord = _settings.Table.FirstOrDefault();
|
||||
if (settingsRecord == null) {
|
||||
_settings.Create(settingsRecord = new IndexingSettingsRecord() );
|
||||
}
|
||||
|
||||
settingsRecord.LatestIndexingUtc = new DateTime(1980, 1, 1);
|
||||
_settings.Update(settingsRecord);
|
||||
}
|
||||
|
||||
public void CreateUpdateIndexTask(ContentItem contentItem) {
|
||||
|
||||
CreateTask(contentItem, IndexingTaskRecord.Update);
|
||||
Logger.Information("Indexing task created for [{0}:{1}]", contentItem.ContentType, contentItem.Id);
|
||||
}
|
||||
|
||||
public void CreateDeleteIndexTask(ContentItem contentItem) {
|
||||
|
||||
CreateTask(contentItem, IndexingTaskRecord.Delete);
|
||||
Logger.Information("Deleting index task created for [{0}:{1}]", contentItem.ContentType, contentItem.Id);
|
||||
}
|
||||
|
||||
public DateTime GetLastTaskDateTime() {
|
||||
return _repository.Table.Max(t => t.CreatedUtc) ?? DateTime.MinValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes existing tasks for the specified content item
|
||||
/// </summary>
|
||||
public void DeleteTasks(ContentItem contentItem) {
|
||||
var tasks = _repository
|
||||
.Fetch(x => x.ContentItemRecord.Id == contentItem.Id)
|
||||
.ToArray();
|
||||
foreach (var task in tasks) {
|
||||
_repository.Delete(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement.Drivers;
|
||||
using Orchard.Core.Localization.Models;
|
||||
|
||||
namespace Orchard.Core.Localization.Drivers {
|
||||
[UsedImplicitly]
|
||||
public class LocalizedDriver : ContentPartDriver<Localized> {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.Core.Localization.Models;
|
||||
using Orchard.Data;
|
||||
using Orchard.Localization;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Localization.Services;
|
||||
using Orchard.Settings;
|
||||
|
||||
namespace Orchard.Core.Localization.Handlers {
|
||||
[UsedImplicitly]
|
||||
public class LocalizedHandler : ContentHandler {
|
||||
private readonly ICultureManager _cultureManager;
|
||||
private readonly IContentManager _contentManager;
|
||||
|
||||
public LocalizedHandler(IRepository<LocalizedRecord> localizedRepository, ICultureManager cultureManager, IContentManager contentManager) {
|
||||
_cultureManager = cultureManager;
|
||||
_contentManager = contentManager;
|
||||
T = NullLocalizer.Instance;
|
||||
|
||||
Filters.Add(StorageFilter.For(localizedRepository));
|
||||
|
||||
OnActivated<Localized>(InitializePart);
|
||||
|
||||
OnLoaded<Localized>(LazyLoadHandlers);
|
||||
|
||||
OnIndexed<Localized>((context, localized) => context.IndexDocument.Add("culture", localized.Culture != null ? localized.Culture.Culture : _cultureManager.GetSiteCulture()).Store(false).Analyze(false));
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
void LazyLoadHandlers(LoadContentContext context, Localized localized) {
|
||||
localized.CultureField.Loader(ctx => _cultureManager.GetCultureById(localized.Record.CultureId));
|
||||
localized.MasterContentItemField.Loader(ctx => _contentManager.Get(localized.Record.MasterContentItemId));
|
||||
}
|
||||
|
||||
void InitializePart(ActivatedContentContext context, Localized localized) {
|
||||
localized.CultureField.Setter(cultureRecord => {
|
||||
localized.Record.CultureId = cultureRecord.Id;
|
||||
return cultureRecord;
|
||||
});
|
||||
localized.MasterContentItemField.Setter(masterContentItem => {
|
||||
localized.Record.MasterContentItemId = masterContentItem.ContentItem.Id;
|
||||
return masterContentItem;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Web.Mvc;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Utilities;
|
||||
using Orchard.Localization.Records;
|
||||
|
||||
namespace Orchard.Core.Localization.Models {
|
||||
public sealed class Localized : ContentPart<LocalizedRecord> {
|
||||
private readonly LazyField<CultureRecord> _culture = new LazyField<CultureRecord>();
|
||||
private readonly LazyField<IContent> _masterContentItem = new LazyField<IContent>();
|
||||
|
||||
public LazyField<CultureRecord> CultureField { get { return _culture; } }
|
||||
public LazyField<IContent> MasterContentItemField { get { return _masterContentItem; } }
|
||||
|
||||
[HiddenInput(DisplayValue = false)]
|
||||
public int Id { get { return ContentItem.Id; } }
|
||||
|
||||
public CultureRecord Culture {
|
||||
get { return _culture.Value; }
|
||||
set { _culture.Value = value; }
|
||||
}
|
||||
|
||||
public IContent MasterContentItem {
|
||||
get { return _masterContentItem.Value; }
|
||||
set { _masterContentItem.Value = value; }
|
||||
}
|
||||
|
||||
public bool HasTranslationGroup {
|
||||
get {
|
||||
return Record.MasterContentItemId != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Orchard.ContentManagement.Records;
|
||||
|
||||
namespace Orchard.Core.Localization.Models {
|
||||
public class LocalizedRecord : ContentPartRecord {
|
||||
public virtual int CultureId { get; set; }
|
||||
public virtual int MasterContentItemId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
name: Localization
|
||||
antiforgery: enabled
|
||||
author: The Orchard Team
|
||||
website: http://orchardproject.net
|
||||
version: 0.1
|
||||
orchardversion: 0.1.2010.0312
|
||||
description: Support for localizing content items for cultures.
|
||||
features:
|
||||
Localization:
|
||||
Description: Localize content items.
|
||||
Category: Core
|
||||
@@ -1,13 +1,15 @@
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.Localization;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Core.Navigation {
|
||||
public class AdminMenu : INavigationProvider {
|
||||
public Localizer T { get; set; }
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add("Site", "12",
|
||||
builder.Add(T("Site"), "12",
|
||||
menu => menu
|
||||
.Add("Manage Menu", "6.0", item => item.Action("Index", "Admin", new { area = "Navigation" }).Permission(Permissions.ManageMainMenu)));
|
||||
.Add(T("Manage Menu"), "6.0", item => item.Action("Index", "Admin", new { area = "Navigation" }).Permission(Permissions.ManageMainMenu)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ using (Html.BeginFormAntiForgeryPost()) { %>
|
||||
%>
|
||||
|
||||
<h2><%: T("Add New Item") %></h2><%
|
||||
using (Html.BeginFormAntiForgeryPost("/admin/navigation/create", FormMethod.Post)) { %>
|
||||
using (Html.BeginFormAntiForgeryPost(Url.Action("create"), FormMethod.Post)) { %>
|
||||
<table class="menu items">
|
||||
<colgroup>
|
||||
<col id="AddText" />
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<Reference Include="System.ComponentModel.DataAnnotations">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Web.ApplicationServices" />
|
||||
<Reference Include="System.Web.DynamicData" />
|
||||
@@ -68,6 +69,14 @@
|
||||
<Compile Include="Common\Drivers\RoutableDriver.cs" />
|
||||
<Compile Include="Common\Controllers\RoutableController.cs" />
|
||||
<Compile Include="Common\Handlers\RoutableAspectHandler.cs" />
|
||||
<Compile Include="Contents\Controllers\ItemController.cs" />
|
||||
<Compile Include="Contents\Handlers\ContentsModuleHandler.cs" />
|
||||
<Compile Include="Localization\Drivers\LocalizedDriver.cs" />
|
||||
<Compile Include="Routable\Controllers\ItemController.cs" />
|
||||
<Compile Include="Routable\Drivers\RoutableDriver.cs" />
|
||||
<Compile Include="Routable\Handlers\RoutableHandler.cs" />
|
||||
<Compile Include="Routable\IRoutablePathConstraint.cs" />
|
||||
<Compile Include="Routable\Models\IsRoutable.cs" />
|
||||
<Compile Include="Common\Permissions.cs" />
|
||||
<Compile Include="Common\Models\CommonVersionRecord.cs" />
|
||||
<Compile Include="Common\Routes.cs" />
|
||||
@@ -86,6 +95,12 @@
|
||||
<Compile Include="Common\ViewModels\BodyEditorViewModel.cs" />
|
||||
<Compile Include="Common\ViewModels\RoutableEditorViewModel.cs" />
|
||||
<Compile Include="Common\ViewModels\OwnerEditorViewModel.cs" />
|
||||
<Compile Include="Contents\AdminMenu.cs" />
|
||||
<Compile Include="Contents\Controllers\AdminController.cs" />
|
||||
<Compile Include="Contents\ViewModels\CreateItemViewModel.cs" />
|
||||
<Compile Include="Contents\ViewModels\ContentTypeListViewModel.cs" />
|
||||
<Compile Include="Contents\ViewModels\EditItemViewModel.cs" />
|
||||
<Compile Include="Contents\ViewModels\ListContentViewModel.cs" />
|
||||
<Compile Include="Dashboard\AdminMenu.cs" />
|
||||
<Compile Include="Dashboard\Controllers\AdminController.cs" />
|
||||
<Compile Include="Dashboard\Routes.cs" />
|
||||
@@ -109,16 +124,19 @@
|
||||
<Compile Include="Feeds\Rss\RssResult.cs" />
|
||||
<Compile Include="HomePage\Controllers\HomeController.cs" />
|
||||
<Compile Include="HomePage\Routes.cs" />
|
||||
<Compile Include="Indexing\Commands\IndexingCommands.cs" />
|
||||
<Compile Include="Indexing\Lucene\DefaultIndexDocument.cs" />
|
||||
<Compile Include="Indexing\Lucene\DefaultIndexProvider.cs" />
|
||||
<Compile Include="Indexing\Lucene\DefaultSearchBuilder.cs" />
|
||||
<Compile Include="Indexing\Lucene\DefaultSearchHit.cs" />
|
||||
<Compile Include="Indexing\Models\IndexingSettingsRecord.cs" />
|
||||
<Compile Include="Indexing\Models\IndexingTask.cs" />
|
||||
<Compile Include="Indexing\Models\IndexingTaskRecord.cs" />
|
||||
<Compile Include="Indexing\Services\CreateIndexingTaskHandler.cs" />
|
||||
<Compile Include="Indexing\Services\IndexingTaskExecutor.cs" />
|
||||
<Compile Include="Indexing\Services\IndexingTaskManager.cs" />
|
||||
<Compile Include="Localization\Handlers\LocalizedHandler.cs" />
|
||||
<Compile Include="Localization\Models\Localized.cs" />
|
||||
<Compile Include="Localization\Models\LocalizedRecord.cs" />
|
||||
<Compile Include="Navigation\AdminMenu.cs" />
|
||||
<Compile Include="Navigation\Controllers\AdminController.cs" />
|
||||
<Compile Include="Navigation\Models\MenuItem.cs" />
|
||||
@@ -136,6 +154,10 @@
|
||||
<Compile Include="Navigation\ViewModels\MenuItemEntry.cs" />
|
||||
<Compile Include="Navigation\ViewModels\NavigationManagementViewModel.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Routable\Routes.cs" />
|
||||
<Compile Include="Routable\Services\RoutablePathConstraint.cs" />
|
||||
<Compile Include="Routable\Services\RoutablePathConstraintUpdator.cs" />
|
||||
<Compile Include="Routable\ViewModels\RoutableDisplayViewModel.cs" />
|
||||
<Compile Include="Scheduling\Models\ScheduledTaskRecord.cs" />
|
||||
<Compile Include="Scheduling\Services\PublishingTaskHandler.cs" />
|
||||
<Compile Include="Scheduling\Services\PublishingTaskManager.cs" />
|
||||
@@ -143,6 +165,12 @@
|
||||
<Compile Include="Scheduling\Services\ScheduledTaskExecutor.cs" />
|
||||
<Compile Include="Scheduling\Models\Task.cs" />
|
||||
<Compile Include="Settings\Drivers\SiteSettingsDriver.cs" />
|
||||
<Compile Include="Settings\Metadata\ContentDefinitionManager.cs" />
|
||||
<Compile Include="Settings\Metadata\Records\ContentFieldDefinitionRecord.cs" />
|
||||
<Compile Include="Settings\Metadata\Records\ContentPartDefinitionRecord.cs" />
|
||||
<Compile Include="Settings\Metadata\Records\ContentPartFieldDefinitionRecord.cs" />
|
||||
<Compile Include="Settings\Metadata\Records\ContentTypeDefinitionRecord.cs" />
|
||||
<Compile Include="Settings\Metadata\Records\ContentTypePartDefinitionRecord.cs" />
|
||||
<Compile Include="Settings\Models\SiteSettingsRecord.cs" />
|
||||
<Compile Include="Settings\Permissions.cs" />
|
||||
<Compile Include="Settings\State\Records\ShellFeatureStateRecord.cs" />
|
||||
@@ -173,7 +201,20 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Common\Module.txt" />
|
||||
<Content Include="Contents\Module.txt" />
|
||||
<Content Include="Contents\Views\Admin\Types.aspx" />
|
||||
<Content Include="Contents\Views\Admin\List.aspx" />
|
||||
<Content Include="Contents\Views\Admin\Edit.aspx" />
|
||||
<Content Include="Contents\Views\Admin\CreatableTypeList.aspx" />
|
||||
<Content Include="Contents\Views\Admin\Create.aspx" />
|
||||
<Content Include="Contents\Views\DisplayTemplates\Items\Contents.Item.ascx" />
|
||||
<Content Include="Contents\Views\EditorTemplates\Items\Contents.Item.ascx" />
|
||||
<Content Include="Contents\Views\Item\Preview.aspx" />
|
||||
<Content Include="Contents\Views\Item\Display.aspx" />
|
||||
<Content Include="Indexing\Module.txt" />
|
||||
<Content Include="Localization\Module.txt" />
|
||||
<Content Include="Routable\Module.txt" />
|
||||
<Content Include="Routable\Views\Item\Display.aspx" />
|
||||
<Content Include="Settings\Module.txt" />
|
||||
<Content Include="Settings\Views\Admin\Index.ascx" />
|
||||
<Content Include="Web.config" />
|
||||
@@ -225,6 +266,8 @@
|
||||
<ItemGroup>
|
||||
<None Include="App_Data\Localization\en-US\orchard.core.po" />
|
||||
<None Include="App_Data\Localization\fr-FR\orchard.core.po" />
|
||||
<Content Include="Contents\Views\Web.config" />
|
||||
<Content Include="Routable\Views\Web.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Routable.Models;
|
||||
using Orchard.Core.Routable.ViewModels;
|
||||
using Orchard.Mvc.ViewModels;
|
||||
|
||||
namespace Orchard.Core.Routable.Controllers {
|
||||
[ValidateInput(false)]
|
||||
public class ItemController : Controller {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IRoutablePathConstraint _routablePathConstraint;
|
||||
|
||||
public ItemController(IContentManager contentManager, IRoutablePathConstraint routablePathConstraint) {
|
||||
_contentManager = contentManager;
|
||||
_routablePathConstraint = routablePathConstraint;
|
||||
}
|
||||
|
||||
public ActionResult Display(string path) {
|
||||
var matchedPath = _routablePathConstraint.FindPath(path);
|
||||
if (string.IsNullOrEmpty(matchedPath)) {
|
||||
throw new ApplicationException("404 - should not have passed path constraint");
|
||||
}
|
||||
|
||||
var hits = _contentManager
|
||||
.Query<IsRoutable, RoutableRecord>(VersionOptions.Published)
|
||||
.Where(r => r.Path == matchedPath)
|
||||
.Slice(0, 2);
|
||||
if (hits.Count() == 0) {
|
||||
throw new ApplicationException("404 - should not have passed path constraint");
|
||||
}
|
||||
if (hits.Count() != 1) {
|
||||
throw new ApplicationException("Ambiguous content");
|
||||
}
|
||||
var model = new RoutableDisplayViewModel {
|
||||
Routable = _contentManager.BuildDisplayModel<IRoutableAspect>(hits.Single(), "Detail")
|
||||
};
|
||||
PrepareDisplayViewModel(model.Routable);
|
||||
return View("Display", model);
|
||||
}
|
||||
|
||||
private void PrepareDisplayViewModel(ContentItemViewModel<IRoutableAspect> itemViewModel) {
|
||||
if (string.IsNullOrEmpty(itemViewModel.TemplateName)) {
|
||||
itemViewModel.TemplateName = "Items/Contents.Item";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Drivers;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Common.ViewModels;
|
||||
using Orchard.Core.Common.Services;
|
||||
using Orchard.Core.Routable.Models;
|
||||
using Orchard.Localization;
|
||||
using Orchard.UI.Notify;
|
||||
|
||||
namespace Orchard.Core.Routable.Drivers {
|
||||
public class RoutableDriver : ContentPartDriver<IsRoutable> {
|
||||
protected override DriverResult Editor(IsRoutable part, IUpdateModel updater) {
|
||||
part.Record.Title = "Routable #" + part.ContentItem.Id;
|
||||
part.Record.Slug = "routable" + part.ContentItem.Id;
|
||||
part.Record.Path = "routable" + part.ContentItem.Id;
|
||||
return base.Editor(part, updater);
|
||||
}
|
||||
|
||||
//private const string TemplateName = "Parts/Common.Routable";
|
||||
|
||||
//private readonly IOrchardServices _services;
|
||||
//private readonly IRoutableService _routableService;
|
||||
//public Localizer T { get; set; }
|
||||
|
||||
//protected override string Prefix {
|
||||
// get { return "Routable"; }
|
||||
//}
|
||||
|
||||
//public Routable(IOrchardServices services, IRoutableService routableService)
|
||||
//{
|
||||
// _services = services;
|
||||
// _routableService = routableService;
|
||||
|
||||
// T = NullLocalizer.Instance;
|
||||
//}
|
||||
|
||||
//protected override DriverResult Editor(RoutableAspect part) {
|
||||
// var model = new RoutableEditorViewModel { Prefix = Prefix, RoutableAspect = part };
|
||||
// return ContentPartTemplate(model, TemplateName, Prefix).Location("primary", "before.5");
|
||||
//}
|
||||
|
||||
//protected override DriverResult Editor(RoutableAspect part, IUpdateModel updater) {
|
||||
// var model = new RoutableEditorViewModel { Prefix = Prefix, RoutableAspect = part };
|
||||
// updater.TryUpdateModel(model, Prefix, null, null);
|
||||
|
||||
// if (!_routableService.IsSlugValid(part.Slug)){
|
||||
// updater.AddModelError("Routable.Slug", T("Please do not use any of the following characters in your slugs: \"/\", \":\", \"?\", \"#\", \"[\", \"]\", \"@\", \"!\", \"$\", \"&\", \"'\", \"(\", \")\", \"*\", \"+\", \",\", \";\", \"=\". No spaces are allowed (please use dashes or underscores instead).").ToString());
|
||||
// }
|
||||
|
||||
// string originalSlug = part.Slug;
|
||||
// if(!_routableService.ProcessSlug(part)) {
|
||||
// _services.Notifier.Warning(T("Slugs in conflict. \"{0}\" is already set for a previously created {2} so now it has the slug \"{1}\"",
|
||||
// originalSlug, part.Slug, part.ContentItem.ContentType));
|
||||
// }
|
||||
|
||||
// return ContentPartTemplate(model, TemplateName, Prefix).Location("primary", "before.5");
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Web.Routing;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Core.Routable.Models;
|
||||
|
||||
namespace Orchard.Core.Routable.Handlers {
|
||||
public class RoutableHandler : ContentHandlerBase {
|
||||
public override void GetContentItemMetadata(GetContentItemMetadataContext context) {
|
||||
var routable = context.ContentItem.As<IsRoutable>();
|
||||
if (routable != null) {
|
||||
context.Metadata.DisplayRouteValues = new RouteValueDictionary {
|
||||
{"Area", "Routable"},
|
||||
{"Controller", "Item"},
|
||||
{"Action", "Display"},
|
||||
{"Path", context.ContentItem.As<IsRoutable>().Record.Path}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Routing;
|
||||
|
||||
namespace Orchard.Core.Routable {
|
||||
public interface IRoutablePathConstraint : IRouteConstraint, ISingletonDependency {
|
||||
void SetPaths(IEnumerable<string> paths);
|
||||
string FindPath(string path);
|
||||
void AddPath(string path);
|
||||
void RemovePath(string path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Core.Common.Models;
|
||||
|
||||
namespace Orchard.Core.Routable.Models {
|
||||
public class IsRoutable : ContentPart<RoutableRecord>, IRoutableAspect {
|
||||
public string Title {
|
||||
get { return Record.Title; }
|
||||
set { Record.Title = value; }
|
||||
}
|
||||
|
||||
public string Slug {
|
||||
get { return Record.Slug; }
|
||||
set { Record.Slug = value; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
Name: Routable
|
||||
antiforgery: enabled
|
||||
author: The Orchard Team
|
||||
website: http://orchardproject.net
|
||||
version: 0.1
|
||||
orchardversion: 0.1.2010.0312
|
||||
description: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas consectetur consequat risus, vel blandit arcu tincidunt eget. Nam rutrum nulla vestibulum dolor dapibus sagittis. Vivamus convallis faucibus accumsan. Suspendisse sapien enim, cursus at dignissim a, sollicitudin sit amet est. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec sed urna magna, in luctus nulla. Pellentesque erat ipsum, convallis sed molestie tempus, mattis vel leo metus.
|
||||
features:
|
||||
Routable:
|
||||
Description: Routable content part.
|
||||
Category: Core2
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Orchard.Mvc.Routes;
|
||||
|
||||
namespace Orchard.Core.Routable {
|
||||
public class Routes : IRouteProvider {
|
||||
private readonly IRoutablePathConstraint _routablePathConstraint;
|
||||
|
||||
public Routes(IRoutablePathConstraint routablePathConstraint) {
|
||||
_routablePathConstraint = routablePathConstraint;
|
||||
}
|
||||
|
||||
public void GetRoutes(ICollection<RouteDescriptor> routes) {
|
||||
foreach (var routeDescriptor in GetRoutes())
|
||||
routes.Add(routeDescriptor);
|
||||
}
|
||||
|
||||
public IEnumerable<RouteDescriptor> GetRoutes() {
|
||||
return new[] {
|
||||
new RouteDescriptor {
|
||||
Priority = 10,
|
||||
Route = new Route(
|
||||
"{*path}",
|
||||
new RouteValueDictionary {
|
||||
{"area", "Routable"},
|
||||
{"controller", "Item"},
|
||||
{"action", "Display"}
|
||||
},
|
||||
new RouteValueDictionary {
|
||||
{"path", _routablePathConstraint}
|
||||
},
|
||||
new RouteValueDictionary {
|
||||
{"area", "Routable"}
|
||||
},
|
||||
new MvcRouteHandler())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Routing;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.Logging;
|
||||
|
||||
namespace Orchard.Core.Routable.Services {
|
||||
[UsedImplicitly]
|
||||
public class RoutablePathConstraint : IRoutablePathConstraint {
|
||||
/// <summary>
|
||||
/// Singleton object, per Orchard Shell instance. We need to protect concurrent access to the dictionary.
|
||||
/// </summary>
|
||||
private readonly object _syncLock = new object();
|
||||
private IDictionary<string, string> _paths = new Dictionary<string, string>();
|
||||
|
||||
public RoutablePathConstraint() {
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public void SetPaths(IEnumerable<string> paths) {
|
||||
// Make a copy to avoid performing potential lazy computation inside the lock
|
||||
var slugsArray = paths.ToArray();
|
||||
|
||||
lock (_syncLock) {
|
||||
_paths = slugsArray.Distinct(StringComparer.OrdinalIgnoreCase).ToDictionary(value => value, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
public string FindPath(string path) {
|
||||
lock (_syncLock) {
|
||||
string actual;
|
||||
return _paths.TryGetValue(path, out actual) ? actual : path;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddPath(string path) {
|
||||
lock (_syncLock) {
|
||||
_paths[path] = path;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemovePath(string path) {
|
||||
lock (_syncLock) {
|
||||
_paths.Remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
|
||||
if (routeDirection == RouteDirection.UrlGeneration)
|
||||
return true;
|
||||
|
||||
object value;
|
||||
if (values.TryGetValue(parameterName, out value)) {
|
||||
var parameterValue = Convert.ToString(value);
|
||||
|
||||
lock (_syncLock) {
|
||||
return _paths.ContainsKey(parameterValue);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Data;
|
||||
using Orchard.Environment;
|
||||
using Orchard.Tasks;
|
||||
|
||||
namespace Orchard.Core.Routable.Services {
|
||||
[UsedImplicitly]
|
||||
public class RoutablePathConstraintUpdator : IOrchardShellEvents, IBackgroundTask {
|
||||
private readonly IRoutablePathConstraint _pageSlugConstraint;
|
||||
private readonly IRepository<RoutableRecord> _repository;
|
||||
|
||||
public RoutablePathConstraintUpdator(IRoutablePathConstraint pageSlugConstraint, IRepository<RoutableRecord> repository) {
|
||||
_pageSlugConstraint = pageSlugConstraint;
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
void IOrchardShellEvents.Activated() {
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void IOrchardShellEvents.Terminating() {
|
||||
}
|
||||
|
||||
void IBackgroundTask.Sweep() {
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void Refresh() {
|
||||
var slugs = _repository.Fetch(r => r.ContentItemVersionRecord.Published && r.Path != "" && r.Path != null).Select(r => r.Path);
|
||||
|
||||
_pageSlugConstraint.SetPaths(slugs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Mvc.ViewModels;
|
||||
|
||||
namespace Orchard.Core.Routable.ViewModels {
|
||||
public class RoutableDisplayViewModel : BaseViewModel {
|
||||
public ContentItemViewModel<IRoutableAspect> Routable {get;set;}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage<Orchard.Core.Routable.ViewModels.RoutableDisplayViewModel>" %>
|
||||
<% Html.AddTitleParts(Model.Routable.Item.Title); %>
|
||||
<%=Html.DisplayForItem(m=>m.Routable) %>
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
<add path="*" verb="*"
|
||||
type="System.Web.HttpNotFoundHandler"/>
|
||||
</httpHandlers>
|
||||
|
||||
<!--
|
||||
Enabling request validation in view pages would cause validation to occur
|
||||
after the input has already been processed by the controller. By default
|
||||
MVC performs request validation before a controller processes the input.
|
||||
To change this behavior apply the ValidateInputAttribute to a
|
||||
controller or action.
|
||||
-->
|
||||
<pages
|
||||
validateRequest="false"
|
||||
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
|
||||
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
|
||||
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||
<controls>
|
||||
<add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
|
||||
</controls>
|
||||
</pages>
|
||||
</system.web>
|
||||
|
||||
<system.webServer>
|
||||
<validation validateIntegratedModeConfiguration="false"/>
|
||||
<handlers>
|
||||
<remove name="BlockViewHandler"/>
|
||||
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler"/>
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
@@ -1,13 +1,15 @@
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.Localization;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Core.Settings {
|
||||
public class AdminMenu : INavigationProvider {
|
||||
public Localizer T { get; set; }
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add("Site", "11",
|
||||
builder.Add(T("Site"), "11",
|
||||
menu => menu
|
||||
.Add("Manage Settings", "2.0", item => item.Action("Index", "Admin", new { area = "Settings" }).Permission(Permissions.ManageSettings)));
|
||||
.Add(T("Manage Settings"), "2.0", item => item.Action("Index", "Admin", new { area = "Settings" }).Permission(Permissions.ManageSettings)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Orchard.Core.Settings.Models;
|
||||
using Orchard.Core.Settings.ViewModels;
|
||||
using Orchard.Localization;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Localization.Services;
|
||||
using Orchard.Settings;
|
||||
using Orchard.UI.Notify;
|
||||
|
||||
@@ -10,10 +11,12 @@ namespace Orchard.Core.Settings.Controllers {
|
||||
[ValidateInput(false)]
|
||||
public class AdminController : Controller, IUpdateModel {
|
||||
private readonly ISiteService _siteService;
|
||||
private readonly ICultureManager _cultureManager;
|
||||
public IOrchardServices Services { get; private set; }
|
||||
|
||||
public AdminController(ISiteService siteService, IOrchardServices services) {
|
||||
public AdminController(ISiteService siteService, IOrchardServices services, ICultureManager cultureManager) {
|
||||
_siteService = siteService;
|
||||
_cultureManager = cultureManager;
|
||||
Services = services;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
@@ -25,7 +28,8 @@ namespace Orchard.Core.Settings.Controllers {
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var model = new SettingsIndexViewModel {
|
||||
Site = _siteService.GetSiteSettings().As<SiteSettings>()
|
||||
Site = _siteService.GetSiteSettings().As<SiteSettings>(),
|
||||
AvailableCultures = _cultureManager.ListCultures()
|
||||
};
|
||||
model.ViewModel = Services.ContentManager.BuildEditorModel(model.Site);
|
||||
return View(model);
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.ContentManagement.MetaData.Models;
|
||||
using Orchard.Core.Settings.Metadata.Records;
|
||||
using Orchard.Data;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
namespace Orchard.Core.Settings.Metadata {
|
||||
public class ContentDefinitionManager : Component, IContentDefinitionManager {
|
||||
private readonly IRepository<ContentTypeDefinitionRecord> _typeDefinitionRepository;
|
||||
private readonly IRepository<ContentPartDefinitionRecord> _partDefinitionRepository;
|
||||
private readonly IMapper<XElement, IDictionary<string, string>> _settingsReader;
|
||||
private readonly IMapper<IDictionary<string, string>, XElement> _settingsWriter;
|
||||
|
||||
public ContentDefinitionManager(
|
||||
IRepository<ContentTypeDefinitionRecord> typeDefinitionRepository,
|
||||
IRepository<ContentPartDefinitionRecord> partDefinitionRepository,
|
||||
IMapper<XElement, IDictionary<string, string>> settingsReader,
|
||||
IMapper<IDictionary<string, string>, XElement> settingsWriter) {
|
||||
_typeDefinitionRepository = typeDefinitionRepository;
|
||||
_partDefinitionRepository = partDefinitionRepository;
|
||||
_settingsReader = settingsReader;
|
||||
_settingsWriter = settingsWriter;
|
||||
}
|
||||
|
||||
public ContentTypeDefinition GetTypeDefinition(string name) {
|
||||
return _typeDefinitionRepository.Fetch(x => x.Name == name).Select(Build).SingleOrDefault();
|
||||
}
|
||||
|
||||
public ContentPartDefinition GetPartDefinition(string name) {
|
||||
return _partDefinitionRepository.Fetch(x => x.Name == name).Select(Build).SingleOrDefault();
|
||||
}
|
||||
|
||||
public IEnumerable<ContentTypeDefinition> ListTypeDefinitions() {
|
||||
return _typeDefinitionRepository.Fetch(x => !x.Hidden).Select(Build).ToReadOnlyCollection();
|
||||
}
|
||||
|
||||
public IEnumerable<ContentPartDefinition> ListPartDefinitions() {
|
||||
return _partDefinitionRepository.Fetch(x => !x.Hidden).Select(Build).ToReadOnlyCollection();
|
||||
}
|
||||
|
||||
public void StoreTypeDefinition(ContentTypeDefinition contentTypeDefinition) {
|
||||
Apply(contentTypeDefinition, Acquire(contentTypeDefinition));
|
||||
}
|
||||
|
||||
public void StorePartDefinition(ContentPartDefinition contentPartDefinition) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private ContentTypeDefinitionRecord Acquire(ContentTypeDefinition contentTypeDefinition) {
|
||||
var result = _typeDefinitionRepository.Fetch(x => x.Name == contentTypeDefinition.Name).SingleOrDefault();
|
||||
if (result == null) {
|
||||
result = new ContentTypeDefinitionRecord { Name = contentTypeDefinition.Name };
|
||||
_typeDefinitionRepository.Create(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private ContentPartDefinitionRecord Acquire(ContentPartDefinition contentPartDefinition) {
|
||||
var result = _partDefinitionRepository.Fetch(x => x.Name == contentPartDefinition.Name).SingleOrDefault();
|
||||
if (result == null) {
|
||||
result = new ContentPartDefinitionRecord { Name = contentPartDefinition.Name };
|
||||
_partDefinitionRepository.Create(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void Apply(ContentTypeDefinition model, ContentTypeDefinitionRecord record) {
|
||||
record.Settings = _settingsWriter.Map(model.Settings).ToString();
|
||||
|
||||
var toRemove = record.ContentTypePartDefinitionRecords
|
||||
.Where(partDefinitionRecord => !model.Parts.Any(part => partDefinitionRecord.ContentPartDefinitionRecord.Name == part.PartDefinition.Name))
|
||||
.ToList();
|
||||
|
||||
foreach (var remove in toRemove) {
|
||||
record.ContentTypePartDefinitionRecords.Remove(remove);
|
||||
}
|
||||
|
||||
foreach (var part in model.Parts) {
|
||||
var partName = part.PartDefinition.Name;
|
||||
var typePartRecord = record.ContentTypePartDefinitionRecords.SingleOrDefault(r => r.ContentPartDefinitionRecord.Name == partName);
|
||||
if (typePartRecord == null) {
|
||||
typePartRecord = new ContentTypePartDefinitionRecord { ContentPartDefinitionRecord = Acquire(part.PartDefinition) };
|
||||
record.ContentTypePartDefinitionRecords.Add(typePartRecord);
|
||||
}
|
||||
Apply(part, typePartRecord);
|
||||
}
|
||||
}
|
||||
|
||||
private void Apply(ContentTypeDefinition.Part model, ContentTypePartDefinitionRecord record) {
|
||||
record.Settings = Compose(_settingsWriter.Map(model.Settings));
|
||||
}
|
||||
|
||||
|
||||
|
||||
ContentTypeDefinition Build(ContentTypeDefinitionRecord source) {
|
||||
return new ContentTypeDefinition(
|
||||
source.Name,
|
||||
source.ContentTypePartDefinitionRecords.Select(Build),
|
||||
_settingsReader.Map(Parse(source.Settings)));
|
||||
}
|
||||
|
||||
ContentTypeDefinition.Part Build(ContentTypePartDefinitionRecord source) {
|
||||
return new ContentTypeDefinition.Part(
|
||||
Build(source.ContentPartDefinitionRecord),
|
||||
_settingsReader.Map(Parse(source.Settings)));
|
||||
}
|
||||
|
||||
ContentPartDefinition Build(ContentPartDefinitionRecord source) {
|
||||
return new ContentPartDefinition(
|
||||
source.Name,
|
||||
source.ContentPartFieldDefinitionRecords.Select(Build),
|
||||
_settingsReader.Map(Parse(source.Settings)));
|
||||
}
|
||||
|
||||
ContentPartDefinition.Field Build(ContentPartFieldDefinitionRecord source) {
|
||||
return new ContentPartDefinition.Field(
|
||||
Build(source.ContentFieldDefinitionRecord),
|
||||
source.Name,
|
||||
_settingsReader.Map(Parse(source.Settings)));
|
||||
}
|
||||
|
||||
ContentFieldDefinition Build(ContentFieldDefinitionRecord source) {
|
||||
return new ContentFieldDefinition(source.Name);
|
||||
}
|
||||
|
||||
XElement Parse(string settings) {
|
||||
if (string.IsNullOrEmpty(settings))
|
||||
return null;
|
||||
|
||||
try {
|
||||
return XElement.Parse(settings);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Error(ex, "Unable to parse settings xml");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
string Compose(XElement map) {
|
||||
if (map == null)
|
||||
return null;
|
||||
|
||||
return map.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Orchard.Core.Settings.Metadata.Records {
|
||||
public class ContentFieldDefinitionRecord {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual string Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Data.Conventions;
|
||||
|
||||
namespace Orchard.Core.Settings.Metadata.Records {
|
||||
public class ContentPartDefinitionRecord {
|
||||
public ContentPartDefinitionRecord() {
|
||||
ContentPartFieldDefinitionRecords = new List<ContentPartFieldDefinitionRecord>();
|
||||
}
|
||||
|
||||
public virtual int Id { get; set; }
|
||||
public virtual string Name { get; set; }
|
||||
public virtual bool Hidden { get; set; }
|
||||
public virtual string Settings { get; set; }
|
||||
|
||||
[CascadeAllDeleteOrphan]
|
||||
public virtual IList<ContentPartFieldDefinitionRecord> ContentPartFieldDefinitionRecords { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Orchard.Core.Settings.Metadata.Records {
|
||||
public class ContentPartFieldDefinitionRecord {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual ContentFieldDefinitionRecord ContentFieldDefinitionRecord { get; set; }
|
||||
public virtual string Name { get; set; }
|
||||
public virtual string Settings { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Data.Conventions;
|
||||
|
||||
namespace Orchard.Core.Settings.Metadata.Records {
|
||||
public class ContentTypeDefinitionRecord {
|
||||
public ContentTypeDefinitionRecord() {
|
||||
ContentTypePartDefinitionRecords = new List<ContentTypePartDefinitionRecord>();
|
||||
}
|
||||
|
||||
public virtual int Id { get; set; }
|
||||
public virtual string Name { get; set; }
|
||||
public virtual bool Hidden { get; set; }
|
||||
public virtual string Settings { get; set; }
|
||||
|
||||
[CascadeAllDeleteOrphan]
|
||||
public virtual IList<ContentTypePartDefinitionRecord> ContentTypePartDefinitionRecords { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Orchard.Core.Settings.Metadata.Records {
|
||||
public class ContentTypePartDefinitionRecord {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual ContentPartDefinitionRecord ContentPartDefinitionRecord { get; set; }
|
||||
public virtual string Settings { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Web.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Mvc.ViewModels;
|
||||
using Orchard.Core.Settings.Models;
|
||||
@@ -6,6 +7,7 @@ using Orchard.Core.Settings.Models;
|
||||
namespace Orchard.Core.Settings.ViewModels {
|
||||
public class SettingsIndexViewModel : BaseViewModel {
|
||||
public SiteSettings Site { get; set; }
|
||||
public IEnumerable<string> AvailableCultures { get; set; }
|
||||
public ContentItemViewModel ViewModel { get; set; }
|
||||
|
||||
|
||||
@@ -20,12 +22,16 @@ namespace Orchard.Core.Settings.ViewModels {
|
||||
set { Site.As<SiteSettings>().Record.PageTitleSeparator = value; }
|
||||
}
|
||||
|
||||
public string SiteName
|
||||
{
|
||||
public string SiteName {
|
||||
get { return Site.As<SiteSettings>().Record.SiteName; }
|
||||
set { Site.As<SiteSettings>().Record.SiteName = value; }
|
||||
}
|
||||
|
||||
public string SiteCulture {
|
||||
get { return Site.As<SiteSettings>().Record.SiteCulture; }
|
||||
set { Site.As<SiteSettings>().Record.SiteCulture = value; }
|
||||
}
|
||||
|
||||
public string SuperUser {
|
||||
get { return Site.As<SiteSettings>().Record.SuperUser; }
|
||||
set { Site.As<SiteSettings>().Record.SuperUser = value; }
|
||||
|
||||
@@ -10,6 +10,11 @@
|
||||
<%: Html.EditorFor(m => m.SiteName)%>
|
||||
<%: Html.ValidationMessage("SiteName", "*") %>
|
||||
</div>
|
||||
<div>
|
||||
<label for="SiteCulture"><%:T("Default Site Culture") %></label>
|
||||
<%=Html.DropDownList("SiteCulture", new SelectList(Model.AvailableCultures, Model.SiteCulture)) %>
|
||||
<%=Html.ValidationMessage("SiteCulture", "*") %>
|
||||
</div>
|
||||
<div>
|
||||
<label for="PageTitleSeparator"><%: T("Page title separator") %></label>
|
||||
<%: Html.EditorFor(x => x.PageTitleSeparator)%>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Linq;
|
||||
using Orchard.Blogs.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Blogs {
|
||||
@@ -10,10 +11,12 @@ namespace Orchard.Blogs {
|
||||
_blogService = blogService;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add("Blogs", "2", BuildMenu);
|
||||
builder.Add(T("Blogs"), "2", BuildMenu);
|
||||
}
|
||||
|
||||
private void BuildMenu(NavigationItemBuilder menu) {
|
||||
@@ -22,20 +25,20 @@ namespace Orchard.Blogs {
|
||||
var singleBlog = blogCount == 1 ? blogs.ElementAt(0) : null;
|
||||
|
||||
if (blogCount > 0 && singleBlog == null)
|
||||
menu.Add("Manage Blogs", "1.0",
|
||||
menu.Add(T("Manage Blogs"), "1.0",
|
||||
item =>
|
||||
item.Action("List", "BlogAdmin", new {area = "Orchard.Blogs"}).Permission(Permissions.MetaListBlogs));
|
||||
else if (singleBlog != null)
|
||||
menu.Add("Manage Blog", "1.0",
|
||||
menu.Add(T("Manage Blog"), "1.0",
|
||||
item =>
|
||||
item.Action("Item", "BlogAdmin", new {area = "Orchard.Blogs", blogSlug = singleBlog.Slug}).Permission(Permissions.MetaListBlogs));
|
||||
|
||||
menu.Add("Add New Blog", "1.1",
|
||||
menu.Add(T("Add New Blog"), "1.1",
|
||||
item =>
|
||||
item.Action("Create", "BlogAdmin", new {area = "Orchard.Blogs"}).Permission(Permissions.ManageBlogs));
|
||||
|
||||
if (singleBlog != null)
|
||||
menu.Add("Add New Post", "1.2",
|
||||
menu.Add(T("Add New Post"), "1.2",
|
||||
item =>
|
||||
item.Action("Create", "BlogPostAdmin", new {area = "Orchard.Blogs", blogSlug = singleBlog.Slug}).Permission(Permissions.PublishBlogPost));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.Blogs.Models;
|
||||
using Orchard.Commands;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Navigation.Models;
|
||||
using Orchard.Security;
|
||||
using System.IO;
|
||||
using Orchard.Blogs.Services;
|
||||
using Orchard.Core.Navigation.Services;
|
||||
|
||||
namespace Orchard.Blogs.Commands {
|
||||
public class BlogCommands : DefaultOrchardCommandHandler {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IMembershipService _membershipService;
|
||||
private readonly IBlogService _blogService;
|
||||
private readonly IMenuService _menuService;
|
||||
|
||||
public BlogCommands(
|
||||
IContentManager contentManager,
|
||||
IMembershipService membershipService,
|
||||
IBlogService blogService,
|
||||
IMenuService menuService) {
|
||||
_contentManager = contentManager;
|
||||
_membershipService = membershipService;
|
||||
_blogService = blogService;
|
||||
_menuService = menuService;
|
||||
}
|
||||
|
||||
[OrchardSwitch]
|
||||
public string FeedUrl { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Slug { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Title { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string MenuText { get; set; }
|
||||
|
||||
[CommandName("blog create")]
|
||||
[CommandHelp("blog create /Slug:<slug> /Title:<title> [/MenuText:<menu text>]\r\n\t" + "Creates a new Blog")]
|
||||
[OrchardSwitches("Slug,Title,MenuText")]
|
||||
public string Create() {
|
||||
var admin = _membershipService.GetUser("admin");
|
||||
|
||||
if(!IsSlugValid(Slug)) {
|
||||
return "Invalid Slug provided. Blog creation failed.";
|
||||
}
|
||||
|
||||
var blog = _contentManager.New("blog");
|
||||
blog.As<ICommonAspect>().Owner = admin;
|
||||
blog.As<RoutableAspect>().Slug = Slug;
|
||||
blog.As<RoutableAspect>().Title = Title;
|
||||
if ( !String.IsNullOrWhiteSpace(MenuText) ) {
|
||||
blog.As<MenuPart>().OnMainMenu = true;
|
||||
blog.As<MenuPart>().MenuPosition = _menuService.Get().Select(menuPart => menuPart.MenuPosition).Max() + 1 + ".0";
|
||||
blog.As<MenuPart>().MenuText = MenuText;
|
||||
}
|
||||
_contentManager.Create(blog);
|
||||
|
||||
return "Blog created successfully";
|
||||
}
|
||||
|
||||
[CommandName("blog import")]
|
||||
[CommandHelp("blog import /Slug:<slug> /FeedUrl:<feed url>\r\n\t" + "Import all items from <feed url> into the blog at the specified <slug>")]
|
||||
[OrchardSwitches("FeedUrl,Slug")]
|
||||
public string Import() {
|
||||
var admin = _membershipService.GetUser("admin");
|
||||
|
||||
XDocument doc;
|
||||
|
||||
try {
|
||||
Context.Output.WriteLine("Loading feed...");
|
||||
doc = XDocument.Load(FeedUrl);
|
||||
Context.Output.WriteLine("Found {0} items", doc.Descendants("item").Count());
|
||||
}
|
||||
catch ( Exception ex ) {
|
||||
Context.Output.WriteLine(T("An error occured while loading the file: " + ex.Message));
|
||||
return "Import terminated.";
|
||||
}
|
||||
|
||||
var blog = _blogService.Get(Slug);
|
||||
|
||||
if ( blog == null ) {
|
||||
return "Blog not found at specified slug: " + Slug;
|
||||
}
|
||||
|
||||
foreach ( var item in doc.Descendants("item") ) {
|
||||
string postName = item.Element("title").Value;
|
||||
|
||||
Context.Output.WriteLine("Adding post: {0}...", postName.Substring(0, Math.Min(postName.Length, 40)));
|
||||
var post = _contentManager.New("blogpost");
|
||||
post.As<ICommonAspect>().Owner = admin;
|
||||
post.As<ICommonAspect>().Container = blog;
|
||||
post.As<RoutableAspect>().Slug = Slugify(postName);
|
||||
post.As<RoutableAspect>().Title = postName;
|
||||
post.As<BodyAspect>().Text = item.Element("description").Value;
|
||||
_contentManager.Create(post);
|
||||
}
|
||||
|
||||
|
||||
return "Import feed completed.";
|
||||
}
|
||||
|
||||
private static string Slugify(string slug) {
|
||||
var dissallowed = new Regex(@"[/:?#\[\]@!$&'()*+,;=\s]+");
|
||||
|
||||
slug = dissallowed.Replace(slug, "-");
|
||||
slug = slug.Trim('-');
|
||||
|
||||
if ( slug.Length > 1000 )
|
||||
slug = slug.Substring(0, 1000);
|
||||
|
||||
return slug.ToLowerInvariant();
|
||||
}
|
||||
|
||||
private static bool IsSlugValid(string slug) {
|
||||
// see http://tools.ietf.org/html/rfc3987 for prohibited chars
|
||||
return slug == null || String.IsNullOrEmpty(slug.Trim()) || Regex.IsMatch(slug, @"^[^/:?#\[\]@!$&'()*+,;=\s]+$");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ namespace Orchard.Blogs.Models {
|
||||
|
||||
public string Name {
|
||||
get { return this.As<RoutableAspect>().Title; }
|
||||
set { this.As<RoutableAspect>().Title = value; }
|
||||
}
|
||||
|
||||
//TODO: (erikpo) Need a data type for slug
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Commands\BlogCommands.cs" />
|
||||
<Compile Include="Controllers\BlogAdminController.cs" />
|
||||
<Compile Include="Drivers\BlogDriver.cs" />
|
||||
<Compile Include="Controllers\BlogPostAdminController.cs" />
|
||||
@@ -122,6 +123,7 @@
|
||||
<Content Include="Scripts\jquery.ui.widget.js" />
|
||||
<Content Include="Scripts\jquery.utils.js" />
|
||||
<Content Include="Scripts\ui.timepickr.js" />
|
||||
<Content Include="Styles\admin.css" />
|
||||
<Content Include="Styles\archives.css" />
|
||||
<Content Include="Styles\datetime.css" />
|
||||
<Content Include="Styles\images\ui-bg_flat_0_aaaaaa_40x100.png" />
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.blogdescription {
|
||||
margin-top:1em;
|
||||
}
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
<%@ Import Namespace="Orchard.Mvc.ViewModels"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
<h1 class="withActions">
|
||||
<a href="<%=Url.BlogForAdmin(Model.Item.Slug) %>"><%: Html.TitleForPage(Model.Item.Name) %></a>
|
||||
<h1><a href="<%=Url.BlogForAdmin(Model.Item.Slug) %>"><%: Html.TitleForPage(Model.Item.Name) %></a>
|
||||
|
||||
</h1>
|
||||
<% Html.Zone("manage"); %><%--
|
||||
<form>
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
<a href="<%=Url.BlogPostCreate(Model.Item) %>" title="<%: T("New Post") %>"><%: T("New Post") %></a><%: T(" | ")%>
|
||||
<a href="<%=Url.BlogEdit(Model.Item.Slug) %>" title="<%: T("Settings") %>"><%: T("Settings") %></a><%: T(" | ")%>
|
||||
<%-- todo: (heskew) this is waaaaa too verbose. need template helpers for all ibuttons --%>
|
||||
<% using (Html.BeginFormAntiForgeryPost(Url.BlogDelete(Model.Item.Slug), FormMethod.Post, new { @class = "inline" })) { %>
|
||||
<% using (Html.BeginFormAntiForgeryPost(Url.BlogDelete(Model.Item.Slug), FormMethod.Post, new { @class = "inline link" })) { %>
|
||||
<button type="submit" class="linkButton" title="<%: T("Remove") %>"><%: T("Remove") %></button><%
|
||||
} %>
|
||||
</div>
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
<%@ Import Namespace="Orchard.Blogs"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%><%
|
||||
Html.RegisterStyle("admin.css");
|
||||
if (AuthorizedFor(Permissions.ManageBlogs)) { %>
|
||||
<div class="folderProperties">
|
||||
<p><a href="<%=Url.BlogEdit(Model.Slug) %>" class="edit"><%: T("Edit") %></a></p>
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.Localization;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Comments {
|
||||
public class AdminMenu : INavigationProvider {
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add("Comments", "3",
|
||||
builder.Add(T("Comments"), "3",
|
||||
menu => menu
|
||||
.Add("Manage Comments", "1.0", item => item.Action("Index", "Admin", new { area = "Orchard.Comments" }).Permission(Permissions.ManageComments))
|
||||
.Add(T("Manage Comments"), "1.0", item => item.Action("Index", "Admin", new { area = "Orchard.Comments" }).Permission(Permissions.ManageComments))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.DevTools.ViewModels;
|
||||
|
||||
namespace Orchard.DevTools.Controllers {
|
||||
[ValidateInput(false)]
|
||||
public class MetadataController : Controller {
|
||||
private readonly IContentDefinitionManager _contentDefinitionManager;
|
||||
private readonly IContentDefinitionWriter _contentDefinitionWriter;
|
||||
private readonly IContentDefinitionReader _contentDefinitionReader;
|
||||
|
||||
public MetadataController(
|
||||
IContentDefinitionManager contentDefinitionManager,
|
||||
IContentDefinitionWriter contentDefinitionWriter,
|
||||
IContentDefinitionReader contentDefinitionReader) {
|
||||
_contentDefinitionManager = contentDefinitionManager;
|
||||
_contentDefinitionWriter = contentDefinitionWriter;
|
||||
_contentDefinitionReader = contentDefinitionReader;
|
||||
}
|
||||
|
||||
public ActionResult Index() {
|
||||
var model = new MetadataIndexViewModel {
|
||||
TypeDefinitions = _contentDefinitionManager.ListTypeDefinitions(),
|
||||
PartDefinitions = _contentDefinitionManager.ListPartDefinitions()
|
||||
};
|
||||
var types = new XElement("Types");
|
||||
foreach (var type in model.TypeDefinitions) {
|
||||
types.Add(_contentDefinitionWriter.Export(type));
|
||||
}
|
||||
|
||||
var parts = new XElement("Parts");
|
||||
foreach (var part in model.PartDefinitions) {
|
||||
parts.Add(_contentDefinitionWriter.Export(part));
|
||||
}
|
||||
|
||||
var stringWriter = new StringWriter();
|
||||
using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true, IndentChars = " " })) {
|
||||
if (xmlWriter != null) {
|
||||
new XElement("Orchard", types, parts).WriteTo(xmlWriter);
|
||||
}
|
||||
}
|
||||
model.ExportText = stringWriter.ToString();
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Index(MetadataIndexViewModel model) {
|
||||
var root = XElement.Parse(model.ExportText);
|
||||
foreach (var element in root.Elements("Types").Elements()) {
|
||||
var typeElement = element;
|
||||
var typeName = XmlConvert.DecodeName(element.Name.LocalName);
|
||||
_contentDefinitionManager.AlterTypeDefinition(typeName, alteration => _contentDefinitionReader.Merge(typeElement, alteration));
|
||||
}
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,7 @@
|
||||
<Compile Include="Commands\ProfilingCommands.cs" />
|
||||
<Compile Include="Controllers\ContentController.cs" />
|
||||
<Compile Include="Controllers\HomeController.cs" />
|
||||
<Compile Include="Controllers\MetadataController.cs" />
|
||||
<Compile Include="Handlers\DebugLinkHandler.cs" />
|
||||
<Compile Include="Models\ShowDebugLink.cs" />
|
||||
<Compile Include="Models\Simple.cs" />
|
||||
@@ -80,6 +81,7 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ViewModels\ContentIndexViewModel.cs" />
|
||||
<Compile Include="ViewModels\ContentDetailsViewModel.cs" />
|
||||
<Compile Include="ViewModels\MetadataIndexViewModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Module.txt" />
|
||||
@@ -90,6 +92,7 @@
|
||||
<Content Include="Views\Home\Index.aspx" />
|
||||
<Content Include="Views\DisplayTemplates\Parts\DevTools.ShowDebugLink.ascx" />
|
||||
<Content Include="Views\EditorTemplates\Parts\DevTools.ShowDebugLink.ascx" />
|
||||
<Content Include="Views\Metadata\Index.aspx" />
|
||||
<Content Include="Web.config" />
|
||||
<Content Include="Views\Web.config" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement.MetaData.Models;
|
||||
using Orchard.Mvc.ViewModels;
|
||||
|
||||
namespace Orchard.DevTools.ViewModels {
|
||||
public class MetadataIndexViewModel : BaseViewModel {
|
||||
public IEnumerable<ContentTypeDefinition> TypeDefinitions { get; set; }
|
||||
public IEnumerable<ContentPartDefinition> PartDefinitions { get; set; }
|
||||
public string ExportText { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,4 +2,7 @@
|
||||
<%@ Import Namespace="Orchard.Mvc.ViewModels"%>
|
||||
<h1><%: Html.TitleForPage(T("Dev Tools").ToString()) %></h1>
|
||||
<p><%: Html.ActionLink(T("Contents").ToString(), "Index", "Content") %></p>
|
||||
|
||||
<p><%: Html.ActionLink(T("Metadata").ToString(), "Index", "Metadata") %></p>
|
||||
<p><%: Html.ActionLink(T("Test Unauthorized Request").ToString(), "NotAuthorized", "Home")%></p>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user