Remove Routable module

--HG--
branch : autoroute
This commit is contained in:
randompete
2011-12-29 14:36:57 +00:00
parent 7c944ccf18
commit ae27eb1e21
27 changed files with 0 additions and 1345 deletions

View File

@@ -1,315 +0,0 @@
using System;
using System.Collections.Generic;
using Autofac;
using JetBrains.Annotations;
using Moq;
using NUnit.Framework;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.Records;
using Orchard.Core.Common.Models;
using Orchard.Core.Routable;
using Orchard.Core.Routable.Handlers;
using Orchard.Core.Routable.Models;
using Orchard.Core.Routable.Services;
using Orchard.Data;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment;
using Orchard.Environment.Extensions;
using Orchard.Mvc;
using Orchard.Security;
using Orchard.Tests.Modules;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.Tests.Stubs;
using Orchard.UI.Notify;
namespace Orchard.Core.Tests.Routable.Services {
// TODO: (PH)
[TestFixture]
public class RoutableServiceTests : DatabaseEnabledTestsBase {
[SetUp]
public override void Init() {
base.Init();
_routableService = _container.Resolve<IRoutableService>();
_contentManager = _container.Resolve<IContentManager>();
}
public override void Register(ContainerBuilder builder) {
builder.RegisterType<DefaultContentManager>().As<IContentManager>();
builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
builder.RegisterInstance(new Mock<IContentDefinitionManager>().Object);
builder.RegisterInstance(new Mock<ITransactionManager>().Object);
builder.RegisterInstance(new Mock<IAuthorizer>().Object);
builder.RegisterInstance(new Mock<INotifier>().Object);
builder.RegisterInstance(new Mock<IContentDisplay>().Object);
builder.RegisterType<StubHttpContextAccessor>().As<IHttpContextAccessor>();
builder.RegisterType<WorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<OrchardServices>().As<IOrchardServices>();
builder.RegisterType<ThingHandler>().As<IContentHandler>();
builder.RegisterType<StuffHandler>().As<IContentHandler>();
builder.RegisterType<RoutableService>().As<IRoutableService>();
builder.RegisterType<RoutablePathConstraint>().As<IRoutablePathConstraint>();
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<ShapeTableLocator>().As<IShapeTableLocator>();
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<DefaultContentQuery>().As<IContentQuery>();
builder.RegisterInstance(new UrlHelper(new RequestContext(new StubHttpContext("~/"), new RouteData()))).As<UrlHelper>();
builder.RegisterType<RoutePartHandler>().As<IContentHandler>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
builder.RegisterType<DefaultContentDisplay>().As<IContentDisplay>();
}
private IRoutableService _routableService;
private IContentManager _contentManager;
[Test]
public void InvalidCharactersShouldBeReplacedByADash() {
var thing = _contentManager.Create<Thing>("thing", t => {
t.As<RoutePart>().Record = new RoutePartRecord();
t.Title = "Please do not use any of the following characters in your permalink: \":\", \"?\", \"#\", \"[\", \"]\", \"@\", \"!\", \"$\", \"&\", \"'\", \"(\", \")\", \"*\", \"+\", \",\", \";\", \"=\", \"\"\", \"<\", \">\", \"\\\"";
});
_routableService.FillSlugFromTitle(thing.As<RoutePart>());
Assert.That(thing.Slug, Is.EqualTo("please-do-not-use-any-of-the-following-characters-in-your-permalink"));
}
[Test]
public void SpacesSlugShouldBeTreatedAsEmpty() {
var contentManager = _container.Resolve<IContentManager>();
var thing = contentManager.Create<Thing>("thing", t => {
t.As<RoutePart>().Record = new RoutePartRecord();
t.Title = "My Title";
t.Slug = " ";
});
_routableService.FillSlugFromTitle(thing.As<RoutePart>());
Assert.That(thing.Slug, Is.EqualTo("my-title"));
}
[Test]
public void SlashInSlugIsAllowed() {
Assert.That(_routableService.IsSlugValid("some/page"), Is.True);
}
[Test]
public void DotsAroundSlugAreAllowed() {
Assert.That(_routableService.IsSlugValid(".slug"), Is.False);
Assert.That(_routableService.IsSlugValid("slug."), Is.False);
Assert.That(_routableService.IsSlugValid("slug.slug"), Is.True);
}
[Test]
public void EmptySlugsShouldBeConsideredValid() {
// so that automatic generation on Publish occurs
Assert.That(_routableService.IsSlugValid(null), Is.True);
Assert.That(_routableService.IsSlugValid(String.Empty), Is.True);
Assert.That(_routableService.IsSlugValid(" "), Is.True);
}
[Test]
public void InvalidCharacterShouldBeRefusedInSlugs() {
Assert.That(_routableService.IsSlugValid("aaaa-_aaaa"), Is.True);
foreach (var c in @":?#[]@!$&'()*+,;= \") {
Assert.That(_routableService.IsSlugValid("a" + c + "b"), Is.False);
}
}
[Test]
public void VeryLongStringTruncatedTo1000Chars() {
var veryVeryLongTitle = "this is a very long title...";
for (var i = 0; i < 100; i++)
veryVeryLongTitle += "aaaaaaaaaa";
var thing = CreateRoutePartFromScratch(veryVeryLongTitle);
_routableService.FillSlugFromTitle(thing);
Assert.That(veryVeryLongTitle.Length, Is.AtLeast(1001));
Assert.That(thing.Slug.Length, Is.EqualTo(1000));
}
[Test]
public void NoExistingLikeSlugsGeneratesSameSlug() {
string slug = _routableService.GenerateUniqueSlug(CreateRoutePartFromScratch("woohoo"), null);
Assert.That(slug, Is.EqualTo("woohoo"));
}
[Test]
public void ExistingSingleLikeSlugThatsAConflictGeneratesADash2() {
string slug = _routableService.GenerateUniqueSlug(CreateRoutePartFromScratch("woohoo"), new List<string> { "woohoo" });
Assert.That(slug, Is.EqualTo("woohoo-2"));
}
[Test]
public void ExistingSingleLikeSlugThatsNotAConflictGeneratesSameSlug() {
string slug = _routableService.GenerateUniqueSlug(CreateRoutePartFromScratch("woohoo"), new List<string> { "woohoo-2" });
Assert.That(slug, Is.EqualTo("woohoo"));
}
[Test]
public void ExistingLikeSlugsWithAConflictGeneratesADashVNext() {
string slug = _routableService.GenerateUniqueSlug(CreateRoutePartFromScratch("woohoo"), new List<string> { "woohoo", "woohoo-2" });
Assert.That(slug, Is.EqualTo("woohoo-3"));
}
[Test]
public void ExistingSlugsWithVersionGapsAndNoMatchGeneratesSameSlug() {
string slug = _routableService.GenerateUniqueSlug(CreateRoutePartFromScratch("woohoo"), new List<string> { "woohoo-2", "woohoo-4", "woohoo-5" });
Assert.That(slug, Is.EqualTo("woohoo"));
}
[Test]
public void ExistingSlugsWithVersionGapsAndAMatchGeneratesADash2() {
string slug = _routableService.GenerateUniqueSlug(CreateRoutePartFromScratch("woohoo-2"), new List<string> { "woohoo-2", "woohoo-4", "woohoo-5" });
Assert.That(slug, Is.EqualTo("woohoo-2-2"));
}
[Test]
public void SlugIsGeneratedLowerCased() {
var thing = CreateRoutePartFromScratch("This Is Some Interesting Title");
_routableService.FillSlugFromTitle(thing);
Assert.That(thing.Slug, Is.EqualTo("this-is-some-interesting-title"));
}
[Test]
public void SlugInConflictWithAnExistingItemsPathIsVersioned() {
CreateRoutePartFromScratch("bar", "bar", "foo");
_contentManager.Flush();
var thing2 = CreateRoutePartFromScratch("fooslashbar", "foo/bar");
Assert.That(thing2.Path, Is.EqualTo("foo/bar-2"));
}
[Test]
public void GeneratedSlugInConflictInSameContaierPathIsVersioned() {
var thing1 = CreateRoutePartFromScratch("Foo", "", "bar");
_contentManager.Flush();
var thing2 = CreateRoutePartWithExistingContainer("Foo", thing1.As<ICommonPart>().Container);
Assert.That(thing2.Path, Is.EqualTo("bar/foo-2"));
Assert.That(thing2.Slug, Is.EqualTo("foo-2"));
}
[Test]
public void GivenSlugInConflictInSameContaierPathIsVersioned() {
var thing1 = CreateRoutePartFromScratch("Hi", "foo", "bar");
_contentManager.Flush();
var thing2 = CreateRoutePartWithExistingContainer("There", thing1.As<ICommonPart>().Container, "foo");
Assert.That(thing2.Path, Is.EqualTo("bar/foo-2"));
Assert.That(thing2.Slug, Is.EqualTo("foo-2"));
}
[Test]
public void GeneratedSlugInConflictInDifferentContaierPathIsNotVersioned() {
var thing1 = CreateRoutePartFromScratch("Foo", "", "rab");
var thing2 = CreateRoutePartFromScratch("Foo", "", "bar");
Assert.That(thing1.Path, Is.EqualTo("rab/foo"));
Assert.That(thing2.Path, Is.EqualTo("bar/foo"));
Assert.That(thing1.Slug, Is.EqualTo("foo"));
Assert.That(thing2.Slug, Is.EqualTo("foo"));
}
private RoutePart CreateRoutePartWithExistingContainer(string title, IContent container, string slug = "") {
var contentManager = _container.Resolve<IContentManager>();
return contentManager.Create<Thing>("thing", t => {
t.As<RoutePart>().Record = new RoutePartRecord();
t.Title = title;
if (!string.IsNullOrWhiteSpace(slug))
t.As<RoutePart>().Slug = slug;
if (container != null)
t.As<ICommonPart>().Container = container;
}).As<RoutePart>();
}
private RoutePart CreateRoutePartFromScratch(string title, string slug = "", string containerPath = "") {
var contentManager = _container.Resolve<IContentManager>();
return contentManager.Create<Thing>("thing", t => {
t.As<RoutePart>().Record = new RoutePartRecord();
if (!string.IsNullOrWhiteSpace(slug))
t.As<RoutePart>().Slug = slug;
t.Title = title;
if (!string.IsNullOrWhiteSpace(containerPath)) {
t.As<ICommonPart>().Container = contentManager.Create<Thing>("thing", tt => {
tt.As<RoutePart>().Path = containerPath;
tt.As<RoutePart>().Slug = containerPath;
tt.As<RoutePart>().Title = "Test Container";
});
}
}).As<RoutePart>();
}
protected override IEnumerable<Type> DatabaseTypes {
get {
return new[] {
typeof(RoutePartRecord),
typeof(ContentTypeRecord),
typeof(ContentItemRecord),
typeof(ContentItemVersionRecord),
typeof(CommonPartRecord),
typeof(CommonPartVersionRecord),
};
}
}
[UsedImplicitly]
public class ThingHandler : ContentHandler {
public ThingHandler() {
Filters.Add(new ActivatingFilter<Thing>("thing"));
Filters.Add(new ActivatingFilter<ContentPart<CommonPartVersionRecord>>("thing"));
Filters.Add(new ActivatingFilter<CommonPart>("thing"));
Filters.Add(new ActivatingFilter<RoutePart>("thing"));
}
}
public class Thing : ContentPart {
public string Title {
get { return this.As<RoutePart>().Title; }
set { this.As<RoutePart>().Title = value; }
}
public string Slug {
get { return this.As<RoutePart>().Slug; }
set { this.As<RoutePart>().Slug = value; }
}
}
[UsedImplicitly]
public class StuffHandler : ContentHandler {
public StuffHandler() {
Filters.Add(new ActivatingFilter<Stuff>("stuff"));
Filters.Add(new ActivatingFilter<ContentPart<CommonPartVersionRecord>>("stuff"));
Filters.Add(new ActivatingFilter<CommonPart>("stuff"));
Filters.Add(new ActivatingFilter<RoutePart>("stuff"));
}
}
public class Stuff : ContentPart {
public string Title {
get { return this.As<RoutePart>().Title; }
set { this.As<RoutePart>().Title = value; }
}
public string Slug {
get { return this.As<RoutePart>().Slug; }
set { this.As<RoutePart>().Slug = value; }
}
}
}
}

