Including Orchard.Autoroute

--HG--
branch : 1.x
This commit is contained in:
Sebastien Ros
2012-08-22 15:34:58 -07:00
parent 67bdd45068
commit 1622228527
38 changed files with 1714 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
using Orchard.Commands;
using Orchard.Autoroute.Services;
namespace Orchard.Autoroute.Commands {
public class AutorouteCommands : DefaultOrchardCommandHandler {
private readonly IAutorouteService _autorouteService;
public AutorouteCommands(IAutorouteService autorouteService) {
_autorouteService = autorouteService;
}
[CommandHelp("autoroute create <content-type> <name> <pattern> <description> <isDefault>\r\n\t" + "Adds a new autoroute pattern to a specific content type")]
[CommandName("autoroute create")]
public void CreatePattern(string contentType, string name, string pattern, string description, bool isDefault) {
_autorouteService.CreatePattern(contentType, name, pattern, description, isDefault);
}
}
}

View File

@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Alias;
using Orchard.Autoroute.Models;
using Orchard.Autoroute.Services;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.Autoroute.ViewModels;
using Orchard.Autoroute.Settings;
using Orchard.Localization;
using Orchard.Security;
using Orchard.UI.Notify;
using Orchard.Utility.Extensions;
namespace Orchard.Autoroute.Drivers {
public class AutoroutePartDriver : ContentPartDriver<AutoroutePart> {
private readonly IAliasService _aliasService;
private readonly IContentManager _contentManager;
private readonly IAutorouteService _autorouteService;
private readonly IAuthorizer _authorizer;
private readonly INotifier _notifier;
public AutoroutePartDriver(
IAliasService aliasService,
IContentManager contentManager,
IAutorouteService autorouteService,
IAuthorizer authorizer,
INotifier notifier) {
_aliasService = aliasService;
_contentManager = contentManager;
_autorouteService = autorouteService;
_authorizer = authorizer;
_notifier = notifier;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
protected override string Prefix { get { return "Autoroute"; }}
protected override DriverResult Editor(AutoroutePart part, dynamic shapeHelper) {
return Editor(part, null, shapeHelper);
}
protected override DriverResult Editor(AutoroutePart part, IUpdateModel updater, dynamic shapeHelper) {
var settings = part.TypePartDefinition.Settings.GetModel<AutorouteSettings>();
// if the content type has no pattern for autoroute, then use a default one
if(!settings.Patterns.Any()) {
settings.AllowCustomPattern = true;
settings.AutomaticAdjustmentOnEdit = false;
settings.DefaultPatternIndex = 0;
settings.Patterns = new List<RoutePattern> {new RoutePattern {Name = "Title", Description = "my-title", Pattern = "{Content.Slug}"}};
_notifier.Warning(T("No route patterns are currently defined for this Content Type. If you don't set one in the settings, a default one will be used."));
}
var viewModel = new AutoroutePartEditViewModel {
CurrentUrl = part.DisplayAlias,
Settings = settings
};
// retrieve home page
var homepage = _aliasService.Get(string.Empty);
var displayRouteValues = _contentManager.GetItemMetadata(part).DisplayRouteValues;
if(homepage.Match(displayRouteValues)) {
viewModel.PromoteToHomePage = true;
}
if (settings.PerItemConfiguration) {
// if enabled, the list of all available patterns is displayed, and the user can
// select which one to use
// todo: later
}
var previous = part.DisplayAlias;
if (updater != null && updater.TryUpdateModel(viewModel, Prefix, null, null)) {
// remove any trailing slash in the permalink
while(!string.IsNullOrEmpty(viewModel.CurrentUrl) && viewModel.CurrentUrl.StartsWith("/")) {
viewModel.CurrentUrl = viewModel.CurrentUrl.Substring(1);
}
part.DisplayAlias = viewModel.CurrentUrl;
// reset the alias if we need to force regeneration, and the user didn't provide a custom one
if(settings.AutomaticAdjustmentOnEdit && previous == part.DisplayAlias) {
part.DisplayAlias = string.Empty;
}
if (!_autorouteService.IsPathValid(part.DisplayAlias)) {
var path = (part.DisplayAlias ?? String.Empty);
if (path.StartsWith(".") || path.EndsWith("."))
updater.AddModelError("CurrentUrl", T("The \".\" can't be used at either end of the permalink."));
else
updater.AddModelError("CurrentUrl", T("Please do not use any of the following characters in your permalink: \":\", \"?\", \"#\", \"[\", \"]\", \"@\", \"!\", \"$\", \"&\", \"'\", \"(\", \")\", \"*\", \"+\", \",\", \";\", \"=\", \", \"<\", \">\", \"\\\". No spaces are allowed (please use dashes or underscores instead)."));
}
// if CurrentUrl is set, the handler won't try to create an alias for it
// but instead keep the value
// if home page is requested, use "/" to have the handler create a homepage alias
if(_authorizer.Authorize(Permissions.SetHomePage) && viewModel.PromoteToHomePage) {
part.DisplayAlias = "/";
}
}
return ContentShape("Parts_Autoroute_Edit",
() => shapeHelper.EditorTemplate(TemplateName: "Parts.Autoroute.Edit", Model: viewModel, Prefix: Prefix));
}
protected override void Importing(AutoroutePart part, ContentManagement.Handlers.ImportContentContext context) {
var displayAlias = context.Attribute(part.PartDefinition.Name, "Alias");
if (displayAlias != null) {
part.DisplayAlias = displayAlias;
}
var customPattern = context.Attribute(part.PartDefinition.Name, "CustomPattern");
if (customPattern != null) {
part.CustomPattern = customPattern;
}
var useCustomPattern = context.Attribute(part.PartDefinition.Name, "UseCustomPattern");
if (useCustomPattern != null) {
part.UseCustomPattern = bool.Parse(useCustomPattern);
}
}
protected override void Exporting(AutoroutePart part, ContentManagement.Handlers.ExportContentContext context) {
context.Element(part.PartDefinition.Name).SetAttributeValue("Alias", String.IsNullOrEmpty(part.Record.DisplayAlias) ? "/" : part.Record.DisplayAlias);
context.Element(part.PartDefinition.Name).SetAttributeValue("CustomPattern", part.Record.CustomPattern);
context.Element(part.PartDefinition.Name).SetAttributeValue("UseCustomPattern", part.Record.UseCustomPattern);
}
}
}

View File

@@ -0,0 +1,95 @@
using System;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.Autoroute.Models;
using Orchard.Data;
using Orchard.Autoroute.Services;
using Orchard.Localization;
using Orchard.UI.Notify;
namespace Orchard.Autoroute.Handlers {
public class AutoroutePartHandler : ContentHandler {
private readonly Lazy<IAutorouteService> _autorouteService;
private readonly IOrchardServices _orchardServices;
public Localizer T { get; set; }
public AutoroutePartHandler(
IRepository<AutoroutePartRecord> autoroutePartRepository,
Lazy<IAutorouteService> autorouteService,
IOrchardServices orchardServices) {
Filters.Add(StorageFilter.For(autoroutePartRepository));
_autorouteService = autorouteService;
_orchardServices = orchardServices;
OnUpdated<AutoroutePart>((ctx, part) => CreateAlias(part));
OnCreated<AutoroutePart>((ctx, part) => {
// non-draftable items
if (part.ContentItem.VersionRecord == null) {
PublishAlias(part);
}
});
// OnVersioned<AutoroutePart>((ctx, part1, part2) => CreateAlias(part1));
OnPublished<AutoroutePart>((ctx, part) => PublishAlias(part));
// Remove alias if removed or unpublished
OnRemoved<AutoroutePart>((ctx, part) => RemoveAlias(part));
OnUnpublished<AutoroutePart>((ctx, part) => RemoveAlias(part));
// Register alias as identity
OnGetContentItemMetadata<AutoroutePart>((ctx, part) => {
if (part.DisplayAlias != null)
ctx.Metadata.Identity.Add("alias", part.DisplayAlias);
});
}
private void CreateAlias(AutoroutePart part) {
ProcessAlias(part);
}
private void PublishAlias(AutoroutePart part) {
ProcessAlias(part);
// should it become the home page ?
if (part.DisplayAlias == "/") {
part.DisplayAlias = String.Empty;
// regenerate the alias for the previous home page
var currentHomePages = _orchardServices.ContentManager.Query<AutoroutePart, AutoroutePartRecord>().Where(x => x.DisplayAlias == "").List();
foreach (var current in currentHomePages) {
if (current != null) {
current.CustomPattern = String.Empty; // force the regeneration
current.DisplayAlias = _autorouteService.Value.GenerateAlias(current);
}
_autorouteService.Value.PublishAlias(current);
}
}
_autorouteService.Value.PublishAlias(part);
}
private void ProcessAlias(AutoroutePart part) {
// generate an alias if one as not already been entered
if (String.IsNullOrWhiteSpace(part.DisplayAlias)) {
part.DisplayAlias = _autorouteService.Value.GenerateAlias(part);
}
// should it become the home page ?
if (part.DisplayAlias != "/") {
var previous = part.Path;
if (!_autorouteService.Value.ProcessPath(part))
_orchardServices.Notifier.Warning(T("Permalinks in conflict. \"{0}\" is already set for a previously created {2} so now it has the slug \"{1}\"",
previous, part.Path, part.ContentItem.ContentType));
}
}
void RemoveAlias(AutoroutePart part) {
_autorouteService.Value.RemoveAliases(part);
}
}
}

View File

@@ -0,0 +1,20 @@
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
namespace Orchard.Autoroute {
public class Migrations : DataMigrationImpl {
public int Create() {
SchemaBuilder.CreateTable("AutoroutePartRecord",
table => table
.ContentPartVersionRecord()
.Column<string>("CustomPattern", c => c.WithLength(2048))
.Column<bool>("UseCustomPattern", c=> c.WithDefault(false))
.Column<string>("DisplayAlias", c => c.WithLength(2048)));
ContentDefinitionManager.AlterPartDefinition("AutoroutePart", part => part.Attachable());
return 1;
}
}
}

View File

@@ -0,0 +1,26 @@
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
namespace Orchard.Autoroute.Models {
public class AutoroutePart : ContentPart<AutoroutePartRecord>, IAliasAspect {
public string CustomPattern {
get { return Record.CustomPattern; }
set { Record.CustomPattern = value; }
}
public bool UseCustomPattern {
get { return Record.UseCustomPattern; }
set { Record.UseCustomPattern = value; }
}
public string DisplayAlias {
get { return Record.DisplayAlias; }
set { Record.DisplayAlias = value; }
}
public string Path {
get { return DisplayAlias; }
}
}
}

View File

@@ -0,0 +1,15 @@
using Orchard.ContentManagement.Records;
using System.ComponentModel.DataAnnotations;
namespace Orchard.Autoroute.Models {
public class AutoroutePartRecord : ContentPartVersionRecord {
public virtual bool UseCustomPattern { get; set; }
[StringLength(2048)]
public virtual string CustomPattern { get; set; }
[StringLength(2048)]
public virtual string DisplayAlias { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
Name: Autoroute
AntiForgery: enabled
Author: The Orchard Team
Website: http://orchardproject.net
Version: 1.5
OrchardVersion: 1.3
Description: Description for the module
Features:
Orchard.Autoroute:
Name: Autoroute
Description: Enables the Autoroute part for tokenized routing
Dependencies: Orchard.Alias, Orchard.Tokens
Category: Content

View File

@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{66FCCD76-2761-47E3-8D11-B45D0001DDAA}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Orchard.Autoroute</RootNamespace>
<AssemblyName>Orchard.Autoroute</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<MvcBuildViews>false</MvcBuildViews>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>4.0</OldToolsVersion>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<TargetFrameworkProfile />
<UseIISExpress>false</UseIISExpress>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.ComponentModel.DataAnnotations">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Web.Abstractions" />
<Reference Include="System.Web.Routing" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Content Include="Recipes\autorouteblogs.recipe.xml" />
<Content Include="Recipes\autoroutepages.recipe.xml" />
<Content Include="Recipes\autoroutelists.recipe.xml" />
<Content Include="Styles\orchard-autoroute-settings.css" />
<Content Include="Web.config" />
<Content Include="Scripts\Web.config" />
<Content Include="Styles\Web.config" />
<Compile Include="Permissions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Content Include="Module.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
<Name>Orchard.Framework</Name>
</ProjectReference>
<ProjectReference Include="..\..\Core\Orchard.Core.csproj">
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
<Name>Orchard.Core</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Alias\Orchard.Alias.csproj">
<Project>{475B6C45-B27C-438B-8966-908B9D6D1077}</Project>
<Name>Orchard.Alias</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Tokens\Orchard.Tokens.csproj">
<Project>{6F759635-13D7-4E94-BCC9-80445D63F117}</Project>
<Name>Orchard.Tokens</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Commands\AutorouteCommands.cs" />
<Compile Include="ResourceManifest.cs" />
<Compile Include="Services\IRouteEvents.cs" />
<Compile Include="Settings\AutorouteSettingsEvents.cs" />
<Compile Include="Settings\RoutePattern.cs" />
<Compile Include="Drivers\AutoroutePartDriver.cs" />
<Compile Include="Handlers\AutoroutePartHandler.cs" />
<Compile Include="Services\ISlugService.cs" />
<Compile Include="Migrations.cs" />
<Compile Include="Providers\SlugTokens.cs" />
<Compile Include="Services\AutorouteService.cs" />
<Compile Include="Services\IAutorouteService.cs" />
<Compile Include="Models\AutoroutePart.cs" />
<Compile Include="Models\AutoroutePartRecord.cs" />
<Compile Include="Services\DefaultSlugService.cs" />
<Compile Include="Services\ISlugEventHandler.cs" />
<Compile Include="Settings\AutorouteSettings.cs" />
<Compile Include="ViewModels\AutoroutePartEditViewModel.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Controllers\" />
</ItemGroup>
<ItemGroup>
<Content Include="Placement.info">
<SubType>Designer</SubType>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Views\DefinitionTemplates\AutorouteSettings.cshtml" />
<Content Include="Views\EditorTemplates\Parts.Autoroute.Edit.cshtml" />
<Content Include="Views\Web.config" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target> -->
<Target Name="AfterBuild" DependsOnTargets="AfterBuildCompiler">
<PropertyGroup>
<AreasManifestDir>$(ProjectDir)\..\Manifests</AreasManifestDir>
</PropertyGroup>
<!-- If this is an area child project, uncomment the following line:
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Child" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
-->
<!-- If this is an area parent project, uncomment the following lines:
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Parent" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
<CopyAreaManifests ManifestPath="$(AreasManifestDir)" CrossCopy="false" RenameViews="true" />
-->
</Target>
<Target Name="AfterBuildCompiler" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />
</Target>
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>False</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>28871</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>
</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>True</UseCustomServer>
<CustomServerUrl>http://orchard.codeplex.com</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@@ -0,0 +1,31 @@
using System.Collections.Generic;
using Orchard.Environment.Extensions.Models;
using Orchard.Security.Permissions;
namespace Orchard.Autoroute {
public class Permissions : IPermissionProvider {
public static readonly Permission SetHomePage = new Permission { Description = "Set Home Page", Name = "SetHomePage" };
public virtual Feature Feature { get; set; }
public IEnumerable<Permission> GetPermissions() {
return new[] {
SetHomePage
};
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
return new[] {
new PermissionStereotype {
Name = "Administrator",
Permissions = new[] {SetHomePage}
},
new PermissionStereotype {
Name = "Editor",
Permissions = new[] {SetHomePage}
}
};
}
}
}

View File

@@ -0,0 +1,3 @@
<Placement>
<Place Parts_Autoroute_Edit="Content:before.51"/>
</Placement>

View File

@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Orchard.Autoroute")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("Orchard")]
[assembly: AssemblyCopyright("Copyright <20> Outercurve Foundation 2009")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("149d93d0-2d4b-4fcb-9461-9de3c553eced")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.5")]
[assembly: AssemblyFileVersion("1.5")]

View File

@@ -0,0 +1,72 @@
using System;
using Orchard.Autoroute.Services;
using Orchard.Tokens;
using Orchard.Localization;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.Autoroute.Models;
using Orchard.Core.Common.Models;
namespace Orchard.Autoroute.Providers {
public class SlugTokens : ITokenProvider {
private readonly ISlugService _slugService;
public SlugTokens(ISlugService slugService) {
T = NullLocalizer.Instance;
_slugService = slugService;
}
public Localizer T { get; set; }
public void Describe(DescribeContext context) {
context.For("Content")
// /my-item
.Token("Slug", T("Slug"), T("A slugified version of the item title appropriate for content Urls"))
// /path/to/my-item
.Token("Path", T("Path"), T("The full path of an item as already generated by Autoroute"))
// /path/to/parent-item/
.Token("ParentPath", T("Parent Path"), T("The parent item's path and slug with an appended forward slash if non-empty"));
context.For("TypeDefinition")
// /blog-post
.Token("Slug", T("Slug"), T("Slugified version of content type display name."));
context.For("Text")
.Token("Slug", T("Slug"), T("Slugify the text"));
}
public void Evaluate(EvaluateContext context) {
context.For<IContent>("Content")
// {Content.Slug}
.Token("Slug", (content => content == null ? String.Empty : _slugService.Slugify(content)))
.Token("Path", (content => {
var autoroute = content.As<AutoroutePart>();
if (autoroute == null) {
return String.Empty;
}
return autoroute.DisplayAlias;
}))
// {Content.ParentPath}
.Token("ParentPath", (content => {
var common = content.As<CommonPart>();
if (common == null || common.Container == null) {
return String.Empty;
}
var ar = common.Container.As<AutoroutePart>();
if (ar == null) {
return String.Empty;
}
if (String.IsNullOrEmpty(ar.DisplayAlias))
return String.Empty;
return ar.DisplayAlias + "/";
}));
context.For<ContentTypeDefinition>("TypeDefinition")
// {Content.ContentType.Slug}
.Token("Slug", (ctd => _slugService.Slugify(ctd.DisplayName)));
context.For<String>("Text")
.Token("Slug", text => _slugService.Slugify(text));
}
}
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0"?>
<Orchard>
<Recipe>
<Name>AutorouteBlogs</Name>
<Description>Adds Autoroute functionality to an existing blog</Description>
<Author>The Orchard Team</Author>
<WebSite>http://orchardproject.net</WebSite>
<Tags>blog, autoroute</Tags>
<Version>1.0</Version>
</Recipe>
<Feature enable="Orchard.Blogs" />
<Metadata>
<Types>
<Blog>
<AutoroutePart />
</Blog>
<BlogPost>
<AutoroutePart />
</BlogPost>
</Types>
</Metadata>
<Migration features="*" />
<Command>
autoroute create "Blog" "Title" "{Content.Slug}" "http://website.com/my-blog" true
autoroute create "BlogPost" "Date and Title" "{Content.Date.Format:yyyy}/{Content.Date.Format:MM}/{Content.Date.Format:dd}/{Content.Slug}" "http://website.com/2011/05/12/a-blog-post" true
</Command>
</Orchard>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0"?>
<Orchard>
<Recipe>
<Name>AutorouteLists</Name>
<Description>Adds Autoroute functionality to an existing blog</Description>
<Author>The Orchard Team</Author>
<WebSite>http://orchardproject.net</WebSite>
<Tags>container, autoroute</Tags>
<Version>1.0</Version>
</Recipe>
<Feature enable="Orchard.Pages" />
<Metadata>
<Types>
<List>
<AutoroutePart />
</List>
</Types>
</Metadata>
<Migration features="*" />
<Command>
autoroute create "List" "Title" "{Content.Slug}" "http://website.com/events" true
</Command>
</Orchard>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0"?>
<Orchard>
<Recipe>
<Name>AutoroutePages</Name>
<Description>Adds Autoroute functionality to an existing blog</Description>
<Author>The Orchard Team</Author>
<WebSite>http://orchardproject.net</WebSite>
<Tags>page, autoroute</Tags>
<Version>1.0</Version>
</Recipe>
<Feature enable="Orchard.Pages" />
<Metadata>
<Types>
<Page>
<AutoroutePart />
</Page>
</Types>
</Metadata>
<Migration features="*" />
<Command>
autoroute create "Page" "Title" "{Content.Slug}" "http://website.com/about-us" false
</Command>
</Orchard>

View File

@@ -0,0 +1,10 @@
using Orchard.UI.Resources;
namespace Orchard.Autoroute {
public class ResourceManifest : IResourceManifestProvider {
public void BuildManifests(ResourceManifestBuilder builder) {
var manifest = builder.Add();
manifest.DefineStyle("AutorouteSettings").SetUrl("orchard-autoroute-settings.css");
}
}
}

View File

@@ -0,0 +1,21 @@
<?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

@@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Orchard.Alias;
using Orchard.Autoroute.Models;
using Orchard.Autoroute.Settings;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Tokens;
namespace Orchard.Autoroute.Services {
public class AutorouteService : IAutorouteService {
private readonly IAliasService _aliasService;
private readonly ITokenizer _tokenizer;
private readonly IContentDefinitionManager _contentDefinitionManager;
private readonly IContentManager _contentManager;
private readonly IRouteEvents _routeEvents;
private const string AliasSource = "Autoroute:View";
public AutorouteService(
IAliasService aliasService,
ITokenizer tokenizer,
IContentDefinitionManager contentDefinitionManager,
IContentManager contentManager,
IRouteEvents routeEvents) {
_aliasService = aliasService;
_tokenizer = tokenizer;
_contentDefinitionManager = contentDefinitionManager;
_contentManager = contentManager;
_routeEvents = routeEvents;
Logger = NullLogger.Instance;
T = NullLocalizer.Instance;
}
public ILogger Logger { get; set; }
public Localizer T { get; set; }
public string GenerateAlias(AutoroutePart part) {
if (part == null) {
throw new ArgumentNullException("part");
}
string pattern = GetDefaultPattern(part.ContentItem.ContentType).Pattern;
// String.Empty forces pattern based generation. "/" forces homepage
if(part.UseCustomPattern
&& (!String.IsNullOrWhiteSpace(part.CustomPattern) || String.Equals(part.CustomPattern, "/"))) {
pattern = part.CustomPattern;
}
// Convert the pattern and route values via tokens
var path = _tokenizer.Replace(pattern, BuildTokenContext(part.ContentItem), new ReplaceOptions { Encoding = ReplaceOptions.NoEncode });
// removing trailing slashes in case the container is empty, and tokens are base on it (e.g. home page)
while(path.StartsWith("/")) {
path = path.Substring(1);
}
return path;
}
public void PublishAlias(AutoroutePart part) {
var displayRouteValues = _contentManager.GetItemMetadata(part).DisplayRouteValues;
_aliasService.Replace(part.DisplayAlias, displayRouteValues, AliasSource);
_routeEvents.Routed(part, part.DisplayAlias);
}
private IDictionary<string, object> BuildTokenContext(IContent item) {
return new Dictionary<string, object> { { "Content", item } };
}
public void CreatePattern(string contentType, string name, string pattern, string description, bool makeDefault) {
var contentDefinition = _contentDefinitionManager.GetTypeDefinition(contentType);
if (contentDefinition == null) {
throw new OrchardException(T("Unknown content type: {0}", contentType));
}
var settings = contentDefinition.Settings.GetModel<AutorouteSettings>();
var routePattern = new RoutePattern {
Description = description,
Name = name,
Pattern = pattern
};
var patterns = settings.Patterns;
patterns.Add(routePattern);
settings.Patterns = patterns;
// define which pattern is the default
if (makeDefault || settings.Patterns.Count == 1) {
settings.DefaultPatternIndex = settings.Patterns.IndexOf(routePattern);
}
_contentDefinitionManager.AlterTypeDefinition(contentType, builder => builder.WithPart("AutoroutePart", settings.Build));
}
public IEnumerable<RoutePattern> GetPatterns(string contentType) {
var settings = GetTypePartSettings(contentType).GetModel<AutorouteSettings>();
return settings.Patterns;
}
public RoutePattern GetDefaultPattern(string contentType) {
var settings = GetTypePartSettings(contentType).GetModel<AutorouteSettings>();
// return a default pattern if none is defined
if(settings.DefaultPatternIndex < settings.Patterns.Count) {
return settings.Patterns.ElementAt(settings.DefaultPatternIndex);
}
return new RoutePattern {Name = "Title", Description = "my-title", Pattern = "{Content.Slug}"};
}
public void RemoveAliases(AutoroutePart part) {
_aliasService.Delete(part.Path, AliasSource);
}
private SettingsDictionary GetTypePartSettings(string contentType) {
var contentDefinition = _contentDefinitionManager.GetTypeDefinition(contentType);
if (contentDefinition == null) {
throw new OrchardException(T("Unknown content type: {0}", contentType));
}
return contentDefinition.Parts.First(x => x.PartDefinition.Name == "AutoroutePart").Settings;
}
public string GenerateUniqueSlug(AutoroutePart part, IEnumerable<string> existingPaths) {
if (existingPaths == null || !existingPaths.Contains(part.Path))
return part.Path;
int? version = existingPaths.Select(s => GetSlugVersion(part.Path, s)).OrderBy(i => i).LastOrDefault();
return version != null
? string.Format("{0}-{1}", part.Path, version)
: part.Path;
}
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<AutoroutePart> GetSimilarPaths(string path) {
return
_contentManager.Query<AutoroutePart, AutoroutePartRecord>()
.Where(part => part.DisplayAlias != null && part.DisplayAlias.StartsWith(path))
.List();
}
public bool IsPathValid(string slug) {
return String.IsNullOrWhiteSpace(slug) || Regex.IsMatch(slug, @"^[^:?#\[\]@!$&'()*+,;=\s\""\<\>\\]+$") && !(slug.StartsWith(".") || slug.EndsWith("."));
}
public bool ProcessPath(AutoroutePart part) {
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 originalPath = part.Path;
var newPath = GenerateUniqueSlug(part, pathsLikeThis.Select(p => p.Path));
part.DisplayAlias = newPath;
if (originalPath != newPath)
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,49 @@
using Orchard.ContentManagement;
using System.Text.RegularExpressions;
using Orchard.Utility.Extensions;
namespace Orchard.Autoroute.Services {
public class DefaultSlugService : ISlugService {
private readonly ISlugEventHandler _slugEventHandler;
public DefaultSlugService(
ISlugEventHandler slugEventHander
) {
_slugEventHandler = slugEventHander;
}
public string Slugify(IContent content) {
var metadata = content.ContentItem.ContentManager.GetItemMetadata(content);
if (metadata == null) return null;
var title = metadata.DisplayText.Trim();
return Slugify(new FillSlugContext(content,title));
}
private string Slugify(FillSlugContext slugContext) {
_slugEventHandler.FillingSlugFromTitle(slugContext);
if (!slugContext.Adjusted) {
var disallowed = new Regex(@"[/:?#\[\]@!$&'()*+,;=\s\""\<\>\\]+");
slugContext.Slug = disallowed.Replace(slugContext.Title, "-").Trim('-','.');
if (slugContext.Slug.Length > 1000)
slugContext.Slug = slugContext.Slug.Substring(0, 1000).Trim('-', '.');
slugContext.Slug = StringExtensions.RemoveDiacritics(slugContext.Slug.ToLower());
}
_slugEventHandler.FilledSlugFromTitle(slugContext);
return slugContext.Slug;
}
public string Slugify(string text) {
return Slugify(new FillSlugContext(null, text));
}
}
}

View File

@@ -0,0 +1,23 @@
using System.Collections.Generic;
using Orchard.Autoroute.Models;
using Orchard.Autoroute.Settings;
namespace Orchard.Autoroute.Services {
/// <summary>
/// Provides main services for Autoroute module
/// </summary>
public interface IAutorouteService : IDependency {
string GenerateAlias(AutoroutePart part);
void PublishAlias(AutoroutePart part);
void RemoveAliases(AutoroutePart part);
void CreatePattern(string contentType, string name, string pattern, string description, bool makeDefault);
RoutePattern GetDefaultPattern(string contentType);
IEnumerable<RoutePattern> GetPatterns(string contentType);
bool ProcessPath(AutoroutePart part);
bool IsPathValid(string slug);
}
}

View File

@@ -0,0 +1,9 @@
using System;
using Orchard.ContentManagement;
using Orchard.Events;
namespace Orchard.Autoroute.Services {
public interface IRouteEvents : IEventHandler {
void Routed(IContent content, String path);
}
}

View File

@@ -0,0 +1,20 @@
using Orchard.Events;
using Orchard.ContentManagement;
namespace Orchard.Autoroute.Services {
public interface ISlugEventHandler : IEventHandler {
void FillingSlugFromTitle(FillSlugContext context);
void FilledSlugFromTitle(FillSlugContext context);
}
public class FillSlugContext {
public FillSlugContext(IContent content, string title) {
Content = content;
Title = title;
}
public IContent Content { get; set; }
public string Title { get; set; }
public string Slug { get; set; }
public bool Adjusted { get; set; }
}
}

View File

@@ -0,0 +1,6 @@
namespace Orchard.Autoroute.Services {
public interface ISlugService : IDependency {
string Slugify(ContentManagement.IContent content);
string Slugify(string text);
}
}

View File

@@ -0,0 +1,56 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web.Script.Serialization;
using Orchard.ContentManagement.MetaData.Builders;
namespace Orchard.Autoroute.Settings {
/// <summary>
/// Settings when attaching part to a content item
/// </summary>
public class AutorouteSettings {
private List<RoutePattern> _patterns;
public AutorouteSettings() {
PerItemConfiguration = false;
AllowCustomPattern = true;
AutomaticAdjustmentOnEdit = false;
PatternDefinitions = "[]";
}
public bool PerItemConfiguration { get; set; }
public bool AllowCustomPattern { get; set; }
public bool AutomaticAdjustmentOnEdit { get; set; }
public int DefaultPatternIndex { get; set; }
/// <summary>
/// A serialized Json array of <see cref="RoutePattern"/> objects
/// </summary>
public string PatternDefinitions { get; set; }
public List<RoutePattern> Patterns {
get {
if (_patterns == null) {
_patterns = new JavaScriptSerializer().Deserialize<RoutePattern[]>(PatternDefinitions).ToList();
}
return _patterns;
}
set {
_patterns = value;
PatternDefinitions = new JavaScriptSerializer().Serialize(_patterns.ToArray());
}
}
public void Build(ContentTypePartDefinitionBuilder builder) {
builder.WithSetting("AutorouteSettings.PerItemConfiguration", PerItemConfiguration.ToString(CultureInfo.InvariantCulture));
builder.WithSetting("AutorouteSettings.AllowCustomPattern", AllowCustomPattern.ToString(CultureInfo.InvariantCulture));
builder.WithSetting("AutorouteSettings.AutomaticAdjustmentOnEdit", AutomaticAdjustmentOnEdit.ToString(CultureInfo.InvariantCulture));
builder.WithSetting("AutorouteSettings.PatternDefinitions", PatternDefinitions);
builder.WithSetting("AutorouteSettings.DefaultPatternIndex", DefaultPatternIndex.ToString(CultureInfo.InvariantCulture));
}
}
}

View File

@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.ViewModels;
using Orchard.Localization;
using Orchard.UI.Notify;
namespace Orchard.Autoroute.Settings {
public class AutorouteSettingsHooks : ContentDefinitionEditorEventsBase {
private readonly INotifier _notifier;
public AutorouteSettingsHooks(INotifier notifier) {
_notifier = notifier;
}
public Localizer T { get; set; }
public override IEnumerable<TemplateViewModel> TypePartEditor(ContentTypePartDefinition definition) {
if (definition.PartDefinition.Name != "AutoroutePart")
yield break;
var settings = definition.Settings.GetModel<AutorouteSettings>();
// add an empty pattern for the editor
settings.Patterns.Add(new RoutePattern());
yield return DefinitionTemplate(settings);
}
public override IEnumerable<TemplateViewModel> TypePartEditorUpdate(ContentTypePartDefinitionBuilder builder, IUpdateModel updateModel) {
if (builder.Name != "AutoroutePart")
yield break;
var settings = new AutorouteSettings {
Patterns = new List<RoutePattern>()
};
if (updateModel.TryUpdateModel(settings, "AutorouteSettings", null, null)) {
var defaultPattern = settings.Patterns[settings.DefaultPatternIndex];
// remove empty patterns
var patterns = settings.Patterns;
patterns.RemoveAll(p => String.IsNullOrWhiteSpace(p.Name) && String.IsNullOrWhiteSpace(p.Pattern) && String.IsNullOrWhiteSpace(p.Description));
if (patterns.Count == 0) {
patterns.Add(new RoutePattern {
Name = "Title",
Description = "my-title",
Pattern = "{Content.Slug}"
});
_notifier.Warning(T("A default pattern has been added to AutoroutePart"));
}
settings.Patterns = patterns;
// search for the pattern which was marked as default, and update its index
settings.DefaultPatternIndex = patterns.IndexOf(defaultPattern);
// if a wrong pattern was selected and there is at least one pattern, default to first
if (settings.DefaultPatternIndex == -1 && settings.Patterns.Any()) {
settings.DefaultPatternIndex = 0;
}
// update the settings builder
settings.Build(builder);
}
yield return DefinitionTemplate(settings);
}
}
}

View File

@@ -0,0 +1,11 @@
namespace Orchard.Autoroute.Settings {
/// <summary>
/// Models the patterns you can choose from
/// </summary>
public class RoutePattern {
public string Name { get; set; }
public string Pattern { get; set; }
public string Description { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="specFlow" type="TechTalk.SpecFlow.Configuration.ConfigurationSectionHandler, TechTalk.SpecFlow" />
</configSections>
<specFlow>
<unitTestProvider name="NUnit" />
<stepAssemblies>
<stepAssembly assembly="Orchard.Specs" />
</stepAssemblies>
</specFlow>
</configuration>

View File

@@ -0,0 +1,27 @@
Feature: Autoroutes
In order to add content to my site
As an author
I want to create, publish and edit routes
Scenario: I can create and publish a new Page with international characters in its route
Given I have installed Orchard
When I go to "admin/contents/create/page"
And I fill in
| name | value |
| Title.Title | Χελλο |
And I hit "Publish Now"
And I go to "Χελλο"
Then I should see "<h1[^>]*>.*?Χελλο.*?</h1>"
Scenario: I can create and publish a new Home Page
Given I have installed Orchard
When I go to "admin/contents/create/page"
And I fill in
| name | value |
| Title.Title | Foo |
| Autoroute.PromoteToHomePage | True |
And I hit "Publish Now"
And I go to "/"
Then I should see "<h1[^>]*>.*?Foo.*?</h1>"
When I go to "/welcome-to-orchard"
Then I should see "<h1[^>]*>.*?Welcome.*?</h1>"

View File

@@ -0,0 +1,137 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:1.9.0.77
// SpecFlow Generator Version:1.9.0.0
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
#region Designer generated code
#pragma warning disable
namespace Orchard.Autoroute.Specs
{
using TechTalk.SpecFlow;
[System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Autoroutes")]
public partial class AutoroutesFeature
{
private static TechTalk.SpecFlow.ITestRunner testRunner;
#line 1 "Autoroute.feature"
#line hidden
[NUnit.Framework.TestFixtureSetUpAttribute()]
public virtual void FeatureSetup()
{
testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner();
TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Autoroutes", " In order to add content to my site\r\n As an author\r\n I want to create, publish" +
" and edit routes", ProgrammingLanguage.CSharp, ((string[])(null)));
testRunner.OnFeatureStart(featureInfo);
}
[NUnit.Framework.TestFixtureTearDownAttribute()]
public virtual void FeatureTearDown()
{
testRunner.OnFeatureEnd();
testRunner = null;
}
[NUnit.Framework.SetUpAttribute()]
public virtual void TestInitialize()
{
}
[NUnit.Framework.TearDownAttribute()]
public virtual void ScenarioTearDown()
{
testRunner.OnScenarioEnd();
}
public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo)
{
testRunner.OnScenarioStart(scenarioInfo);
}
public virtual void ScenarioCleanup()
{
testRunner.CollectScenarioErrors();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("I can create and publish a new Page with international characters in its route")]
public virtual void ICanCreateAndPublishANewPageWithInternationalCharactersInItsRoute()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can create and publish a new Page with international characters in its route", ((string[])(null)));
#line 6
this.ScenarioSetup(scenarioInfo);
#line 7
testRunner.Given("I have installed Orchard", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 8
testRunner.When("I go to \"admin/contents/create/page\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line hidden
TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table1.AddRow(new string[] {
"Title.Title",
"Χελλο"});
#line 9
testRunner.And("I fill in", ((string)(null)), table1, "And ");
#line 12
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 13
testRunner.And("I go to \"Χελλο\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 14
testRunner.Then("I should see \"<h1[^>]*>.*?Χελλο.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("I can create and publish a new Home Page")]
public virtual void ICanCreateAndPublishANewHomePage()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can create and publish a new Home Page", ((string[])(null)));
#line 16
this.ScenarioSetup(scenarioInfo);
#line 17
testRunner.Given("I have installed Orchard", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 18
testRunner.When("I go to \"admin/contents/create/page\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line hidden
TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table2.AddRow(new string[] {
"Title.Title",
"Foo"});
table2.AddRow(new string[] {
"Autoroute.PromoteToHomePage",
"True"});
#line 19
testRunner.And("I fill in", ((string)(null)), table2, "And ");
#line 23
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 24
testRunner.And("I go to \"/\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 25
testRunner.Then("I should see \"<h1[^>]*>.*?Foo.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 26
testRunner.When("I go to \"/welcome-to-orchard\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 27
testRunner.Then("I should see \"<h1[^>]*>.*?Welcome.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
this.ScenarioCleanup();
}
}
}
#pragma warning restore
#endregion

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{AC4402A1-61C4-4229-B840-FB1777DAE10C}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Orchard.Autoroute.Specs</RootNamespace>
<AssemblyName>Orchard.Autoroute.Specs</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="nunit.framework">
<HintPath>..\..\..\..\..\lib\nunit\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="TechTalk.SpecFlow">
<HintPath>..\..\..\..\..\lib\specflow\TechTalk.SpecFlow.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Autoroute.feature.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Autoroute.feature</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Orchard.Specs\Orchard.Specs.csproj">
<Project>{7354DF37-934B-46CF-A13C-455D5F5F5413}</Project>
<Name>Orchard.Specs</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="Autoroute.feature">
<Generator>SpecFlowSingleFileGenerator</Generator>
<LastGenOutput>Autoroute.feature.cs</LastGenOutput>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,33 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Orchard.Autoroute.Specs")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("Orchard")]
[assembly: AssemblyCopyright("Outercurve Foundation 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,21 @@
<?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

@@ -0,0 +1,23 @@
.autoroute-settings-default {
width: 5%;
}
.autoroute-settings-name {
width: 20%
}
.autoroute-settings-pat {
width: 30%
}
.autoroute-settings-desc {
width: 40%
}
.autoroute-settings-actions {
width: 10%;
text-align: right;
}
.autoroute-settings-patterns tr td input {
width: 100%;
}

View File

@@ -0,0 +1,11 @@
using Orchard.Autoroute.Settings;
namespace Orchard.Autoroute.ViewModels {
public class AutoroutePartEditViewModel {
public AutorouteSettings Settings { get; set; }
public bool PromoteToHomePage { get; set; }
public string CurrentUrl { get; set; }
public string CustomPattern { get; set; }
}
}

View File

@@ -0,0 +1,54 @@
@model Orchard.Autoroute.Settings.AutorouteSettings
@using Orchard.Utility.Extensions;
@{
Style.Require("AutorouteSettings");
}
@*<fieldset>
<div>
@Html.CheckBoxFor(m => m.PerItemConfiguration)
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.PerItemConfiguration)">@T("Per item configuration")</label>
<span class="hint">@T("Allow the user to change the pattern on each item")</span>
</div>
</fieldset>
*@<fieldset>
<div>
@Html.CheckBoxFor(m => m.AllowCustomPattern)
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.AllowCustomPattern)">@T("Allow custom patterns")</label>
<span class="hint">@T("Allow the user to change the pattern on each item")</span>
</div>
</fieldset>
<fieldset>
<div>
@Html.CheckBoxFor(m => m.AutomaticAdjustmentOnEdit)
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.AutomaticAdjustmentOnEdit)">@T("Automatically regenerate when editing content")</label>
<span class="hint">@T("This option will cause the Url to automatically be regenerated when you edit existing content and publish it again, otherwise it will always keep the old route, or you have to perform bulk update in the Autoroute admin.")</span>
</div>
</fieldset>
<fieldset>
<label>@T("Patterns:")</label>
<table class="items autoroute-settings-patterns">
<tr>
<th class="autoroute-settings-default">@T("Default")</th>
<th class="autoroute-settings-name">@T("Name")<span class="hint">@T("Name of the pattern")</span></th>
<th class="autoroute-settings-pat">@T("Pattern")<span class="hint">@T("The definition of the pattern")</span></th>
<th class="autoroute-settings-desc">@T("Description")<span class="hint">@T("The description of the pattern, displayed in the editor")</span></th>
<th class="autoroute-settings-actions">&nbsp;</th>
</tr>
@for (int index = 0; index < Model.Patterns.Count; index++) {
<tr>
<td>@Html.RadioButtonFor(m => m.DefaultPatternIndex, index, new { @class = "radio" })</td>
<td>@Html.TextBoxFor(m => m.Patterns[index].Name, new { @class = "text-box"})</td>
<td>@Html.TextBoxFor(m => m.Patterns[index].Pattern, new { @class = "tokenized text-box" })</td>
<td>@Html.TextBoxFor(m => m.Patterns[index].Description, new { @class = "text-box" })</td>
<td>&nbsp;</td>
</tr>
}
<tr>
</tr>
</table>
</fieldset>
@Display.TokenHint()

View File

@@ -0,0 +1,38 @@
@model Orchard.Autoroute.ViewModels.AutoroutePartEditViewModel
@using Orchard.Autoroute
@using Orchard.Utility.Extensions;
@if(Model.Settings.DefaultPatternIndex == -1) {
<div class="message message-Error">@T("The current Content Type does not have a default Autoroute Pattern. Please edit the settings first.")</div>
return;
}
@{
var defaultPattern = Model.Settings.Patterns[Model.Settings.DefaultPatternIndex];
}
<fieldset class="permalink">
<label>@T("Permalink")</label>
@if (Model.Settings.AllowCustomPattern) {
<span>@ViewContext.RequestContext.HttpContext.Request.ToApplicationRootUrlString()/</span>
<span>@Html.TextBoxFor(m => m.CurrentUrl, new { @class = "text" })</span>
}
else {
<span>@ViewContext.RequestContext.HttpContext.Request.ToApplicationRootUrlString()/</span>
<span>@Model.CurrentUrl</span>
}
<span class="hint">@T("Leave empty to use the default permalink pattern ({0}: {1})", defaultPattern.Name, defaultPattern.Description)</span>
</fieldset>
<fieldset>
@if (AuthorizedFor(Permissions.SetHomePage) && !Model.PromoteToHomePage) {
<span class="checkbox-and-label">
@Html.EditorFor(m => m.PromoteToHomePage)
<label for="@Html.FieldIdFor(m => m.PromoteToHomePage)" class="forcheckbox">@T("Set as home page")</label>
</span>
<span class="hint">@T("Check to promote this content as the home page")</span>
}
</fieldset>

View File

@@ -0,0 +1,41 @@
<?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>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<remove name="host" />
<remove name="pages" />
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<pages pageBaseType="Orchard.Mvc.ViewEngines.Razor.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="System.Linq"/>
<add namespace="System.Collections.Generic"/>
<add namespace="Orchard.Mvc.Html"/>
</namespaces>
</pages>
</system.web.webPages.razor>
<system.web>
<compilation targetFramework="4.0">
<assemblies>
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</assemblies>
</compilation>
</system.web>
</configuration>