View File

@@ -130,8 +130,6 @@
<Compile Include="Navigation\Services\AdminMenuNavigationProvider.cs" />
<Compile Include="Navigation\Services\MainMenuNavigationProvider.cs" />
<Compile Include="Navigation\Settings\AdminMenuPartTypeSettings.cs" />
<Compile Include="Routable\Events\ISlugEventHandler.cs" />
<Compile Include="Routable\ResourceManifest.cs" />
<Compile Include="Contents\ViewModels\ListContentsViewModel.cs" />
<Compile Include="Contents\ViewModels\ListContentTypesViewModel.cs" />
<Compile Include="Reports\AdminMenu.cs" />
@@ -140,12 +138,6 @@
<Compile Include="Navigation\Migrations.cs" />
<Compile Include="Reports\ViewModels\DisplayReportViewModel.cs" />
<Compile Include="Reports\ViewModels\ReportsAdminIndexViewModel.cs" />
<Compile Include="Routable\Controllers\ItemController.cs" />
<Compile Include="Routable\Migrations.cs" />
<Compile Include="Routable\Drivers\RoutePartDriver.cs" />
<Compile Include="Routable\Handlers\RoutePartHandler.cs" />
<Compile Include="Routable\IRoutablePathConstraint.cs" />
<Compile Include="Routable\Models\RoutePart.cs" />
<Compile Include="Common\Utilities\LazyField.cs" />
<Compile Include="Common\Handlers\CommonPartHandler.cs" />
<Compile Include="Common\Models\CommonPart.cs" />
@@ -197,14 +189,6 @@
<Compile Include="Navigation\ViewModels\MenuItemEntry.cs" />
<Compile Include="Navigation\ViewModels\NavigationManagementViewModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Routable\Models\RoutePartRecord.cs" />
<Compile Include="Routable\Routes.cs" />
<Compile Include="Routable\Services\IRoutableService.cs" />
<Compile Include="Routable\Services\RoutablePathConstraint.cs" />
<Compile Include="Routable\Services\RoutablePathConstraintUpdator.cs" />
<Compile Include="Routable\Services\RoutableService.cs" />
<Compile Include="Routable\ViewModels\RoutableEditorViewModel.cs" />
<Compile Include="Routable\ViewModels\RoutableDisplayViewModel.cs" />
<Compile Include="Scheduling\Migrations.cs" />
<Compile Include="Scheduling\Models\ScheduledTaskRecord.cs" />
<Compile Include="Scheduling\Services\ScheduledTaskManager.cs" />
@@ -294,9 +278,6 @@
<Content Include="Reports\Styles\menu.reports-admin.css" />
<Content Include="Reports\Views\Admin\Display.cshtml" />
<Content Include="Reports\Views\Admin\Index.cshtml" />
<Content Include="Routable\Module.txt" />
<Content Include="Routable\Scripts\jquery.slugify.js" />
<Content Include="Routable\Views\EditorTemplates\Parts.Routable.RoutePart.cshtml" />
<Content Include="Settings\Module.txt" />
<Content Include="Settings\Styles\admin.css" />
<Content Include="Settings\Styles\images\menu.settings.png" />
@@ -304,9 +285,6 @@
<Content Include="Settings\Views\Admin\Index.cshtml" />
<Content Include="Settings\Views\Admin\Culture.cshtml" />
<Content Include="Contents\Views\Content.Edit.cshtml" />
<Content Include="Routable\Placement.info">
<SubType>Designer</SubType>
</Content>
<Content Include="Settings\Placement.info" />
<Content Include="Settings\Views\DisplayTemplates\CurrentCulture.cshtml" />
<Content Include="Settings\Views\DisplayTemplates\RemovableCulture.cshtml" />
@@ -369,7 +347,6 @@
</ItemGroup>
<ItemGroup>
<Content Include="Contents\Views\Web.config" />
<Content Include="Routable\Views\Web.config" />
<Content Include="Reports\Views\Web.config" />
<Content Include="Contents\Views\Content.cshtml" />
<Content Include="Contents\Views\Content.SummaryAdmin.cshtml" />
@@ -387,7 +364,6 @@
</Content>
<Content Include="Contents\Views\Content.ControlWrapper.cshtml" />
<Content Include="Navigation\Placement.info" />
<Content Include="Routable\Views\Parts.RoutableTitle.cshtml" />
<Content Include="Contents\Views\Content.Summary.cshtml" />
<Content Include="Contents\Views\Content.SaveButton.cshtml" />
<Content Include="Contents\Views\Content.PublishButton.cshtml" />
@@ -397,9 +373,6 @@
<Content Include="Shapes\Styles\Web.config">
<SubType>Designer</SubType>
</Content>
<Content Include="Routable\Scripts\Web.config">
<SubType>Designer</SubType>
</Content>
<Content Include="Settings\Styles\Web.config">
<SubType>Designer</SubType>
</Content>
@@ -411,8 +384,6 @@
<Content Include="Containers\Views\EditorTemplates\Containable.cshtml" />
<Content Include="Containers\Views\Parts.ContainerWidget.cshtml" />
<Content Include="Containers\Views\EditorTemplates\CustomProperties.cshtml" />
<Content Include="Routable\Views\Parts.RoutableTitle_Summary.cshtml" />
<Content Include="Routable\Views\Parts.RoutableTitle_SummaryAdmin.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Shapes\Views\ShapeResult\Display.cshtml" />

View File

@@ -1,94 +0,0 @@
using System.Linq;
using System.Web.Mvc;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Contents;
using Orchard.Core.Routable.Models;
using Orchard.Core.Routable.Services;
using Orchard.Data;
using Orchard.Localization;
using Orchard.Mvc;
using Orchard.Themes;
namespace Orchard.Core.Routable.Controllers {
[ValidateInput(false)]
public class ItemController : Controller, IUpdateModel {
private readonly ITransactionManager _transactionManager;
private readonly IRoutablePathConstraint _routablePathConstraint;
public ItemController(
ITransactionManager transactionManager,
IRoutablePathConstraint routablePathConstraint,
IOrchardServices services
) {
_transactionManager = transactionManager;
_routablePathConstraint = routablePathConstraint;
Services = services;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public IOrchardServices Services { get; private set; }
[Themed]
public ActionResult Display(string path) {
var matchedPath = _routablePathConstraint.FindPath(path);
if (matchedPath == null) {
return HttpNotFound(T("Should not have passed path constraint").Text);
}
var hits = Services.ContentManager
.Query<RoutePart, RoutePartRecord>(VersionOptions.Published)
.Where(r => r.Path == matchedPath)
.Slice(0, 2)
.ToList();
if (hits.Count() == 0) {
return HttpNotFound(T("Should not have passed path constraint").Text);
}
if (hits.Count() != 1) {
return HttpNotFound(T("Ambiguous content").Text);
}
dynamic model = Services.ContentManager.BuildDisplay(hits.Single());
return new ShapeResult(this, model);
}
public ActionResult Slugify(string contentType, int? id, int? containerId) {
const string slug = "";
ContentItem contentItem = null;
if (string.IsNullOrEmpty(contentType))
return Json(slug);
if (id != null)
contentItem = Services.ContentManager.Get((int)id, VersionOptions.Latest);
if (contentItem == null) {
contentItem = Services.ContentManager.Create(contentType, VersionOptions.Draft);
if (containerId != null) {
var containerItem = Services.ContentManager.Get((int)containerId);
contentItem.As<ICommonPart>().Container = containerItem;
}
}
Services.ContentManager.UpdateEditor(contentItem, this);
Services.ContentManager.Publish(contentItem);
_transactionManager.Cancel();
return Json(contentItem.As<IRoutableAspect>().GetEffectiveSlug() ?? slug);
}
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());
}
}
}

View File

@@ -1,118 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Handlers;
using Orchard.Core.Routable.Models;
using Orchard.Core.Routable.Services;
using Orchard.Core.Routable.ViewModels;
using Orchard.Localization;
using Orchard.Mvc;
using Orchard.Services;
using Orchard.Utility.Extensions;
namespace Orchard.Core.Routable.Drivers {
public class RoutePartDriver : ContentPartDriver<RoutePart> {
private readonly IOrchardServices _services;
private readonly IRoutableService _routableService;
private readonly IHttpContextAccessor _httpContextAccessor;
public RoutePartDriver(IOrchardServices services,
IRoutableService routableService,
IHttpContextAccessor httpContextAccessor) {
_services = services;
_routableService = routableService;
_httpContextAccessor = httpContextAccessor;
T = NullLocalizer.Instance;
}
private const string TemplateName = "Parts.Routable.RoutePart";
public Localizer T { get; set; }
protected override string Prefix {
get { return "Routable"; }
}
static int? GetContainerId(IContent item) {
var commonPart = item.As<ICommonPart>();
if (commonPart != null && commonPart.Container != null) {
return commonPart.Container.ContentItem.Id;
}
return null;
}
protected override DriverResult Display(RoutePart part, string displayType, dynamic shapeHelper) {
return Combined(
ContentShape("Parts_RoutableTitle",
() => shapeHelper.Parts_RoutableTitle(ContentPart: part, Title: part.Title, Path: part.Path)),
ContentShape("Parts_RoutableTitle_Summary",
() => shapeHelper.Parts_RoutableTitle_Summary(ContentPart: part, Title: part.Title, Path: part.Path)),
ContentShape("Parts_RoutableTitle_SummaryAdmin",
() => shapeHelper.Parts_RoutableTitle_SummaryAdmin(ContentPart: part, Title: part.Title, Path: part.Path))
);
}
protected override DriverResult Editor(RoutePart part, dynamic shapeHelper) {
var model = new RoutableEditorViewModel {
ContentType = part.ContentItem.ContentType,
Id = part.ContentItem.Id,
Slug = part.Slug,
Title = part.Title,
ContainerId = GetContainerId(part),
};
var request = _httpContextAccessor.Current().Request;
var containerUrl = new UriBuilder(request.ToRootUrlString()) { Path = (request.ApplicationPath ?? "").TrimEnd('/') + "/" + (part.GetContainerPath() ?? "") };
model.ContainerAbsoluteUrl = containerUrl.Uri.ToString().TrimEnd('/');
return ContentShape("Parts_Routable_Edit",
() => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix));
}
protected override DriverResult Editor(RoutePart part, IUpdateModel updater, dynamic shapeHelper) {
var model = new RoutableEditorViewModel();
updater.TryUpdateModel(model, Prefix, null, null);
part.Title = model.Title;
part.Slug = model.Slug;
part.PromoteToHomePage = model.PromoteToHomePage;
if ( !_routableService.IsSlugValid(part.Slug) ) {
var slug = (part.Slug ?? String.Empty);
if ( slug.StartsWith(".") || slug.EndsWith(".") )
updater.AddModelError("Routable.Slug", T("The \".\" can't be used at either end of the permalink."));
else
updater.AddModelError("Routable.Slug", T("Please do not use any of the following characters in your permalink: \":\", \"?\", \"#\", \"[\", \"]\", \"@\", \"!\", \"$\", \"&\", \"'\", \"(\", \")\", \"*\", \"+\", \",\", \";\", \"=\", \", \"<\", \">\", \"\\\". No spaces are allowed (please use dashes or underscores instead)."));
}
return Editor(part, shapeHelper);
}
protected override void Importing(RoutePart part, ImportContentContext context) {
var title = context.Attribute(part.PartDefinition.Name, "Title");
if (title != null) {
part.Title = title;
}
var slug = context.Attribute(part.PartDefinition.Name, "Slug");
if (slug != null) {
part.Slug = slug;
}
var path = context.Attribute(part.PartDefinition.Name, "Path");
if (path != null) {
part.Path = path;
}
}
protected override void Exporting(RoutePart part, ExportContentContext context) {
context.Element(part.PartDefinition.Name).SetAttributeValue("Title", part.Title);
context.Element(part.PartDefinition.Name).SetAttributeValue("Slug", part.Slug);
context.Element(part.PartDefinition.Name).SetAttributeValue("Path", part.Path);
}
}
}

View File

@@ -1,17 +0,0 @@
using Orchard.Events;
namespace Orchard.Core.Routable.Events {
public interface ISlugEventHandler : IEventHandler {
void FillingSlugFromTitle(FillSlugContext context);
void FilledSlugFromTitle(FillSlugContext context);
}
public class FillSlugContext {
public FillSlugContext(string slug) {
Slug = slug;
}
public string Slug { get; set; }
public bool Adjusted { get; set; }
}
}

View File

@@ -1,127 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Routing;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.Core.Routable.Models;
using Orchard.Core.Routable.Services;
using Orchard.Data;
using Orchard.Localization;
using Orchard.Services;
using Orchard.UI.Notify;
namespace Orchard.Core.Routable.Handlers {
public class RoutePartHandler : ContentHandler {
private readonly IOrchardServices _services;
private readonly IRoutablePathConstraint _routablePathConstraint;
private readonly IRoutableService _routableService;
private readonly IContentManager _contentManager;
private readonly IWorkContextAccessor _workContextAccessor;
public RoutePartHandler(
IOrchardServices services,
IRepository<RoutePartRecord> repository,
IRoutablePathConstraint routablePathConstraint,
IRoutableService routableService,
IContentManager contentManager,
IWorkContextAccessor workContextAccessor) {
_services = services;
_routablePathConstraint = routablePathConstraint;
_routableService = routableService;
_contentManager = contentManager;
_workContextAccessor = workContextAccessor;
T = NullLocalizer.Instance;
Filters.Add(StorageFilter.For(repository));
Action<RoutePart> processSlug = (
routable => {
if (!_routableService.ProcessSlug(routable))
_services.Notifier.Warning(T("Permalinks in conflict. \"{0}\" is already set for a previously created {2} so now it has the slug \"{1}\"",
routable.Slug, routable.GetEffectiveSlug(), routable.ContentItem.ContentType));
});
OnGetDisplayShape<RoutePart>(SetModelProperties);
OnGetEditorShape<RoutePart>(SetModelProperties);
OnUpdateEditorShape<RoutePart>(SetModelProperties);
Action<PublishContentContext, RoutePart> handler = (context, route) => {
FinalizePath(route, context, processSlug);
};
OnPublished<RoutePart>(handler);
OnUnpublished<RoutePart>(handler);
OnRemoved<RoutePart>((context, route) => {
if (!string.IsNullOrWhiteSpace(route.Path))
_routablePathConstraint.RemovePath(route.Path);
});
OnIndexing<RoutePart>((context, part) => context.DocumentIndex.Add("title", part.Record.Title).RemoveTags().Analyze());
}
protected override void GetItemMetadata(GetContentItemMetadataContext context) {
var part = context.ContentItem.As<RoutePart>();
if (part != null) {
context.Metadata.Identity.Add("Route.Slug", part.Slug);
}
}
private void FinalizePath(RoutePart route, PublishContentContext context, Action<RoutePart> processSlug) {
var path = route.Path;
route.Path = route.GetPathWithSlug(route.Slug);
if (context.PublishingItemVersionRecord != null)
processSlug(route);
// if the path has changed by having the slug changed on the way in (e.g. user input) or to avoid conflict
// then update and publish all contained items
if (path != route.Path) {
_routablePathConstraint.RemovePath(path);
_routableService.FixContainedPaths(route);
}
if (!string.IsNullOrWhiteSpace(route.Path))
_routablePathConstraint.AddPath(route.Path);
}
private static void SetModelProperties(BuildShapeContext context, RoutePart routable) {
var item = context.Shape;
item.Title = routable.Title;
item.Slug = routable.Slug;
item.Path = routable.Path;
}
public Localizer T { get; set; }
}
public class RoutePartHandlerBase : ContentHandlerBase {
private readonly IWorkContextAccessor _workContextAccessor;
public RoutePartHandlerBase(IWorkContextAccessor workContextAccessor) {
_workContextAccessor = workContextAccessor;
}
public override void GetContentItemMetadata(GetContentItemMetadataContext context) {
var routable = context.ContentItem.As<RoutePart>();
if (routable == null)
return;
// set the display route values if it hasn't been set or only has been set by the Contents module.
// allows other modules to set their own display. probably not common enough to warrant some priority implementation
if (context.Metadata.DisplayRouteValues == null || context.Metadata.DisplayRouteValues["Area"] as string == "Contents") {
var itemPath = routable.Path;
context.Metadata.DisplayRouteValues = new RouteValueDictionary {
{"Area", "Routable"},
{"Controller", "Item"},
{"Action", "Display"},
{"path", itemPath}
};
}
}
}
}

View File

@@ -1,11 +0,0 @@
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);
}
}

View File

@@ -1,22 +0,0 @@
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
namespace Orchard.Core.Routable {
public class Migrations : DataMigrationImpl {
public int Create() {
SchemaBuilder.CreateTable("RoutePartRecord",
table => table
.ContentPartVersionRecord()
.Column<string>("Title", column => column.WithLength(1024))
.Column<string>("Slug", column => column.WithLength(1024))
.Column<string>("Path", column => column.WithLength(2048))
);
ContentDefinitionManager.AlterPartDefinition("RoutePart", builder => builder.Attachable());
return 1;
}
}
}

View File

@@ -1,23 +0,0 @@
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
namespace Orchard.Core.Routable.Models {
public class RoutePart : ContentPart<RoutePartRecord>, IRoutableAspect {
public string Title {
get { return Record.Title; }
set { Record.Title = value; }
}
public string Slug {
get { return Record.Slug; }
set { Record.Slug = value; }
}
public string Path {
get { return Record.Path; }
set { Record.Path = value; }
}
public bool PromoteToHomePage { get; set; }
}
}

View File

@@ -1,15 +0,0 @@
using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement.Records;
namespace Orchard.Core.Routable.Models {
public class RoutePartRecord : ContentPartVersionRecord {
[StringLength(1024)]
public virtual string Title { get; set; }
[StringLength(1024)]
public virtual string Slug { get; set; }
[StringLength(2048)]
public virtual string Path { get; set; }
}
}

View File

@@ -1,10 +0,0 @@
Name: Routable
AntiForgery: enabled
Author: The Orchard Team
Website: http://orchardproject.net
Version: 1.3.0
OrchardVersion: 1.3.0
Description: The routable module enables content items to be accessed through a friendly human-readable URL.
FeatureDescription: Routable content part.
Dependencies: Settings
Category: Core

View File

@@ -1,15 +0,0 @@
<Placement>
<!-- available display shapes -->
<!--
Parts_RoutableTitle
Parts_RoutableTitle_Summary
Parts_RoutableTitle_SummaryAdmin
-->
<Place Parts_Routable_Edit="Content:before.5"/>
<Match DisplayType="Detail">
<Place Parts_RoutableTitle="Header:5"/>
</Match>
<Match DisplayType="Summary">
<Place Parts_RoutableTitle_Summary="Header:5"/>
</Match>
</Placement>

View File

@@ -1,9 +0,0 @@
using Orchard.UI.Resources;
namespace Orchard.Core.Routable {
public class ResourceManifest : IResourceManifestProvider {
public void BuildManifests(ResourceManifestBuilder builder) {
builder.Add().DefineScript("Slugify").SetUrl("jquery.slugify.js").SetDependencies("jQuery");
}
}
}

View File

@@ -1,56 +0,0 @@
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 {
Route = new Route(
"Admin/Common/Routable/Slugify",
new RouteValueDictionary {
{"area", "Routable"},
{"controller", "Item"},
{"action", "Slugify"}
},
new RouteValueDictionary(),
new RouteValueDictionary {
{"area", "Routable"}
},
new MvcRouteHandler())
},
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())
}
};
}
}
}

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appSettings>
<add key="webpages:Enabled" value="false" />
</appSettings>
<system.web>
<httpHandlers>
<!-- iis6 - for any request in this location, return via managed static file handler -->
<add path="*" verb="*" type="System.Web.StaticFileHandler" />
</httpHandlers>
</system.web>
<system.webServer>
<handlers accessPolicy="Script,Read">
<!--
iis7 - for any request to a file exists on disk, return it via native http module.
accessPolicy 'Script' is to allow for a managed 404 page.
-->
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
</handlers>
</system.webServer>
</configuration>

View File

@@ -1,24 +0,0 @@
jQuery.fn.extend({
slugify: function(options) {
//todo: (heskew) need messaging system
if (!options.target || !options.url)
return;
var args = {
"contentType": options.contentType,
"id": options.id,
"containerId": options.containerId,
__RequestVerificationToken: $("input[name=__RequestVerificationToken]").val()
};
args[$(this).attr("name")] = $(this).val();
jQuery.post(
options.url,
args,
function(data) {
options.target.val(data);
},
"json"
);
}
});

View File

@@ -1,30 +0,0 @@
using System.Collections.Generic;
using Orchard.ContentManagement.Aspects;
namespace Orchard.Core.Routable.Services {
public interface IRoutableService : IDependency {
void FillSlugFromTitle<TModel>(TModel model) where TModel : IRoutableAspect;
string GenerateUniqueSlug(IRoutableAspect part, IEnumerable<string> existingPaths);
/// <summary>
/// Returns any content item with similar path
/// </summary>
IEnumerable<IRoutableAspect> GetSimilarPaths(string path);
/// <summary>
/// Validates the given slug
/// </summary>
bool IsSlugValid(string slug);
/// <summary>
/// Defines the slug of a RoutableAspect and validate its unicity
/// </summary>
/// <returns>True if the slug has been created, False if a conflict occured</returns>
bool ProcessSlug(IRoutableAspect part);
/// <summary>
/// Updated the paths of all contained items to reflect the current path of this item
/// </summary>
void FixContainedPaths(IRoutableAspect part);
}
}

View File

@@ -1,69 +0,0 @@
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) {
if (path != null && _paths.ContainsKey(path))
_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;
}
}
}

View File

@@ -1,36 +0,0 @@
using System.Linq;
using JetBrains.Annotations;
using Orchard.Core.Routable.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<RoutePartRecord> _repository;
public RoutablePathConstraintUpdator(IRoutablePathConstraint pageSlugConstraint, IRepository<RoutePartRecord> 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);
}
}
}

View File

@@ -1,186 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Common.Models;
using Orchard.Core.Routable.Events;
using Orchard.Core.Routable.Models;
using Orchard.Utility.Extensions;
namespace Orchard.Core.Routable.Services {
public class RoutableService : IRoutableService {
private readonly IContentManager _contentManager;
private readonly IEnumerable<ISlugEventHandler> _slugEventHandlers;
private readonly IRoutablePathConstraint _routablePathConstraint;
public RoutableService(IContentManager contentManager, IEnumerable<ISlugEventHandler> slugEventHandlers, IRoutablePathConstraint routablePathConstraint) {
_contentManager = contentManager;
_slugEventHandlers = slugEventHandlers;
_routablePathConstraint = routablePathConstraint;
}
public void FixContainedPaths(IRoutableAspect part) {
var items = _contentManager.Query(VersionOptions.Published)
.Join<CommonPartRecord>().Where(cr => cr.Container.Id == part.Id)
.List()
.Select(item => item.As<IRoutableAspect>()).Where(item => item != null);
foreach (var itemRoute in items) {
var route = itemRoute.As<IRoutableAspect>();
if(route == null) {
continue;
}
var path = route.Path;
route.Path = route.GetPathWithSlug(route.Slug);
// if the path has changed by having the slug changed on the way in (e.g. user input) or to avoid conflict
// then update and publish all contained items
if (path != route.Path) {
_routablePathConstraint.RemovePath(path);
FixContainedPaths(route);
}
if (!string.IsNullOrWhiteSpace(route.Path))
_routablePathConstraint.AddPath(route.Path);
}
}
public void FillSlugFromTitle<TModel>(TModel model) where TModel : IRoutableAspect {
if ((model.Slug != null && !string.IsNullOrEmpty(model.Slug.Trim())) || string.IsNullOrEmpty(model.Title))
return;
var slugContext = new FillSlugContext(model.Title);
foreach (ISlugEventHandler slugEventHandler in _slugEventHandlers) {
slugEventHandler.FillingSlugFromTitle(slugContext);
}
if (!slugContext.Adjusted) {
var disallowed = new Regex(@"[/:?#\[\]@!$&'()*+,;=\s\""\<\>\\]+");
slugContext.Slug = disallowed.Replace(slugContext.Slug, "-").Trim('-');
if (slugContext.Slug.Length > 1000)
slugContext.Slug = slugContext.Slug.Substring(0, 1000);
// dots are not allowed at the begin and the end of routes
slugContext.Slug = StringExtensions.RemoveDiacritics(slugContext.Slug.Trim('.').ToLower());
}
foreach (ISlugEventHandler slugEventHandler in _slugEventHandlers) {
slugEventHandler.FilledSlugFromTitle(slugContext);
}
model.Slug = slugContext.Slug;
}
public string GenerateUniqueSlug(IRoutableAspect part, IEnumerable<string> existingPaths) {
if (existingPaths == null) {
return part.Slug;
}
// materializing the enumeration
existingPaths = existingPaths.ToArray();
if(!existingPaths.Contains(part.Path)) {
return part.Slug;
}
int? version = existingPaths.Select(s => GetSlugVersion(part.Path, s)).OrderBy(i => i).LastOrDefault();
return version != null
? string.Format("{0}-{1}", part.Slug, version)
: part.Slug;
}
private static int? GetSlugVersion(string path, string potentialConflictingPath) {
int v;
string[] slugParts = potentialConflictingPath.Split(new[] { path }, StringSplitOptions.RemoveEmptyEntries);
if (slugParts.Length == 0)
return 2;
return int.TryParse(slugParts[0].TrimStart('-'), out v)
? (int?)++v
: null;
}
public IEnumerable<IRoutableAspect> GetSimilarPaths(string path) {
return
_contentManager.Query<RoutePart, RoutePartRecord>()
.Where(routable => routable.Path != null && routable.Path.StartsWith(path, StringComparison.OrdinalIgnoreCase))
.List()
.Select(i => i.As<RoutePart>())
.ToArray();
}
public bool IsSlugValid(string slug) {
return String.IsNullOrWhiteSpace(slug) || Regex.IsMatch(slug, @"^[^:?#\[\]@!$&'()*+,;=\s\""\<\>\\]+$") && !(slug.StartsWith(".") || slug.EndsWith("."));
}
public bool ProcessSlug(IRoutableAspect part) {
FillSlugFromTitle(part);
if (string.IsNullOrEmpty(part.Slug))
return true;
part.Path = part.GetPathWithSlug(part.Slug);
var pathsLikeThis = GetSimilarPaths(part.Path).ToArray();
// Don't include *this* part in the list
// of slugs to consider for conflict detection
pathsLikeThis = pathsLikeThis.Where(p => p.ContentItem.Id != part.ContentItem.Id).ToArray();
if (pathsLikeThis.Any()) {
var originalSlug = part.Slug;
var newSlug = GenerateUniqueSlug(part, pathsLikeThis.Select(p => p.Path));
part.Path = part.GetPathWithSlug(newSlug);
part.Slug = newSlug;
if (originalSlug != newSlug)
return false;
}
return true;
}
}
public static class RoutableAspectExtensions {
public static string GetContainerPath(this IRoutableAspect routableAspect) {
var commonAspect = routableAspect.As<ICommonPart>();
if (commonAspect != null && commonAspect.Container != null) {
var routable = commonAspect.Container.As<IRoutableAspect>();
if (routable != null)
return routable.Path;
}
return null;
}
public static string GetPathWithSlug(this IRoutableAspect routableAspect, string slug) {
var containerPath = routableAspect.GetContainerPath();
return !string.IsNullOrEmpty(containerPath)
? string.Format("{0}/{1}", containerPath, slug)
: slug;
}
public static string GetChildPath(this IRoutableAspect routableAspect, string slug) {
return string.Format("{0}/{1}", routableAspect.Path, slug);
}
public static string GetEffectiveSlug(this IRoutableAspect routableAspect) {
var containerPath = routableAspect.GetContainerPath();
if (string.IsNullOrWhiteSpace(routableAspect.Path))
return "";
var slugParts = routableAspect.Path.Split(new []{string.Format("{0}/", containerPath)}, StringSplitOptions.RemoveEmptyEntries);
return slugParts.FirstOrDefault();
}
}
}

View File

@@ -1,8 +0,0 @@
using Orchard.Core.Routable.Models;
namespace Orchard.Core.Routable.ViewModels {
public class RoutableDisplayViewModel {
public string Title { get { return RoutePart.Title; } }
public RoutePart RoutePart { get; set; }
}
}

View File

@@ -1,19 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Orchard.Core.Routable.ViewModels {
public class RoutableEditorViewModel {
public int Id { get; set; }
public string ContentType { get; set; }
[Required]
[StringLength(1024)]
public string Title { get; set; }
[StringLength(1024)]
public string Slug { get; set; }
public int? ContainerId { get; set; }
public bool PromoteToHomePage { get; set; }
public string ContainerAbsoluteUrl { get; set; }
}
}

View File

@@ -1,37 +0,0 @@
@model Orchard.Core.Routable.ViewModels.RoutableEditorViewModel
@using Orchard.Utility.Extensions;
@{ Script.Require("Slugify"); }
<fieldset>
@Html.LabelFor(m => m.Title, T("Title"))
@Html.TextBoxFor(m => m.Title, new { @class = "large text" })
</fieldset>
<fieldset class="permalink">
<label class="sub" for="Slug">@T("Permalink")<br /><span>@Model.ContainerAbsoluteUrl/</span></label>
<span>@Html.TextBoxFor(m => m.Slug, new { @class = "text" })</span>
<span class="checkbox-and-label">
@Html.EditorFor(m => m.PromoteToHomePage)
<label for="@ViewData.TemplateInfo.GetFullHtmlFieldId("PromoteToHomePage")" class="forcheckbox">@T("Set as home page")</label>
</span>
</fieldset>
@using(Script.Foot()){
<script type="text/javascript">
//<![CDATA[
$(function(){
//pull slug input from tab order
$("#@Html.FieldIdFor(m=>m.Slug)").attr("tabindex",-1);
$("#@Html.FieldIdFor(m=>m.Title)").blur(function(){
var slug = $("#@Html.FieldIdFor(m=>m.Slug)");
if (slug.val()) { return true; }
$(this).slugify({
target:slug,
contentType:"@Model.ContentType",
id:"@Model.Id",
@if (Model.ContainerId != null) {<text>containerId:@Model.ContainerId,</text>}
url:"@Url.Action("Slugify","Item",new RouteValueDictionary{{"Area","Routable"}})"
})
})
})
//]]>
</script>
}

View File

@@ -1 +0,0 @@
<h1>@Model.Title</h1>

View File

@@ -1,6 +0,0 @@
@{
Orchard.ContentManagement.ContentItem contentItem = Model.ContentPart.ContentItem;
string title = Model.Title.ToString();
}
<h1>@Html.ItemDisplayLink(title, contentItem)</h1>

View File

@@ -1,6 +0,0 @@
@{
Orchard.ContentManagement.ContentItem contentItem = Model.ContentPart.ContentItem;
string title = Model.Title.ToString();
}
<h1>@Html.ItemEditLink(title, contentItem)</h1>

View File

@@ -1,41 +0,0 @@
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="webpages:Enabled" value="false" />
</appSettings>
<system.web>
<httpHandlers>
</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=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<controls>
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" namespace="System.Web.Mvc" tagPrefix="mvc" />
</controls>
</pages>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<handlers>
</handlers>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="2.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>